4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr]());
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr]());
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
220 //echild.dom.removeAttribute('xtype');
222 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 // if object has flexy:if - then it may or may not be rendered.
230 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
231 // skip a flexy if element.
232 Roo.log('skipping render');
235 // actually if flexy:foreach is found, we really want to create
236 // multiple copies here...
238 //Roo.log(this[cntr]());
239 cn.render(this[cntr]());
241 // then add the element..
248 if (typeof (tree.menu) != 'undefined') {
249 tree.menu.parentType = cn.xtype;
250 tree.menu.triggerEl = cn.el;
251 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
255 if (!tree.items || !tree.items.length) {
259 var items = tree.items;
262 //Roo.log(items.length);
264 for(var i =0;i < items.length;i++) {
265 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
286 * @class Roo.bootstrap.Body
287 * @extends Roo.bootstrap.Component
288 * Bootstrap Body class
292 * @param {Object} config The config object
295 Roo.bootstrap.Body = function(config){
296 Roo.bootstrap.Body.superclass.constructor.call(this, config);
297 this.el = Roo.get(document.body);
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
305 onRender : function(ct, position){
308 //this.el.addClass([this.fieldClass, this.cls]);
326 * @class Roo.bootstrap.ButtonGroup
327 * @extends Roo.bootstrap.Component
328 * Bootstrap ButtonGroup class
329 * @cfg {String} size lg | sm | xs (default empty normal)
330 * @cfg {String} align vertical | justified (default none)
331 * @cfg {String} direction up | down (default down)
332 * @cfg {Boolean} toolbar false | true
333 * @cfg {Boolean} btn true | false
338 * @param {Object} config The config object
341 Roo.bootstrap.ButtonGroup = function(config){
342 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
353 getAutoCreate : function(){
359 cfg.html = this.html || cfg.html;
370 if (['vertical','justified'].indexOf(this.align)!==-1) {
371 cfg.cls = 'btn-group-' + this.align;
373 if (this.align == 'justified') {
374 console.log(this.items);
378 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379 cfg.cls += ' btn-group-' + this.size;
382 if (this.direction == 'up') {
383 cfg.cls += ' dropup' ;
399 * @class Roo.bootstrap.Button
400 * @extends Roo.bootstrap.Component
401 * Bootstrap Button class
402 * @cfg {String} html The button content
403 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404 * @cfg {String} size empty | lg | sm | xs
405 * @cfg {String} tag empty | a | input | submit
406 * @cfg {String} href empty or href
407 * @cfg {Boolean} disabled false | true
408 * @cfg {Boolean} isClose false | true
409 * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410 * @cfg {String} badge text for badge
411 * @cfg {String} theme default (or empty) | glow
412 * @cfg {Boolean} inverse false | true
413 * @cfg {Boolean} toggle false | true
414 * @cfg {String} ontext text for on toggle state
415 * @cfg {String} offtext text for off toggle state
416 * @cfg {Boolean} defaulton true | false
417 * @cfg {Boolean} preventDefault (true | false) default true
418 * @cfg {Boolean} removeClass true | false remove the standard class..
419 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
422 * Create a new button
423 * @param {Object} config The config object
427 Roo.bootstrap.Button = function(config){
428 Roo.bootstrap.Button.superclass.constructor.call(this, config);
433 * When a butotn is pressed
434 * @param {Roo.EventObject} e
439 * After the button has been toggles
440 * @param {Roo.EventObject} e
441 * @param {boolean} pressed (also available as button.pressed)
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
465 preventDefault: true,
474 getAutoCreate : function(){
482 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
488 cfg.html = this.html || cfg.html;
490 if (this.toggle == true) {
493 cls: 'slider-frame roo-button',
498 'data-off-text':'OFF',
499 cls: 'slider-button',
505 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506 cfg.cls += ' '+this.weight;
515 cfg["aria-hidden"] = true;
517 cfg.html = "×";
523 if (this.theme==='default') {
524 cfg.cls = 'btn roo-button';
526 //if (this.parentType != 'Navbar') {
527 this.weight = this.weight.length ? this.weight : 'default';
529 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
531 cfg.cls += ' btn-' + this.weight;
533 } else if (this.theme==='glow') {
536 cfg.cls = 'btn-glow roo-button';
538 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
540 cfg.cls += ' ' + this.weight;
546 this.cls += ' inverse';
551 cfg.cls += ' active';
555 cfg.disabled = 'disabled';
559 Roo.log('changing to ul' );
561 this.glyphicon = 'caret';
564 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
566 //gsRoo.log(this.parentType);
567 if (this.parentType === 'Navbar' && !this.parent().bar) {
568 Roo.log('changing to li?');
577 href : this.href || '#'
580 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
581 cfg.cls += ' dropdown';
588 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
590 if (this.glyphicon) {
591 cfg.html = ' ' + cfg.html;
596 cls: 'glyphicon glyphicon-' + this.glyphicon
606 // cfg.cls='btn roo-button';
610 var value = cfg.html;
615 cls: 'glyphicon glyphicon-' + this.glyphicon,
634 cfg.cls += ' dropdown';
635 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
638 if (cfg.tag !== 'a' && this.href !== '') {
639 throw "Tag must be a to set href.";
640 } else if (this.href.length > 0) {
641 cfg.href = this.href;
644 if(this.removeClass){
649 cfg.target = this.target;
654 initEvents: function() {
655 // Roo.log('init events?');
656 // Roo.log(this.el.dom);
657 if (this.el.hasClass('roo-button')) {
658 this.el.on('click', this.onClick, this);
660 this.el.select('.roo-button').on('click', this.onClick, this);
666 onClick : function(e)
672 Roo.log('button on click ');
673 if(this.preventDefault){
676 if (this.pressed === true || this.pressed === false) {
677 this.pressed = !this.pressed;
678 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679 this.fireEvent('toggle', this, e, this.pressed);
683 this.fireEvent('click', this, e);
687 * Enables this button
691 this.disabled = false;
692 this.el.removeClass('disabled');
696 * Disable this button
700 this.disabled = true;
701 this.el.addClass('disabled');
704 * sets the active state on/off,
705 * @param {Boolean} state (optional) Force a particular state
707 setActive : function(v) {
709 this.el[v ? 'addClass' : 'removeClass']('active');
712 * toggles the current active state
714 toggleActive : function()
716 var active = this.el.hasClass('active');
717 this.setActive(!active);
734 * @class Roo.bootstrap.Column
735 * @extends Roo.bootstrap.Component
736 * Bootstrap Column class
737 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739 * @cfg {Number} md colspan out of 12 for computer-sized screens
740 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741 * @cfg {String} html content of column.
744 * Create a new Column
745 * @param {Object} config The config object
748 Roo.bootstrap.Column = function(config){
749 Roo.bootstrap.Column.superclass.constructor.call(this, config);
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
761 getAutoCreate : function(){
762 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
770 ['xs','sm','md','lg'].map(function(size){
771 if (settings[size]) {
772 cfg.cls += ' col-' + size + '-' + settings[size];
775 if (this.html.length) {
776 cfg.html = this.html;
795 * @class Roo.bootstrap.Container
796 * @extends Roo.bootstrap.Component
797 * Bootstrap Container class
798 * @cfg {Boolean} jumbotron is it a jumbotron element
799 * @cfg {String} html content of element
800 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802 * @cfg {String} header content of header (for panel)
803 * @cfg {String} footer content of footer (for panel)
804 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
807 * Create a new Container
808 * @param {Object} config The config object
811 Roo.bootstrap.Container = function(config){
812 Roo.bootstrap.Container.superclass.constructor.call(this, config);
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
825 getChildContainer : function() {
831 if (this.panel.length) {
832 return this.el.select('.panel-body',true).first();
839 getAutoCreate : function(){
845 if (this.jumbotron) {
846 cfg.cls = 'jumbotron';
849 cfg.cls = this.cls + '';
852 if (this.sticky.length) {
854 var bd = Roo.get(document.body);
855 if (!bd.hasClass('bootstrap-sticky')) {
856 bd.addClass('bootstrap-sticky');
857 Roo.select('html',true).setStyle('height', '100%');
860 cfg.cls += 'bootstrap-sticky-' + this.sticky;
864 if (this.well.length) {
868 cfg.cls +=' well well-' +this.well;
878 if (this.panel.length) {
879 cfg.cls += ' panel panel-' + this.panel;
881 if (this.header.length) {
884 cls : 'panel-heading',
900 if (this.footer.length) {
902 cls : 'panel-footer',
910 body.html = this.html || cfg.html;
912 if (!cfg.cls.length) {
913 cfg.cls = 'container';
930 * @class Roo.bootstrap.Img
931 * @extends Roo.bootstrap.Component
932 * Bootstrap Img class
933 * @cfg {Boolean} imgResponsive false | true
934 * @cfg {String} border rounded | circle | thumbnail
935 * @cfg {String} src image source
936 * @cfg {String} alt image alternative text
937 * @cfg {String} href a tag href
938 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
942 * @param {Object} config The config object
945 Roo.bootstrap.Img = function(config){
946 Roo.bootstrap.Img.superclass.constructor.call(this, config);
952 * The img click event for the img.
953 * @param {Roo.EventObject} e
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
967 getAutoCreate : function(){
971 cls: 'img-responsive',
975 cfg.html = this.html || cfg.html;
977 cfg.src = this.src || cfg.src;
979 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980 cfg.cls += ' img-' + this.border;
997 a.target = this.target;
1003 return (this.href) ? a : cfg;
1006 initEvents: function() {
1009 this.el.on('click', this.onClick, this);
1013 onClick : function(e)
1015 Roo.log('img onclick');
1016 this.fireEvent('click', this, e);
1029 * @class Roo.bootstrap.Header
1030 * @extends Roo.bootstrap.Component
1031 * Bootstrap Header class
1032 * @cfg {String} html content of header
1033 * @cfg {Number} level (1|2|3|4|5|6) default 1
1036 * Create a new Header
1037 * @param {Object} config The config object
1041 Roo.bootstrap.Header = function(config){
1042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1056 tag: 'h' + (1 *this.level),
1057 html: this.html || 'fill in html'
1069 * Ext JS Library 1.1.1
1070 * Copyright(c) 2006-2007, Ext JS, LLC.
1072 * Originally Released Under LGPL - original licence link has changed is not relivant.
1075 * <script type="text/javascript">
1079 * @class Roo.bootstrap.MenuMgr
1080 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1083 Roo.bootstrap.MenuMgr = function(){
1084 var menus, active, groups = {}, attached = false, lastShow = new Date();
1086 // private - called when first menu is created
1089 active = new Roo.util.MixedCollection();
1090 Roo.get(document).addKeyListener(27, function(){
1091 if(active.length > 0){
1099 if(active && active.length > 0){
1100 var c = active.clone();
1110 if(active.length < 1){
1111 Roo.get(document).un("mouseup", onMouseDown);
1119 var last = active.last();
1120 lastShow = new Date();
1123 Roo.get(document).on("mouseup", onMouseDown);
1128 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129 m.parentMenu.activeChild = m;
1130 }else if(last && last.isVisible()){
1131 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1136 function onBeforeHide(m){
1138 m.activeChild.hide();
1140 if(m.autoHideTimer){
1141 clearTimeout(m.autoHideTimer);
1142 delete m.autoHideTimer;
1147 function onBeforeShow(m){
1148 var pm = m.parentMenu;
1149 if(!pm && !m.allowOtherMenus){
1151 }else if(pm && pm.activeChild && active != m){
1152 pm.activeChild.hide();
1157 function onMouseDown(e){
1158 Roo.log("on MouseDown");
1159 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1167 function onBeforeCheck(mi, state){
1169 var g = groups[mi.group];
1170 for(var i = 0, l = g.length; i < l; i++){
1172 g[i].setChecked(false);
1181 * Hides all menus that are currently visible
1183 hideAll : function(){
1188 register : function(menu){
1192 menus[menu.id] = menu;
1193 menu.on("beforehide", onBeforeHide);
1194 menu.on("hide", onHide);
1195 menu.on("beforeshow", onBeforeShow);
1196 menu.on("show", onShow);
1198 if(g && menu.events["checkchange"]){
1202 groups[g].push(menu);
1203 menu.on("checkchange", onCheck);
1208 * Returns a {@link Roo.menu.Menu} object
1209 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210 * be used to generate and return a new Menu instance.
1212 get : function(menu){
1213 if(typeof menu == "string"){ // menu id
1215 }else if(menu.events){ // menu instance
1218 /*else if(typeof menu.length == 'number'){ // array of menu items?
1219 return new Roo.bootstrap.Menu({items:menu});
1220 }else{ // otherwise, must be a config
1221 return new Roo.bootstrap.Menu(menu);
1228 unregister : function(menu){
1229 delete menus[menu.id];
1230 menu.un("beforehide", onBeforeHide);
1231 menu.un("hide", onHide);
1232 menu.un("beforeshow", onBeforeShow);
1233 menu.un("show", onShow);
1235 if(g && menu.events["checkchange"]){
1236 groups[g].remove(menu);
1237 menu.un("checkchange", onCheck);
1242 registerCheckable : function(menuItem){
1243 var g = menuItem.group;
1248 groups[g].push(menuItem);
1249 menuItem.on("beforecheckchange", onBeforeCheck);
1254 unregisterCheckable : function(menuItem){
1255 var g = menuItem.group;
1257 groups[g].remove(menuItem);
1258 menuItem.un("beforecheckchange", onBeforeCheck);
1270 * @class Roo.bootstrap.Menu
1271 * @extends Roo.bootstrap.Component
1272 * Bootstrap Menu class - container for MenuItems
1273 * @cfg {String} type type of menu
1277 * @param {Object} config The config object
1281 Roo.bootstrap.Menu = function(config){
1282 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283 if (this.registerMenu) {
1284 Roo.bootstrap.MenuMgr.register(this);
1289 * Fires before this menu is displayed
1290 * @param {Roo.menu.Menu} this
1295 * Fires before this menu is hidden
1296 * @param {Roo.menu.Menu} this
1301 * Fires after this menu is displayed
1302 * @param {Roo.menu.Menu} this
1307 * Fires after this menu is hidden
1308 * @param {Roo.menu.Menu} this
1313 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314 * @param {Roo.menu.Menu} this
1315 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316 * @param {Roo.EventObject} e
1321 * Fires when the mouse is hovering over this menu
1322 * @param {Roo.menu.Menu} this
1323 * @param {Roo.EventObject} e
1324 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1329 * Fires when the mouse exits this menu
1330 * @param {Roo.menu.Menu} this
1331 * @param {Roo.EventObject} e
1332 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337 * Fires when a menu item contained in this menu is clicked
1338 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339 * @param {Roo.EventObject} e
1343 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1350 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1353 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1355 registerMenu : true,
1357 menuItems :false, // stores the menu items..
1363 getChildContainer : function() {
1367 getAutoCreate : function(){
1369 //if (['right'].indexOf(this.align)!==-1) {
1370 // cfg.cn[1].cls += ' pull-right'
1374 cls : 'dropdown-menu' ,
1375 style : 'z-index:1000'
1379 if (this.type === 'submenu') {
1380 cfg.cls = 'submenu active'
1385 initEvents : function() {
1387 // Roo.log("ADD event");
1388 // Roo.log(this.triggerEl.dom);
1389 this.triggerEl.on('click', this.onTriggerPress, this);
1390 this.triggerEl.addClass('dropdown-toggle');
1391 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1393 this.el.on("mouseover", this.onMouseOver, this);
1394 this.el.on("mouseout", this.onMouseOut, this);
1398 findTargetItem : function(e){
1399 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1403 //Roo.log(t); Roo.log(t.id);
1405 //Roo.log(this.menuitems);
1406 return this.menuitems.get(t.id);
1408 //return this.items.get(t.menuItemId);
1413 onClick : function(e){
1414 Roo.log("menu.onClick");
1415 var t = this.findTargetItem(e);
1421 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1422 if(t == this.activeItem && t.shouldDeactivate(e)){
1423 this.activeItem.deactivate();
1424 delete this.activeItem;
1428 this.setActiveItem(t, true);
1435 Roo.log('pass click event');
1439 this.fireEvent("click", this, t, e);
1443 onMouseOver : function(e){
1444 var t = this.findTargetItem(e);
1447 // if(t.canActivate && !t.disabled){
1448 // this.setActiveItem(t, true);
1452 this.fireEvent("mouseover", this, e, t);
1454 isVisible : function(){
1455 return !this.hidden;
1457 onMouseOut : function(e){
1458 var t = this.findTargetItem(e);
1461 // if(t == this.activeItem && t.shouldDeactivate(e)){
1462 // this.activeItem.deactivate();
1463 // delete this.activeItem;
1466 this.fireEvent("mouseout", this, e, t);
1471 * Displays this menu relative to another element
1472 * @param {String/HTMLElement/Roo.Element} element The element to align to
1473 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474 * the element (defaults to this.defaultAlign)
1475 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1477 show : function(el, pos, parentMenu){
1478 this.parentMenu = parentMenu;
1482 this.fireEvent("beforeshow", this);
1483 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1486 * Displays this menu at a specific xy position
1487 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1490 showAt : function(xy, parentMenu, /* private: */_e){
1491 this.parentMenu = parentMenu;
1496 this.fireEvent("beforeshow", this);
1498 //xy = this.el.adjustForConstraints(xy);
1500 //this.el.setXY(xy);
1502 this.hideMenuItems();
1503 this.hidden = false;
1504 this.triggerEl.addClass('open');
1506 this.fireEvent("show", this);
1512 this.doFocus.defer(50, this);
1516 doFocus : function(){
1518 this.focusEl.focus();
1523 * Hides this menu and optionally all parent menus
1524 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1526 hide : function(deep){
1528 this.hideMenuItems();
1529 if(this.el && this.isVisible()){
1530 this.fireEvent("beforehide", this);
1531 if(this.activeItem){
1532 this.activeItem.deactivate();
1533 this.activeItem = null;
1535 this.triggerEl.removeClass('open');;
1537 this.fireEvent("hide", this);
1539 if(deep === true && this.parentMenu){
1540 this.parentMenu.hide(true);
1544 onTriggerPress : function(e)
1547 Roo.log('trigger press');
1548 //Roo.log(e.getTarget());
1549 // Roo.log(this.triggerEl.dom);
1550 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1553 if (this.isVisible()) {
1557 this.show(this.triggerEl, false, false);
1566 hideMenuItems : function()
1568 //$(backdrop).remove()
1569 Roo.select('.open',true).each(function(aa) {
1571 aa.removeClass('open');
1572 //var parent = getParent($(this))
1573 //var relatedTarget = { relatedTarget: this }
1575 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576 //if (e.isDefaultPrevented()) return
1577 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1580 addxtypeChild : function (tree, cntr) {
1581 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1583 this.menuitems.add(comp);
1604 * @class Roo.bootstrap.MenuItem
1605 * @extends Roo.bootstrap.Component
1606 * Bootstrap MenuItem class
1607 * @cfg {String} html the menu label
1608 * @cfg {String} href the link
1609 * @cfg {Boolean} preventDefault (true | false) default true
1613 * Create a new MenuItem
1614 * @param {Object} config The config object
1618 Roo.bootstrap.MenuItem = function(config){
1619 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1624 * The raw click event for the entire grid.
1625 * @param {Roo.EventObject} e
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1635 preventDefault: true,
1637 getAutoCreate : function(){
1640 cls: 'dropdown-menu-item',
1650 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1655 initEvents: function() {
1657 //this.el.select('a').on('click', this.onClick, this);
1660 onClick : function(e)
1662 Roo.log('item on click ');
1663 //if(this.preventDefault){
1664 // e.preventDefault();
1666 //this.parent().hideMenuItems();
1668 this.fireEvent('click', this, e);
1687 * @class Roo.bootstrap.MenuSeparator
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap MenuSeparator class
1692 * Create a new MenuItem
1693 * @param {Object} config The config object
1697 Roo.bootstrap.MenuSeparator = function(config){
1698 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1703 getAutoCreate : function(){
1718 <div class="modal fade">
1719 <div class="modal-dialog">
1720 <div class="modal-content">
1721 <div class="modal-header">
1722 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1723 <h4 class="modal-title">Modal title</h4>
1725 <div class="modal-body">
1726 <p>One fine body…</p>
1728 <div class="modal-footer">
1729 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730 <button type="button" class="btn btn-primary">Save changes</button>
1732 </div><!-- /.modal-content -->
1733 </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1744 * @class Roo.bootstrap.Modal
1745 * @extends Roo.bootstrap.Component
1746 * Bootstrap Modal class
1747 * @cfg {String} title Title of dialog
1748 * @cfg {Array} buttons Array of buttons or standard button set..
1751 * Create a new Modal Dialog
1752 * @param {Object} config The config object
1755 Roo.bootstrap.Modal = function(config){
1756 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1761 * The raw btnclick event for the button
1762 * @param {Roo.EventObject} e
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1770 title : 'test dialog',
1774 onRender : function(ct, position)
1776 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1779 var cfg = Roo.apply({}, this.getAutoCreate());
1782 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1784 //if (!cfg.name.length) {
1788 cfg.cls += ' ' + this.cls;
1791 cfg.style = this.style;
1793 this.el = Roo.get(document.body).createChild(cfg, position);
1795 //var type = this.el.dom.type;
1797 if(this.tabIndex !== undefined){
1798 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1803 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804 this.maskEl.enableDisplayMode("block");
1806 //this.el.addClass("x-dlg-modal");
1809 Roo.each(this.buttons, function(bb) {
1810 b = Roo.apply({}, bb);
1811 b.xns = b.xns || Roo.bootstrap;
1812 b.xtype = b.xtype || 'Button';
1813 if (typeof(b.listeners) == 'undefined') {
1814 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1817 var btn = Roo.factory(b);
1819 btn.onRender(this.el.select('.modal-footer').first());
1823 // render the children.
1826 if(typeof(this.items) != 'undefined'){
1827 var items = this.items;
1830 for(var i =0;i < items.length;i++) {
1831 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1835 this.items = nitems;
1837 //this.el.addClass([this.fieldClass, this.cls]);
1840 getAutoCreate : function(){
1845 html : this.html || ''
1853 cls: "modal-dialog",
1856 cls : "modal-content",
1859 cls : 'modal-header',
1868 cls : 'modal-title',
1876 cls : 'modal-footer'
1892 getChildContainer : function() {
1894 return this.el.select('.modal-body',true).first();
1897 getButtonContainer : function() {
1898 return this.el.select('.modal-footer',true).first();
1901 initEvents : function()
1903 this.el.select('.modal-header .close').on('click', this.hide, this);
1905 // this.addxtype(this);
1909 if (!this.rendered) {
1913 this.el.addClass('on');
1914 this.el.removeClass('fade');
1915 this.el.setStyle('display', 'block');
1916 Roo.get(document.body).addClass("x-body-masked");
1917 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1919 this.el.setStyle('zIndex', '10001');
1920 this.fireEvent('show', this);
1926 Roo.log('Modal hide?!');
1928 Roo.get(document.body).removeClass("x-body-masked");
1929 this.el.removeClass('on');
1930 this.el.addClass('fade');
1931 this.el.setStyle('display', 'none');
1932 this.fireEvent('hide', this);
1934 onButtonClick: function(btn,e)
1937 this.fireEvent('btnclick', btn.name, e);
1942 Roo.apply(Roo.bootstrap.Modal, {
1944 * Button config that displays a single OK button
1953 * Button config that displays Yes and No buttons
1969 * Button config that displays OK and Cancel buttons
1984 * Button config that displays Yes, No and Cancel buttons
2011 * @class Roo.bootstrap.Navbar
2012 * @extends Roo.bootstrap.Component
2013 * Bootstrap Navbar class
2014 * @cfg {Boolean} sidebar has side bar
2015 * @cfg {Boolean} bar is a bar?
2016 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017 * @cfg {String} brand what is brand
2018 * @cfg {Boolean} inverse is inverted color
2019 * @cfg {String} type (nav | pills | tabs)
2020 * @cfg {Boolean} arrangement stacked | justified
2021 * @cfg {String} align (left | right) alignment
2022 * @cfg {String} brand_href href of the brand
2023 * @cfg {Boolean} main (true|false) main nav bar? default false
2024 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2028 * Create a new Navbar
2029 * @param {Object} config The config object
2033 Roo.bootstrap.Navbar = function(config){
2034 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2052 getAutoCreate : function(){
2057 if (this.sidebar === true) {
2065 if (this.bar === true) {
2073 cls: 'navbar-header',
2078 cls: 'navbar-toggle',
2079 'data-toggle': 'collapse',
2084 html: 'Toggle navigation'
2104 cls: 'collapse navbar-collapse'
2109 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2111 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112 cfg.cls += ' navbar-' + this.position;
2113 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2116 if (this.brand !== '') {
2119 href: this.brand_href ? this.brand_href : '#',
2120 cls: 'navbar-brand',
2128 cfg.cls += ' main-nav';
2134 } else if (this.bar === false) {
2137 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2147 if (['tabs','pills'].indexOf(this.type)!==-1) {
2148 cfg.cn[0].cls += ' nav-' + this.type
2150 if (this.type!=='nav') {
2151 Roo.log('nav type must be nav/tabs/pills')
2153 cfg.cn[0].cls += ' navbar-nav'
2156 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157 cfg.cn[0].cls += ' nav-' + this.arrangement;
2160 if (this.align === 'right') {
2161 cfg.cn[0].cls += ' navbar-right';
2164 cfg.cls += ' navbar-inverse';
2172 initEvents :function ()
2174 //Roo.log(this.el.select('.navbar-toggle',true));
2175 this.el.select('.navbar-toggle',true).on('click', function() {
2176 // Roo.log('click');
2177 this.el.select('.navbar-collapse',true).toggleClass('in');
2185 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2187 var size = this.el.getSize();
2188 this.maskEl.setSize(size.width, size.height);
2189 this.maskEl.enableDisplayMode("block");
2198 getChildContainer : function()
2200 if (this.bar === true) {
2201 return this.el.select('.collapse',true).first();
2229 * @class Roo.bootstrap.NavGroup
2230 * @extends Roo.bootstrap.Component
2231 * Bootstrap NavGroup class
2232 * @cfg {String} align left | right
2233 * @cfg {Boolean} inverse false | true
2234 * @cfg {String} type (nav|pills|tab) default nav
2237 * Create a new nav group
2238 * @param {Object} config The config object
2241 Roo.bootstrap.NavGroup = function(config){
2242 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2252 getAutoCreate : function(){
2253 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2260 if (['tabs','pills'].indexOf(this.type)!==-1) {
2261 cfg.cls += ' nav-' + this.type
2263 if (this.type!=='nav') {
2264 Roo.log('nav type must be nav/tabs/pills')
2266 cfg.cls += ' navbar-nav'
2269 if (this.parent().sidebar === true) {
2272 cls: 'dashboard-menu'
2278 if (this.form === true) {
2284 if (this.align === 'right') {
2285 cfg.cls += ' navbar-right';
2287 cfg.cls += ' navbar-left';
2291 if (this.align === 'right') {
2292 cfg.cls += ' navbar-right';
2296 cfg.cls += ' navbar-inverse';
2316 * @class Roo.bootstrap.Navbar.Item
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap Navbar.Button class
2319 * @cfg {String} href link to
2320 * @cfg {String} html content of button
2321 * @cfg {String} badge text inside badge
2322 * @cfg {String} glyphicon name of glyphicon
2323 * @cfg {String} icon name of font awesome icon
2324 * @cfg {Boolena} active Is item active
2325 * @cfg {Boolean} preventDefault (true | false) default false
2328 * Create a new Navbar Button
2329 * @param {Object} config The config object
2331 Roo.bootstrap.Navbar.Item = function(config){
2332 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2337 * The raw click event for the entire grid.
2338 * @param {Roo.EventObject} e
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2353 preventDefault : false,
2355 getAutoCreate : function(){
2357 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2359 if (this.parent().parent().sidebar === true) {
2372 cfg.cn[0].html = this.html;
2376 this.cls += ' active';
2380 cfg.cn[0].cls += ' dropdown-toggle';
2381 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2385 cfg.cn[0].tag = 'a',
2386 cfg.cn[0].href = this.href;
2389 if (this.glyphicon) {
2390 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2394 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2406 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2416 if (this.glyphicon) {
2417 if(cfg.html){cfg.html = ' ' + this.html};
2421 cls: 'glyphicon glyphicon-' + this.glyphicon
2426 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2431 cfg.cn[0].html += " <span class='caret'></span>";
2432 //}else if (!this.href) {
2433 // cfg.cn[0].tag='p';
2434 // cfg.cn[0].cls='navbar-text';
2437 cfg.cn[0].href=this.href||'#';
2438 cfg.cn[0].html=this.html;
2441 if (this.badge !== '') {
2444 cfg.cn[0].html + ' ',
2455 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2460 initEvents: function() {
2461 // Roo.log('init events?');
2462 // Roo.log(this.el.dom);
2463 this.el.select('a',true).on('click', this.onClick, this);
2466 onClick : function(e)
2468 if(this.preventDefault){
2472 if(this.fireEvent('click', this, e) === false){
2476 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477 this.onTabsClick(e);
2481 onTabsClick : function(e)
2483 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484 v.removeClass('active');
2487 this.el.addClass('active');
2489 if(this.href && this.href.substring(0,1) == '#'){
2490 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2492 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493 v.removeClass('active');
2496 tab.addClass('active');
2511 * @class Roo.bootstrap.Row
2512 * @extends Roo.bootstrap.Component
2513 * Bootstrap Row class (contains columns...)
2517 * @param {Object} config The config object
2520 Roo.bootstrap.Row = function(config){
2521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2526 getAutoCreate : function(){
2545 * @class Roo.bootstrap.Element
2546 * @extends Roo.bootstrap.Component
2547 * Bootstrap Element class
2548 * @cfg {String} html contents of the element
2549 * @cfg {String} tag tag of the element
2550 * @cfg {String} cls class of the element
2553 * Create a new Element
2554 * @param {Object} config The config object
2557 Roo.bootstrap.Element = function(config){
2558 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2568 getAutoCreate : function(){
2593 * @class Roo.bootstrap.Pagination
2594 * @extends Roo.bootstrap.Component
2595 * Bootstrap Pagination class
2596 * @cfg {String} size xs | sm | md | lg
2597 * @cfg {Boolean} inverse false | true
2600 * Create a new Pagination
2601 * @param {Object} config The config object
2604 Roo.bootstrap.Pagination = function(config){
2605 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2614 getAutoCreate : function(){
2620 cfg.cls += ' inverse';
2626 cfg.cls += " " + this.cls;
2644 * @class Roo.bootstrap.PaginationItem
2645 * @extends Roo.bootstrap.Component
2646 * Bootstrap PaginationItem class
2647 * @cfg {String} html text
2648 * @cfg {String} href the link
2649 * @cfg {Boolean} preventDefault (true | false) default true
2650 * @cfg {Boolean} active (true | false) default false
2654 * Create a new PaginationItem
2655 * @param {Object} config The config object
2659 Roo.bootstrap.PaginationItem = function(config){
2660 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2665 * The raw click event for the entire grid.
2666 * @param {Roo.EventObject} e
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2676 preventDefault: true,
2680 getAutoCreate : function(){
2686 href : this.href ? this.href : '#',
2687 html : this.html ? this.html : ''
2697 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2703 initEvents: function() {
2705 this.el.on('click', this.onClick, this);
2708 onClick : function(e)
2710 Roo.log('PaginationItem on click ');
2711 if(this.preventDefault){
2715 this.fireEvent('click', this, e);
2731 * @class Roo.bootstrap.Slider
2732 * @extends Roo.bootstrap.Component
2733 * Bootstrap Slider class
2736 * Create a new Slider
2737 * @param {Object} config The config object
2740 Roo.bootstrap.Slider = function(config){
2741 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2746 getAutoCreate : function(){
2750 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2754 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2772 * @class Roo.bootstrap.Table
2773 * @extends Roo.bootstrap.Component
2774 * Bootstrap Table class
2775 * @cfg {String} cls table class
2776 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777 * @cfg {String} bgcolor Specifies the background color for a table
2778 * @cfg {Number} border Specifies whether the table cells should have borders or not
2779 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780 * @cfg {Number} cellspacing Specifies the space between cells
2781 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783 * @cfg {String} sortable Specifies that the table should be sortable
2784 * @cfg {String} summary Specifies a summary of the content of a table
2785 * @cfg {Number} width Specifies the width of a table
2787 * @cfg {boolean} striped Should the rows be alternative striped
2788 * @cfg {boolean} bordered Add borders to the table
2789 * @cfg {boolean} hover Add hover highlighting
2790 * @cfg {boolean} condensed Format condensed
2791 * @cfg {boolean} responsive Format condensed
2797 * Create a new Table
2798 * @param {Object} config The config object
2801 Roo.bootstrap.Table = function(config){
2802 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2805 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806 this.sm = this.selModel;
2807 this.sm.xmodule = this.xmodule || false;
2809 if (this.cm && typeof(this.cm.config) == 'undefined') {
2810 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811 this.cm = this.colModel;
2812 this.cm.xmodule = this.xmodule || false;
2815 this.store= Roo.factory(this.store, Roo.data);
2816 this.ds = this.store;
2817 this.ds.xmodule = this.xmodule || false;
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2844 getAutoCreate : function(){
2845 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2854 cfg.cls += ' table-striped';
2857 cfg.cls += ' table-hover';
2859 if (this.bordered) {
2860 cfg.cls += ' table-bordered';
2862 if (this.condensed) {
2863 cfg.cls += ' table-condensed';
2865 if (this.responsive) {
2866 cfg.cls += ' table-responsive';
2873 cfg.cls+= ' ' +this.cls;
2876 // this lot should be simplifed...
2879 cfg.align=this.align;
2882 cfg.bgcolor=this.bgcolor;
2885 cfg.border=this.border;
2887 if (this.cellpadding) {
2888 cfg.cellpadding=this.cellpadding;
2890 if (this.cellspacing) {
2891 cfg.cellspacing=this.cellspacing;
2894 cfg.frame=this.frame;
2897 cfg.rules=this.rules;
2899 if (this.sortable) {
2900 cfg.sortable=this.sortable;
2903 cfg.summary=this.summary;
2906 cfg.width=this.width;
2909 if(this.store || this.cm){
2910 cfg.cn.push(this.renderHeader());
2911 cfg.cn.push(this.renderBody());
2912 cfg.cn.push(this.renderFooter());
2914 cfg.cls+= ' TableGrid';
2920 // initTableGrid : function()
2929 // var cm = this.cm;
2931 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2934 // html: cm.getColumnHeader(i)
2938 // cfg.push(header);
2945 initEvents : function()
2947 if(!this.store || !this.cm){
2951 Roo.log('initEvents with ds!!!!');
2953 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2954 // this.maskEl.enableDisplayMode("block");
2955 // this.maskEl.show();
2957 this.store.on('load', this.onLoad, this);
2958 this.store.on('beforeload', this.onBeforeLoad, this);
2966 renderHeader : function()
2975 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2976 var config = cm.config[i];
2977 Roo.log('header!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
2981 html: cm.getColumnHeader(i)
2988 renderBody : function()
2998 renderFooter : function()
3010 Roo.log('ds onload');
3014 var tbody = this.el.select('tbody', true).first();
3018 if(this.store.getCount() > 0){
3019 this.store.data.each(function(d){
3025 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3026 var renderer = cm.getRenderer(i);
3027 var config = cm.config[i];
3031 if(typeof(renderer) !== 'undefined'){
3032 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3035 if(typeof(value) === 'object'){
3045 html: (typeof(value) === 'object') ? '' : value
3048 if(typeof(config.width) != 'undefined'){
3049 td.width = config.width;
3056 tbody.createChild(row);
3064 Roo.each(renders, function(r){
3065 _this.renderColumn(r);
3069 // if(this.loadMask){
3070 // this.maskEl.hide();
3074 onBeforeLoad : function()
3076 Roo.log('ds onBeforeLoad');
3080 // if(this.loadMask){
3081 // this.maskEl.show();
3087 this.el.select('tbody', true).first().dom.innerHTML = '';
3090 getSelectionModel : function(){
3092 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3094 return this.selModel;
3097 renderColumn : function(r)
3100 r.cfg.render(Roo.get(r.id));
3103 Roo.each(r.cfg.cn, function(c){
3108 _this.renderColumn(child);
3125 * @class Roo.bootstrap.TableCell
3126 * @extends Roo.bootstrap.Component
3127 * Bootstrap TableCell class
3128 * @cfg {String} html cell contain text
3129 * @cfg {String} cls cell class
3130 * @cfg {String} tag cell tag (td|th) default td
3131 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3132 * @cfg {String} align Aligns the content in a cell
3133 * @cfg {String} axis Categorizes cells
3134 * @cfg {String} bgcolor Specifies the background color of a cell
3135 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3136 * @cfg {Number} colspan Specifies the number of columns a cell should span
3137 * @cfg {String} headers Specifies one or more header cells a cell is related to
3138 * @cfg {Number} height Sets the height of a cell
3139 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3140 * @cfg {Number} rowspan Sets the number of rows a cell should span
3141 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3142 * @cfg {String} valign Vertical aligns the content in a cell
3143 * @cfg {Number} width Specifies the width of a cell
3146 * Create a new TableCell
3147 * @param {Object} config The config object
3150 Roo.bootstrap.TableCell = function(config){
3151 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3154 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3174 getAutoCreate : function(){
3175 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3195 cfg.align=this.align
3201 cfg.bgcolor=this.bgcolor
3204 cfg.charoff=this.charoff
3207 cfg.colspan=this.colspan
3210 cfg.headers=this.headers
3213 cfg.height=this.height
3216 cfg.nowrap=this.nowrap
3219 cfg.rowspan=this.rowspan
3222 cfg.scope=this.scope
3225 cfg.valign=this.valign
3228 cfg.width=this.width
3247 * @class Roo.bootstrap.TableRow
3248 * @extends Roo.bootstrap.Component
3249 * Bootstrap TableRow class
3250 * @cfg {String} cls row class
3251 * @cfg {String} align Aligns the content in a table row
3252 * @cfg {String} bgcolor Specifies a background color for a table row
3253 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3254 * @cfg {String} valign Vertical aligns the content in a table row
3257 * Create a new TableRow
3258 * @param {Object} config The config object
3261 Roo.bootstrap.TableRow = function(config){
3262 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3265 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3273 getAutoCreate : function(){
3274 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3284 cfg.align = this.align;
3287 cfg.bgcolor = this.bgcolor;
3290 cfg.charoff = this.charoff;
3293 cfg.valign = this.valign;
3311 * @class Roo.bootstrap.TableBody
3312 * @extends Roo.bootstrap.Component
3313 * Bootstrap TableBody class
3314 * @cfg {String} cls element class
3315 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3316 * @cfg {String} align Aligns the content inside the element
3317 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3318 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3321 * Create a new TableBody
3322 * @param {Object} config The config object
3325 Roo.bootstrap.TableBody = function(config){
3326 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3329 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3337 getAutoCreate : function(){
3338 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3352 cfg.align = this.align;
3355 cfg.charoff = this.charoff;
3358 cfg.valign = this.valign;
3365 // initEvents : function()
3372 // this.store = Roo.factory(this.store, Roo.data);
3373 // this.store.on('load', this.onLoad, this);
3375 // this.store.load();
3379 // onLoad: function ()
3381 // this.fireEvent('load', this);
3391 * Ext JS Library 1.1.1
3392 * Copyright(c) 2006-2007, Ext JS, LLC.
3394 * Originally Released Under LGPL - original licence link has changed is not relivant.
3397 * <script type="text/javascript">
3400 // as we use this in bootstrap.
3401 Roo.namespace('Roo.form');
3403 * @class Roo.form.Action
3404 * Internal Class used to handle form actions
3406 * @param {Roo.form.BasicForm} el The form element or its id
3407 * @param {Object} config Configuration options
3412 // define the action interface
3413 Roo.form.Action = function(form, options){
3415 this.options = options || {};
3418 * Client Validation Failed
3421 Roo.form.Action.CLIENT_INVALID = 'client';
3423 * Server Validation Failed
3426 Roo.form.Action.SERVER_INVALID = 'server';
3428 * Connect to Server Failed
3431 Roo.form.Action.CONNECT_FAILURE = 'connect';
3433 * Reading Data from Server Failed
3436 Roo.form.Action.LOAD_FAILURE = 'load';
3438 Roo.form.Action.prototype = {
3440 failureType : undefined,
3441 response : undefined,
3445 run : function(options){
3450 success : function(response){
3455 handleResponse : function(response){
3459 // default connection failure
3460 failure : function(response){
3462 this.response = response;
3463 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3464 this.form.afterAction(this, false);
3467 processResponse : function(response){
3468 this.response = response;
3469 if(!response.responseText){
3472 this.result = this.handleResponse(response);
3476 // utility functions used internally
3477 getUrl : function(appendParams){
3478 var url = this.options.url || this.form.url || this.form.el.dom.action;
3480 var p = this.getParams();
3482 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3488 getMethod : function(){
3489 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3492 getParams : function(){
3493 var bp = this.form.baseParams;
3494 var p = this.options.params;
3496 if(typeof p == "object"){
3497 p = Roo.urlEncode(Roo.applyIf(p, bp));
3498 }else if(typeof p == 'string' && bp){
3499 p += '&' + Roo.urlEncode(bp);
3502 p = Roo.urlEncode(bp);
3507 createCallback : function(){
3509 success: this.success,
3510 failure: this.failure,
3512 timeout: (this.form.timeout*1000),
3513 upload: this.form.fileUpload ? this.success : undefined
3518 Roo.form.Action.Submit = function(form, options){
3519 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3522 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3525 haveProgress : false,
3526 uploadComplete : false,
3528 // uploadProgress indicator.
3529 uploadProgress : function()
3531 if (!this.form.progressUrl) {
3535 if (!this.haveProgress) {
3536 Roo.MessageBox.progress("Uploading", "Uploading");
3538 if (this.uploadComplete) {
3539 Roo.MessageBox.hide();
3543 this.haveProgress = true;
3545 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3547 var c = new Roo.data.Connection();
3549 url : this.form.progressUrl,
3554 success : function(req){
3555 //console.log(data);
3559 rdata = Roo.decode(req.responseText)
3561 Roo.log("Invalid data from server..");
3565 if (!rdata || !rdata.success) {
3567 Roo.MessageBox.alert(Roo.encode(rdata));
3570 var data = rdata.data;
3572 if (this.uploadComplete) {
3573 Roo.MessageBox.hide();
3578 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3579 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3582 this.uploadProgress.defer(2000,this);
3585 failure: function(data) {
3586 Roo.log('progress url failed ');
3597 // run get Values on the form, so it syncs any secondary forms.
3598 this.form.getValues();
3600 var o = this.options;
3601 var method = this.getMethod();
3602 var isPost = method == 'POST';
3603 if(o.clientValidation === false || this.form.isValid()){
3605 if (this.form.progressUrl) {
3606 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3607 (new Date() * 1) + '' + Math.random());
3612 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3613 form:this.form.el.dom,
3614 url:this.getUrl(!isPost),
3616 params:isPost ? this.getParams() : null,
3617 isUpload: this.form.fileUpload
3620 this.uploadProgress();
3622 }else if (o.clientValidation !== false){ // client validation failed
3623 this.failureType = Roo.form.Action.CLIENT_INVALID;
3624 this.form.afterAction(this, false);
3628 success : function(response)
3630 this.uploadComplete= true;
3631 if (this.haveProgress) {
3632 Roo.MessageBox.hide();
3636 var result = this.processResponse(response);
3637 if(result === true || result.success){
3638 this.form.afterAction(this, true);
3642 this.form.markInvalid(result.errors);
3643 this.failureType = Roo.form.Action.SERVER_INVALID;
3645 this.form.afterAction(this, false);
3647 failure : function(response)
3649 this.uploadComplete= true;
3650 if (this.haveProgress) {
3651 Roo.MessageBox.hide();
3654 this.response = response;
3655 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3656 this.form.afterAction(this, false);
3659 handleResponse : function(response){
3660 if(this.form.errorReader){
3661 var rs = this.form.errorReader.read(response);
3664 for(var i = 0, len = rs.records.length; i < len; i++) {
3665 var r = rs.records[i];
3669 if(errors.length < 1){
3673 success : rs.success,
3679 ret = Roo.decode(response.responseText);
3683 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3693 Roo.form.Action.Load = function(form, options){
3694 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3695 this.reader = this.form.reader;
3698 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3703 Roo.Ajax.request(Roo.apply(
3704 this.createCallback(), {
3705 method:this.getMethod(),
3706 url:this.getUrl(false),
3707 params:this.getParams()
3711 success : function(response){
3713 var result = this.processResponse(response);
3714 if(result === true || !result.success || !result.data){
3715 this.failureType = Roo.form.Action.LOAD_FAILURE;
3716 this.form.afterAction(this, false);
3719 this.form.clearInvalid();
3720 this.form.setValues(result.data);
3721 this.form.afterAction(this, true);
3724 handleResponse : function(response){
3725 if(this.form.reader){
3726 var rs = this.form.reader.read(response);
3727 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3729 success : rs.success,
3733 return Roo.decode(response.responseText);
3737 Roo.form.Action.ACTION_TYPES = {
3738 'load' : Roo.form.Action.Load,
3739 'submit' : Roo.form.Action.Submit
3748 * @class Roo.bootstrap.Form
3749 * @extends Roo.bootstrap.Component
3750 * Bootstrap Form class
3751 * @cfg {String} method GET | POST (default POST)
3752 * @cfg {String} labelAlign top | left (default top)
3753 * @cfg {String} align left | right - for navbars
3758 * @param {Object} config The config object
3762 Roo.bootstrap.Form = function(config){
3763 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3766 * @event clientvalidation
3767 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3768 * @param {Form} this
3769 * @param {Boolean} valid true if the form has passed client-side validation
3771 clientvalidation: true,
3773 * @event beforeaction
3774 * Fires before any action is performed. Return false to cancel the action.
3775 * @param {Form} this
3776 * @param {Action} action The action to be performed
3780 * @event actionfailed
3781 * Fires when an action fails.
3782 * @param {Form} this
3783 * @param {Action} action The action that failed
3785 actionfailed : true,
3787 * @event actioncomplete
3788 * Fires when an action is completed.
3789 * @param {Form} this
3790 * @param {Action} action The action that completed
3792 actioncomplete : true
3797 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3800 * @cfg {String} method
3801 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3806 * The URL to use for form actions if one isn't supplied in the action options.
3809 * @cfg {Boolean} fileUpload
3810 * Set to true if this form is a file upload.
3814 * @cfg {Object} baseParams
3815 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3819 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3823 * @cfg {Sting} align (left|right) for navbar forms
3828 activeAction : null,
3831 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3832 * element by passing it or its id or mask the form itself by passing in true.
3835 waitMsgTarget : false,
3840 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3841 * element by passing it or its id or mask the form itself by passing in true.
3845 getAutoCreate : function(){
3849 method : this.method || 'POST',
3850 id : this.id || Roo.id(),
3853 if (this.parent().xtype.match(/^Nav/)) {
3854 cfg.cls = 'navbar-form navbar-' + this.align;
3858 if (this.labelAlign == 'left' ) {
3859 cfg.cls += ' form-horizontal';
3865 initEvents : function()
3867 this.el.on('submit', this.onSubmit, this);
3872 onSubmit : function(e){
3877 * Returns true if client-side validation on the form is successful.
3880 isValid : function(){
3881 var items = this.getItems();
3883 items.each(function(f){
3892 * Returns true if any fields in this form have changed since their original load.
3895 isDirty : function(){
3897 var items = this.getItems();
3898 items.each(function(f){
3908 * Performs a predefined action (submit or load) or custom actions you define on this form.
3909 * @param {String} actionName The name of the action type
3910 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3911 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3912 * accept other config options):
3914 Property Type Description
3915 ---------------- --------------- ----------------------------------------------------------------------------------
3916 url String The url for the action (defaults to the form's url)
3917 method String The form method to use (defaults to the form's method, or POST if not defined)
3918 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3919 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3920 validate the form on the client (defaults to false)
3922 * @return {BasicForm} this
3924 doAction : function(action, options){
3925 if(typeof action == 'string'){
3926 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3928 if(this.fireEvent('beforeaction', this, action) !== false){
3929 this.beforeAction(action);
3930 action.run.defer(100, action);
3936 beforeAction : function(action){
3937 var o = action.options;
3939 // not really supported yet.. ??
3941 //if(this.waitMsgTarget === true){
3942 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3943 //}else if(this.waitMsgTarget){
3944 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3945 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3947 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3953 afterAction : function(action, success){
3954 this.activeAction = null;
3955 var o = action.options;
3957 //if(this.waitMsgTarget === true){
3959 //}else if(this.waitMsgTarget){
3960 // this.waitMsgTarget.unmask();
3962 // Roo.MessageBox.updateProgress(1);
3963 // Roo.MessageBox.hide();
3970 Roo.callback(o.success, o.scope, [this, action]);
3971 this.fireEvent('actioncomplete', this, action);
3975 // failure condition..
3976 // we have a scenario where updates need confirming.
3977 // eg. if a locking scenario exists..
3978 // we look for { errors : { needs_confirm : true }} in the response.
3980 (typeof(action.result) != 'undefined') &&
3981 (typeof(action.result.errors) != 'undefined') &&
3982 (typeof(action.result.errors.needs_confirm) != 'undefined')
3985 Roo.log("not supported yet");
3988 Roo.MessageBox.confirm(
3989 "Change requires confirmation",
3990 action.result.errorMsg,
3995 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4005 Roo.callback(o.failure, o.scope, [this, action]);
4006 // show an error message if no failed handler is set..
4007 if (!this.hasListener('actionfailed')) {
4008 Roo.log("need to add dialog support");
4010 Roo.MessageBox.alert("Error",
4011 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4012 action.result.errorMsg :
4013 "Saving Failed, please check your entries or try again"
4018 this.fireEvent('actionfailed', this, action);
4023 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4024 * @param {String} id The value to search for
4027 findField : function(id){
4028 var items = this.getItems();
4029 var field = items.get(id);
4031 items.each(function(f){
4032 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4039 return field || null;
4042 * Mark fields in this form invalid in bulk.
4043 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4044 * @return {BasicForm} this
4046 markInvalid : function(errors){
4047 if(errors instanceof Array){
4048 for(var i = 0, len = errors.length; i < len; i++){
4049 var fieldError = errors[i];
4050 var f = this.findField(fieldError.id);
4052 f.markInvalid(fieldError.msg);
4058 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4059 field.markInvalid(errors[id]);
4063 //Roo.each(this.childForms || [], function (f) {
4064 // f.markInvalid(errors);
4071 * Set values for fields in this form in bulk.
4072 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4073 * @return {BasicForm} this
4075 setValues : function(values){
4076 if(values instanceof Array){ // array of objects
4077 for(var i = 0, len = values.length; i < len; i++){
4079 var f = this.findField(v.id);
4081 f.setValue(v.value);
4082 if(this.trackResetOnLoad){
4083 f.originalValue = f.getValue();
4087 }else{ // object hash
4090 if(typeof values[id] != 'function' && (field = this.findField(id))){
4092 if (field.setFromData &&
4094 field.displayField &&
4095 // combos' with local stores can
4096 // be queried via setValue()
4097 // to set their value..
4098 (field.store && !field.store.isLocal)
4102 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4103 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4104 field.setFromData(sd);
4107 field.setValue(values[id]);
4111 if(this.trackResetOnLoad){
4112 field.originalValue = field.getValue();
4118 //Roo.each(this.childForms || [], function (f) {
4119 // f.setValues(values);
4126 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4127 * they are returned as an array.
4128 * @param {Boolean} asString
4131 getValues : function(asString){
4132 //if (this.childForms) {
4133 // copy values from the child forms
4134 // Roo.each(this.childForms, function (f) {
4135 // this.setValues(f.getValues());
4141 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4142 if(asString === true){
4145 return Roo.urlDecode(fs);
4149 * Returns the fields in this form as an object with key/value pairs.
4150 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4153 getFieldValues : function(with_hidden)
4155 var items = this.getItems();
4157 items.each(function(f){
4161 var v = f.getValue();
4162 if (f.inputType =='radio') {
4163 if (typeof(ret[f.getName()]) == 'undefined') {
4164 ret[f.getName()] = ''; // empty..
4167 if (!f.el.dom.checked) {
4175 // not sure if this supported any more..
4176 if ((typeof(v) == 'object') && f.getRawValue) {
4177 v = f.getRawValue() ; // dates..
4179 // combo boxes where name != hiddenName...
4180 if (f.name != f.getName()) {
4181 ret[f.name] = f.getRawValue();
4183 ret[f.getName()] = v;
4190 * Clears all invalid messages in this form.
4191 * @return {BasicForm} this
4193 clearInvalid : function(){
4194 var items = this.getItems();
4196 items.each(function(f){
4207 * @return {BasicForm} this
4210 var items = this.getItems();
4211 items.each(function(f){
4215 Roo.each(this.childForms || [], function (f) {
4222 getItems : function()
4224 var r=new Roo.util.MixedCollection(false, function(o){
4225 return o.id || (o.id = Roo.id());
4227 var iter = function(el) {
4234 Roo.each(el.items,function(e) {
4253 * Ext JS Library 1.1.1
4254 * Copyright(c) 2006-2007, Ext JS, LLC.
4256 * Originally Released Under LGPL - original licence link has changed is not relivant.
4259 * <script type="text/javascript">
4262 * @class Roo.form.VTypes
4263 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4266 Roo.form.VTypes = function(){
4267 // closure these in so they are only created once.
4268 var alpha = /^[a-zA-Z_]+$/;
4269 var alphanum = /^[a-zA-Z0-9_]+$/;
4270 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4271 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4273 // All these messages and functions are configurable
4276 * The function used to validate email addresses
4277 * @param {String} value The email address
4279 'email' : function(v){
4280 return email.test(v);
4283 * The error text to display when the email validation function returns false
4286 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4288 * The keystroke filter mask to be applied on email input
4291 'emailMask' : /[a-z0-9_\.\-@]/i,
4294 * The function used to validate URLs
4295 * @param {String} value The URL
4297 'url' : function(v){
4301 * The error text to display when the url validation function returns false
4304 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4307 * The function used to validate alpha values
4308 * @param {String} value The value
4310 'alpha' : function(v){
4311 return alpha.test(v);
4314 * The error text to display when the alpha validation function returns false
4317 'alphaText' : 'This field should only contain letters and _',
4319 * The keystroke filter mask to be applied on alpha input
4322 'alphaMask' : /[a-z_]/i,
4325 * The function used to validate alphanumeric values
4326 * @param {String} value The value
4328 'alphanum' : function(v){
4329 return alphanum.test(v);
4332 * The error text to display when the alphanumeric validation function returns false
4335 'alphanumText' : 'This field should only contain letters, numbers and _',
4337 * The keystroke filter mask to be applied on alphanumeric input
4340 'alphanumMask' : /[a-z0-9_]/i
4350 * @class Roo.bootstrap.Input
4351 * @extends Roo.bootstrap.Component
4352 * Bootstrap Input class
4353 * @cfg {Boolean} disabled is it disabled
4354 * @cfg {String} fieldLabel - the label associated
4355 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4356 * @cfg {String} name name of the input
4357 * @cfg {string} fieldLabel - the label associated
4358 * @cfg {string} inputType - input / file submit ...
4359 * @cfg {string} placeholder - placeholder to put in text.
4360 * @cfg {string} before - input group add on before
4361 * @cfg {string} after - input group add on after
4362 * @cfg {string} size - (lg|sm) or leave empty..
4363 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4364 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4365 * @cfg {Number} md colspan out of 12 for computer-sized screens
4366 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4367 * @cfg {string} value default value of the input
4368 * @cfg {Number} labelWidth set the width of label (0-12)
4369 * @cfg {String} labelAlign (top|left)
4370 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4374 * Create a new Input
4375 * @param {Object} config The config object
4378 Roo.bootstrap.Input = function(config){
4379 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4384 * Fires when this field receives input focus.
4385 * @param {Roo.form.Field} this
4390 * Fires when this field loses input focus.
4391 * @param {Roo.form.Field} this
4396 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4397 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4398 * @param {Roo.form.Field} this
4399 * @param {Roo.EventObject} e The event object
4404 * Fires just before the field blurs if the field value has changed.
4405 * @param {Roo.form.Field} this
4406 * @param {Mixed} newValue The new value
4407 * @param {Mixed} oldValue The original value
4412 * Fires after the field has been marked as invalid.
4413 * @param {Roo.form.Field} this
4414 * @param {String} msg The validation message
4419 * Fires after the field has been validated with no errors.
4420 * @param {Roo.form.Field} this
4425 * Fires after the key up
4426 * @param {Roo.form.Field} this
4427 * @param {Roo.EventObject} e The event Object
4433 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4435 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4436 automatic validation (defaults to "keyup").
4438 validationEvent : "keyup",
4440 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4442 validateOnBlur : true,
4444 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4446 validationDelay : 250,
4448 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4450 focusClass : "x-form-focus", // not needed???
4454 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4456 invalidClass : "has-error",
4459 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4461 selectOnFocus : false,
4464 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4468 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4473 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4475 disableKeyFilter : false,
4478 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4482 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4486 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4488 blankText : "This field is required",
4491 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4495 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4497 maxLength : Number.MAX_VALUE,
4499 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4501 minLengthText : "The minimum length for this field is {0}",
4503 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4505 maxLengthText : "The maximum length for this field is {0}",
4509 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4510 * If available, this function will be called only after the basic validators all return true, and will be passed the
4511 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4515 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4516 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4517 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4521 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4544 parentLabelAlign : function()
4547 while (parent.parent()) {
4548 parent = parent.parent();
4549 if (typeof(parent.labelAlign) !='undefined') {
4550 return parent.labelAlign;
4557 getAutoCreate : function(){
4559 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4565 if(this.inputType != 'hidden'){
4566 cfg.cls = 'form-group' //input-group
4572 type : this.inputType,
4574 cls : 'form-control',
4575 placeholder : this.placeholder || ''
4579 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4580 input.maxLength = this.maxLength;
4583 if (this.disabled) {
4584 input.disabled=true;
4587 if (this.readOnly) {
4588 input.readonly=true;
4592 input.name = this.name;
4595 input.cls += ' input-' + this.size;
4598 ['xs','sm','md','lg'].map(function(size){
4599 if (settings[size]) {
4600 cfg.cls += ' col-' + size + '-' + settings[size];
4604 var inputblock = input;
4606 if (this.before || this.after) {
4609 cls : 'input-group',
4613 inputblock.cn.push({
4615 cls : 'input-group-addon',
4619 inputblock.cn.push(input);
4621 inputblock.cn.push({
4623 cls : 'input-group-addon',
4630 if (align ==='left' && this.fieldLabel.length) {
4631 Roo.log("left and has label");
4637 cls : 'control-label col-sm-' + this.labelWidth,
4638 html : this.fieldLabel
4642 cls : "col-sm-" + (12 - this.labelWidth),
4649 } else if ( this.fieldLabel.length) {
4655 //cls : 'input-group-addon',
4656 html : this.fieldLabel
4666 Roo.log(" no label && no align");
4675 Roo.log('input-parentType: ' + this.parentType);
4677 if (this.parentType === 'Navbar' && this.parent().bar) {
4678 cfg.cls += ' navbar-form';
4686 * return the real input element.
4688 inputEl: function ()
4690 return this.el.select('input.form-control',true).first();
4692 setDisabled : function(v)
4694 var i = this.inputEl().dom;
4696 i.removeAttribute('disabled');
4700 i.setAttribute('disabled','true');
4702 initEvents : function()
4705 this.inputEl().on("keydown" , this.fireKey, this);
4706 this.inputEl().on("focus", this.onFocus, this);
4707 this.inputEl().on("blur", this.onBlur, this);
4709 this.inputEl().relayEvent('keyup', this);
4711 // reference to original value for reset
4712 this.originalValue = this.getValue();
4713 //Roo.form.TextField.superclass.initEvents.call(this);
4714 if(this.validationEvent == 'keyup'){
4715 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4716 this.inputEl().on('keyup', this.filterValidation, this);
4718 else if(this.validationEvent !== false){
4719 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4722 if(this.selectOnFocus){
4723 this.on("focus", this.preFocus, this);
4726 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4727 this.inputEl().on("keypress", this.filterKeys, this);
4730 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4731 this.el.on("click", this.autoSize, this);
4734 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4735 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4739 filterValidation : function(e){
4740 if(!e.isNavKeyPress()){
4741 this.validationTask.delay(this.validationDelay);
4745 * Validates the field value
4746 * @return {Boolean} True if the value is valid, else false
4748 validate : function(){
4749 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4750 if(this.disabled || this.validateValue(this.getRawValue())){
4751 this.clearInvalid();
4759 * Validates a value according to the field's validation rules and marks the field as invalid
4760 * if the validation fails
4761 * @param {Mixed} value The value to validate
4762 * @return {Boolean} True if the value is valid, else false
4764 validateValue : function(value){
4765 if(value.length < 1) { // if it's blank
4766 if(this.allowBlank){
4767 this.clearInvalid();
4770 this.markInvalid(this.blankText);
4774 if(value.length < this.minLength){
4775 this.markInvalid(String.format(this.minLengthText, this.minLength));
4778 if(value.length > this.maxLength){
4779 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4783 var vt = Roo.form.VTypes;
4784 if(!vt[this.vtype](value, this)){
4785 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4789 if(typeof this.validator == "function"){
4790 var msg = this.validator(value);
4792 this.markInvalid(msg);
4796 if(this.regex && !this.regex.test(value)){
4797 this.markInvalid(this.regexText);
4806 fireKey : function(e){
4807 //Roo.log('field ' + e.getKey());
4808 if(e.isNavKeyPress()){
4809 this.fireEvent("specialkey", this, e);
4812 focus : function (selectText){
4814 this.inputEl().focus();
4815 if(selectText === true){
4816 this.inputEl().dom.select();
4822 onFocus : function(){
4823 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4824 // this.el.addClass(this.focusClass);
4827 this.hasFocus = true;
4828 this.startValue = this.getValue();
4829 this.fireEvent("focus", this);
4833 beforeBlur : Roo.emptyFn,
4837 onBlur : function(){
4839 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4840 //this.el.removeClass(this.focusClass);
4842 this.hasFocus = false;
4843 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4846 var v = this.getValue();
4847 if(String(v) !== String(this.startValue)){
4848 this.fireEvent('change', this, v, this.startValue);
4850 this.fireEvent("blur", this);
4854 * Resets the current field value to the originally loaded value and clears any validation messages
4857 this.setValue(this.originalValue);
4858 this.clearInvalid();
4861 * Returns the name of the field
4862 * @return {Mixed} name The name field
4864 getName: function(){
4868 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4869 * @return {Mixed} value The field value
4871 getValue : function(){
4872 return this.inputEl().getValue();
4875 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4876 * @return {Mixed} value The field value
4878 getRawValue : function(){
4879 var v = this.inputEl().getValue();
4885 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4886 * @param {Mixed} value The value to set
4888 setRawValue : function(v){
4889 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4892 selectText : function(start, end){
4893 var v = this.getRawValue();
4895 start = start === undefined ? 0 : start;
4896 end = end === undefined ? v.length : end;
4897 var d = this.inputEl().dom;
4898 if(d.setSelectionRange){
4899 d.setSelectionRange(start, end);
4900 }else if(d.createTextRange){
4901 var range = d.createTextRange();
4902 range.moveStart("character", start);
4903 range.moveEnd("character", v.length-end);
4910 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4911 * @param {Mixed} value The value to set
4913 setValue : function(v){
4916 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4922 processValue : function(value){
4923 if(this.stripCharsRe){
4924 var newValue = value.replace(this.stripCharsRe, '');
4925 if(newValue !== value){
4926 this.setRawValue(newValue);
4933 preFocus : function(){
4935 if(this.selectOnFocus){
4936 this.inputEl().dom.select();
4939 filterKeys : function(e){
4941 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4944 var c = e.getCharCode(), cc = String.fromCharCode(c);
4945 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4948 if(!this.maskRe.test(cc)){
4953 * Clear any invalid styles/messages for this field
4955 clearInvalid : function(){
4957 if(!this.el || this.preventMark){ // not rendered
4960 this.el.removeClass(this.invalidClass);
4962 switch(this.msgTarget){
4964 this.el.dom.qtip = '';
4967 this.el.dom.title = '';
4971 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4976 this.errorIcon.dom.qtip = '';
4977 this.errorIcon.hide();
4978 this.un('resize', this.alignErrorIcon, this);
4982 var t = Roo.getDom(this.msgTarget);
4984 t.style.display = 'none';
4988 this.fireEvent('valid', this);
4991 * Mark this field as invalid
4992 * @param {String} msg The validation message
4994 markInvalid : function(msg){
4995 if(!this.el || this.preventMark){ // not rendered
4998 this.el.addClass(this.invalidClass);
5000 msg = msg || this.invalidText;
5001 switch(this.msgTarget){
5003 this.el.dom.qtip = msg;
5004 this.el.dom.qclass = 'x-form-invalid-tip';
5005 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5006 Roo.QuickTips.enable();
5010 this.el.dom.title = msg;
5014 var elp = this.el.findParent('.x-form-element', 5, true);
5015 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5016 this.errorEl.setWidth(elp.getWidth(true)-20);
5018 this.errorEl.update(msg);
5019 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5022 if(!this.errorIcon){
5023 var elp = this.el.findParent('.x-form-element', 5, true);
5024 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5026 this.alignErrorIcon();
5027 this.errorIcon.dom.qtip = msg;
5028 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5029 this.errorIcon.show();
5030 this.on('resize', this.alignErrorIcon, this);
5033 var t = Roo.getDom(this.msgTarget);
5035 t.style.display = this.msgDisplay;
5039 this.fireEvent('invalid', this, msg);
5042 SafariOnKeyDown : function(event)
5044 // this is a workaround for a password hang bug on chrome/ webkit.
5046 var isSelectAll = false;
5048 if(this.inputEl().dom.selectionEnd > 0){
5049 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5051 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5052 event.preventDefault();
5057 if(isSelectAll){ // backspace and delete key
5059 event.preventDefault();
5060 // this is very hacky as keydown always get's upper case.
5062 var cc = String.fromCharCode(event.getCharCode());
5063 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5067 adjustWidth : function(tag, w){
5068 tag = tag.toLowerCase();
5069 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5070 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5074 if(tag == 'textarea'){
5077 }else if(Roo.isOpera){
5081 if(tag == 'textarea'){
5100 * @class Roo.bootstrap.TextArea
5101 * @extends Roo.bootstrap.Input
5102 * Bootstrap TextArea class
5103 * @cfg {Number} cols Specifies the visible width of a text area
5104 * @cfg {Number} rows Specifies the visible number of lines in a text area
5105 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5106 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5107 * @cfg {string} html text
5110 * Create a new TextArea
5111 * @param {Object} config The config object
5114 Roo.bootstrap.TextArea = function(config){
5115 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5119 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5129 getAutoCreate : function(){
5131 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5142 value : this.value || '',
5143 html: this.html || '',
5144 cls : 'form-control',
5145 placeholder : this.placeholder || ''
5149 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5150 input.maxLength = this.maxLength;
5154 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5158 input.cols = this.cols;
5161 if (this.readOnly) {
5162 input.readonly = true;
5166 input.name = this.name;
5170 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5174 ['xs','sm','md','lg'].map(function(size){
5175 if (settings[size]) {
5176 cfg.cls += ' col-' + size + '-' + settings[size];
5180 var inputblock = input;
5182 if (this.before || this.after) {
5185 cls : 'input-group',
5189 inputblock.cn.push({
5191 cls : 'input-group-addon',
5195 inputblock.cn.push(input);
5197 inputblock.cn.push({
5199 cls : 'input-group-addon',
5206 if (align ==='left' && this.fieldLabel.length) {
5207 Roo.log("left and has label");
5213 cls : 'control-label col-sm-' + this.labelWidth,
5214 html : this.fieldLabel
5218 cls : "col-sm-" + (12 - this.labelWidth),
5225 } else if ( this.fieldLabel.length) {
5231 //cls : 'input-group-addon',
5232 html : this.fieldLabel
5242 Roo.log(" no label && no align");
5252 if (this.disabled) {
5253 input.disabled=true;
5260 * return the real textarea element.
5262 inputEl: function ()
5264 return this.el.select('textarea.form-control',true).first();
5272 * trigger field - base class for combo..
5277 * @class Roo.bootstrap.TriggerField
5278 * @extends Roo.bootstrap.Input
5279 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5280 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5281 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5282 * for which you can provide a custom implementation. For example:
5284 var trigger = new Roo.bootstrap.TriggerField();
5285 trigger.onTriggerClick = myTriggerFn;
5286 trigger.applyTo('my-field');
5289 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5290 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5291 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5292 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5294 * Create a new TriggerField.
5295 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5296 * to the base TextField)
5298 Roo.bootstrap.TriggerField = function(config){
5299 this.mimicing = false;
5300 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5303 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5305 * @cfg {String} triggerClass A CSS class to apply to the trigger
5308 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5312 /** @cfg {Boolean} grow @hide */
5313 /** @cfg {Number} growMin @hide */
5314 /** @cfg {Number} growMax @hide */
5320 autoSize: Roo.emptyFn,
5327 actionMode : 'wrap',
5331 getAutoCreate : function(){
5333 var parent = this.parent();
5335 var align = this.parentLabelAlign();
5340 cls: 'form-group' //input-group
5347 type : this.inputType,
5348 cls : 'form-control',
5349 autocomplete: 'off',
5350 placeholder : this.placeholder || ''
5354 input.name = this.name;
5357 input.cls += ' input-' + this.size;
5360 if (this.disabled) {
5361 input.disabled=true;
5364 var inputblock = input;
5366 if (this.before || this.after) {
5369 cls : 'input-group',
5373 inputblock.cn.push({
5375 cls : 'input-group-addon',
5379 inputblock.cn.push(input);
5381 inputblock.cn.push({
5383 cls : 'input-group-addon',
5396 cls: 'form-hidden-field'
5404 Roo.log('multiple');
5412 cls: 'form-hidden-field'
5416 cls: 'select2-choices',
5420 cls: 'select2-search-field',
5433 cls: 'select2-container input-group',
5438 cls: 'typeahead typeahead-long dropdown-menu',
5439 style: 'display:none'
5447 cls : 'input-group-addon btn dropdown-toggle',
5455 cls: 'combobox-clear',
5469 combobox.cls += ' select2-container-multi';
5472 if (align ==='left' && this.fieldLabel.length) {
5474 Roo.log("left and has label");
5480 cls : 'control-label col-sm-' + this.labelWidth,
5481 html : this.fieldLabel
5485 cls : "col-sm-" + (12 - this.labelWidth),
5492 } else if ( this.fieldLabel.length) {
5498 //cls : 'input-group-addon',
5499 html : this.fieldLabel
5509 Roo.log(" no label && no align");
5516 ['xs','sm','md','lg'].map(function(size){
5517 if (settings[size]) {
5518 cfg.cls += ' col-' + size + '-' + settings[size];
5529 onResize : function(w, h){
5530 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5531 // if(typeof w == 'number'){
5532 // var x = w - this.trigger.getWidth();
5533 // this.inputEl().setWidth(this.adjustWidth('input', x));
5534 // this.trigger.setStyle('left', x+'px');
5539 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5542 getResizeEl : function(){
5543 return this.inputEl();
5547 getPositionEl : function(){
5548 return this.inputEl();
5552 alignErrorIcon : function(){
5553 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5557 initEvents : function(){
5559 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5560 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5562 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5563 if(this.hideTrigger){
5564 this.trigger.setDisplayed(false);
5566 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5570 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5573 //this.trigger.addClassOnOver('x-form-trigger-over');
5574 //this.trigger.addClassOnClick('x-form-trigger-click');
5577 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5582 initTrigger : function(){
5587 onDestroy : function(){
5589 this.trigger.removeAllListeners();
5590 // this.trigger.remove();
5593 // this.wrap.remove();
5595 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5599 onFocus : function(){
5600 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5603 this.wrap.addClass('x-trigger-wrap-focus');
5604 this.mimicing = true;
5605 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5606 if(this.monitorTab){
5607 this.el.on("keydown", this.checkTab, this);
5614 checkTab : function(e){
5615 if(e.getKey() == e.TAB){
5621 onBlur : function(){
5626 mimicBlur : function(e, t){
5628 if(!this.wrap.contains(t) && this.validateBlur()){
5635 triggerBlur : function(){
5636 this.mimicing = false;
5637 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5638 if(this.monitorTab){
5639 this.el.un("keydown", this.checkTab, this);
5641 //this.wrap.removeClass('x-trigger-wrap-focus');
5642 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5646 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5647 validateBlur : function(e, t){
5652 onDisable : function(){
5653 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5655 // this.wrap.addClass('x-item-disabled');
5660 onEnable : function(){
5661 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5663 // this.el.removeClass('x-item-disabled');
5668 onShow : function(){
5669 var ae = this.getActionEl();
5672 ae.dom.style.display = '';
5673 ae.dom.style.visibility = 'visible';
5679 onHide : function(){
5680 var ae = this.getActionEl();
5681 ae.dom.style.display = 'none';
5685 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5686 * by an implementing function.
5688 * @param {EventObject} e
5690 onTriggerClick : Roo.emptyFn
5694 * Ext JS Library 1.1.1
5695 * Copyright(c) 2006-2007, Ext JS, LLC.
5697 * Originally Released Under LGPL - original licence link has changed is not relivant.
5700 * <script type="text/javascript">
5705 * @class Roo.data.SortTypes
5707 * Defines the default sorting (casting?) comparison functions used when sorting data.
5709 Roo.data.SortTypes = {
5711 * Default sort that does nothing
5712 * @param {Mixed} s The value being converted
5713 * @return {Mixed} The comparison value
5720 * The regular expression used to strip tags
5724 stripTagsRE : /<\/?[^>]+>/gi,
5727 * Strips all HTML tags to sort on text only
5728 * @param {Mixed} s The value being converted
5729 * @return {String} The comparison value
5731 asText : function(s){
5732 return String(s).replace(this.stripTagsRE, "");
5736 * Strips all HTML tags to sort on text only - Case insensitive
5737 * @param {Mixed} s The value being converted
5738 * @return {String} The comparison value
5740 asUCText : function(s){
5741 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5745 * Case insensitive string
5746 * @param {Mixed} s The value being converted
5747 * @return {String} The comparison value
5749 asUCString : function(s) {
5750 return String(s).toUpperCase();
5755 * @param {Mixed} s The value being converted
5756 * @return {Number} The comparison value
5758 asDate : function(s) {
5762 if(s instanceof Date){
5765 return Date.parse(String(s));
5770 * @param {Mixed} s The value being converted
5771 * @return {Float} The comparison value
5773 asFloat : function(s) {
5774 var val = parseFloat(String(s).replace(/,/g, ""));
5775 if(isNaN(val)) val = 0;
5781 * @param {Mixed} s The value being converted
5782 * @return {Number} The comparison value
5784 asInt : function(s) {
5785 var val = parseInt(String(s).replace(/,/g, ""));
5786 if(isNaN(val)) val = 0;
5791 * Ext JS Library 1.1.1
5792 * Copyright(c) 2006-2007, Ext JS, LLC.
5794 * Originally Released Under LGPL - original licence link has changed is not relivant.
5797 * <script type="text/javascript">
5801 * @class Roo.data.Record
5802 * Instances of this class encapsulate both record <em>definition</em> information, and record
5803 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5804 * to access Records cached in an {@link Roo.data.Store} object.<br>
5806 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5807 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5810 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5812 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5813 * {@link #create}. The parameters are the same.
5814 * @param {Array} data An associative Array of data values keyed by the field name.
5815 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5816 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5817 * not specified an integer id is generated.
5819 Roo.data.Record = function(data, id){
5820 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5825 * Generate a constructor for a specific record layout.
5826 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5827 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5828 * Each field definition object may contain the following properties: <ul>
5829 * <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,
5830 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5831 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5832 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5833 * is being used, then this is a string containing the javascript expression to reference the data relative to
5834 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5835 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5836 * this may be omitted.</p></li>
5837 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5838 * <ul><li>auto (Default, implies no conversion)</li>
5843 * <li>date</li></ul></p></li>
5844 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5845 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5846 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5847 * by the Reader into an object that will be stored in the Record. It is passed the
5848 * following parameters:<ul>
5849 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5851 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5853 * <br>usage:<br><pre><code>
5854 var TopicRecord = Roo.data.Record.create(
5855 {name: 'title', mapping: 'topic_title'},
5856 {name: 'author', mapping: 'username'},
5857 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5858 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5859 {name: 'lastPoster', mapping: 'user2'},
5860 {name: 'excerpt', mapping: 'post_text'}
5863 var myNewRecord = new TopicRecord({
5864 title: 'Do my job please',
5867 lastPost: new Date(),
5868 lastPoster: 'Animal',
5869 excerpt: 'No way dude!'
5871 myStore.add(myNewRecord);
5876 Roo.data.Record.create = function(o){
5878 f.superclass.constructor.apply(this, arguments);
5880 Roo.extend(f, Roo.data.Record);
5881 var p = f.prototype;
5882 p.fields = new Roo.util.MixedCollection(false, function(field){
5885 for(var i = 0, len = o.length; i < len; i++){
5886 p.fields.add(new Roo.data.Field(o[i]));
5888 f.getField = function(name){
5889 return p.fields.get(name);
5894 Roo.data.Record.AUTO_ID = 1000;
5895 Roo.data.Record.EDIT = 'edit';
5896 Roo.data.Record.REJECT = 'reject';
5897 Roo.data.Record.COMMIT = 'commit';
5899 Roo.data.Record.prototype = {
5901 * Readonly flag - true if this record has been modified.
5910 join : function(store){
5915 * Set the named field to the specified value.
5916 * @param {String} name The name of the field to set.
5917 * @param {Object} value The value to set the field to.
5919 set : function(name, value){
5920 if(this.data[name] == value){
5927 if(typeof this.modified[name] == 'undefined'){
5928 this.modified[name] = this.data[name];
5930 this.data[name] = value;
5931 if(!this.editing && this.store){
5932 this.store.afterEdit(this);
5937 * Get the value of the named field.
5938 * @param {String} name The name of the field to get the value of.
5939 * @return {Object} The value of the field.
5941 get : function(name){
5942 return this.data[name];
5946 beginEdit : function(){
5947 this.editing = true;
5952 cancelEdit : function(){
5953 this.editing = false;
5954 delete this.modified;
5958 endEdit : function(){
5959 this.editing = false;
5960 if(this.dirty && this.store){
5961 this.store.afterEdit(this);
5966 * Usually called by the {@link Roo.data.Store} which owns the Record.
5967 * Rejects all changes made to the Record since either creation, or the last commit operation.
5968 * Modified fields are reverted to their original values.
5970 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5971 * of reject operations.
5973 reject : function(){
5974 var m = this.modified;
5976 if(typeof m[n] != "function"){
5977 this.data[n] = m[n];
5981 delete this.modified;
5982 this.editing = false;
5984 this.store.afterReject(this);
5989 * Usually called by the {@link Roo.data.Store} which owns the Record.
5990 * Commits all changes made to the Record since either creation, or the last commit operation.
5992 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5993 * of commit operations.
5995 commit : function(){
5997 delete this.modified;
5998 this.editing = false;
6000 this.store.afterCommit(this);
6005 hasError : function(){
6006 return this.error != null;
6010 clearError : function(){
6015 * Creates a copy of this record.
6016 * @param {String} id (optional) A new record id if you don't want to use this record's id
6019 copy : function(newId) {
6020 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6024 * Ext JS Library 1.1.1
6025 * Copyright(c) 2006-2007, Ext JS, LLC.
6027 * Originally Released Under LGPL - original licence link has changed is not relivant.
6030 * <script type="text/javascript">
6036 * @class Roo.data.Store
6037 * @extends Roo.util.Observable
6038 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6039 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6041 * 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
6042 * has no knowledge of the format of the data returned by the Proxy.<br>
6044 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6045 * instances from the data object. These records are cached and made available through accessor functions.
6047 * Creates a new Store.
6048 * @param {Object} config A config object containing the objects needed for the Store to access data,
6049 * and read the data into Records.
6051 Roo.data.Store = function(config){
6052 this.data = new Roo.util.MixedCollection(false);
6053 this.data.getKey = function(o){
6056 this.baseParams = {};
6063 "multisort" : "_multisort"
6066 if(config && config.data){
6067 this.inlineData = config.data;
6071 Roo.apply(this, config);
6073 if(this.reader){ // reader passed
6074 this.reader = Roo.factory(this.reader, Roo.data);
6075 this.reader.xmodule = this.xmodule || false;
6076 if(!this.recordType){
6077 this.recordType = this.reader.recordType;
6079 if(this.reader.onMetaChange){
6080 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6084 if(this.recordType){
6085 this.fields = this.recordType.prototype.fields;
6091 * @event datachanged
6092 * Fires when the data cache has changed, and a widget which is using this Store
6093 * as a Record cache should refresh its view.
6094 * @param {Store} this
6099 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6100 * @param {Store} this
6101 * @param {Object} meta The JSON metadata
6106 * Fires when Records have been added to the Store
6107 * @param {Store} this
6108 * @param {Roo.data.Record[]} records The array of Records added
6109 * @param {Number} index The index at which the record(s) were added
6114 * Fires when a Record has been removed from the Store
6115 * @param {Store} this
6116 * @param {Roo.data.Record} record The Record that was removed
6117 * @param {Number} index The index at which the record was removed
6122 * Fires when a Record has been updated
6123 * @param {Store} this
6124 * @param {Roo.data.Record} record The Record that was updated
6125 * @param {String} operation The update operation being performed. Value may be one of:
6127 Roo.data.Record.EDIT
6128 Roo.data.Record.REJECT
6129 Roo.data.Record.COMMIT
6135 * Fires when the data cache has been cleared.
6136 * @param {Store} this
6141 * Fires before a request is made for a new data object. If the beforeload handler returns false
6142 * the load action will be canceled.
6143 * @param {Store} this
6144 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6148 * @event beforeloadadd
6149 * Fires after a new set of Records has been loaded.
6150 * @param {Store} this
6151 * @param {Roo.data.Record[]} records The Records that were loaded
6152 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6154 beforeloadadd : true,
6157 * Fires after a new set of Records has been loaded, before they are added to the store.
6158 * @param {Store} this
6159 * @param {Roo.data.Record[]} records The Records that were loaded
6160 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6161 * @params {Object} return from reader
6165 * @event loadexception
6166 * Fires if an exception occurs in the Proxy during loading.
6167 * Called with the signature of the Proxy's "loadexception" event.
6168 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6171 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6172 * @param {Object} load options
6173 * @param {Object} jsonData from your request (normally this contains the Exception)
6175 loadexception : true
6179 this.proxy = Roo.factory(this.proxy, Roo.data);
6180 this.proxy.xmodule = this.xmodule || false;
6181 this.relayEvents(this.proxy, ["loadexception"]);
6183 this.sortToggle = {};
6184 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6186 Roo.data.Store.superclass.constructor.call(this);
6188 if(this.inlineData){
6189 this.loadData(this.inlineData);
6190 delete this.inlineData;
6194 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6196 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6197 * without a remote query - used by combo/forms at present.
6201 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6204 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6207 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6208 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6211 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6212 * on any HTTP request
6215 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6218 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6222 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6223 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6228 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6229 * loaded or when a record is removed. (defaults to false).
6231 pruneModifiedRecords : false,
6237 * Add Records to the Store and fires the add event.
6238 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6240 add : function(records){
6241 records = [].concat(records);
6242 for(var i = 0, len = records.length; i < len; i++){
6243 records[i].join(this);
6245 var index = this.data.length;
6246 this.data.addAll(records);
6247 this.fireEvent("add", this, records, index);
6251 * Remove a Record from the Store and fires the remove event.
6252 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6254 remove : function(record){
6255 var index = this.data.indexOf(record);
6256 this.data.removeAt(index);
6257 if(this.pruneModifiedRecords){
6258 this.modified.remove(record);
6260 this.fireEvent("remove", this, record, index);
6264 * Remove all Records from the Store and fires the clear event.
6266 removeAll : function(){
6268 if(this.pruneModifiedRecords){
6271 this.fireEvent("clear", this);
6275 * Inserts Records to the Store at the given index and fires the add event.
6276 * @param {Number} index The start index at which to insert the passed Records.
6277 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6279 insert : function(index, records){
6280 records = [].concat(records);
6281 for(var i = 0, len = records.length; i < len; i++){
6282 this.data.insert(index, records[i]);
6283 records[i].join(this);
6285 this.fireEvent("add", this, records, index);
6289 * Get the index within the cache of the passed Record.
6290 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6291 * @return {Number} The index of the passed Record. Returns -1 if not found.
6293 indexOf : function(record){
6294 return this.data.indexOf(record);
6298 * Get the index within the cache of the Record with the passed id.
6299 * @param {String} id The id of the Record to find.
6300 * @return {Number} The index of the Record. Returns -1 if not found.
6302 indexOfId : function(id){
6303 return this.data.indexOfKey(id);
6307 * Get the Record with the specified id.
6308 * @param {String} id The id of the Record to find.
6309 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6311 getById : function(id){
6312 return this.data.key(id);
6316 * Get the Record at the specified index.
6317 * @param {Number} index The index of the Record to find.
6318 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6320 getAt : function(index){
6321 return this.data.itemAt(index);
6325 * Returns a range of Records between specified indices.
6326 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6327 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6328 * @return {Roo.data.Record[]} An array of Records
6330 getRange : function(start, end){
6331 return this.data.getRange(start, end);
6335 storeOptions : function(o){
6336 o = Roo.apply({}, o);
6339 this.lastOptions = o;
6343 * Loads the Record cache from the configured Proxy using the configured Reader.
6345 * If using remote paging, then the first load call must specify the <em>start</em>
6346 * and <em>limit</em> properties in the options.params property to establish the initial
6347 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6349 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6350 * and this call will return before the new data has been loaded. Perform any post-processing
6351 * in a callback function, or in a "load" event handler.</strong>
6353 * @param {Object} options An object containing properties which control loading options:<ul>
6354 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6355 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6356 * passed the following arguments:<ul>
6357 * <li>r : Roo.data.Record[]</li>
6358 * <li>options: Options object from the load call</li>
6359 * <li>success: Boolean success indicator</li></ul></li>
6360 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6361 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6364 load : function(options){
6365 options = options || {};
6366 if(this.fireEvent("beforeload", this, options) !== false){
6367 this.storeOptions(options);
6368 var p = Roo.apply(options.params || {}, this.baseParams);
6369 // if meta was not loaded from remote source.. try requesting it.
6370 if (!this.reader.metaFromRemote) {
6373 if(this.sortInfo && this.remoteSort){
6374 var pn = this.paramNames;
6375 p[pn["sort"]] = this.sortInfo.field;
6376 p[pn["dir"]] = this.sortInfo.direction;
6378 if (this.multiSort) {
6379 var pn = this.paramNames;
6380 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6383 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6388 * Reloads the Record cache from the configured Proxy using the configured Reader and
6389 * the options from the last load operation performed.
6390 * @param {Object} options (optional) An object containing properties which may override the options
6391 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6392 * the most recently used options are reused).
6394 reload : function(options){
6395 this.load(Roo.applyIf(options||{}, this.lastOptions));
6399 // Called as a callback by the Reader during a load operation.
6400 loadRecords : function(o, options, success){
6401 if(!o || success === false){
6402 if(success !== false){
6403 this.fireEvent("load", this, [], options, o);
6405 if(options.callback){
6406 options.callback.call(options.scope || this, [], options, false);
6410 // if data returned failure - throw an exception.
6411 if (o.success === false) {
6412 // show a message if no listener is registered.
6413 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6414 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6416 // loadmask wil be hooked into this..
6417 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6420 var r = o.records, t = o.totalRecords || r.length;
6422 this.fireEvent("beforeloadadd", this, r, options, o);
6424 if(!options || options.add !== true){
6425 if(this.pruneModifiedRecords){
6428 for(var i = 0, len = r.length; i < len; i++){
6432 this.data = this.snapshot;
6433 delete this.snapshot;
6436 this.data.addAll(r);
6437 this.totalLength = t;
6439 this.fireEvent("datachanged", this);
6441 this.totalLength = Math.max(t, this.data.length+r.length);
6444 this.fireEvent("load", this, r, options, o);
6445 if(options.callback){
6446 options.callback.call(options.scope || this, r, options, true);
6452 * Loads data from a passed data block. A Reader which understands the format of the data
6453 * must have been configured in the constructor.
6454 * @param {Object} data The data block from which to read the Records. The format of the data expected
6455 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6456 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6458 loadData : function(o, append){
6459 var r = this.reader.readRecords(o);
6460 this.loadRecords(r, {add: append}, true);
6464 * Gets the number of cached records.
6466 * <em>If using paging, this may not be the total size of the dataset. If the data object
6467 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6468 * the data set size</em>
6470 getCount : function(){
6471 return this.data.length || 0;
6475 * Gets the total number of records in the dataset as returned by the server.
6477 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6478 * the dataset size</em>
6480 getTotalCount : function(){
6481 return this.totalLength || 0;
6485 * Returns the sort state of the Store as an object with two properties:
6487 field {String} The name of the field by which the Records are sorted
6488 direction {String} The sort order, "ASC" or "DESC"
6491 getSortState : function(){
6492 return this.sortInfo;
6496 applySort : function(){
6497 if(this.sortInfo && !this.remoteSort){
6498 var s = this.sortInfo, f = s.field;
6499 var st = this.fields.get(f).sortType;
6500 var fn = function(r1, r2){
6501 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6502 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6504 this.data.sort(s.direction, fn);
6505 if(this.snapshot && this.snapshot != this.data){
6506 this.snapshot.sort(s.direction, fn);
6512 * Sets the default sort column and order to be used by the next load operation.
6513 * @param {String} fieldName The name of the field to sort by.
6514 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6516 setDefaultSort : function(field, dir){
6517 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6522 * If remote sorting is used, the sort is performed on the server, and the cache is
6523 * reloaded. If local sorting is used, the cache is sorted internally.
6524 * @param {String} fieldName The name of the field to sort by.
6525 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6527 sort : function(fieldName, dir){
6528 var f = this.fields.get(fieldName);
6530 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6532 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6533 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6538 this.sortToggle[f.name] = dir;
6539 this.sortInfo = {field: f.name, direction: dir};
6540 if(!this.remoteSort){
6542 this.fireEvent("datachanged", this);
6544 this.load(this.lastOptions);
6549 * Calls the specified function for each of the Records in the cache.
6550 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6551 * Returning <em>false</em> aborts and exits the iteration.
6552 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6554 each : function(fn, scope){
6555 this.data.each(fn, scope);
6559 * Gets all records modified since the last commit. Modified records are persisted across load operations
6560 * (e.g., during paging).
6561 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6563 getModifiedRecords : function(){
6564 return this.modified;
6568 createFilterFn : function(property, value, anyMatch){
6569 if(!value.exec){ // not a regex
6570 value = String(value);
6571 if(value.length == 0){
6574 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6577 return value.test(r.data[property]);
6582 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6583 * @param {String} property A field on your records
6584 * @param {Number} start The record index to start at (defaults to 0)
6585 * @param {Number} end The last record index to include (defaults to length - 1)
6586 * @return {Number} The sum
6588 sum : function(property, start, end){
6589 var rs = this.data.items, v = 0;
6591 end = (end || end === 0) ? end : rs.length-1;
6593 for(var i = start; i <= end; i++){
6594 v += (rs[i].data[property] || 0);
6600 * Filter the records by a specified property.
6601 * @param {String} field A field on your records
6602 * @param {String/RegExp} value Either a string that the field
6603 * should start with or a RegExp to test against the field
6604 * @param {Boolean} anyMatch True to match any part not just the beginning
6606 filter : function(property, value, anyMatch){
6607 var fn = this.createFilterFn(property, value, anyMatch);
6608 return fn ? this.filterBy(fn) : this.clearFilter();
6612 * Filter by a function. The specified function will be called with each
6613 * record in this data source. If the function returns true the record is included,
6614 * otherwise it is filtered.
6615 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6616 * @param {Object} scope (optional) The scope of the function (defaults to this)
6618 filterBy : function(fn, scope){
6619 this.snapshot = this.snapshot || this.data;
6620 this.data = this.queryBy(fn, scope||this);
6621 this.fireEvent("datachanged", this);
6625 * Query the records by a specified property.
6626 * @param {String} field A field on your records
6627 * @param {String/RegExp} value Either a string that the field
6628 * should start with or a RegExp to test against the field
6629 * @param {Boolean} anyMatch True to match any part not just the beginning
6630 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6632 query : function(property, value, anyMatch){
6633 var fn = this.createFilterFn(property, value, anyMatch);
6634 return fn ? this.queryBy(fn) : this.data.clone();
6638 * Query by a function. The specified function will be called with each
6639 * record in this data source. If the function returns true the record is included
6641 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6642 * @param {Object} scope (optional) The scope of the function (defaults to this)
6643 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6645 queryBy : function(fn, scope){
6646 var data = this.snapshot || this.data;
6647 return data.filterBy(fn, scope||this);
6651 * Collects unique values for a particular dataIndex from this store.
6652 * @param {String} dataIndex The property to collect
6653 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6654 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6655 * @return {Array} An array of the unique values
6657 collect : function(dataIndex, allowNull, bypassFilter){
6658 var d = (bypassFilter === true && this.snapshot) ?
6659 this.snapshot.items : this.data.items;
6660 var v, sv, r = [], l = {};
6661 for(var i = 0, len = d.length; i < len; i++){
6662 v = d[i].data[dataIndex];
6664 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6673 * Revert to a view of the Record cache with no filtering applied.
6674 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6676 clearFilter : function(suppressEvent){
6677 if(this.snapshot && this.snapshot != this.data){
6678 this.data = this.snapshot;
6679 delete this.snapshot;
6680 if(suppressEvent !== true){
6681 this.fireEvent("datachanged", this);
6687 afterEdit : function(record){
6688 if(this.modified.indexOf(record) == -1){
6689 this.modified.push(record);
6691 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6695 afterReject : function(record){
6696 this.modified.remove(record);
6697 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6701 afterCommit : function(record){
6702 this.modified.remove(record);
6703 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6707 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6708 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6710 commitChanges : function(){
6711 var m = this.modified.slice(0);
6713 for(var i = 0, len = m.length; i < len; i++){
6719 * Cancel outstanding changes on all changed records.
6721 rejectChanges : function(){
6722 var m = this.modified.slice(0);
6724 for(var i = 0, len = m.length; i < len; i++){
6729 onMetaChange : function(meta, rtype, o){
6730 this.recordType = rtype;
6731 this.fields = rtype.prototype.fields;
6732 delete this.snapshot;
6733 this.sortInfo = meta.sortInfo || this.sortInfo;
6735 this.fireEvent('metachange', this, this.reader.meta);
6738 moveIndex : function(data, type)
6740 var index = this.indexOf(data);
6742 var newIndex = index + type;
6746 this.insert(newIndex, data);
6751 * Ext JS Library 1.1.1
6752 * Copyright(c) 2006-2007, Ext JS, LLC.
6754 * Originally Released Under LGPL - original licence link has changed is not relivant.
6757 * <script type="text/javascript">
6761 * @class Roo.data.SimpleStore
6762 * @extends Roo.data.Store
6763 * Small helper class to make creating Stores from Array data easier.
6764 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6765 * @cfg {Array} fields An array of field definition objects, or field name strings.
6766 * @cfg {Array} data The multi-dimensional array of data
6768 * @param {Object} config
6770 Roo.data.SimpleStore = function(config){
6771 Roo.data.SimpleStore.superclass.constructor.call(this, {
6773 reader: new Roo.data.ArrayReader({
6776 Roo.data.Record.create(config.fields)
6778 proxy : new Roo.data.MemoryProxy(config.data)
6782 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6784 * Ext JS Library 1.1.1
6785 * Copyright(c) 2006-2007, Ext JS, LLC.
6787 * Originally Released Under LGPL - original licence link has changed is not relivant.
6790 * <script type="text/javascript">
6795 * @extends Roo.data.Store
6796 * @class Roo.data.JsonStore
6797 * Small helper class to make creating Stores for JSON data easier. <br/>
6799 var store = new Roo.data.JsonStore({
6800 url: 'get-images.php',
6802 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6805 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6806 * JsonReader and HttpProxy (unless inline data is provided).</b>
6807 * @cfg {Array} fields An array of field definition objects, or field name strings.
6809 * @param {Object} config
6811 Roo.data.JsonStore = function(c){
6812 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6813 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6814 reader: new Roo.data.JsonReader(c, c.fields)
6817 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6819 * Ext JS Library 1.1.1
6820 * Copyright(c) 2006-2007, Ext JS, LLC.
6822 * Originally Released Under LGPL - original licence link has changed is not relivant.
6825 * <script type="text/javascript">
6829 Roo.data.Field = function(config){
6830 if(typeof config == "string"){
6831 config = {name: config};
6833 Roo.apply(this, config);
6839 var st = Roo.data.SortTypes;
6840 // named sortTypes are supported, here we look them up
6841 if(typeof this.sortType == "string"){
6842 this.sortType = st[this.sortType];
6845 // set default sortType for strings and dates
6849 this.sortType = st.asUCString;
6852 this.sortType = st.asDate;
6855 this.sortType = st.none;
6860 var stripRe = /[\$,%]/g;
6862 // prebuilt conversion function for this field, instead of
6863 // switching every time we're reading a value
6865 var cv, dateFormat = this.dateFormat;
6870 cv = function(v){ return v; };
6873 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6877 return v !== undefined && v !== null && v !== '' ?
6878 parseInt(String(v).replace(stripRe, ""), 10) : '';
6883 return v !== undefined && v !== null && v !== '' ?
6884 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6889 cv = function(v){ return v === true || v === "true" || v == 1; };
6896 if(v instanceof Date){
6900 if(dateFormat == "timestamp"){
6901 return new Date(v*1000);
6903 return Date.parseDate(v, dateFormat);
6905 var parsed = Date.parse(v);
6906 return parsed ? new Date(parsed) : null;
6915 Roo.data.Field.prototype = {
6923 * Ext JS Library 1.1.1
6924 * Copyright(c) 2006-2007, Ext JS, LLC.
6926 * Originally Released Under LGPL - original licence link has changed is not relivant.
6929 * <script type="text/javascript">
6932 // Base class for reading structured data from a data source. This class is intended to be
6933 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6936 * @class Roo.data.DataReader
6937 * Base class for reading structured data from a data source. This class is intended to be
6938 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6941 Roo.data.DataReader = function(meta, recordType){
6945 this.recordType = recordType instanceof Array ?
6946 Roo.data.Record.create(recordType) : recordType;
6949 Roo.data.DataReader.prototype = {
6951 * Create an empty record
6952 * @param {Object} data (optional) - overlay some values
6953 * @return {Roo.data.Record} record created.
6955 newRow : function(d) {
6957 this.recordType.prototype.fields.each(function(c) {
6959 case 'int' : da[c.name] = 0; break;
6960 case 'date' : da[c.name] = new Date(); break;
6961 case 'float' : da[c.name] = 0.0; break;
6962 case 'boolean' : da[c.name] = false; break;
6963 default : da[c.name] = ""; break;
6967 return new this.recordType(Roo.apply(da, d));
6972 * Ext JS Library 1.1.1
6973 * Copyright(c) 2006-2007, Ext JS, LLC.
6975 * Originally Released Under LGPL - original licence link has changed is not relivant.
6978 * <script type="text/javascript">
6982 * @class Roo.data.DataProxy
6983 * @extends Roo.data.Observable
6984 * This class is an abstract base class for implementations which provide retrieval of
6985 * unformatted data objects.<br>
6987 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6988 * (of the appropriate type which knows how to parse the data object) to provide a block of
6989 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6991 * Custom implementations must implement the load method as described in
6992 * {@link Roo.data.HttpProxy#load}.
6994 Roo.data.DataProxy = function(){
6998 * Fires before a network request is made to retrieve a data object.
6999 * @param {Object} This DataProxy object.
7000 * @param {Object} params The params parameter to the load function.
7005 * Fires before the load method's callback is called.
7006 * @param {Object} This DataProxy object.
7007 * @param {Object} o The data object.
7008 * @param {Object} arg The callback argument object passed to the load function.
7012 * @event loadexception
7013 * Fires if an Exception occurs during data retrieval.
7014 * @param {Object} This DataProxy object.
7015 * @param {Object} o The data object.
7016 * @param {Object} arg The callback argument object passed to the load function.
7017 * @param {Object} e The Exception.
7019 loadexception : true
7021 Roo.data.DataProxy.superclass.constructor.call(this);
7024 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7027 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7031 * Ext JS Library 1.1.1
7032 * Copyright(c) 2006-2007, Ext JS, LLC.
7034 * Originally Released Under LGPL - original licence link has changed is not relivant.
7037 * <script type="text/javascript">
7040 * @class Roo.data.MemoryProxy
7041 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7042 * to the Reader when its load method is called.
7044 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7046 Roo.data.MemoryProxy = function(data){
7050 Roo.data.MemoryProxy.superclass.constructor.call(this);
7054 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7056 * Load data from the requested source (in this case an in-memory
7057 * data object passed to the constructor), read the data object into
7058 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7059 * process that block using the passed callback.
7060 * @param {Object} params This parameter is not used by the MemoryProxy class.
7061 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7062 * object into a block of Roo.data.Records.
7063 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7064 * The function must be passed <ul>
7065 * <li>The Record block object</li>
7066 * <li>The "arg" argument from the load function</li>
7067 * <li>A boolean success indicator</li>
7069 * @param {Object} scope The scope in which to call the callback
7070 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7072 load : function(params, reader, callback, scope, arg){
7073 params = params || {};
7076 result = reader.readRecords(this.data);
7078 this.fireEvent("loadexception", this, arg, null, e);
7079 callback.call(scope, null, arg, false);
7082 callback.call(scope, result, arg, true);
7086 update : function(params, records){
7091 * Ext JS Library 1.1.1
7092 * Copyright(c) 2006-2007, Ext JS, LLC.
7094 * Originally Released Under LGPL - original licence link has changed is not relivant.
7097 * <script type="text/javascript">
7100 * @class Roo.data.HttpProxy
7101 * @extends Roo.data.DataProxy
7102 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7103 * configured to reference a certain URL.<br><br>
7105 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7106 * from which the running page was served.<br><br>
7108 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7110 * Be aware that to enable the browser to parse an XML document, the server must set
7111 * the Content-Type header in the HTTP response to "text/xml".
7113 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7114 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7115 * will be used to make the request.
7117 Roo.data.HttpProxy = function(conn){
7118 Roo.data.HttpProxy.superclass.constructor.call(this);
7119 // is conn a conn config or a real conn?
7121 this.useAjax = !conn || !conn.events;
7125 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7126 // thse are take from connection...
7129 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7132 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7133 * extra parameters to each request made by this object. (defaults to undefined)
7136 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7137 * to each request made by this object. (defaults to undefined)
7140 * @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)
7143 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7146 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7152 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7156 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7157 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7158 * a finer-grained basis than the DataProxy events.
7160 getConnection : function(){
7161 return this.useAjax ? Roo.Ajax : this.conn;
7165 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7166 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7167 * process that block using the passed callback.
7168 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7169 * for the request to the remote server.
7170 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7171 * object into a block of Roo.data.Records.
7172 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7173 * The function must be passed <ul>
7174 * <li>The Record block object</li>
7175 * <li>The "arg" argument from the load function</li>
7176 * <li>A boolean success indicator</li>
7178 * @param {Object} scope The scope in which to call the callback
7179 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7181 load : function(params, reader, callback, scope, arg){
7182 if(this.fireEvent("beforeload", this, params) !== false){
7184 params : params || {},
7186 callback : callback,
7191 callback : this.loadResponse,
7195 Roo.applyIf(o, this.conn);
7196 if(this.activeRequest){
7197 Roo.Ajax.abort(this.activeRequest);
7199 this.activeRequest = Roo.Ajax.request(o);
7201 this.conn.request(o);
7204 callback.call(scope||this, null, arg, false);
7209 loadResponse : function(o, success, response){
7210 delete this.activeRequest;
7212 this.fireEvent("loadexception", this, o, response);
7213 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7218 result = o.reader.read(response);
7220 this.fireEvent("loadexception", this, o, response, e);
7221 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7225 this.fireEvent("load", this, o, o.request.arg);
7226 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7230 update : function(dataSet){
7235 updateResponse : function(dataSet){
7240 * Ext JS Library 1.1.1
7241 * Copyright(c) 2006-2007, Ext JS, LLC.
7243 * Originally Released Under LGPL - original licence link has changed is not relivant.
7246 * <script type="text/javascript">
7250 * @class Roo.data.ScriptTagProxy
7251 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7252 * other than the originating domain of the running page.<br><br>
7254 * <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
7255 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7257 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7258 * source code that is used as the source inside a <script> tag.<br><br>
7260 * In order for the browser to process the returned data, the server must wrap the data object
7261 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7262 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7263 * depending on whether the callback name was passed:
7266 boolean scriptTag = false;
7267 String cb = request.getParameter("callback");
7270 response.setContentType("text/javascript");
7272 response.setContentType("application/x-json");
7274 Writer out = response.getWriter();
7276 out.write(cb + "(");
7278 out.print(dataBlock.toJsonString());
7285 * @param {Object} config A configuration object.
7287 Roo.data.ScriptTagProxy = function(config){
7288 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7289 Roo.apply(this, config);
7290 this.head = document.getElementsByTagName("head")[0];
7293 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7295 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7297 * @cfg {String} url The URL from which to request the data object.
7300 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7304 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7305 * the server the name of the callback function set up by the load call to process the returned data object.
7306 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7307 * javascript output which calls this named function passing the data object as its only parameter.
7309 callbackParam : "callback",
7311 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7312 * name to the request.
7317 * Load data from the configured URL, read the data object into
7318 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7319 * process that block using the passed callback.
7320 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7321 * for the request to the remote server.
7322 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7323 * object into a block of Roo.data.Records.
7324 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7325 * The function must be passed <ul>
7326 * <li>The Record block object</li>
7327 * <li>The "arg" argument from the load function</li>
7328 * <li>A boolean success indicator</li>
7330 * @param {Object} scope The scope in which to call the callback
7331 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7333 load : function(params, reader, callback, scope, arg){
7334 if(this.fireEvent("beforeload", this, params) !== false){
7336 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7339 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7341 url += "&_dc=" + (new Date().getTime());
7343 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7346 cb : "stcCallback"+transId,
7347 scriptId : "stcScript"+transId,
7351 callback : callback,
7357 window[trans.cb] = function(o){
7358 conn.handleResponse(o, trans);
7361 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7363 if(this.autoAbort !== false){
7367 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7369 var script = document.createElement("script");
7370 script.setAttribute("src", url);
7371 script.setAttribute("type", "text/javascript");
7372 script.setAttribute("id", trans.scriptId);
7373 this.head.appendChild(script);
7377 callback.call(scope||this, null, arg, false);
7382 isLoading : function(){
7383 return this.trans ? true : false;
7387 * Abort the current server request.
7390 if(this.isLoading()){
7391 this.destroyTrans(this.trans);
7396 destroyTrans : function(trans, isLoaded){
7397 this.head.removeChild(document.getElementById(trans.scriptId));
7398 clearTimeout(trans.timeoutId);
7400 window[trans.cb] = undefined;
7402 delete window[trans.cb];
7405 // if hasn't been loaded, wait for load to remove it to prevent script error
7406 window[trans.cb] = function(){
7407 window[trans.cb] = undefined;
7409 delete window[trans.cb];
7416 handleResponse : function(o, trans){
7418 this.destroyTrans(trans, true);
7421 result = trans.reader.readRecords(o);
7423 this.fireEvent("loadexception", this, o, trans.arg, e);
7424 trans.callback.call(trans.scope||window, null, trans.arg, false);
7427 this.fireEvent("load", this, o, trans.arg);
7428 trans.callback.call(trans.scope||window, result, trans.arg, true);
7432 handleFailure : function(trans){
7434 this.destroyTrans(trans, false);
7435 this.fireEvent("loadexception", this, null, trans.arg);
7436 trans.callback.call(trans.scope||window, null, trans.arg, false);
7440 * Ext JS Library 1.1.1
7441 * Copyright(c) 2006-2007, Ext JS, LLC.
7443 * Originally Released Under LGPL - original licence link has changed is not relivant.
7446 * <script type="text/javascript">
7450 * @class Roo.data.JsonReader
7451 * @extends Roo.data.DataReader
7452 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7453 * based on mappings in a provided Roo.data.Record constructor.
7455 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7456 * in the reply previously.
7461 var RecordDef = Roo.data.Record.create([
7462 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7463 {name: 'occupation'} // This field will use "occupation" as the mapping.
7465 var myReader = new Roo.data.JsonReader({
7466 totalProperty: "results", // The property which contains the total dataset size (optional)
7467 root: "rows", // The property which contains an Array of row objects
7468 id: "id" // The property within each row object that provides an ID for the record (optional)
7472 * This would consume a JSON file like this:
7474 { 'results': 2, 'rows': [
7475 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7476 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7479 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7480 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7481 * paged from the remote server.
7482 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7483 * @cfg {String} root name of the property which contains the Array of row objects.
7484 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7486 * Create a new JsonReader
7487 * @param {Object} meta Metadata configuration options
7488 * @param {Object} recordType Either an Array of field definition objects,
7489 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7491 Roo.data.JsonReader = function(meta, recordType){
7494 // set some defaults:
7496 totalProperty: 'total',
7497 successProperty : 'success',
7502 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7504 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7507 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7508 * Used by Store query builder to append _requestMeta to params.
7511 metaFromRemote : false,
7513 * This method is only used by a DataProxy which has retrieved data from a remote server.
7514 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7515 * @return {Object} data A data block which is used by an Roo.data.Store object as
7516 * a cache of Roo.data.Records.
7518 read : function(response){
7519 var json = response.responseText;
7521 var o = /* eval:var:o */ eval("("+json+")");
7523 throw {message: "JsonReader.read: Json object not found"};
7529 this.metaFromRemote = true;
7530 this.meta = o.metaData;
7531 this.recordType = Roo.data.Record.create(o.metaData.fields);
7532 this.onMetaChange(this.meta, this.recordType, o);
7534 return this.readRecords(o);
7537 // private function a store will implement
7538 onMetaChange : function(meta, recordType, o){
7545 simpleAccess: function(obj, subsc) {
7552 getJsonAccessor: function(){
7554 return function(expr) {
7556 return(re.test(expr))
7557 ? new Function("obj", "return obj." + expr)
7567 * Create a data block containing Roo.data.Records from an XML document.
7568 * @param {Object} o An object which contains an Array of row objects in the property specified
7569 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7570 * which contains the total size of the dataset.
7571 * @return {Object} data A data block which is used by an Roo.data.Store object as
7572 * a cache of Roo.data.Records.
7574 readRecords : function(o){
7576 * After any data loads, the raw JSON data is available for further custom processing.
7580 var s = this.meta, Record = this.recordType,
7581 f = Record.prototype.fields, fi = f.items, fl = f.length;
7583 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7585 if(s.totalProperty) {
7586 this.getTotal = this.getJsonAccessor(s.totalProperty);
7588 if(s.successProperty) {
7589 this.getSuccess = this.getJsonAccessor(s.successProperty);
7591 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7593 var g = this.getJsonAccessor(s.id);
7594 this.getId = function(rec) {
7596 return (r === undefined || r === "") ? null : r;
7599 this.getId = function(){return null;};
7602 for(var jj = 0; jj < fl; jj++){
7604 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7605 this.ef[jj] = this.getJsonAccessor(map);
7609 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7610 if(s.totalProperty){
7611 var vt = parseInt(this.getTotal(o), 10);
7616 if(s.successProperty){
7617 var vs = this.getSuccess(o);
7618 if(vs === false || vs === 'false'){
7623 for(var i = 0; i < c; i++){
7626 var id = this.getId(n);
7627 for(var j = 0; j < fl; j++){
7629 var v = this.ef[j](n);
7631 Roo.log('missing convert for ' + f.name);
7635 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7637 var record = new Record(values, id);
7639 records[i] = record;
7645 totalRecords : totalRecords
7650 * Ext JS Library 1.1.1
7651 * Copyright(c) 2006-2007, Ext JS, LLC.
7653 * Originally Released Under LGPL - original licence link has changed is not relivant.
7656 * <script type="text/javascript">
7660 * @class Roo.data.ArrayReader
7661 * @extends Roo.data.DataReader
7662 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7663 * Each element of that Array represents a row of data fields. The
7664 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7665 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7669 var RecordDef = Roo.data.Record.create([
7670 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7671 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7673 var myReader = new Roo.data.ArrayReader({
7674 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7678 * This would consume an Array like this:
7680 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7682 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7684 * Create a new JsonReader
7685 * @param {Object} meta Metadata configuration options.
7686 * @param {Object} recordType Either an Array of field definition objects
7687 * as specified to {@link Roo.data.Record#create},
7688 * or an {@link Roo.data.Record} object
7689 * created using {@link Roo.data.Record#create}.
7691 Roo.data.ArrayReader = function(meta, recordType){
7692 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7695 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7697 * Create a data block containing Roo.data.Records from an XML document.
7698 * @param {Object} o An Array of row objects which represents the dataset.
7699 * @return {Object} data A data block which is used by an Roo.data.Store object as
7700 * a cache of Roo.data.Records.
7702 readRecords : function(o){
7703 var sid = this.meta ? this.meta.id : null;
7704 var recordType = this.recordType, fields = recordType.prototype.fields;
7707 for(var i = 0; i < root.length; i++){
7710 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7711 for(var j = 0, jlen = fields.length; j < jlen; j++){
7712 var f = fields.items[j];
7713 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7714 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7718 var record = new recordType(values, id);
7720 records[records.length] = record;
7724 totalRecords : records.length
7733 * @class Roo.bootstrap.ComboBox
7734 * @extends Roo.bootstrap.TriggerField
7735 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7736 * @cfg {Boolean} append (true|false) default false
7738 * Create a new ComboBox.
7739 * @param {Object} config Configuration options
7741 Roo.bootstrap.ComboBox = function(config){
7742 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7746 * Fires when the dropdown list is expanded
7747 * @param {Roo.bootstrap.ComboBox} combo This combo box
7752 * Fires when the dropdown list is collapsed
7753 * @param {Roo.bootstrap.ComboBox} combo This combo box
7757 * @event beforeselect
7758 * Fires before a list item is selected. Return false to cancel the selection.
7759 * @param {Roo.bootstrap.ComboBox} combo This combo box
7760 * @param {Roo.data.Record} record The data record returned from the underlying store
7761 * @param {Number} index The index of the selected item in the dropdown list
7763 'beforeselect' : true,
7766 * Fires when a list item is selected
7767 * @param {Roo.bootstrap.ComboBox} combo This combo box
7768 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7769 * @param {Number} index The index of the selected item in the dropdown list
7773 * @event beforequery
7774 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7775 * The event object passed has these properties:
7776 * @param {Roo.bootstrap.ComboBox} combo This combo box
7777 * @param {String} query The query
7778 * @param {Boolean} forceAll true to force "all" query
7779 * @param {Boolean} cancel true to cancel the query
7780 * @param {Object} e The query event object
7782 'beforequery': true,
7785 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7786 * @param {Roo.bootstrap.ComboBox} combo This combo box
7791 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7792 * @param {Roo.bootstrap.ComboBox} combo This combo box
7793 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7798 * Fires when the remove value from the combobox array
7799 * @param {Roo.bootstrap.ComboBox} combo This combo box
7806 this.selectedIndex = -1;
7807 if(this.mode == 'local'){
7808 if(config.queryDelay === undefined){
7809 this.queryDelay = 10;
7811 if(config.minChars === undefined){
7817 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7820 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7821 * rendering into an Roo.Editor, defaults to false)
7824 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7825 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7828 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7831 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7832 * the dropdown list (defaults to undefined, with no header element)
7836 * @cfg {String/Roo.Template} tpl The template to use to render the output
7840 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7842 listWidth: undefined,
7844 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7845 * mode = 'remote' or 'text' if mode = 'local')
7847 displayField: undefined,
7849 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7850 * mode = 'remote' or 'value' if mode = 'local').
7851 * Note: use of a valueField requires the user make a selection
7852 * in order for a value to be mapped.
7854 valueField: undefined,
7858 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7859 * field's data value (defaults to the underlying DOM element's name)
7861 hiddenName: undefined,
7863 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7867 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7869 selectedClass: 'active',
7872 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7876 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7877 * anchor positions (defaults to 'tl-bl')
7879 listAlign: 'tl-bl?',
7881 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7885 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7886 * query specified by the allQuery config option (defaults to 'query')
7888 triggerAction: 'query',
7890 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7891 * (defaults to 4, does not apply if editable = false)
7895 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7896 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7900 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7901 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7905 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7906 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7910 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7911 * when editable = true (defaults to false)
7913 selectOnFocus:false,
7915 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7917 queryParam: 'query',
7919 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7920 * when mode = 'remote' (defaults to 'Loading...')
7922 loadingText: 'Loading...',
7924 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7928 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7932 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7933 * traditional select (defaults to true)
7937 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7941 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7945 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7946 * listWidth has a higher value)
7950 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7951 * allow the user to set arbitrary text into the field (defaults to false)
7953 forceSelection:false,
7955 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7956 * if typeAhead = true (defaults to 250)
7958 typeAheadDelay : 250,
7960 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7961 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7963 valueNotFoundText : undefined,
7965 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7970 * @cfg {Boolean} disableClear Disable showing of clear button.
7972 disableClear : false,
7974 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7976 alwaysQuery : false,
7979 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
7993 // element that contains real text value.. (when hidden is used..)
7996 initEvents: function(){
7999 throw "can not find store for combo";
8001 this.store = Roo.factory(this.store, Roo.data);
8005 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8008 if(this.hiddenName){
8010 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8012 this.hiddenField.dom.value =
8013 this.hiddenValue !== undefined ? this.hiddenValue :
8014 this.value !== undefined ? this.value : '';
8016 // prevent input submission
8017 this.el.dom.removeAttribute('name');
8018 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8023 // this.el.dom.setAttribute('autocomplete', 'off');
8026 var cls = 'x-combo-list';
8027 this.list = this.el.select('ul.dropdown-menu',true).first();
8029 //this.list = new Roo.Layer({
8030 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8033 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8034 this.list.setWidth(lw);
8036 this.list.on('mouseover', this.onViewOver, this);
8037 this.list.on('mousemove', this.onViewMove, this);
8039 this.list.on('scroll', this.onViewScroll, this);
8042 this.list.swallowEvent('mousewheel');
8043 this.assetHeight = 0;
8046 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8047 this.assetHeight += this.header.getHeight();
8050 this.innerList = this.list.createChild({cls:cls+'-inner'});
8051 this.innerList.on('mouseover', this.onViewOver, this);
8052 this.innerList.on('mousemove', this.onViewMove, this);
8053 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8055 if(this.allowBlank && !this.pageSize && !this.disableClear){
8056 this.footer = this.list.createChild({cls:cls+'-ft'});
8057 this.pageTb = new Roo.Toolbar(this.footer);
8061 this.footer = this.list.createChild({cls:cls+'-ft'});
8062 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8063 {pageSize: this.pageSize});
8067 if (this.pageTb && this.allowBlank && !this.disableClear) {
8069 this.pageTb.add(new Roo.Toolbar.Fill(), {
8070 cls: 'x-btn-icon x-btn-clear',
8076 _this.onSelect(false, -1);
8081 this.assetHeight += this.footer.getHeight();
8086 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8089 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8090 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8092 //this.view.wrapEl.setDisplayed(false);
8093 this.view.on('click', this.onViewClick, this);
8097 this.store.on('beforeload', this.onBeforeLoad, this);
8098 this.store.on('load', this.onLoad, this);
8099 this.store.on('loadexception', this.onLoadException, this);
8102 this.resizer = new Roo.Resizable(this.list, {
8103 pinned:true, handles:'se'
8105 this.resizer.on('resize', function(r, w, h){
8106 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8108 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8109 this.restrictHeight();
8111 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8115 this.editable = true;
8116 this.setEditable(false);
8121 if (typeof(this.events.add.listeners) != 'undefined') {
8123 this.addicon = this.wrap.createChild(
8124 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8126 this.addicon.on('click', function(e) {
8127 this.fireEvent('add', this);
8130 if (typeof(this.events.edit.listeners) != 'undefined') {
8132 this.editicon = this.wrap.createChild(
8133 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8135 this.editicon.setStyle('margin-left', '40px');
8137 this.editicon.on('click', function(e) {
8139 // we fire even if inothing is selected..
8140 this.fireEvent('edit', this, this.lastData );
8146 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8148 this.inKeyMode = true;
8152 "down" : function(e){
8153 if(!this.isExpanded()){
8154 this.onTriggerClick();
8156 this.inKeyMode = true;
8161 "enter" : function(e){
8166 "esc" : function(e){
8170 "tab" : function(e){
8173 if(this.fireEvent("specialkey", this, e)){
8174 this.onViewClick(false);
8182 doRelay : function(foo, bar, hname){
8183 if(hname == 'down' || this.scope.isExpanded()){
8184 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8193 this.queryDelay = Math.max(this.queryDelay || 10,
8194 this.mode == 'local' ? 10 : 250);
8197 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8200 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8202 if(this.editable !== false){
8203 this.inputEl().on("keyup", this.onKeyUp, this);
8205 if(this.forceSelection){
8206 this.on('blur', this.doForce, this);
8210 this.choices = this.el.select('ul.select2-choices', true).first();
8211 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8215 onDestroy : function(){
8217 this.view.setStore(null);
8218 this.view.el.removeAllListeners();
8219 this.view.el.remove();
8220 this.view.purgeListeners();
8223 this.list.dom.innerHTML = '';
8226 this.store.un('beforeload', this.onBeforeLoad, this);
8227 this.store.un('load', this.onLoad, this);
8228 this.store.un('loadexception', this.onLoadException, this);
8230 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8234 fireKey : function(e){
8235 if(e.isNavKeyPress() && !this.list.isVisible()){
8236 this.fireEvent("specialkey", this, e);
8241 onResize: function(w, h){
8242 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8244 // if(typeof w != 'number'){
8245 // // we do not handle it!?!?
8248 // var tw = this.trigger.getWidth();
8249 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8250 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8252 // this.inputEl().setWidth( this.adjustWidth('input', x));
8254 // //this.trigger.setStyle('left', x+'px');
8256 // if(this.list && this.listWidth === undefined){
8257 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8258 // this.list.setWidth(lw);
8259 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8267 * Allow or prevent the user from directly editing the field text. If false is passed,
8268 * the user will only be able to select from the items defined in the dropdown list. This method
8269 * is the runtime equivalent of setting the 'editable' config option at config time.
8270 * @param {Boolean} value True to allow the user to directly edit the field text
8272 setEditable : function(value){
8273 if(value == this.editable){
8276 this.editable = value;
8278 this.inputEl().dom.setAttribute('readOnly', true);
8279 this.inputEl().on('mousedown', this.onTriggerClick, this);
8280 this.inputEl().addClass('x-combo-noedit');
8282 this.inputEl().dom.setAttribute('readOnly', false);
8283 this.inputEl().un('mousedown', this.onTriggerClick, this);
8284 this.inputEl().removeClass('x-combo-noedit');
8290 onBeforeLoad : function(combo,opts){
8295 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8297 this.restrictHeight();
8298 this.selectedIndex = -1;
8302 onLoad : function(){
8304 this.hasQuery = false;
8310 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8311 this.loading.hide();
8314 if(this.store.getCount() > 0){
8316 this.restrictHeight();
8317 if(this.lastQuery == this.allQuery){
8319 this.inputEl().dom.select();
8321 if(!this.selectByValue(this.value, true)){
8322 this.select(0, true);
8326 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8327 this.taTask.delay(this.typeAheadDelay);
8331 this.onEmptyResults();
8337 onLoadException : function()
8339 this.hasQuery = false;
8341 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8342 this.loading.hide();
8346 Roo.log(this.store.reader.jsonData);
8347 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8349 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8355 onTypeAhead : function(){
8356 if(this.store.getCount() > 0){
8357 var r = this.store.getAt(0);
8358 var newValue = r.data[this.displayField];
8359 var len = newValue.length;
8360 var selStart = this.getRawValue().length;
8362 if(selStart != len){
8363 this.setRawValue(newValue);
8364 this.selectText(selStart, newValue.length);
8370 onSelect : function(record, index){
8372 if(this.fireEvent('beforeselect', this, record, index) !== false){
8374 this.setFromData(index > -1 ? record.data : false);
8377 this.fireEvent('select', this, record, index);
8382 * Returns the currently selected field value or empty string if no value is set.
8383 * @return {String} value The selected value
8385 getValue : function(){
8388 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8391 if(this.valueField){
8392 return typeof this.value != 'undefined' ? this.value : '';
8394 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8399 * Clears any text/value currently set in the field
8401 clearValue : function(){
8402 if(this.hiddenField){
8403 this.hiddenField.dom.value = '';
8406 this.setRawValue('');
8407 this.lastSelectionText = '';
8412 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8413 * will be displayed in the field. If the value does not match the data value of an existing item,
8414 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8415 * Otherwise the field will be blank (although the value will still be set).
8416 * @param {String} value The value to match
8418 setValue : function(v){
8425 if(this.valueField){
8426 var r = this.findRecord(this.valueField, v);
8428 text = r.data[this.displayField];
8429 }else if(this.valueNotFoundText !== undefined){
8430 text = this.valueNotFoundText;
8433 this.lastSelectionText = text;
8434 if(this.hiddenField){
8435 this.hiddenField.dom.value = v;
8437 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8441 * @property {Object} the last set data for the element
8446 * Sets the value of the field based on a object which is related to the record format for the store.
8447 * @param {Object} value the value to set as. or false on reset?
8449 setFromData : function(o){
8456 var dv = ''; // display value
8457 var vv = ''; // value value..
8459 if (this.displayField) {
8460 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8462 // this is an error condition!!!
8463 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8466 if(this.valueField){
8467 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8470 if(this.hiddenField){
8471 this.hiddenField.dom.value = vv;
8473 this.lastSelectionText = dv;
8474 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8478 // no hidden field.. - we store the value in 'value', but still display
8479 // display field!!!!
8480 this.lastSelectionText = dv;
8481 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8488 // overridden so that last data is reset..
8489 this.setValue(this.originalValue);
8490 this.clearInvalid();
8491 this.lastData = false;
8493 this.view.clearSelections();
8497 findRecord : function(prop, value){
8499 if(this.store.getCount() > 0){
8500 this.store.each(function(r){
8501 if(r.data[prop] == value){
8513 // returns hidden if it's set..
8514 if (!this.rendered) {return ''};
8515 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8519 onViewMove : function(e, t){
8520 this.inKeyMode = false;
8524 onViewOver : function(e, t){
8525 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8528 var item = this.view.findItemFromChild(t);
8530 var index = this.view.indexOf(item);
8531 this.select(index, false);
8536 onViewClick : function(doFocus)
8538 var index = this.view.getSelectedIndexes()[0];
8539 var r = this.store.getAt(index);
8541 this.onSelect(r, index);
8543 if(doFocus !== false && !this.blockFocus){
8544 this.inputEl().focus();
8549 restrictHeight : function(){
8550 //this.innerList.dom.style.height = '';
8551 //var inner = this.innerList.dom;
8552 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8553 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8554 //this.list.beginUpdate();
8555 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8556 this.list.alignTo(this.inputEl(), this.listAlign);
8557 //this.list.endUpdate();
8561 onEmptyResults : function(){
8566 * Returns true if the dropdown list is expanded, else false.
8568 isExpanded : function(){
8569 return this.list.isVisible();
8573 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8574 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8575 * @param {String} value The data value of the item to select
8576 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8577 * selected item if it is not currently in view (defaults to true)
8578 * @return {Boolean} True if the value matched an item in the list, else false
8580 selectByValue : function(v, scrollIntoView){
8581 if(v !== undefined && v !== null){
8582 var r = this.findRecord(this.valueField || this.displayField, v);
8584 this.select(this.store.indexOf(r), scrollIntoView);
8592 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8593 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8594 * @param {Number} index The zero-based index of the list item to select
8595 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8596 * selected item if it is not currently in view (defaults to true)
8598 select : function(index, scrollIntoView){
8599 this.selectedIndex = index;
8600 this.view.select(index);
8601 if(scrollIntoView !== false){
8602 var el = this.view.getNode(index);
8604 //this.innerList.scrollChildIntoView(el, false);
8611 selectNext : function(){
8612 var ct = this.store.getCount();
8614 if(this.selectedIndex == -1){
8616 }else if(this.selectedIndex < ct-1){
8617 this.select(this.selectedIndex+1);
8623 selectPrev : function(){
8624 var ct = this.store.getCount();
8626 if(this.selectedIndex == -1){
8628 }else if(this.selectedIndex != 0){
8629 this.select(this.selectedIndex-1);
8635 onKeyUp : function(e){
8636 if(this.editable !== false && !e.isSpecialKey()){
8637 this.lastKey = e.getKey();
8638 this.dqTask.delay(this.queryDelay);
8643 validateBlur : function(){
8644 return !this.list || !this.list.isVisible();
8648 initQuery : function(){
8649 this.doQuery(this.getRawValue());
8653 doForce : function(){
8654 if(this.el.dom.value.length > 0){
8656 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8662 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8663 * query allowing the query action to be canceled if needed.
8664 * @param {String} query The SQL query to execute
8665 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8666 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8667 * saved in the current store (defaults to false)
8669 doQuery : function(q, forceAll){
8671 if(q === undefined || q === null){
8680 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8685 forceAll = qe.forceAll;
8686 if(forceAll === true || (q.length >= this.minChars)){
8688 this.hasQuery = true;
8690 if(this.lastQuery != q || this.alwaysQuery){
8692 if(this.mode == 'local'){
8693 this.selectedIndex = -1;
8695 this.store.clearFilter();
8697 this.store.filter(this.displayField, q);
8701 this.store.baseParams[this.queryParam] = q;
8703 var options = {params : this.getParams(q)};
8707 options.params.start = this.page * this.pageSize;
8710 this.store.load(options);
8714 this.selectedIndex = -1;
8719 this.loadNext = false;
8723 getParams : function(q){
8725 //p[this.queryParam] = q;
8729 p.limit = this.pageSize;
8735 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8737 collapse : function(){
8738 if(!this.isExpanded()){
8743 Roo.get(document).un('mousedown', this.collapseIf, this);
8744 Roo.get(document).un('mousewheel', this.collapseIf, this);
8745 if (!this.editable) {
8746 Roo.get(document).un('keydown', this.listKeyPress, this);
8748 this.fireEvent('collapse', this);
8752 collapseIf : function(e){
8753 var in_combo = e.within(this.el);
8754 var in_list = e.within(this.list);
8756 if (in_combo || in_list) {
8757 //e.stopPropagation();
8766 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8768 expand : function(){
8770 if(this.isExpanded() || !this.hasFocus){
8774 this.list.alignTo(this.inputEl(), this.listAlign);
8776 Roo.get(document).on('mousedown', this.collapseIf, this);
8777 Roo.get(document).on('mousewheel', this.collapseIf, this);
8778 if (!this.editable) {
8779 Roo.get(document).on('keydown', this.listKeyPress, this);
8782 this.fireEvent('expand', this);
8786 // Implements the default empty TriggerField.onTriggerClick function
8787 onTriggerClick : function()
8789 Roo.log('trigger click');
8796 this.loadNext = false;
8798 if(this.isExpanded()){
8800 if (!this.blockFocus) {
8801 this.inputEl().focus();
8805 this.hasFocus = true;
8806 if(this.triggerAction == 'all') {
8807 this.doQuery(this.allQuery, true);
8809 this.doQuery(this.getRawValue());
8811 if (!this.blockFocus) {
8812 this.inputEl().focus();
8816 listKeyPress : function(e)
8818 //Roo.log('listkeypress');
8819 // scroll to first matching element based on key pres..
8820 if (e.isSpecialKey()) {
8823 var k = String.fromCharCode(e.getKey()).toUpperCase();
8826 var csel = this.view.getSelectedNodes();
8827 var cselitem = false;
8829 var ix = this.view.indexOf(csel[0]);
8830 cselitem = this.store.getAt(ix);
8831 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8837 this.store.each(function(v) {
8839 // start at existing selection.
8840 if (cselitem.id == v.id) {
8846 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8847 match = this.store.indexOf(v);
8853 if (match === false) {
8854 return true; // no more action?
8857 this.view.select(match);
8858 var sn = Roo.get(this.view.getSelectedNodes()[0])
8859 //sn.scrollIntoView(sn.dom.parentNode, false);
8862 onViewScroll : function(e, t){
8864 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8868 this.hasQuery = true;
8870 this.loading = this.list.select('.loading', true).first();
8872 if(this.loading === null){
8873 this.list.createChild({
8875 cls: 'loading select2-more-results select2-active',
8876 html: 'Loading more results...'
8879 this.loading = this.list.select('.loading', true).first();
8881 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8883 this.loading.hide();
8886 this.loading.show();
8891 this.loadNext = true;
8893 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8898 addItem : function(o)
8900 var dv = ''; // display value
8902 if (this.displayField) {
8903 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8905 // this is an error condition!!!
8906 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8913 var choice = this.choices.createChild({
8915 cls: 'select2-search-choice',
8924 cls: 'select2-search-choice-close',
8929 }, this.searchField);
8931 var close = choice.select('a.select2-search-choice-close', true).first()
8933 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8940 this.inputEl().dom.value = '';
8944 onRemoveItem : function(e, _self, o)
8946 Roo.log('remove item');
8947 var index = this.item.indexOf(o.data) * 1;
8950 Roo.log('not this item?!');
8954 this.item.splice(index, 1);
8959 this.fireEvent('remove', this);
8963 syncValue : function()
8965 if(!this.item.length){
8972 Roo.each(this.item, function(i){
8973 if(_this.valueField){
8974 value.push(i[_this.valueField]);
8981 this.value = value.join(',');
8983 if(this.hiddenField){
8984 this.hiddenField.dom.value = this.value;
8988 clearItem : function()
8996 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9006 * @cfg {Boolean} grow
9010 * @cfg {Number} growMin
9014 * @cfg {Number} growMax
9024 * Ext JS Library 1.1.1
9025 * Copyright(c) 2006-2007, Ext JS, LLC.
9027 * Originally Released Under LGPL - original licence link has changed is not relivant.
9030 * <script type="text/javascript">
9035 * @extends Roo.util.Observable
9036 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9037 * This class also supports single and multi selection modes. <br>
9038 * Create a data model bound view:
9040 var store = new Roo.data.Store(...);
9042 var view = new Roo.View({
9044 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9047 selectedClass: "ydataview-selected",
9051 // listen for node click?
9052 view.on("click", function(vw, index, node, e){
9053 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9057 dataModel.load("foobar.xml");
9059 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9061 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9062 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9064 * Note: old style constructor is still suported (container, template, config)
9068 * @param {Object} config The config object
9071 Roo.View = function(config, depreciated_tpl, depreciated_config){
9073 if (typeof(depreciated_tpl) == 'undefined') {
9074 // new way.. - universal constructor.
9075 Roo.apply(this, config);
9076 this.el = Roo.get(this.el);
9079 this.el = Roo.get(config);
9080 this.tpl = depreciated_tpl;
9081 Roo.apply(this, depreciated_config);
9083 this.wrapEl = this.el.wrap().wrap();
9084 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9087 if(typeof(this.tpl) == "string"){
9088 this.tpl = new Roo.Template(this.tpl);
9090 // support xtype ctors..
9091 this.tpl = new Roo.factory(this.tpl, Roo);
9103 * @event beforeclick
9104 * Fires before a click is processed. Returns false to cancel the default action.
9105 * @param {Roo.View} this
9106 * @param {Number} index The index of the target node
9107 * @param {HTMLElement} node The target node
9108 * @param {Roo.EventObject} e The raw event object
9110 "beforeclick" : true,
9113 * Fires when a template node is clicked.
9114 * @param {Roo.View} this
9115 * @param {Number} index The index of the target node
9116 * @param {HTMLElement} node The target node
9117 * @param {Roo.EventObject} e The raw event object
9122 * Fires when a template node is double clicked.
9123 * @param {Roo.View} this
9124 * @param {Number} index The index of the target node
9125 * @param {HTMLElement} node The target node
9126 * @param {Roo.EventObject} e The raw event object
9130 * @event contextmenu
9131 * Fires when a template node is right clicked.
9132 * @param {Roo.View} this
9133 * @param {Number} index The index of the target node
9134 * @param {HTMLElement} node The target node
9135 * @param {Roo.EventObject} e The raw event object
9137 "contextmenu" : true,
9139 * @event selectionchange
9140 * Fires when the selected nodes change.
9141 * @param {Roo.View} this
9142 * @param {Array} selections Array of the selected nodes
9144 "selectionchange" : true,
9147 * @event beforeselect
9148 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9149 * @param {Roo.View} this
9150 * @param {HTMLElement} node The node to be selected
9151 * @param {Array} selections Array of currently selected nodes
9153 "beforeselect" : true,
9155 * @event preparedata
9156 * Fires on every row to render, to allow you to change the data.
9157 * @param {Roo.View} this
9158 * @param {Object} data to be rendered (change this)
9160 "preparedata" : true
9168 "click": this.onClick,
9169 "dblclick": this.onDblClick,
9170 "contextmenu": this.onContextMenu,
9174 this.selections = [];
9176 this.cmp = new Roo.CompositeElementLite([]);
9178 this.store = Roo.factory(this.store, Roo.data);
9179 this.setStore(this.store, true);
9182 if ( this.footer && this.footer.xtype) {
9184 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9186 this.footer.dataSource = this.store
9187 this.footer.container = fctr;
9188 this.footer = Roo.factory(this.footer, Roo);
9189 fctr.insertFirst(this.el);
9191 // this is a bit insane - as the paging toolbar seems to detach the el..
9192 // dom.parentNode.parentNode.parentNode
9193 // they get detached?
9197 Roo.View.superclass.constructor.call(this);
9202 Roo.extend(Roo.View, Roo.util.Observable, {
9205 * @cfg {Roo.data.Store} store Data store to load data from.
9210 * @cfg {String|Roo.Element} el The container element.
9215 * @cfg {String|Roo.Template} tpl The template used by this View
9219 * @cfg {String} dataName the named area of the template to use as the data area
9220 * Works with domtemplates roo-name="name"
9224 * @cfg {String} selectedClass The css class to add to selected nodes
9226 selectedClass : "x-view-selected",
9228 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9233 * @cfg {String} text to display on mask (default Loading)
9237 * @cfg {Boolean} multiSelect Allow multiple selection
9239 multiSelect : false,
9241 * @cfg {Boolean} singleSelect Allow single selection
9243 singleSelect: false,
9246 * @cfg {Boolean} toggleSelect - selecting
9248 toggleSelect : false,
9251 * Returns the element this view is bound to.
9252 * @return {Roo.Element}
9261 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9263 refresh : function(){
9267 // if we are using something like 'domtemplate', then
9268 // the what gets used is:
9269 // t.applySubtemplate(NAME, data, wrapping data..)
9270 // the outer template then get' applied with
9271 // the store 'extra data'
9272 // and the body get's added to the
9273 // roo-name="data" node?
9274 // <span class='roo-tpl-{name}'></span> ?????
9278 this.clearSelections();
9281 var records = this.store.getRange();
9282 if(records.length < 1) {
9284 // is this valid?? = should it render a template??
9286 this.el.update(this.emptyText);
9290 if (this.dataName) {
9291 this.el.update(t.apply(this.store.meta)); //????
9292 el = this.el.child('.roo-tpl-' + this.dataName);
9295 for(var i = 0, len = records.length; i < len; i++){
9296 var data = this.prepareData(records[i].data, i, records[i]);
9297 this.fireEvent("preparedata", this, data, i, records[i]);
9298 html[html.length] = Roo.util.Format.trim(
9300 t.applySubtemplate(this.dataName, data, this.store.meta) :
9307 el.update(html.join(""));
9308 this.nodes = el.dom.childNodes;
9309 this.updateIndexes(0);
9314 * Function to override to reformat the data that is sent to
9315 * the template for each node.
9316 * DEPRICATED - use the preparedata event handler.
9317 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9318 * a JSON object for an UpdateManager bound view).
9320 prepareData : function(data, index, record)
9322 this.fireEvent("preparedata", this, data, index, record);
9326 onUpdate : function(ds, record){
9327 Roo.log('on update');
9328 this.clearSelections();
9329 var index = this.store.indexOf(record);
9330 var n = this.nodes[index];
9331 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9332 n.parentNode.removeChild(n);
9333 this.updateIndexes(index, index);
9339 onAdd : function(ds, records, index)
9341 Roo.log(['on Add', ds, records, index] );
9342 this.clearSelections();
9343 if(this.nodes.length == 0){
9347 var n = this.nodes[index];
9348 for(var i = 0, len = records.length; i < len; i++){
9349 var d = this.prepareData(records[i].data, i, records[i]);
9351 this.tpl.insertBefore(n, d);
9354 this.tpl.append(this.el, d);
9357 this.updateIndexes(index);
9360 onRemove : function(ds, record, index){
9361 Roo.log('onRemove');
9362 this.clearSelections();
9363 var el = this.dataName ?
9364 this.el.child('.roo-tpl-' + this.dataName) :
9367 el.dom.removeChild(this.nodes[index]);
9368 this.updateIndexes(index);
9372 * Refresh an individual node.
9373 * @param {Number} index
9375 refreshNode : function(index){
9376 this.onUpdate(this.store, this.store.getAt(index));
9379 updateIndexes : function(startIndex, endIndex){
9380 var ns = this.nodes;
9381 startIndex = startIndex || 0;
9382 endIndex = endIndex || ns.length - 1;
9383 for(var i = startIndex; i <= endIndex; i++){
9384 ns[i].nodeIndex = i;
9389 * Changes the data store this view uses and refresh the view.
9390 * @param {Store} store
9392 setStore : function(store, initial){
9393 if(!initial && this.store){
9394 this.store.un("datachanged", this.refresh);
9395 this.store.un("add", this.onAdd);
9396 this.store.un("remove", this.onRemove);
9397 this.store.un("update", this.onUpdate);
9398 this.store.un("clear", this.refresh);
9399 this.store.un("beforeload", this.onBeforeLoad);
9400 this.store.un("load", this.onLoad);
9401 this.store.un("loadexception", this.onLoad);
9405 store.on("datachanged", this.refresh, this);
9406 store.on("add", this.onAdd, this);
9407 store.on("remove", this.onRemove, this);
9408 store.on("update", this.onUpdate, this);
9409 store.on("clear", this.refresh, this);
9410 store.on("beforeload", this.onBeforeLoad, this);
9411 store.on("load", this.onLoad, this);
9412 store.on("loadexception", this.onLoad, this);
9420 * onbeforeLoad - masks the loading area.
9423 onBeforeLoad : function(store,opts)
9425 Roo.log('onBeforeLoad');
9429 this.el.mask(this.mask ? this.mask : "Loading" );
9431 onLoad : function ()
9438 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9439 * @param {HTMLElement} node
9440 * @return {HTMLElement} The template node
9442 findItemFromChild : function(node){
9443 var el = this.dataName ?
9444 this.el.child('.roo-tpl-' + this.dataName,true) :
9447 if(!node || node.parentNode == el){
9450 var p = node.parentNode;
9451 while(p && p != el){
9452 if(p.parentNode == el){
9461 onClick : function(e){
9462 var item = this.findItemFromChild(e.getTarget());
9464 var index = this.indexOf(item);
9465 if(this.onItemClick(item, index, e) !== false){
9466 this.fireEvent("click", this, index, item, e);
9469 this.clearSelections();
9474 onContextMenu : function(e){
9475 var item = this.findItemFromChild(e.getTarget());
9477 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9482 onDblClick : function(e){
9483 var item = this.findItemFromChild(e.getTarget());
9485 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9489 onItemClick : function(item, index, e)
9491 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9494 if (this.toggleSelect) {
9495 var m = this.isSelected(item) ? 'unselect' : 'select';
9498 _t[m](item, true, false);
9501 if(this.multiSelect || this.singleSelect){
9502 if(this.multiSelect && e.shiftKey && this.lastSelection){
9503 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9505 this.select(item, this.multiSelect && e.ctrlKey);
9506 this.lastSelection = item;
9514 * Get the number of selected nodes.
9517 getSelectionCount : function(){
9518 return this.selections.length;
9522 * Get the currently selected nodes.
9523 * @return {Array} An array of HTMLElements
9525 getSelectedNodes : function(){
9526 return this.selections;
9530 * Get the indexes of the selected nodes.
9533 getSelectedIndexes : function(){
9534 var indexes = [], s = this.selections;
9535 for(var i = 0, len = s.length; i < len; i++){
9536 indexes.push(s[i].nodeIndex);
9542 * Clear all selections
9543 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9545 clearSelections : function(suppressEvent){
9546 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9547 this.cmp.elements = this.selections;
9548 this.cmp.removeClass(this.selectedClass);
9549 this.selections = [];
9551 this.fireEvent("selectionchange", this, this.selections);
9557 * Returns true if the passed node is selected
9558 * @param {HTMLElement/Number} node The node or node index
9561 isSelected : function(node){
9562 var s = this.selections;
9566 node = this.getNode(node);
9567 return s.indexOf(node) !== -1;
9572 * @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
9573 * @param {Boolean} keepExisting (optional) true to keep existing selections
9574 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9576 select : function(nodeInfo, keepExisting, suppressEvent){
9577 if(nodeInfo instanceof Array){
9579 this.clearSelections(true);
9581 for(var i = 0, len = nodeInfo.length; i < len; i++){
9582 this.select(nodeInfo[i], true, true);
9586 var node = this.getNode(nodeInfo);
9587 if(!node || this.isSelected(node)){
9588 return; // already selected.
9591 this.clearSelections(true);
9593 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9594 Roo.fly(node).addClass(this.selectedClass);
9595 this.selections.push(node);
9597 this.fireEvent("selectionchange", this, this.selections);
9605 * @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
9606 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9607 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9609 unselect : function(nodeInfo, keepExisting, suppressEvent)
9611 if(nodeInfo instanceof Array){
9612 Roo.each(this.selections, function(s) {
9613 this.unselect(s, nodeInfo);
9617 var node = this.getNode(nodeInfo);
9618 if(!node || !this.isSelected(node)){
9619 Roo.log("not selected");
9620 return; // not selected.
9624 Roo.each(this.selections, function(s) {
9626 Roo.fly(node).removeClass(this.selectedClass);
9633 this.selections= ns;
9634 this.fireEvent("selectionchange", this, this.selections);
9638 * Gets a template node.
9639 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9640 * @return {HTMLElement} The node or null if it wasn't found
9642 getNode : function(nodeInfo){
9643 if(typeof nodeInfo == "string"){
9644 return document.getElementById(nodeInfo);
9645 }else if(typeof nodeInfo == "number"){
9646 return this.nodes[nodeInfo];
9652 * Gets a range template nodes.
9653 * @param {Number} startIndex
9654 * @param {Number} endIndex
9655 * @return {Array} An array of nodes
9657 getNodes : function(start, end){
9658 var ns = this.nodes;
9660 end = typeof end == "undefined" ? ns.length - 1 : end;
9663 for(var i = start; i <= end; i++){
9667 for(var i = start; i >= end; i--){
9675 * Finds the index of the passed node
9676 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9677 * @return {Number} The index of the node or -1
9679 indexOf : function(node){
9680 node = this.getNode(node);
9681 if(typeof node.nodeIndex == "number"){
9682 return node.nodeIndex;
9684 var ns = this.nodes;
9685 for(var i = 0, len = ns.length; i < len; i++){
9696 * based on jquery fullcalendar
9700 Roo.bootstrap = Roo.bootstrap || {};
9702 * @class Roo.bootstrap.Calendar
9703 * @extends Roo.bootstrap.Component
9704 * Bootstrap Calendar class
9705 * @cfg {Boolean} loadMask (true|false) default false
9708 * Create a new Container
9709 * @param {Object} config The config object
9714 Roo.bootstrap.Calendar = function(config){
9715 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9719 * Fires when a date is selected
9720 * @param {DatePicker} this
9721 * @param {Date} date The selected date
9725 * @event monthchange
9726 * Fires when the displayed month changes
9727 * @param {DatePicker} this
9728 * @param {Date} date The selected month
9730 'monthchange': true,
9733 * Fires when mouse over an event
9734 * @param {Calendar} this
9735 * @param {event} Event
9740 * Fires when the mouse leaves an
9741 * @param {Calendar} this
9747 * Fires when the mouse click an
9748 * @param {Calendar} this
9757 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9760 * @cfg {Number} startDay
9761 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9767 getAutoCreate : function(){
9770 var fc_button = function(name, corner, style, content ) {
9771 return Roo.apply({},{
9773 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9775 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9778 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9786 style : 'width:100%',
9793 cls : 'fc-header-left',
9795 fc_button('prev', 'left', 'arrow', '‹' ),
9796 fc_button('next', 'right', 'arrow', '›' ),
9797 { tag: 'span', cls: 'fc-header-space' },
9798 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9806 cls : 'fc-header-center',
9810 cls: 'fc-header-title',
9813 html : 'month / year'
9821 cls : 'fc-header-right',
9823 /* fc_button('month', 'left', '', 'month' ),
9824 fc_button('week', '', '', 'week' ),
9825 fc_button('day', 'right', '', 'day' )
9837 var cal_heads = function() {
9839 // fixme - handle this.
9841 for (var i =0; i < Date.dayNames.length; i++) {
9842 var d = Date.dayNames[i];
9845 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9846 html : d.substring(0,3)
9850 ret[0].cls += ' fc-first';
9851 ret[6].cls += ' fc-last';
9854 var cal_cell = function(n) {
9857 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9862 cls: 'fc-day-number',
9866 cls: 'fc-day-content',
9870 style: 'position: relative;' // height: 17px;
9882 var cal_rows = function() {
9885 for (var r = 0; r < 6; r++) {
9892 for (var i =0; i < Date.dayNames.length; i++) {
9893 var d = Date.dayNames[i];
9894 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9897 row.cn[0].cls+=' fc-first';
9898 row.cn[0].cn[0].style = 'min-height:90px';
9899 row.cn[6].cls+=' fc-last';
9903 ret[0].cls += ' fc-first';
9904 ret[4].cls += ' fc-prev-last';
9905 ret[5].cls += ' fc-last';
9912 cls: 'fc-border-separate',
9913 style : 'width:100%',
9921 cls : 'fc-first fc-last',
9940 style : "position: relative;",
9943 cls : 'fc-view fc-view-month fc-grid',
9944 style : 'position: relative',
9945 unselectable : 'on',
9948 cls : 'fc-event-container',
9949 style : 'position:absolute;z-index:8;top:0;left:0;'
9967 initEvents : function()
9970 throw "can not find store for calendar";
9976 style: "text-align:center",
9980 style: "background-color:white;width:50%;margin:250 auto",
9984 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9995 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9997 var size = this.el.select('.fc-content', true).first().getSize();
9998 this.maskEl.setSize(size.width, size.height);
9999 this.maskEl.enableDisplayMode("block");
10000 if(!this.loadMask){
10001 this.maskEl.hide();
10004 this.store = Roo.factory(this.store, Roo.data);
10005 this.store.on('load', this.onLoad, this);
10006 this.store.on('beforeload', this.onBeforeLoad, this);
10010 this.cells = this.el.select('.fc-day',true);
10011 //Roo.log(this.cells);
10012 this.textNodes = this.el.query('.fc-day-number');
10013 this.cells.addClassOnOver('fc-state-hover');
10015 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10016 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10017 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10018 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10020 this.on('monthchange', this.onMonthChange, this);
10022 this.update(new Date().clearTime());
10025 resize : function() {
10026 var sz = this.el.getSize();
10028 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10029 this.el.select('.fc-day-content div',true).setHeight(34);
10034 showPrevMonth : function(e){
10035 this.update(this.activeDate.add("mo", -1));
10037 showToday : function(e){
10038 this.update(new Date().clearTime());
10041 showNextMonth : function(e){
10042 this.update(this.activeDate.add("mo", 1));
10046 showPrevYear : function(){
10047 this.update(this.activeDate.add("y", -1));
10051 showNextYear : function(){
10052 this.update(this.activeDate.add("y", 1));
10057 update : function(date)
10059 var vd = this.activeDate;
10060 this.activeDate = date;
10061 // if(vd && this.el){
10062 // var t = date.getTime();
10063 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10064 // Roo.log('using add remove');
10066 // this.fireEvent('monthchange', this, date);
10068 // this.cells.removeClass("fc-state-highlight");
10069 // this.cells.each(function(c){
10070 // if(c.dateValue == t){
10071 // c.addClass("fc-state-highlight");
10072 // setTimeout(function(){
10073 // try{c.dom.firstChild.focus();}catch(e){}
10083 var days = date.getDaysInMonth();
10085 var firstOfMonth = date.getFirstDateOfMonth();
10086 var startingPos = firstOfMonth.getDay()-this.startDay;
10088 if(startingPos < this.startDay){
10092 var pm = date.add(Date.MONTH, -1);
10093 var prevStart = pm.getDaysInMonth()-startingPos;
10095 this.cells = this.el.select('.fc-day',true);
10096 this.textNodes = this.el.query('.fc-day-number');
10097 this.cells.addClassOnOver('fc-state-hover');
10099 var cells = this.cells.elements;
10100 var textEls = this.textNodes;
10102 Roo.each(cells, function(cell){
10103 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10106 days += startingPos;
10108 // convert everything to numbers so it's fast
10109 var day = 86400000;
10110 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10113 //Roo.log(prevStart);
10115 var today = new Date().clearTime().getTime();
10116 var sel = date.clearTime().getTime();
10117 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10118 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10119 var ddMatch = this.disabledDatesRE;
10120 var ddText = this.disabledDatesText;
10121 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10122 var ddaysText = this.disabledDaysText;
10123 var format = this.format;
10125 var setCellClass = function(cal, cell){
10127 //Roo.log('set Cell Class');
10129 var t = d.getTime();
10133 cell.dateValue = t;
10135 cell.className += " fc-today";
10136 cell.className += " fc-state-highlight";
10137 cell.title = cal.todayText;
10140 // disable highlight in other month..
10141 //cell.className += " fc-state-highlight";
10146 cell.className = " fc-state-disabled";
10147 cell.title = cal.minText;
10151 cell.className = " fc-state-disabled";
10152 cell.title = cal.maxText;
10156 if(ddays.indexOf(d.getDay()) != -1){
10157 cell.title = ddaysText;
10158 cell.className = " fc-state-disabled";
10161 if(ddMatch && format){
10162 var fvalue = d.dateFormat(format);
10163 if(ddMatch.test(fvalue)){
10164 cell.title = ddText.replace("%0", fvalue);
10165 cell.className = " fc-state-disabled";
10169 if (!cell.initialClassName) {
10170 cell.initialClassName = cell.dom.className;
10173 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10178 for(; i < startingPos; i++) {
10179 textEls[i].innerHTML = (++prevStart);
10180 d.setDate(d.getDate()+1);
10182 cells[i].className = "fc-past fc-other-month";
10183 setCellClass(this, cells[i]);
10188 for(; i < days; i++){
10189 intDay = i - startingPos + 1;
10190 textEls[i].innerHTML = (intDay);
10191 d.setDate(d.getDate()+1);
10193 cells[i].className = ''; // "x-date-active";
10194 setCellClass(this, cells[i]);
10198 for(; i < 42; i++) {
10199 textEls[i].innerHTML = (++extraDays);
10200 d.setDate(d.getDate()+1);
10202 cells[i].className = "fc-future fc-other-month";
10203 setCellClass(this, cells[i]);
10206 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10208 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10210 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10211 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10213 if(totalRows != 6){
10214 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10215 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10218 this.fireEvent('monthchange', this, date);
10222 if(!this.internalRender){
10223 var main = this.el.dom.firstChild;
10224 var w = main.offsetWidth;
10225 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10226 Roo.fly(main).setWidth(w);
10227 this.internalRender = true;
10228 // opera does not respect the auto grow header center column
10229 // then, after it gets a width opera refuses to recalculate
10230 // without a second pass
10231 if(Roo.isOpera && !this.secondPass){
10232 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10233 this.secondPass = true;
10234 this.update.defer(10, this, [date]);
10241 findCell : function(dt) {
10242 dt = dt.clearTime().getTime();
10244 this.cells.each(function(c){
10245 //Roo.log("check " +c.dateValue + '?=' + dt);
10246 if(c.dateValue == dt){
10256 findCells : function(ev) {
10257 var s = ev.start.clone().clearTime().getTime();
10259 var e= ev.end.clone().clearTime().getTime();
10262 this.cells.each(function(c){
10263 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10265 if(c.dateValue > e){
10268 if(c.dateValue < s){
10277 findBestRow: function(cells)
10281 for (var i =0 ; i < cells.length;i++) {
10282 ret = Math.max(cells[i].rows || 0,ret);
10289 addItem : function(ev)
10291 // look for vertical location slot in
10292 var cells = this.findCells(ev);
10294 ev.row = this.findBestRow(cells);
10296 // work out the location.
10300 for(var i =0; i < cells.length; i++) {
10308 if (crow.start.getY() == cells[i].getY()) {
10310 crow.end = cells[i];
10326 for (var i = 0; i < cells.length;i++) {
10327 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10331 this.calevents.push(ev);
10334 clearEvents: function() {
10336 if(!this.calevents){
10340 Roo.each(this.cells.elements, function(c){
10344 Roo.each(this.calevents, function(e) {
10345 Roo.each(e.els, function(el) {
10346 el.un('mouseenter' ,this.onEventEnter, this);
10347 el.un('mouseleave' ,this.onEventLeave, this);
10354 renderEvents: function()
10356 // first make sure there is enough space..
10358 this.cells.each(function(c) {
10360 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10363 for (var e = 0; e < this.calevents.length; e++) {
10364 var ev = this.calevents[e];
10365 var cells = ev.cells;
10366 var rows = ev.rows;
10368 for(var i =0; i < rows.length; i++) {
10371 // how many rows should it span..
10374 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10375 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10377 unselectable : "on",
10380 cls: 'fc-event-inner',
10384 // cls: 'fc-event-time',
10385 // html : cells.length > 1 ? '' : ev.time
10389 cls: 'fc-event-title',
10390 html : String.format('{0}', ev.title)
10397 cls: 'ui-resizable-handle ui-resizable-e',
10398 html : '  '
10404 cfg.cls += ' fc-event-start';
10406 if ((i+1) == rows.length) {
10407 cfg.cls += ' fc-event-end';
10410 var ctr = this.el.select('.fc-event-container',true).first();
10411 var cg = ctr.createChild(cfg);
10413 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10414 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10415 cg.on('click', this.onEventClick, this, ev);
10419 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10420 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10422 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10423 cg.setWidth(ebox.right - sbox.x -2);
10431 onEventEnter: function (e, el,event,d) {
10432 this.fireEvent('evententer', this, el, event);
10435 onEventLeave: function (e, el,event,d) {
10436 this.fireEvent('eventleave', this, el, event);
10439 onEventClick: function (e, el,event,d) {
10440 this.fireEvent('eventclick', this, el, event);
10443 onMonthChange: function () {
10447 onLoad: function ()
10449 this.calevents = [];
10452 if(this.store.getCount() > 0){
10453 this.store.data.each(function(d){
10456 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10457 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10458 time : d.data.start_time,
10459 title : d.data.title,
10460 description : d.data.description,
10461 venue : d.data.venue
10466 this.renderEvents();
10469 this.maskEl.hide();
10473 onBeforeLoad: function()
10475 this.clearEvents();
10478 this.maskEl.show();
10492 * @class Roo.bootstrap.Popover
10493 * @extends Roo.bootstrap.Component
10494 * Bootstrap Popover class
10495 * @cfg {String} html contents of the popover (or false to use children..)
10496 * @cfg {String} title of popover (or false to hide)
10497 * @cfg {String} placement how it is placed
10498 * @cfg {String} trigger click || hover (or false to trigger manually)
10499 * @cfg {String} over what (parent or false to trigger manually.)
10502 * Create a new Popover
10503 * @param {Object} config The config object
10506 Roo.bootstrap.Popover = function(config){
10507 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10510 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10512 title: 'Fill in a title',
10515 placement : 'right',
10516 trigger : 'hover', // hover
10520 can_build_overlaid : false,
10522 getChildContainer : function()
10524 return this.el.select('.popover-content',true).first();
10527 getAutoCreate : function(){
10528 Roo.log('make popover?');
10530 cls : 'popover roo-dynamic',
10531 style: 'display:block',
10537 cls : 'popover-inner',
10541 cls: 'popover-title',
10545 cls : 'popover-content',
10556 setTitle: function(str)
10558 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10560 setContent: function(str)
10562 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10564 // as it get's added to the bottom of the page.
10565 onRender : function(ct, position)
10567 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10569 var cfg = Roo.apply({}, this.getAutoCreate());
10573 cfg.cls += ' ' + this.cls;
10576 cfg.style = this.style;
10578 Roo.log("adding to ")
10579 this.el = Roo.get(document.body).createChild(cfg, position);
10585 initEvents : function()
10587 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10588 this.el.enableDisplayMode('block');
10590 if (this.over === false) {
10593 if (this.triggers === false) {
10596 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10597 var triggers = this.trigger ? this.trigger.split(' ') : [];
10598 Roo.each(triggers, function(trigger) {
10600 if (trigger == 'click') {
10601 on_el.on('click', this.toggle, this);
10602 } else if (trigger != 'manual') {
10603 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10604 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10606 on_el.on(eventIn ,this.enter, this);
10607 on_el.on(eventOut, this.leave, this);
10618 toggle : function () {
10619 this.hoverState == 'in' ? this.leave() : this.enter();
10622 enter : function () {
10625 clearTimeout(this.timeout);
10627 this.hoverState = 'in'
10629 if (!this.delay || !this.delay.show) {
10634 this.timeout = setTimeout(function () {
10635 if (_t.hoverState == 'in') {
10638 }, this.delay.show)
10640 leave : function() {
10641 clearTimeout(this.timeout);
10643 this.hoverState = 'out'
10645 if (!this.delay || !this.delay.hide) {
10650 this.timeout = setTimeout(function () {
10651 if (_t.hoverState == 'out') {
10654 }, this.delay.hide)
10657 show : function (on_el)
10660 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10663 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10664 if (this.html !== false) {
10665 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10667 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10668 if (!this.title.length) {
10669 this.el.select('.popover-title',true).hide();
10672 var placement = typeof this.placement == 'function' ?
10673 this.placement.call(this, this.el, on_el) :
10676 var autoToken = /\s?auto?\s?/i;
10677 var autoPlace = autoToken.test(placement);
10679 placement = placement.replace(autoToken, '') || 'top';
10683 //this.el.setXY([0,0]);
10685 this.el.dom.style.display='block';
10686 this.el.addClass(placement);
10688 //this.el.appendTo(on_el);
10690 var p = this.getPosition();
10691 var box = this.el.getBox();
10696 var align = Roo.bootstrap.Popover.alignment[placement]
10697 this.el.alignTo(on_el, align[0],align[1]);
10698 //var arrow = this.el.select('.arrow',true).first();
10699 //arrow.set(align[2],
10701 this.el.addClass('in');
10702 this.hoverState = null;
10704 if (this.el.hasClass('fade')) {
10711 this.el.setXY([0,0]);
10712 this.el.removeClass('in');
10719 Roo.bootstrap.Popover.alignment = {
10720 'left' : ['r-l', [-10,0], 'right'],
10721 'right' : ['l-r', [10,0], 'left'],
10722 'bottom' : ['t-b', [0,10], 'top'],
10723 'top' : [ 'b-t', [0,-10], 'bottom']
10734 * @class Roo.bootstrap.Progress
10735 * @extends Roo.bootstrap.Component
10736 * Bootstrap Progress class
10737 * @cfg {Boolean} striped striped of the progress bar
10738 * @cfg {Boolean} active animated of the progress bar
10742 * Create a new Progress
10743 * @param {Object} config The config object
10746 Roo.bootstrap.Progress = function(config){
10747 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10750 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10755 getAutoCreate : function(){
10763 cfg.cls += ' progress-striped';
10767 cfg.cls += ' active';
10786 * @class Roo.bootstrap.ProgressBar
10787 * @extends Roo.bootstrap.Component
10788 * Bootstrap ProgressBar class
10789 * @cfg {Number} aria_valuenow aria-value now
10790 * @cfg {Number} aria_valuemin aria-value min
10791 * @cfg {Number} aria_valuemax aria-value max
10792 * @cfg {String} label label for the progress bar
10793 * @cfg {String} panel (success | info | warning | danger )
10794 * @cfg {String} role role of the progress bar
10795 * @cfg {String} sr_only text
10799 * Create a new ProgressBar
10800 * @param {Object} config The config object
10803 Roo.bootstrap.ProgressBar = function(config){
10804 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10807 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10811 aria_valuemax : 100,
10817 getAutoCreate : function()
10822 cls: 'progress-bar',
10823 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10835 cfg.role = this.role;
10838 if(this.aria_valuenow){
10839 cfg['aria-valuenow'] = this.aria_valuenow;
10842 if(this.aria_valuemin){
10843 cfg['aria-valuemin'] = this.aria_valuemin;
10846 if(this.aria_valuemax){
10847 cfg['aria-valuemax'] = this.aria_valuemax;
10850 if(this.label && !this.sr_only){
10851 cfg.html = this.label;
10855 cfg.cls += ' progress-bar-' + this.panel;
10861 update : function(aria_valuenow)
10863 this.aria_valuenow = aria_valuenow;
10865 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10880 * @class Roo.bootstrap.TabPanel
10881 * @extends Roo.bootstrap.Component
10882 * Bootstrap TabPanel class
10883 * @cfg {Boolean} active panel active
10884 * @cfg {String} html panel content
10885 * @cfg {String} tabId tab relate id
10889 * Create a new TabPanel
10890 * @param {Object} config The config object
10893 Roo.bootstrap.TabPanel = function(config){
10894 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10897 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10903 getAutoCreate : function(){
10907 html: this.html || ''
10911 cfg.cls += ' active';
10915 cfg.tabId = this.tabId;
10933 * @class Roo.bootstrap.DateField
10934 * @extends Roo.bootstrap.Input
10935 * Bootstrap DateField class
10936 * @cfg {Number} weekStart default 0
10937 * @cfg {Number} weekStart default 0
10938 * @cfg {Number} viewMode default empty, (months|years)
10939 * @cfg {Number} minViewMode default empty, (months|years)
10940 * @cfg {Number} startDate default -Infinity
10941 * @cfg {Number} endDate default Infinity
10942 * @cfg {Boolean} todayHighlight default false
10943 * @cfg {Boolean} todayBtn default false
10944 * @cfg {Boolean} calendarWeeks default false
10945 * @cfg {Object} daysOfWeekDisabled default empty
10947 * @cfg {Boolean} keyboardNavigation default true
10948 * @cfg {String} language default en
10951 * Create a new DateField
10952 * @param {Object} config The config object
10955 Roo.bootstrap.DateField = function(config){
10956 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10960 * Fires when this field show.
10961 * @param {Roo.bootstrap.DateField} this
10962 * @param {Mixed} date The date value
10967 * Fires when this field hide.
10968 * @param {Roo.bootstrap.DateField} this
10969 * @param {Mixed} date The date value
10974 * Fires when select a date.
10975 * @param {Roo.bootstrap.DateField} this
10976 * @param {Mixed} date The date value
10982 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10985 * @cfg {String} format
10986 * The default date format string which can be overriden for localization support. The format must be
10987 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10991 * @cfg {String} altFormats
10992 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10993 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10995 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11003 todayHighlight : false,
11009 keyboardNavigation: true,
11011 calendarWeeks: false,
11013 startDate: -Infinity,
11017 daysOfWeekDisabled: [],
11021 UTCDate: function()
11023 return new Date(Date.UTC.apply(Date, arguments));
11026 UTCToday: function()
11028 var today = new Date();
11029 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11032 getDate: function() {
11033 var d = this.getUTCDate();
11034 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11037 getUTCDate: function() {
11041 setDate: function(d) {
11042 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11045 setUTCDate: function(d) {
11047 this.setValue(this.formatDate(this.date));
11050 onRender: function(ct, position)
11053 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11055 this.language = this.language || 'en';
11056 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11057 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11059 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11060 this.format = this.format || 'm/d/y';
11061 this.isInline = false;
11062 this.isInput = true;
11063 this.component = this.el.select('.add-on', true).first() || false;
11064 this.component = (this.component && this.component.length === 0) ? false : this.component;
11065 this.hasInput = this.component && this.inputEL().length;
11067 if (typeof(this.minViewMode === 'string')) {
11068 switch (this.minViewMode) {
11070 this.minViewMode = 1;
11073 this.minViewMode = 2;
11076 this.minViewMode = 0;
11081 if (typeof(this.viewMode === 'string')) {
11082 switch (this.viewMode) {
11095 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11097 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11099 this.picker().on('mousedown', this.onMousedown, this);
11100 this.picker().on('click', this.onClick, this);
11102 this.picker().addClass('datepicker-dropdown');
11104 this.startViewMode = this.viewMode;
11107 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11108 if(!this.calendarWeeks){
11113 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11114 v.attr('colspan', function(i, val){
11115 return parseInt(val) + 1;
11120 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11122 this.setStartDate(this.startDate);
11123 this.setEndDate(this.endDate);
11125 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11132 if(this.isInline) {
11137 picker : function()
11139 return this.el.select('.datepicker', true).first();
11142 fillDow: function()
11144 var dowCnt = this.weekStart;
11153 if(this.calendarWeeks){
11161 while (dowCnt < this.weekStart + 7) {
11165 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11169 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11172 fillMonths: function()
11175 var months = this.picker().select('>.datepicker-months td', true).first();
11177 months.dom.innerHTML = '';
11183 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11186 months.createChild(month);
11191 update: function(){
11193 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11195 if (this.date < this.startDate) {
11196 this.viewDate = new Date(this.startDate);
11197 } else if (this.date > this.endDate) {
11198 this.viewDate = new Date(this.endDate);
11200 this.viewDate = new Date(this.date);
11207 var d = new Date(this.viewDate),
11208 year = d.getUTCFullYear(),
11209 month = d.getUTCMonth(),
11210 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11211 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11212 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11213 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11214 currentDate = this.date && this.date.valueOf(),
11215 today = this.UTCToday();
11217 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11219 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11221 // this.picker.select('>tfoot th.today').
11222 // .text(dates[this.language].today)
11223 // .toggle(this.todayBtn !== false);
11225 this.updateNavArrows();
11228 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11230 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11232 prevMonth.setUTCDate(day);
11234 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11236 var nextMonth = new Date(prevMonth);
11238 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11240 nextMonth = nextMonth.valueOf();
11242 var fillMonths = false;
11244 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11246 while(prevMonth.valueOf() < nextMonth) {
11249 if (prevMonth.getUTCDay() === this.weekStart) {
11251 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11259 if(this.calendarWeeks){
11260 // ISO 8601: First week contains first thursday.
11261 // ISO also states week starts on Monday, but we can be more abstract here.
11263 // Start of current week: based on weekstart/current date
11264 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11265 // Thursday of this week
11266 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11267 // First Thursday of year, year from thursday
11268 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11269 // Calendar week: ms between thursdays, div ms per day, div 7 days
11270 calWeek = (th - yth) / 864e5 / 7 + 1;
11272 fillMonths.cn.push({
11280 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11282 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11285 if (this.todayHighlight &&
11286 prevMonth.getUTCFullYear() == today.getFullYear() &&
11287 prevMonth.getUTCMonth() == today.getMonth() &&
11288 prevMonth.getUTCDate() == today.getDate()) {
11289 clsName += ' today';
11292 if (currentDate && prevMonth.valueOf() === currentDate) {
11293 clsName += ' active';
11296 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11297 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11298 clsName += ' disabled';
11301 fillMonths.cn.push({
11303 cls: 'day ' + clsName,
11304 html: prevMonth.getDate()
11307 prevMonth.setDate(prevMonth.getDate()+1);
11310 var currentYear = this.date && this.date.getUTCFullYear();
11311 var currentMonth = this.date && this.date.getUTCMonth();
11313 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11315 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11316 v.removeClass('active');
11318 if(currentYear === year && k === currentMonth){
11319 v.addClass('active');
11322 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11323 v.addClass('disabled');
11329 year = parseInt(year/10, 10) * 10;
11331 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11333 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11336 for (var i = -1; i < 11; i++) {
11337 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11339 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11347 showMode: function(dir) {
11349 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11351 Roo.each(this.picker().select('>div',true).elements, function(v){
11352 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11355 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11360 if(this.isInline) return;
11362 this.picker().removeClass(['bottom', 'top']);
11364 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11366 * place to the top of element!
11370 this.picker().addClass('top');
11371 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11376 this.picker().addClass('bottom');
11378 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11381 parseDate : function(value){
11382 if(!value || value instanceof Date){
11385 var v = Date.parseDate(value, this.format);
11386 if (!v && this.useIso) {
11387 v = Date.parseDate(value, 'Y-m-d');
11389 if(!v && this.altFormats){
11390 if(!this.altFormatsArray){
11391 this.altFormatsArray = this.altFormats.split("|");
11393 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11394 v = Date.parseDate(value, this.altFormatsArray[i]);
11400 formatDate : function(date, fmt){
11401 return (!date || !(date instanceof Date)) ?
11402 date : date.dateFormat(fmt || this.format);
11405 onFocus : function()
11407 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11411 onBlur : function()
11413 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11419 this.picker().show();
11423 this.fireEvent('show', this, this.date);
11428 if(this.isInline) return;
11429 this.picker().hide();
11430 this.viewMode = this.startViewMode;
11433 this.fireEvent('hide', this, this.date);
11437 onMousedown: function(e){
11438 e.stopPropagation();
11439 e.preventDefault();
11442 keyup: function(e){
11443 Roo.bootstrap.DateField.superclass.keyup.call(this);
11448 setValue: function(v){
11449 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11451 this.fireEvent('select', this, this.date);
11455 fireKey: function(e){
11456 if (!this.picker().isVisible()){
11457 if (e.keyCode == 27) // allow escape to hide and re-show picker
11461 var dateChanged = false,
11463 newDate, newViewDate;
11467 e.preventDefault();
11471 if (!this.keyboardNavigation) break;
11472 dir = e.keyCode == 37 ? -1 : 1;
11475 newDate = this.moveYear(this.date, dir);
11476 newViewDate = this.moveYear(this.viewDate, dir);
11477 } else if (e.shiftKey){
11478 newDate = this.moveMonth(this.date, dir);
11479 newViewDate = this.moveMonth(this.viewDate, dir);
11481 newDate = new Date(this.date);
11482 newDate.setUTCDate(this.date.getUTCDate() + dir);
11483 newViewDate = new Date(this.viewDate);
11484 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11486 if (this.dateWithinRange(newDate)){
11487 this.date = newDate;
11488 this.viewDate = newViewDate;
11489 this.setValue(this.formatDate(this.date));
11491 e.preventDefault();
11492 dateChanged = true;
11497 if (!this.keyboardNavigation) break;
11498 dir = e.keyCode == 38 ? -1 : 1;
11500 newDate = this.moveYear(this.date, dir);
11501 newViewDate = this.moveYear(this.viewDate, dir);
11502 } else if (e.shiftKey){
11503 newDate = this.moveMonth(this.date, dir);
11504 newViewDate = this.moveMonth(this.viewDate, dir);
11506 newDate = new Date(this.date);
11507 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11508 newViewDate = new Date(this.viewDate);
11509 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11511 if (this.dateWithinRange(newDate)){
11512 this.date = newDate;
11513 this.viewDate = newViewDate;
11514 this.setValue(this.formatDate(this.date));
11516 e.preventDefault();
11517 dateChanged = true;
11521 this.setValue(this.formatDate(this.date));
11523 e.preventDefault();
11526 this.setValue(this.formatDate(this.date));
11533 onClick: function(e) {
11534 e.stopPropagation();
11535 e.preventDefault();
11537 var target = e.getTarget();
11539 if(target.nodeName.toLowerCase() === 'i'){
11540 target = Roo.get(target).dom.parentNode;
11543 var nodeName = target.nodeName;
11544 var className = target.className;
11545 var html = target.innerHTML;
11547 switch(nodeName.toLowerCase()) {
11549 switch(className) {
11555 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11556 switch(this.viewMode){
11558 this.viewDate = this.moveMonth(this.viewDate, dir);
11562 this.viewDate = this.moveYear(this.viewDate, dir);
11568 var date = new Date();
11569 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11571 this.setValue(this.formatDate(this.date));
11577 if (className.indexOf('disabled') === -1) {
11578 this.viewDate.setUTCDate(1);
11579 if (className.indexOf('month') !== -1) {
11580 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11582 var year = parseInt(html, 10) || 0;
11583 this.viewDate.setUTCFullYear(year);
11592 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11593 var day = parseInt(html, 10) || 1;
11594 var year = this.viewDate.getUTCFullYear(),
11595 month = this.viewDate.getUTCMonth();
11597 if (className.indexOf('old') !== -1) {
11604 } else if (className.indexOf('new') !== -1) {
11612 this.date = this.UTCDate(year, month, day,0,0,0,0);
11613 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11615 this.setValue(this.formatDate(this.date));
11622 setStartDate: function(startDate){
11623 this.startDate = startDate || -Infinity;
11624 if (this.startDate !== -Infinity) {
11625 this.startDate = this.parseDate(this.startDate);
11628 this.updateNavArrows();
11631 setEndDate: function(endDate){
11632 this.endDate = endDate || Infinity;
11633 if (this.endDate !== Infinity) {
11634 this.endDate = this.parseDate(this.endDate);
11637 this.updateNavArrows();
11640 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11641 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11642 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11643 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11645 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11646 return parseInt(d, 10);
11649 this.updateNavArrows();
11652 updateNavArrows: function() {
11653 var d = new Date(this.viewDate),
11654 year = d.getUTCFullYear(),
11655 month = d.getUTCMonth();
11657 Roo.each(this.picker().select('.prev', true).elements, function(v){
11659 switch (this.viewMode) {
11662 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11668 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11675 Roo.each(this.picker().select('.next', true).elements, function(v){
11677 switch (this.viewMode) {
11680 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11686 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11694 moveMonth: function(date, dir){
11695 if (!dir) return date;
11696 var new_date = new Date(date.valueOf()),
11697 day = new_date.getUTCDate(),
11698 month = new_date.getUTCMonth(),
11699 mag = Math.abs(dir),
11701 dir = dir > 0 ? 1 : -1;
11704 // If going back one month, make sure month is not current month
11705 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11707 return new_date.getUTCMonth() == month;
11709 // If going forward one month, make sure month is as expected
11710 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11712 return new_date.getUTCMonth() != new_month;
11714 new_month = month + dir;
11715 new_date.setUTCMonth(new_month);
11716 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11717 if (new_month < 0 || new_month > 11)
11718 new_month = (new_month + 12) % 12;
11720 // For magnitudes >1, move one month at a time...
11721 for (var i=0; i<mag; i++)
11722 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11723 new_date = this.moveMonth(new_date, dir);
11724 // ...then reset the day, keeping it in the new month
11725 new_month = new_date.getUTCMonth();
11726 new_date.setUTCDate(day);
11728 return new_month != new_date.getUTCMonth();
11731 // Common date-resetting loop -- if date is beyond end of month, make it
11734 new_date.setUTCDate(--day);
11735 new_date.setUTCMonth(new_month);
11740 moveYear: function(date, dir){
11741 return this.moveMonth(date, dir*12);
11744 dateWithinRange: function(date){
11745 return date >= this.startDate && date <= this.endDate;
11749 remove: function() {
11750 this.picker().remove();
11755 Roo.apply(Roo.bootstrap.DateField, {
11766 html: '<i class="icon-arrow-left"/>'
11776 html: '<i class="icon-arrow-right"/>'
11818 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11819 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11820 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11821 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11822 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11835 navFnc: 'FullYear',
11840 navFnc: 'FullYear',
11845 Roo.apply(Roo.bootstrap.DateField, {
11849 cls: 'datepicker dropdown-menu',
11853 cls: 'datepicker-days',
11857 cls: 'table-condensed',
11859 Roo.bootstrap.DateField.head,
11863 Roo.bootstrap.DateField.footer
11870 cls: 'datepicker-months',
11874 cls: 'table-condensed',
11876 Roo.bootstrap.DateField.head,
11877 Roo.bootstrap.DateField.content,
11878 Roo.bootstrap.DateField.footer
11885 cls: 'datepicker-years',
11889 cls: 'table-condensed',
11891 Roo.bootstrap.DateField.head,
11892 Roo.bootstrap.DateField.content,
11893 Roo.bootstrap.DateField.footer
11912 * @class Roo.bootstrap.TimeField
11913 * @extends Roo.bootstrap.Input
11914 * Bootstrap DateField class
11918 * Create a new TimeField
11919 * @param {Object} config The config object
11922 Roo.bootstrap.TimeField = function(config){
11923 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11927 * Fires when this field show.
11928 * @param {Roo.bootstrap.DateField} this
11929 * @param {Mixed} date The date value
11934 * Fires when this field hide.
11935 * @param {Roo.bootstrap.DateField} this
11936 * @param {Mixed} date The date value
11941 * Fires when select a date.
11942 * @param {Roo.bootstrap.DateField} this
11943 * @param {Mixed} date The date value
11949 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11952 * @cfg {String} format
11953 * The default time format string which can be overriden for localization support. The format must be
11954 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11958 onRender: function(ct, position)
11961 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11963 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11965 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11967 this.pop = this.picker().select('>.datepicker-time',true).first();
11968 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11970 this.picker().on('mousedown', this.onMousedown, this);
11971 this.picker().on('click', this.onClick, this);
11973 this.picker().addClass('datepicker-dropdown');
11978 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11979 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11980 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11981 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11982 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11983 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11987 fireKey: function(e){
11988 if (!this.picker().isVisible()){
11989 if (e.keyCode == 27) // allow escape to hide and re-show picker
11994 e.preventDefault();
12002 this.onTogglePeriod();
12005 this.onIncrementMinutes();
12008 this.onDecrementMinutes();
12017 onClick: function(e) {
12018 e.stopPropagation();
12019 e.preventDefault();
12022 picker : function()
12024 return this.el.select('.datepicker', true).first();
12027 fillTime: function()
12029 var time = this.pop.select('tbody', true).first();
12031 time.dom.innerHTML = '';
12046 cls: 'hours-up glyphicon glyphicon-chevron-up'
12066 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12087 cls: 'timepicker-hour',
12102 cls: 'timepicker-minute',
12117 cls: 'btn btn-primary period',
12139 cls: 'hours-down glyphicon glyphicon-chevron-down'
12159 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12177 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12184 var hours = this.time.getHours();
12185 var minutes = this.time.getMinutes();
12198 hours = hours - 12;
12202 hours = '0' + hours;
12206 minutes = '0' + minutes;
12209 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12210 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12211 this.pop.select('button', true).first().dom.innerHTML = period;
12217 this.picker().removeClass(['bottom', 'top']);
12219 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12221 * place to the top of element!
12225 this.picker().addClass('top');
12226 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12231 this.picker().addClass('bottom');
12233 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12236 onFocus : function()
12238 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12242 onBlur : function()
12244 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12250 this.picker().show();
12255 this.fireEvent('show', this, this.date);
12260 this.picker().hide();
12263 this.fireEvent('hide', this, this.date);
12266 setTime : function()
12269 this.setValue(this.time.format(this.format));
12271 this.fireEvent('select', this, this.date);
12276 onMousedown: function(e){
12277 e.stopPropagation();
12278 e.preventDefault();
12281 onIncrementHours: function()
12283 Roo.log('onIncrementHours');
12284 this.time = this.time.add(Date.HOUR, 1);
12289 onDecrementHours: function()
12291 Roo.log('onDecrementHours');
12292 this.time = this.time.add(Date.HOUR, -1);
12296 onIncrementMinutes: function()
12298 Roo.log('onIncrementMinutes');
12299 this.time = this.time.add(Date.MINUTE, 1);
12303 onDecrementMinutes: function()
12305 Roo.log('onDecrementMinutes');
12306 this.time = this.time.add(Date.MINUTE, -1);
12310 onTogglePeriod: function()
12312 Roo.log('onTogglePeriod');
12313 this.time = this.time.add(Date.HOUR, 12);
12320 Roo.apply(Roo.bootstrap.TimeField, {
12350 cls: 'btn btn-info ok',
12362 Roo.apply(Roo.bootstrap.TimeField, {
12366 cls: 'datepicker dropdown-menu',
12370 cls: 'datepicker-time',
12374 cls: 'table-condensed',
12376 Roo.bootstrap.TimeField.content,
12377 Roo.bootstrap.TimeField.footer
12396 * @class Roo.bootstrap.CheckBox
12397 * @extends Roo.bootstrap.Input
12398 * Bootstrap CheckBox class
12400 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12401 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12402 * @cfg {String} boxLabel The text that appears beside the checkbox
12403 * @cfg {Boolean} checked initnal the element
12406 * Create a new CheckBox
12407 * @param {Object} config The config object
12410 Roo.bootstrap.CheckBox = function(config){
12411 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12416 * Fires when the element is checked or unchecked.
12417 * @param {Roo.bootstrap.CheckBox} this This input
12418 * @param {Boolean} checked The new checked value
12424 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12426 inputType: 'checkbox',
12432 getAutoCreate : function()
12434 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12440 cfg.cls = 'form-group' //input-group
12445 type : this.inputType,
12446 value : (!this.checked) ? this.valueOff : this.inputValue,
12448 placeholder : this.placeholder || ''
12452 if (this.disabled) {
12453 input.disabled=true;
12457 input.checked = this.checked;
12461 input.name = this.name;
12465 input.cls += ' input-' + this.size;
12469 ['xs','sm','md','lg'].map(function(size){
12470 if (settings[size]) {
12471 cfg.cls += ' col-' + size + '-' + settings[size];
12475 var inputblock = input;
12477 if (this.before || this.after) {
12480 cls : 'input-group',
12484 inputblock.cn.push({
12486 cls : 'input-group-addon',
12490 inputblock.cn.push(input);
12492 inputblock.cn.push({
12494 cls : 'input-group-addon',
12501 if (align ==='left' && this.fieldLabel.length) {
12502 Roo.log("left and has label");
12508 cls : 'control-label col-md-' + this.labelWidth,
12509 html : this.fieldLabel
12513 cls : "col-md-" + (12 - this.labelWidth),
12520 } else if ( this.fieldLabel.length) {
12525 tag: this.boxLabel ? 'span' : 'label',
12527 cls: 'control-label box-input-label',
12528 //cls : 'input-group-addon',
12529 html : this.fieldLabel
12539 Roo.log(" no label && no align");
12554 html: this.boxLabel
12563 * return the real input element.
12565 inputEl: function ()
12567 return this.el.select('input.form-box',true).first();
12570 initEvents : function()
12572 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12574 this.inputEl().on('click', this.onClick, this);
12578 onClick : function()
12580 this.setChecked(!this.checked);
12583 setChecked : function(state,suppressEvent)
12585 this.checked = state;
12587 this.inputEl().dom.checked = state;
12589 if(suppressEvent !== true){
12590 this.fireEvent('check', this, state);
12593 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12597 setValue : function(v,suppressEvent)
12599 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12613 * @class Roo.bootstrap.Radio
12614 * @extends Roo.bootstrap.CheckBox
12615 * Bootstrap Radio class
12618 * Create a new Radio
12619 * @param {Object} config The config object
12622 Roo.bootstrap.Radio = function(config){
12623 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12627 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12629 inputType: 'radio',
12633 getAutoCreate : function()
12635 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12641 cfg.cls = 'form-group' //input-group
12646 type : this.inputType,
12647 value : (!this.checked) ? this.valueOff : this.inputValue,
12649 placeholder : this.placeholder || ''
12653 if (this.disabled) {
12654 input.disabled=true;
12658 input.checked = this.checked;
12662 input.name = this.name;
12666 input.cls += ' input-' + this.size;
12670 ['xs','sm','md','lg'].map(function(size){
12671 if (settings[size]) {
12672 cfg.cls += ' col-' + size + '-' + settings[size];
12676 var inputblock = input;
12678 if (this.before || this.after) {
12681 cls : 'input-group',
12685 inputblock.cn.push({
12687 cls : 'input-group-addon',
12691 inputblock.cn.push(input);
12693 inputblock.cn.push({
12695 cls : 'input-group-addon',
12702 if (align ==='left' && this.fieldLabel.length) {
12703 Roo.log("left and has label");
12709 cls : 'control-label col-md-' + this.labelWidth,
12710 html : this.fieldLabel
12714 cls : "col-md-" + (12 - this.labelWidth),
12721 } else if ( this.fieldLabel.length) {
12728 cls: 'control-label box-input-label',
12729 //cls : 'input-group-addon',
12730 html : this.fieldLabel
12740 Roo.log(" no label && no align");
12755 html: this.boxLabel
12763 onClick : function()
12765 this.setChecked(true);
12768 setChecked : function(state,suppressEvent)
12771 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12772 v.dom.checked = false;
12776 this.checked = state;
12777 this.inputEl().dom.checked = state;
12779 if(suppressEvent !== true){
12780 this.fireEvent('check', this, state);
12783 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12787 getGroupValue : function()
12790 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12791 if(v.dom.checked == true){
12792 value = v.dom.value;
12800 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12801 * @return {Mixed} value The field value
12803 getValue : function(){
12804 return this.getGroupValue();
12810 //<script type="text/javascript">
12813 * Based Ext JS Library 1.1.1
12814 * Copyright(c) 2006-2007, Ext JS, LLC.
12820 * @class Roo.HtmlEditorCore
12821 * @extends Roo.Component
12822 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12824 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12827 Roo.HtmlEditorCore = function(config){
12830 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12833 * @event initialize
12834 * Fires when the editor is fully initialized (including the iframe)
12835 * @param {Roo.HtmlEditorCore} this
12840 * Fires when the editor is first receives the focus. Any insertion must wait
12841 * until after this event.
12842 * @param {Roo.HtmlEditorCore} this
12846 * @event beforesync
12847 * Fires before the textarea is updated with content from the editor iframe. Return false
12848 * to cancel the sync.
12849 * @param {Roo.HtmlEditorCore} this
12850 * @param {String} html
12854 * @event beforepush
12855 * Fires before the iframe editor is updated with content from the textarea. Return false
12856 * to cancel the push.
12857 * @param {Roo.HtmlEditorCore} this
12858 * @param {String} html
12863 * Fires when the textarea is updated with content from the editor iframe.
12864 * @param {Roo.HtmlEditorCore} this
12865 * @param {String} html
12870 * Fires when the iframe editor is updated with content from the textarea.
12871 * @param {Roo.HtmlEditorCore} this
12872 * @param {String} html
12877 * @event editorevent
12878 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12879 * @param {Roo.HtmlEditorCore} this
12887 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12891 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12897 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12902 * @cfg {Number} height (in pixels)
12906 * @cfg {Number} width (in pixels)
12911 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12914 stylesheets: false,
12919 // private properties
12920 validationEvent : false,
12922 initialized : false,
12924 sourceEditMode : false,
12925 onFocus : Roo.emptyFn,
12927 hideMode:'offsets',
12935 * Protected method that will not generally be called directly. It
12936 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12937 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12939 getDocMarkup : function(){
12942 Roo.log(this.stylesheets);
12944 // inherit styels from page...??
12945 if (this.stylesheets === false) {
12947 Roo.get(document.head).select('style').each(function(node) {
12948 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12951 Roo.get(document.head).select('link').each(function(node) {
12952 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12955 } else if (!this.stylesheets.length) {
12957 st = '<style type="text/css">' +
12958 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12961 Roo.each(this.stylesheets, function(s) {
12962 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12967 st += '<style type="text/css">' +
12968 'IMG { cursor: pointer } ' +
12972 return '<html><head>' + st +
12973 //<style type="text/css">' +
12974 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12976 ' </head><body class="roo-htmleditor-body"></body></html>';
12980 onRender : function(ct, position)
12983 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12984 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12987 this.el.dom.style.border = '0 none';
12988 this.el.dom.setAttribute('tabIndex', -1);
12989 this.el.addClass('x-hidden hide');
12993 if(Roo.isIE){ // fix IE 1px bogus margin
12994 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12998 this.frameId = Roo.id();
13002 var iframe = this.owner.wrap.createChild({
13004 cls: 'form-control', // bootstrap..
13006 name: this.frameId,
13007 frameBorder : 'no',
13008 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13013 this.iframe = iframe.dom;
13015 this.assignDocWin();
13017 this.doc.designMode = 'on';
13020 this.doc.write(this.getDocMarkup());
13024 var task = { // must defer to wait for browser to be ready
13026 //console.log("run task?" + this.doc.readyState);
13027 this.assignDocWin();
13028 if(this.doc.body || this.doc.readyState == 'complete'){
13030 this.doc.designMode="on";
13034 Roo.TaskMgr.stop(task);
13035 this.initEditor.defer(10, this);
13042 Roo.TaskMgr.start(task);
13049 onResize : function(w, h)
13051 Roo.log('resize: ' +w + ',' + h );
13052 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13056 if(typeof w == 'number'){
13058 this.iframe.style.width = w + 'px';
13060 if(typeof h == 'number'){
13062 this.iframe.style.height = h + 'px';
13064 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13071 * Toggles the editor between standard and source edit mode.
13072 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13074 toggleSourceEdit : function(sourceEditMode){
13076 this.sourceEditMode = sourceEditMode === true;
13078 if(this.sourceEditMode){
13080 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13083 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13084 //this.iframe.className = '';
13087 //this.setSize(this.owner.wrap.getSize());
13088 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13095 * Protected method that will not generally be called directly. If you need/want
13096 * custom HTML cleanup, this is the method you should override.
13097 * @param {String} html The HTML to be cleaned
13098 * return {String} The cleaned HTML
13100 cleanHtml : function(html){
13101 html = String(html);
13102 if(html.length > 5){
13103 if(Roo.isSafari){ // strip safari nonsense
13104 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13107 if(html == ' '){
13114 * HTML Editor -> Textarea
13115 * Protected method that will not generally be called directly. Syncs the contents
13116 * of the editor iframe with the textarea.
13118 syncValue : function(){
13119 if(this.initialized){
13120 var bd = (this.doc.body || this.doc.documentElement);
13121 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13122 var html = bd.innerHTML;
13124 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13125 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13127 html = '<div style="'+m[0]+'">' + html + '</div>';
13130 html = this.cleanHtml(html);
13131 // fix up the special chars.. normaly like back quotes in word...
13132 // however we do not want to do this with chinese..
13133 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13134 var cc = b.charCodeAt();
13136 (cc >= 0x4E00 && cc < 0xA000 ) ||
13137 (cc >= 0x3400 && cc < 0x4E00 ) ||
13138 (cc >= 0xf900 && cc < 0xfb00 )
13144 if(this.owner.fireEvent('beforesync', this, html) !== false){
13145 this.el.dom.value = html;
13146 this.owner.fireEvent('sync', this, html);
13152 * Protected method that will not generally be called directly. Pushes the value of the textarea
13153 * into the iframe editor.
13155 pushValue : function(){
13156 if(this.initialized){
13157 var v = this.el.dom.value.trim();
13159 // if(v.length < 1){
13163 if(this.owner.fireEvent('beforepush', this, v) !== false){
13164 var d = (this.doc.body || this.doc.documentElement);
13166 this.cleanUpPaste();
13167 this.el.dom.value = d.innerHTML;
13168 this.owner.fireEvent('push', this, v);
13174 deferFocus : function(){
13175 this.focus.defer(10, this);
13179 focus : function(){
13180 if(this.win && !this.sourceEditMode){
13187 assignDocWin: function()
13189 var iframe = this.iframe;
13192 this.doc = iframe.contentWindow.document;
13193 this.win = iframe.contentWindow;
13195 if (!Roo.get(this.frameId)) {
13198 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13199 this.win = Roo.get(this.frameId).dom.contentWindow;
13204 initEditor : function(){
13205 //console.log("INIT EDITOR");
13206 this.assignDocWin();
13210 this.doc.designMode="on";
13212 this.doc.write(this.getDocMarkup());
13215 var dbody = (this.doc.body || this.doc.documentElement);
13216 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13217 // this copies styles from the containing element into thsi one..
13218 // not sure why we need all of this..
13219 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13220 ss['background-attachment'] = 'fixed'; // w3c
13221 dbody.bgProperties = 'fixed'; // ie
13222 Roo.DomHelper.applyStyles(dbody, ss);
13223 Roo.EventManager.on(this.doc, {
13224 //'mousedown': this.onEditorEvent,
13225 'mouseup': this.onEditorEvent,
13226 'dblclick': this.onEditorEvent,
13227 'click': this.onEditorEvent,
13228 'keyup': this.onEditorEvent,
13233 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13235 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13236 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13238 this.initialized = true;
13240 this.owner.fireEvent('initialize', this);
13245 onDestroy : function(){
13251 //for (var i =0; i < this.toolbars.length;i++) {
13252 // // fixme - ask toolbars for heights?
13253 // this.toolbars[i].onDestroy();
13256 //this.wrap.dom.innerHTML = '';
13257 //this.wrap.remove();
13262 onFirstFocus : function(){
13264 this.assignDocWin();
13267 this.activated = true;
13270 if(Roo.isGecko){ // prevent silly gecko errors
13272 var s = this.win.getSelection();
13273 if(!s.focusNode || s.focusNode.nodeType != 3){
13274 var r = s.getRangeAt(0);
13275 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13280 this.execCmd('useCSS', true);
13281 this.execCmd('styleWithCSS', false);
13284 this.owner.fireEvent('activate', this);
13288 adjustFont: function(btn){
13289 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13290 //if(Roo.isSafari){ // safari
13293 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13294 if(Roo.isSafari){ // safari
13295 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13296 v = (v < 10) ? 10 : v;
13297 v = (v > 48) ? 48 : v;
13298 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13303 v = Math.max(1, v+adjust);
13305 this.execCmd('FontSize', v );
13308 onEditorEvent : function(e){
13309 this.owner.fireEvent('editorevent', this, e);
13310 // this.updateToolbar();
13311 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13314 insertTag : function(tg)
13316 // could be a bit smarter... -> wrap the current selected tRoo..
13317 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13319 range = this.createRange(this.getSelection());
13320 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13321 wrappingNode.appendChild(range.extractContents());
13322 range.insertNode(wrappingNode);
13329 this.execCmd("formatblock", tg);
13333 insertText : function(txt)
13337 var range = this.createRange();
13338 range.deleteContents();
13339 //alert(Sender.getAttribute('label'));
13341 range.insertNode(this.doc.createTextNode(txt));
13347 * Executes a Midas editor command on the editor document and performs necessary focus and
13348 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13349 * @param {String} cmd The Midas command
13350 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13352 relayCmd : function(cmd, value){
13354 this.execCmd(cmd, value);
13355 this.owner.fireEvent('editorevent', this);
13356 //this.updateToolbar();
13357 this.owner.deferFocus();
13361 * Executes a Midas editor command directly on the editor document.
13362 * For visual commands, you should use {@link #relayCmd} instead.
13363 * <b>This should only be called after the editor is initialized.</b>
13364 * @param {String} cmd The Midas command
13365 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13367 execCmd : function(cmd, value){
13368 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13375 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13377 * @param {String} text | dom node..
13379 insertAtCursor : function(text)
13384 if(!this.activated){
13390 var r = this.doc.selection.createRange();
13401 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13405 // from jquery ui (MIT licenced)
13407 var win = this.win;
13409 if (win.getSelection && win.getSelection().getRangeAt) {
13410 range = win.getSelection().getRangeAt(0);
13411 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13412 range.insertNode(node);
13413 } else if (win.document.selection && win.document.selection.createRange) {
13414 // no firefox support
13415 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13416 win.document.selection.createRange().pasteHTML(txt);
13418 // no firefox support
13419 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13420 this.execCmd('InsertHTML', txt);
13429 mozKeyPress : function(e){
13431 var c = e.getCharCode(), cmd;
13434 c = String.fromCharCode(c).toLowerCase();
13448 this.cleanUpPaste.defer(100, this);
13456 e.preventDefault();
13464 fixKeys : function(){ // load time branching for fastest keydown performance
13466 return function(e){
13467 var k = e.getKey(), r;
13470 r = this.doc.selection.createRange();
13473 r.pasteHTML('    ');
13480 r = this.doc.selection.createRange();
13482 var target = r.parentElement();
13483 if(!target || target.tagName.toLowerCase() != 'li'){
13485 r.pasteHTML('<br />');
13491 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13492 this.cleanUpPaste.defer(100, this);
13498 }else if(Roo.isOpera){
13499 return function(e){
13500 var k = e.getKey();
13504 this.execCmd('InsertHTML','    ');
13507 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13508 this.cleanUpPaste.defer(100, this);
13513 }else if(Roo.isSafari){
13514 return function(e){
13515 var k = e.getKey();
13519 this.execCmd('InsertText','\t');
13523 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13524 this.cleanUpPaste.defer(100, this);
13532 getAllAncestors: function()
13534 var p = this.getSelectedNode();
13537 a.push(p); // push blank onto stack..
13538 p = this.getParentElement();
13542 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13546 a.push(this.doc.body);
13550 lastSelNode : false,
13553 getSelection : function()
13555 this.assignDocWin();
13556 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13559 getSelectedNode: function()
13561 // this may only work on Gecko!!!
13563 // should we cache this!!!!
13568 var range = this.createRange(this.getSelection()).cloneRange();
13571 var parent = range.parentElement();
13573 var testRange = range.duplicate();
13574 testRange.moveToElementText(parent);
13575 if (testRange.inRange(range)) {
13578 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13581 parent = parent.parentElement;
13586 // is ancestor a text element.
13587 var ac = range.commonAncestorContainer;
13588 if (ac.nodeType == 3) {
13589 ac = ac.parentNode;
13592 var ar = ac.childNodes;
13595 var other_nodes = [];
13596 var has_other_nodes = false;
13597 for (var i=0;i<ar.length;i++) {
13598 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13601 // fullly contained node.
13603 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13608 // probably selected..
13609 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13610 other_nodes.push(ar[i]);
13614 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13619 has_other_nodes = true;
13621 if (!nodes.length && other_nodes.length) {
13622 nodes= other_nodes;
13624 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13630 createRange: function(sel)
13632 // this has strange effects when using with
13633 // top toolbar - not sure if it's a great idea.
13634 //this.editor.contentWindow.focus();
13635 if (typeof sel != "undefined") {
13637 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13639 return this.doc.createRange();
13642 return this.doc.createRange();
13645 getParentElement: function()
13648 this.assignDocWin();
13649 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13651 var range = this.createRange(sel);
13654 var p = range.commonAncestorContainer;
13655 while (p.nodeType == 3) { // text node
13666 * Range intersection.. the hard stuff...
13670 * [ -- selected range --- ]
13674 * if end is before start or hits it. fail.
13675 * if start is after end or hits it fail.
13677 * if either hits (but other is outside. - then it's not
13683 // @see http://www.thismuchiknow.co.uk/?p=64.
13684 rangeIntersectsNode : function(range, node)
13686 var nodeRange = node.ownerDocument.createRange();
13688 nodeRange.selectNode(node);
13690 nodeRange.selectNodeContents(node);
13693 var rangeStartRange = range.cloneRange();
13694 rangeStartRange.collapse(true);
13696 var rangeEndRange = range.cloneRange();
13697 rangeEndRange.collapse(false);
13699 var nodeStartRange = nodeRange.cloneRange();
13700 nodeStartRange.collapse(true);
13702 var nodeEndRange = nodeRange.cloneRange();
13703 nodeEndRange.collapse(false);
13705 return rangeStartRange.compareBoundaryPoints(
13706 Range.START_TO_START, nodeEndRange) == -1 &&
13707 rangeEndRange.compareBoundaryPoints(
13708 Range.START_TO_START, nodeStartRange) == 1;
13712 rangeCompareNode : function(range, node)
13714 var nodeRange = node.ownerDocument.createRange();
13716 nodeRange.selectNode(node);
13718 nodeRange.selectNodeContents(node);
13722 range.collapse(true);
13724 nodeRange.collapse(true);
13726 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13727 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13729 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13731 var nodeIsBefore = ss == 1;
13732 var nodeIsAfter = ee == -1;
13734 if (nodeIsBefore && nodeIsAfter)
13736 if (!nodeIsBefore && nodeIsAfter)
13737 return 1; //right trailed.
13739 if (nodeIsBefore && !nodeIsAfter)
13740 return 2; // left trailed.
13745 // private? - in a new class?
13746 cleanUpPaste : function()
13748 // cleans up the whole document..
13749 Roo.log('cleanuppaste');
13751 this.cleanUpChildren(this.doc.body);
13752 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13753 if (clean != this.doc.body.innerHTML) {
13754 this.doc.body.innerHTML = clean;
13759 cleanWordChars : function(input) {// change the chars to hex code
13760 var he = Roo.HtmlEditorCore;
13762 var output = input;
13763 Roo.each(he.swapCodes, function(sw) {
13764 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13766 output = output.replace(swapper, sw[1]);
13773 cleanUpChildren : function (n)
13775 if (!n.childNodes.length) {
13778 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13779 this.cleanUpChild(n.childNodes[i]);
13786 cleanUpChild : function (node)
13789 //console.log(node);
13790 if (node.nodeName == "#text") {
13791 // clean up silly Windows -- stuff?
13794 if (node.nodeName == "#comment") {
13795 node.parentNode.removeChild(node);
13796 // clean up silly Windows -- stuff?
13800 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13802 node.parentNode.removeChild(node);
13807 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13809 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13810 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13812 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13813 // remove_keep_children = true;
13816 if (remove_keep_children) {
13817 this.cleanUpChildren(node);
13818 // inserts everything just before this node...
13819 while (node.childNodes.length) {
13820 var cn = node.childNodes[0];
13821 node.removeChild(cn);
13822 node.parentNode.insertBefore(cn, node);
13824 node.parentNode.removeChild(node);
13828 if (!node.attributes || !node.attributes.length) {
13829 this.cleanUpChildren(node);
13833 function cleanAttr(n,v)
13836 if (v.match(/^\./) || v.match(/^\//)) {
13839 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13842 if (v.match(/^#/)) {
13845 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13846 node.removeAttribute(n);
13850 function cleanStyle(n,v)
13852 if (v.match(/expression/)) { //XSS?? should we even bother..
13853 node.removeAttribute(n);
13856 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13857 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13860 var parts = v.split(/;/);
13863 Roo.each(parts, function(p) {
13864 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13868 var l = p.split(':').shift().replace(/\s+/g,'');
13869 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13871 if ( cblack.indexOf(l) > -1) {
13872 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13873 //node.removeAttribute(n);
13877 // only allow 'c whitelisted system attributes'
13878 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13879 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13880 //node.removeAttribute(n);
13890 if (clean.length) {
13891 node.setAttribute(n, clean.join(';'));
13893 node.removeAttribute(n);
13899 for (var i = node.attributes.length-1; i > -1 ; i--) {
13900 var a = node.attributes[i];
13903 if (a.name.toLowerCase().substr(0,2)=='on') {
13904 node.removeAttribute(a.name);
13907 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13908 node.removeAttribute(a.name);
13911 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13912 cleanAttr(a.name,a.value); // fixme..
13915 if (a.name == 'style') {
13916 cleanStyle(a.name,a.value);
13919 /// clean up MS crap..
13920 // tecnically this should be a list of valid class'es..
13923 if (a.name == 'class') {
13924 if (a.value.match(/^Mso/)) {
13925 node.className = '';
13928 if (a.value.match(/body/)) {
13929 node.className = '';
13940 this.cleanUpChildren(node);
13946 // hide stuff that is not compatible
13960 * @event specialkey
13964 * @cfg {String} fieldClass @hide
13967 * @cfg {String} focusClass @hide
13970 * @cfg {String} autoCreate @hide
13973 * @cfg {String} inputType @hide
13976 * @cfg {String} invalidClass @hide
13979 * @cfg {String} invalidText @hide
13982 * @cfg {String} msgFx @hide
13985 * @cfg {String} validateOnBlur @hide
13989 Roo.HtmlEditorCore.white = [
13990 'area', 'br', 'img', 'input', 'hr', 'wbr',
13992 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
13993 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
13994 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
13995 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
13996 'table', 'ul', 'xmp',
13998 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14001 'dir', 'menu', 'ol', 'ul', 'dl',
14007 Roo.HtmlEditorCore.black = [
14008 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14010 'base', 'basefont', 'bgsound', 'blink', 'body',
14011 'frame', 'frameset', 'head', 'html', 'ilayer',
14012 'iframe', 'layer', 'link', 'meta', 'object',
14013 'script', 'style' ,'title', 'xml' // clean later..
14015 Roo.HtmlEditorCore.clean = [
14016 'script', 'style', 'title', 'xml'
14018 Roo.HtmlEditorCore.remove = [
14023 Roo.HtmlEditorCore.ablack = [
14027 Roo.HtmlEditorCore.aclean = [
14028 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14032 Roo.HtmlEditorCore.pwhite= [
14033 'http', 'https', 'mailto'
14036 // white listed style attributes.
14037 Roo.HtmlEditorCore.cwhite= [
14038 // 'text-align', /// default is to allow most things..
14044 // black listed style attributes.
14045 Roo.HtmlEditorCore.cblack= [
14046 // 'font-size' -- this can be set by the project
14050 Roo.HtmlEditorCore.swapCodes =[
14069 * @class Roo.bootstrap.HtmlEditor
14070 * @extends Roo.bootstrap.TextArea
14071 * Bootstrap HtmlEditor class
14074 * Create a new HtmlEditor
14075 * @param {Object} config The config object
14078 Roo.bootstrap.HtmlEditor = function(config){
14079 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14080 if (!this.toolbars) {
14081 this.toolbars = [];
14083 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14086 * @event initialize
14087 * Fires when the editor is fully initialized (including the iframe)
14088 * @param {HtmlEditor} this
14093 * Fires when the editor is first receives the focus. Any insertion must wait
14094 * until after this event.
14095 * @param {HtmlEditor} this
14099 * @event beforesync
14100 * Fires before the textarea is updated with content from the editor iframe. Return false
14101 * to cancel the sync.
14102 * @param {HtmlEditor} this
14103 * @param {String} html
14107 * @event beforepush
14108 * Fires before the iframe editor is updated with content from the textarea. Return false
14109 * to cancel the push.
14110 * @param {HtmlEditor} this
14111 * @param {String} html
14116 * Fires when the textarea is updated with content from the editor iframe.
14117 * @param {HtmlEditor} this
14118 * @param {String} html
14123 * Fires when the iframe editor is updated with content from the textarea.
14124 * @param {HtmlEditor} this
14125 * @param {String} html
14129 * @event editmodechange
14130 * Fires when the editor switches edit modes
14131 * @param {HtmlEditor} this
14132 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14134 editmodechange: true,
14136 * @event editorevent
14137 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14138 * @param {HtmlEditor} this
14142 * @event firstfocus
14143 * Fires when on first focus - needed by toolbars..
14144 * @param {HtmlEditor} this
14149 * Auto save the htmlEditor value as a file into Events
14150 * @param {HtmlEditor} this
14154 * @event savedpreview
14155 * preview the saved version of htmlEditor
14156 * @param {HtmlEditor} this
14163 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14167 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14172 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14177 * @cfg {Number} height (in pixels)
14181 * @cfg {Number} width (in pixels)
14186 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14189 stylesheets: false,
14194 // private properties
14195 validationEvent : false,
14197 initialized : false,
14200 onFocus : Roo.emptyFn,
14202 hideMode:'offsets',
14205 tbContainer : false,
14207 toolbarContainer :function() {
14208 return this.wrap.select('.x-html-editor-tb',true).first();
14212 * Protected method that will not generally be called directly. It
14213 * is called when the editor creates its toolbar. Override this method if you need to
14214 * add custom toolbar buttons.
14215 * @param {HtmlEditor} editor
14217 createToolbar : function(){
14219 Roo.log("create toolbars");
14221 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14222 this.toolbars[0].render(this.toolbarContainer());
14226 // if (!editor.toolbars || !editor.toolbars.length) {
14227 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14230 // for (var i =0 ; i < editor.toolbars.length;i++) {
14231 // editor.toolbars[i] = Roo.factory(
14232 // typeof(editor.toolbars[i]) == 'string' ?
14233 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14234 // Roo.bootstrap.HtmlEditor);
14235 // editor.toolbars[i].init(editor);
14241 onRender : function(ct, position)
14243 // Roo.log("Call onRender: " + this.xtype);
14245 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14247 this.wrap = this.inputEl().wrap({
14248 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14251 this.editorcore.onRender(ct, position);
14253 if (this.resizable) {
14254 this.resizeEl = new Roo.Resizable(this.wrap, {
14258 minHeight : this.height,
14259 height: this.height,
14260 handles : this.resizable,
14263 resize : function(r, w, h) {
14264 _t.onResize(w,h); // -something
14270 this.createToolbar(this);
14273 if(!this.width && this.resizable){
14274 this.setSize(this.wrap.getSize());
14276 if (this.resizeEl) {
14277 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14278 // should trigger onReize..
14284 onResize : function(w, h)
14286 Roo.log('resize: ' +w + ',' + h );
14287 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14291 if(this.inputEl() ){
14292 if(typeof w == 'number'){
14293 var aw = w - this.wrap.getFrameWidth('lr');
14294 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14297 if(typeof h == 'number'){
14298 var tbh = -11; // fixme it needs to tool bar size!
14299 for (var i =0; i < this.toolbars.length;i++) {
14300 // fixme - ask toolbars for heights?
14301 tbh += this.toolbars[i].el.getHeight();
14302 //if (this.toolbars[i].footer) {
14303 // tbh += this.toolbars[i].footer.el.getHeight();
14311 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14312 ah -= 5; // knock a few pixes off for look..
14313 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14317 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14318 this.editorcore.onResize(ew,eh);
14323 * Toggles the editor between standard and source edit mode.
14324 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14326 toggleSourceEdit : function(sourceEditMode)
14328 this.editorcore.toggleSourceEdit(sourceEditMode);
14330 if(this.editorcore.sourceEditMode){
14331 Roo.log('editor - showing textarea');
14334 // Roo.log(this.syncValue());
14336 this.inputEl().removeClass('hide');
14337 this.inputEl().dom.removeAttribute('tabIndex');
14338 this.inputEl().focus();
14340 Roo.log('editor - hiding textarea');
14342 // Roo.log(this.pushValue());
14345 this.inputEl().addClass('hide');
14346 this.inputEl().dom.setAttribute('tabIndex', -1);
14347 //this.deferFocus();
14350 if(this.resizable){
14351 this.setSize(this.wrap.getSize());
14354 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14357 // private (for BoxComponent)
14358 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14360 // private (for BoxComponent)
14361 getResizeEl : function(){
14365 // private (for BoxComponent)
14366 getPositionEl : function(){
14371 initEvents : function(){
14372 this.originalValue = this.getValue();
14376 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14379 // markInvalid : Roo.emptyFn,
14381 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14384 // clearInvalid : Roo.emptyFn,
14386 setValue : function(v){
14387 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14388 this.editorcore.pushValue();
14393 deferFocus : function(){
14394 this.focus.defer(10, this);
14398 focus : function(){
14399 this.editorcore.focus();
14405 onDestroy : function(){
14411 for (var i =0; i < this.toolbars.length;i++) {
14412 // fixme - ask toolbars for heights?
14413 this.toolbars[i].onDestroy();
14416 this.wrap.dom.innerHTML = '';
14417 this.wrap.remove();
14422 onFirstFocus : function(){
14423 //Roo.log("onFirstFocus");
14424 this.editorcore.onFirstFocus();
14425 for (var i =0; i < this.toolbars.length;i++) {
14426 this.toolbars[i].onFirstFocus();
14432 syncValue : function()
14434 this.editorcore.syncValue();
14437 pushValue : function()
14439 this.editorcore.pushValue();
14443 // hide stuff that is not compatible
14457 * @event specialkey
14461 * @cfg {String} fieldClass @hide
14464 * @cfg {String} focusClass @hide
14467 * @cfg {String} autoCreate @hide
14470 * @cfg {String} inputType @hide
14473 * @cfg {String} invalidClass @hide
14476 * @cfg {String} invalidText @hide
14479 * @cfg {String} msgFx @hide
14482 * @cfg {String} validateOnBlur @hide
14493 * @class Roo.bootstrap.HtmlEditorToolbar1
14498 new Roo.bootstrap.HtmlEditor({
14501 new Roo.bootstrap.HtmlEditorToolbar1({
14502 disable : { fonts: 1 , format: 1, ..., ... , ...],
14508 * @cfg {Object} disable List of elements to disable..
14509 * @cfg {Array} btns List of additional buttons.
14513 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14516 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14519 Roo.apply(this, config);
14521 // default disabled, based on 'good practice'..
14522 this.disable = this.disable || {};
14523 Roo.applyIf(this.disable, {
14526 specialElements : true
14528 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14530 this.editor = config.editor;
14531 this.editorcore = config.editor.editorcore;
14533 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14535 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14536 // dont call parent... till later.
14538 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14544 editorcore : false,
14549 "h1","h2","h3","h4","h5","h6",
14551 "abbr", "acronym", "address", "cite", "samp", "var",
14555 onRender : function(ct, position)
14557 // Roo.log("Call onRender: " + this.xtype);
14559 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14561 this.el.dom.style.marginBottom = '0';
14563 var editorcore = this.editorcore;
14564 var editor= this.editor;
14567 var btn = function(id,cmd , toggle, handler){
14569 var event = toggle ? 'toggle' : 'click';
14574 xns: Roo.bootstrap,
14577 enableToggle:toggle !== false,
14579 pressed : toggle ? false : null,
14582 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14583 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14592 xns: Roo.bootstrap,
14593 glyphicon : 'font',
14597 xns: Roo.bootstrap,
14601 Roo.each(this.formats, function(f) {
14602 style.menu.items.push({
14604 xns: Roo.bootstrap,
14605 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14610 editorcore.insertTag(this.tagname);
14617 children.push(style);
14620 btn('bold',false,true);
14621 btn('italic',false,true);
14622 btn('align-left', 'justifyleft',true);
14623 btn('align-center', 'justifycenter',true);
14624 btn('align-right' , 'justifyright',true);
14625 btn('link', false, false, function(btn) {
14626 //Roo.log("create link?");
14627 var url = prompt(this.createLinkText, this.defaultLinkValue);
14628 if(url && url != 'http:/'+'/'){
14629 this.editorcore.relayCmd('createlink', url);
14632 btn('list','insertunorderedlist',true);
14633 btn('pencil', false,true, function(btn){
14636 this.toggleSourceEdit(btn.pressed);
14642 xns: Roo.bootstrap,
14647 xns: Roo.bootstrap,
14652 cog.menu.items.push({
14654 xns: Roo.bootstrap,
14655 html : Clean styles,
14660 editorcore.insertTag(this.tagname);
14669 this.xtype = 'Navbar';
14671 for(var i=0;i< children.length;i++) {
14673 this.buttons.add(this.addxtypeChild(children[i]));
14677 editor.on('editorevent', this.updateToolbar, this);
14679 onBtnClick : function(id)
14681 this.editorcore.relayCmd(id);
14682 this.editorcore.focus();
14686 * Protected method that will not generally be called directly. It triggers
14687 * a toolbar update by reading the markup state of the current selection in the editor.
14689 updateToolbar: function(){
14691 if(!this.editorcore.activated){
14692 this.editor.onFirstFocus(); // is this neeed?
14696 var btns = this.buttons;
14697 var doc = this.editorcore.doc;
14698 btns.get('bold').setActive(doc.queryCommandState('bold'));
14699 btns.get('italic').setActive(doc.queryCommandState('italic'));
14700 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14702 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14703 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14704 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14706 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14707 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14710 var ans = this.editorcore.getAllAncestors();
14711 if (this.formatCombo) {
14714 var store = this.formatCombo.store;
14715 this.formatCombo.setValue("");
14716 for (var i =0; i < ans.length;i++) {
14717 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14719 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14727 // hides menus... - so this cant be on a menu...
14728 Roo.bootstrap.MenuMgr.hideAll();
14730 Roo.bootstrap.MenuMgr.hideAll();
14731 //this.editorsyncValue();
14733 onFirstFocus: function() {
14734 this.buttons.each(function(item){
14738 toggleSourceEdit : function(sourceEditMode){
14741 if(sourceEditMode){
14742 Roo.log("disabling buttons");
14743 this.buttons.each( function(item){
14744 if(item.cmd != 'pencil'){
14750 Roo.log("enabling buttons");
14751 if(this.editorcore.initialized){
14752 this.buttons.each( function(item){
14758 Roo.log("calling toggole on editor");
14759 // tell the editor that it's been pressed..
14760 this.editor.toggleSourceEdit(sourceEditMode);
14770 * @class Roo.bootstrap.Table.AbstractSelectionModel
14771 * @extends Roo.util.Observable
14772 * Abstract base class for grid SelectionModels. It provides the interface that should be
14773 * implemented by descendant classes. This class should not be directly instantiated.
14776 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14777 this.locked = false;
14778 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14782 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14783 /** @ignore Called by the grid automatically. Do not call directly. */
14784 init : function(grid){
14790 * Locks the selections.
14793 this.locked = true;
14797 * Unlocks the selections.
14799 unlock : function(){
14800 this.locked = false;
14804 * Returns true if the selections are locked.
14805 * @return {Boolean}
14807 isLocked : function(){
14808 return this.locked;
14812 * @class Roo.bootstrap.Table.ColumnModel
14813 * @extends Roo.util.Observable
14814 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14815 * the columns in the table.
14818 * @param {Object} config An Array of column config objects. See this class's
14819 * config objects for details.
14821 Roo.bootstrap.Table.ColumnModel = function(config){
14823 * The config passed into the constructor
14825 this.config = config;
14828 // if no id, create one
14829 // if the column does not have a dataIndex mapping,
14830 // map it to the order it is in the config
14831 for(var i = 0, len = config.length; i < len; i++){
14833 if(typeof c.dataIndex == "undefined"){
14836 if(typeof c.renderer == "string"){
14837 c.renderer = Roo.util.Format[c.renderer];
14839 if(typeof c.id == "undefined"){
14842 // if(c.editor && c.editor.xtype){
14843 // c.editor = Roo.factory(c.editor, Roo.grid);
14845 // if(c.editor && c.editor.isFormField){
14846 // c.editor = new Roo.grid.GridEditor(c.editor);
14849 this.lookup[c.id] = c;
14853 * The width of columns which have no width specified (defaults to 100)
14856 this.defaultWidth = 100;
14859 * Default sortable of columns which have no sortable specified (defaults to false)
14862 this.defaultSortable = false;
14866 * @event widthchange
14867 * Fires when the width of a column changes.
14868 * @param {ColumnModel} this
14869 * @param {Number} columnIndex The column index
14870 * @param {Number} newWidth The new width
14872 "widthchange": true,
14874 * @event headerchange
14875 * Fires when the text of a header changes.
14876 * @param {ColumnModel} this
14877 * @param {Number} columnIndex The column index
14878 * @param {Number} newText The new header text
14880 "headerchange": true,
14882 * @event hiddenchange
14883 * Fires when a column is hidden or "unhidden".
14884 * @param {ColumnModel} this
14885 * @param {Number} columnIndex The column index
14886 * @param {Boolean} hidden true if hidden, false otherwise
14888 "hiddenchange": true,
14890 * @event columnmoved
14891 * Fires when a column is moved.
14892 * @param {ColumnModel} this
14893 * @param {Number} oldIndex
14894 * @param {Number} newIndex
14896 "columnmoved" : true,
14898 * @event columlockchange
14899 * Fires when a column's locked state is changed
14900 * @param {ColumnModel} this
14901 * @param {Number} colIndex
14902 * @param {Boolean} locked true if locked
14904 "columnlockchange" : true
14906 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14908 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14910 * @cfg {String} header The header text to display in the Grid view.
14913 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14914 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14915 * specified, the column's index is used as an index into the Record's data Array.
14918 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14919 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14922 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14923 * Defaults to the value of the {@link #defaultSortable} property.
14924 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14927 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14930 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14933 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14936 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14939 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14940 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14941 * default renderer uses the raw data value.
14944 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14948 * Returns the id of the column at the specified index.
14949 * @param {Number} index The column index
14950 * @return {String} the id
14952 getColumnId : function(index){
14953 return this.config[index].id;
14957 * Returns the column for a specified id.
14958 * @param {String} id The column id
14959 * @return {Object} the column
14961 getColumnById : function(id){
14962 return this.lookup[id];
14967 * Returns the column for a specified dataIndex.
14968 * @param {String} dataIndex The column dataIndex
14969 * @return {Object|Boolean} the column or false if not found
14971 getColumnByDataIndex: function(dataIndex){
14972 var index = this.findColumnIndex(dataIndex);
14973 return index > -1 ? this.config[index] : false;
14977 * Returns the index for a specified column id.
14978 * @param {String} id The column id
14979 * @return {Number} the index, or -1 if not found
14981 getIndexById : function(id){
14982 for(var i = 0, len = this.config.length; i < len; i++){
14983 if(this.config[i].id == id){
14991 * Returns the index for a specified column dataIndex.
14992 * @param {String} dataIndex The column dataIndex
14993 * @return {Number} the index, or -1 if not found
14996 findColumnIndex : function(dataIndex){
14997 for(var i = 0, len = this.config.length; i < len; i++){
14998 if(this.config[i].dataIndex == dataIndex){
15006 moveColumn : function(oldIndex, newIndex){
15007 var c = this.config[oldIndex];
15008 this.config.splice(oldIndex, 1);
15009 this.config.splice(newIndex, 0, c);
15010 this.dataMap = null;
15011 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15014 isLocked : function(colIndex){
15015 return this.config[colIndex].locked === true;
15018 setLocked : function(colIndex, value, suppressEvent){
15019 if(this.isLocked(colIndex) == value){
15022 this.config[colIndex].locked = value;
15023 if(!suppressEvent){
15024 this.fireEvent("columnlockchange", this, colIndex, value);
15028 getTotalLockedWidth : function(){
15029 var totalWidth = 0;
15030 for(var i = 0; i < this.config.length; i++){
15031 if(this.isLocked(i) && !this.isHidden(i)){
15032 this.totalWidth += this.getColumnWidth(i);
15038 getLockedCount : function(){
15039 for(var i = 0, len = this.config.length; i < len; i++){
15040 if(!this.isLocked(i)){
15047 * Returns the number of columns.
15050 getColumnCount : function(visibleOnly){
15051 if(visibleOnly === true){
15053 for(var i = 0, len = this.config.length; i < len; i++){
15054 if(!this.isHidden(i)){
15060 return this.config.length;
15064 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15065 * @param {Function} fn
15066 * @param {Object} scope (optional)
15067 * @return {Array} result
15069 getColumnsBy : function(fn, scope){
15071 for(var i = 0, len = this.config.length; i < len; i++){
15072 var c = this.config[i];
15073 if(fn.call(scope||this, c, i) === true){
15081 * Returns true if the specified column is sortable.
15082 * @param {Number} col The column index
15083 * @return {Boolean}
15085 isSortable : function(col){
15086 if(typeof this.config[col].sortable == "undefined"){
15087 return this.defaultSortable;
15089 return this.config[col].sortable;
15093 * Returns the rendering (formatting) function defined for the column.
15094 * @param {Number} col The column index.
15095 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15097 getRenderer : function(col){
15098 if(!this.config[col].renderer){
15099 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15101 return this.config[col].renderer;
15105 * Sets the rendering (formatting) function for a column.
15106 * @param {Number} col The column index
15107 * @param {Function} fn The function to use to process the cell's raw data
15108 * to return HTML markup for the grid view. The render function is called with
15109 * the following parameters:<ul>
15110 * <li>Data value.</li>
15111 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15112 * <li>css A CSS style string to apply to the table cell.</li>
15113 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15114 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15115 * <li>Row index</li>
15116 * <li>Column index</li>
15117 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15119 setRenderer : function(col, fn){
15120 this.config[col].renderer = fn;
15124 * Returns the width for the specified column.
15125 * @param {Number} col The column index
15128 getColumnWidth : function(col){
15129 return this.config[col].width * 1 || this.defaultWidth;
15133 * Sets the width for a column.
15134 * @param {Number} col The column index
15135 * @param {Number} width The new width
15137 setColumnWidth : function(col, width, suppressEvent){
15138 this.config[col].width = width;
15139 this.totalWidth = null;
15140 if(!suppressEvent){
15141 this.fireEvent("widthchange", this, col, width);
15146 * Returns the total width of all columns.
15147 * @param {Boolean} includeHidden True to include hidden column widths
15150 getTotalWidth : function(includeHidden){
15151 if(!this.totalWidth){
15152 this.totalWidth = 0;
15153 for(var i = 0, len = this.config.length; i < len; i++){
15154 if(includeHidden || !this.isHidden(i)){
15155 this.totalWidth += this.getColumnWidth(i);
15159 return this.totalWidth;
15163 * Returns the header for the specified column.
15164 * @param {Number} col The column index
15167 getColumnHeader : function(col){
15168 return this.config[col].header;
15172 * Sets the header for a column.
15173 * @param {Number} col The column index
15174 * @param {String} header The new header
15176 setColumnHeader : function(col, header){
15177 this.config[col].header = header;
15178 this.fireEvent("headerchange", this, col, header);
15182 * Returns the tooltip for the specified column.
15183 * @param {Number} col The column index
15186 getColumnTooltip : function(col){
15187 return this.config[col].tooltip;
15190 * Sets the tooltip for a column.
15191 * @param {Number} col The column index
15192 * @param {String} tooltip The new tooltip
15194 setColumnTooltip : function(col, tooltip){
15195 this.config[col].tooltip = tooltip;
15199 * Returns the dataIndex for the specified column.
15200 * @param {Number} col The column index
15203 getDataIndex : function(col){
15204 return this.config[col].dataIndex;
15208 * Sets the dataIndex for a column.
15209 * @param {Number} col The column index
15210 * @param {Number} dataIndex The new dataIndex
15212 setDataIndex : function(col, dataIndex){
15213 this.config[col].dataIndex = dataIndex;
15219 * Returns true if the cell is editable.
15220 * @param {Number} colIndex The column index
15221 * @param {Number} rowIndex The row index
15222 * @return {Boolean}
15224 isCellEditable : function(colIndex, rowIndex){
15225 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15229 * Returns the editor defined for the cell/column.
15230 * return false or null to disable editing.
15231 * @param {Number} colIndex The column index
15232 * @param {Number} rowIndex The row index
15235 getCellEditor : function(colIndex, rowIndex){
15236 return this.config[colIndex].editor;
15240 * Sets if a column is editable.
15241 * @param {Number} col The column index
15242 * @param {Boolean} editable True if the column is editable
15244 setEditable : function(col, editable){
15245 this.config[col].editable = editable;
15250 * Returns true if the column is hidden.
15251 * @param {Number} colIndex The column index
15252 * @return {Boolean}
15254 isHidden : function(colIndex){
15255 return this.config[colIndex].hidden;
15260 * Returns true if the column width cannot be changed
15262 isFixed : function(colIndex){
15263 return this.config[colIndex].fixed;
15267 * Returns true if the column can be resized
15268 * @return {Boolean}
15270 isResizable : function(colIndex){
15271 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15274 * Sets if a column is hidden.
15275 * @param {Number} colIndex The column index
15276 * @param {Boolean} hidden True if the column is hidden
15278 setHidden : function(colIndex, hidden){
15279 this.config[colIndex].hidden = hidden;
15280 this.totalWidth = null;
15281 this.fireEvent("hiddenchange", this, colIndex, hidden);
15285 * Sets the editor for a column.
15286 * @param {Number} col The column index
15287 * @param {Object} editor The editor object
15289 setEditor : function(col, editor){
15290 this.config[col].editor = editor;
15294 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15295 if(typeof value == "string" && value.length < 1){
15301 // Alias for backwards compatibility
15302 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15305 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15306 * @class Roo.bootstrap.Table.RowSelectionModel
15307 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15308 * It supports multiple selections and keyboard selection/navigation.
15310 * @param {Object} config
15313 Roo.bootstrap.Table.RowSelectionModel = function(config){
15314 Roo.apply(this, config);
15315 this.selections = new Roo.util.MixedCollection(false, function(o){
15320 this.lastActive = false;
15324 * @event selectionchange
15325 * Fires when the selection changes
15326 * @param {SelectionModel} this
15328 "selectionchange" : true,
15330 * @event afterselectionchange
15331 * Fires after the selection changes (eg. by key press or clicking)
15332 * @param {SelectionModel} this
15334 "afterselectionchange" : true,
15336 * @event beforerowselect
15337 * Fires when a row is selected being selected, return false to cancel.
15338 * @param {SelectionModel} this
15339 * @param {Number} rowIndex The selected index
15340 * @param {Boolean} keepExisting False if other selections will be cleared
15342 "beforerowselect" : true,
15345 * Fires when a row is selected.
15346 * @param {SelectionModel} this
15347 * @param {Number} rowIndex The selected index
15348 * @param {Roo.data.Record} r The record
15350 "rowselect" : true,
15352 * @event rowdeselect
15353 * Fires when a row is deselected.
15354 * @param {SelectionModel} this
15355 * @param {Number} rowIndex The selected index
15357 "rowdeselect" : true
15359 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15360 this.locked = false;
15363 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15365 * @cfg {Boolean} singleSelect
15366 * True to allow selection of only one row at a time (defaults to false)
15368 singleSelect : false,
15371 initEvents : function(){
15373 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15374 this.grid.on("mousedown", this.handleMouseDown, this);
15375 }else{ // allow click to work like normal
15376 this.grid.on("rowclick", this.handleDragableRowClick, this);
15379 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15380 "up" : function(e){
15382 this.selectPrevious(e.shiftKey);
15383 }else if(this.last !== false && this.lastActive !== false){
15384 var last = this.last;
15385 this.selectRange(this.last, this.lastActive-1);
15386 this.grid.getView().focusRow(this.lastActive);
15387 if(last !== false){
15391 this.selectFirstRow();
15393 this.fireEvent("afterselectionchange", this);
15395 "down" : function(e){
15397 this.selectNext(e.shiftKey);
15398 }else if(this.last !== false && this.lastActive !== false){
15399 var last = this.last;
15400 this.selectRange(this.last, this.lastActive+1);
15401 this.grid.getView().focusRow(this.lastActive);
15402 if(last !== false){
15406 this.selectFirstRow();
15408 this.fireEvent("afterselectionchange", this);
15413 var view = this.grid.view;
15414 view.on("refresh", this.onRefresh, this);
15415 view.on("rowupdated", this.onRowUpdated, this);
15416 view.on("rowremoved", this.onRemove, this);
15420 onRefresh : function(){
15421 var ds = this.grid.dataSource, i, v = this.grid.view;
15422 var s = this.selections;
15423 s.each(function(r){
15424 if((i = ds.indexOfId(r.id)) != -1){
15433 onRemove : function(v, index, r){
15434 this.selections.remove(r);
15438 onRowUpdated : function(v, index, r){
15439 if(this.isSelected(r)){
15440 v.onRowSelect(index);
15446 * @param {Array} records The records to select
15447 * @param {Boolean} keepExisting (optional) True to keep existing selections
15449 selectRecords : function(records, keepExisting){
15451 this.clearSelections();
15453 var ds = this.grid.dataSource;
15454 for(var i = 0, len = records.length; i < len; i++){
15455 this.selectRow(ds.indexOf(records[i]), true);
15460 * Gets the number of selected rows.
15463 getCount : function(){
15464 return this.selections.length;
15468 * Selects the first row in the grid.
15470 selectFirstRow : function(){
15475 * Select the last row.
15476 * @param {Boolean} keepExisting (optional) True to keep existing selections
15478 selectLastRow : function(keepExisting){
15479 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15483 * Selects the row immediately following the last selected row.
15484 * @param {Boolean} keepExisting (optional) True to keep existing selections
15486 selectNext : function(keepExisting){
15487 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15488 this.selectRow(this.last+1, keepExisting);
15489 this.grid.getView().focusRow(this.last);
15494 * Selects the row that precedes the last selected row.
15495 * @param {Boolean} keepExisting (optional) True to keep existing selections
15497 selectPrevious : function(keepExisting){
15499 this.selectRow(this.last-1, keepExisting);
15500 this.grid.getView().focusRow(this.last);
15505 * Returns the selected records
15506 * @return {Array} Array of selected records
15508 getSelections : function(){
15509 return [].concat(this.selections.items);
15513 * Returns the first selected record.
15516 getSelected : function(){
15517 return this.selections.itemAt(0);
15522 * Clears all selections.
15524 clearSelections : function(fast){
15525 if(this.locked) return;
15527 var ds = this.grid.dataSource;
15528 var s = this.selections;
15529 s.each(function(r){
15530 this.deselectRow(ds.indexOfId(r.id));
15534 this.selections.clear();
15541 * Selects all rows.
15543 selectAll : function(){
15544 if(this.locked) return;
15545 this.selections.clear();
15546 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15547 this.selectRow(i, true);
15552 * Returns True if there is a selection.
15553 * @return {Boolean}
15555 hasSelection : function(){
15556 return this.selections.length > 0;
15560 * Returns True if the specified row is selected.
15561 * @param {Number/Record} record The record or index of the record to check
15562 * @return {Boolean}
15564 isSelected : function(index){
15565 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15566 return (r && this.selections.key(r.id) ? true : false);
15570 * Returns True if the specified record id is selected.
15571 * @param {String} id The id of record to check
15572 * @return {Boolean}
15574 isIdSelected : function(id){
15575 return (this.selections.key(id) ? true : false);
15579 handleMouseDown : function(e, t){
15580 var view = this.grid.getView(), rowIndex;
15581 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15584 if(e.shiftKey && this.last !== false){
15585 var last = this.last;
15586 this.selectRange(last, rowIndex, e.ctrlKey);
15587 this.last = last; // reset the last
15588 view.focusRow(rowIndex);
15590 var isSelected = this.isSelected(rowIndex);
15591 if(e.button !== 0 && isSelected){
15592 view.focusRow(rowIndex);
15593 }else if(e.ctrlKey && isSelected){
15594 this.deselectRow(rowIndex);
15595 }else if(!isSelected){
15596 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15597 view.focusRow(rowIndex);
15600 this.fireEvent("afterselectionchange", this);
15603 handleDragableRowClick : function(grid, rowIndex, e)
15605 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15606 this.selectRow(rowIndex, false);
15607 grid.view.focusRow(rowIndex);
15608 this.fireEvent("afterselectionchange", this);
15613 * Selects multiple rows.
15614 * @param {Array} rows Array of the indexes of the row to select
15615 * @param {Boolean} keepExisting (optional) True to keep existing selections
15617 selectRows : function(rows, keepExisting){
15619 this.clearSelections();
15621 for(var i = 0, len = rows.length; i < len; i++){
15622 this.selectRow(rows[i], true);
15627 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15628 * @param {Number} startRow The index of the first row in the range
15629 * @param {Number} endRow The index of the last row in the range
15630 * @param {Boolean} keepExisting (optional) True to retain existing selections
15632 selectRange : function(startRow, endRow, keepExisting){
15633 if(this.locked) return;
15635 this.clearSelections();
15637 if(startRow <= endRow){
15638 for(var i = startRow; i <= endRow; i++){
15639 this.selectRow(i, true);
15642 for(var i = startRow; i >= endRow; i--){
15643 this.selectRow(i, true);
15649 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15650 * @param {Number} startRow The index of the first row in the range
15651 * @param {Number} endRow The index of the last row in the range
15653 deselectRange : function(startRow, endRow, preventViewNotify){
15654 if(this.locked) return;
15655 for(var i = startRow; i <= endRow; i++){
15656 this.deselectRow(i, preventViewNotify);
15662 * @param {Number} row The index of the row to select
15663 * @param {Boolean} keepExisting (optional) True to keep existing selections
15665 selectRow : function(index, keepExisting, preventViewNotify){
15666 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15667 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15668 if(!keepExisting || this.singleSelect){
15669 this.clearSelections();
15671 var r = this.grid.dataSource.getAt(index);
15672 this.selections.add(r);
15673 this.last = this.lastActive = index;
15674 if(!preventViewNotify){
15675 this.grid.getView().onRowSelect(index);
15677 this.fireEvent("rowselect", this, index, r);
15678 this.fireEvent("selectionchange", this);
15684 * @param {Number} row The index of the row to deselect
15686 deselectRow : function(index, preventViewNotify){
15687 if(this.locked) return;
15688 if(this.last == index){
15691 if(this.lastActive == index){
15692 this.lastActive = false;
15694 var r = this.grid.dataSource.getAt(index);
15695 this.selections.remove(r);
15696 if(!preventViewNotify){
15697 this.grid.getView().onRowDeselect(index);
15699 this.fireEvent("rowdeselect", this, index);
15700 this.fireEvent("selectionchange", this);
15704 restoreLast : function(){
15706 this.last = this._last;
15711 acceptsNav : function(row, col, cm){
15712 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15716 onEditorKey : function(field, e){
15717 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15722 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15724 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15726 }else if(k == e.ENTER && !e.ctrlKey){
15730 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15732 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15734 }else if(k == e.ESC){
15738 g.startEditing(newCell[0], newCell[1]);
15749 * @class Roo.bootstrap.MessageBar
15750 * @extends Roo.bootstrap.Component
15751 * Bootstrap MessageBar class
15752 * @cfg {String} html contents of the MessageBar
15753 * @cfg {String} weight (info | success | warning | danger) default info
15754 * @cfg {String} beforeClass insert the bar before the given class
15755 * @cfg {Boolean} closable (true | false) default false
15756 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15759 * Create a new Element
15760 * @param {Object} config The config object
15763 Roo.bootstrap.MessageBar = function(config){
15764 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15767 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15773 beforeClass: 'bootstrap-sticky-wrap',
15775 getAutoCreate : function(){
15779 cls: 'alert alert-dismissable alert-' + this.weight,
15784 html: this.html || ''
15790 cfg.cls += ' alert-messages-fixed';
15804 onRender : function(ct, position)
15806 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15809 var cfg = Roo.apply({}, this.getAutoCreate());
15813 cfg.cls += ' ' + this.cls;
15816 cfg.style = this.style;
15818 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15820 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15823 this.el.select('>button.close').on('click', this.hide, this);
15829 if (!this.rendered) {
15835 this.fireEvent('show', this);
15841 if (!this.rendered) {
15847 this.fireEvent('hide', this);
15850 update : function()
15852 // var e = this.el.dom.firstChild;
15854 // if(this.closable){
15855 // e = e.nextSibling;
15858 // e.data = this.html || '';
15860 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';