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!!!!');
2955 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956 e.on('click', _this.sort, _this);
2958 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 // this.maskEl.enableDisplayMode("block");
2960 // this.maskEl.show();
2962 this.store.on('load', this.onLoad, this);
2963 this.store.on('beforeload', this.onBeforeLoad, this);
2971 sort : function(e,el)
2973 var col = Roo.get(el)
2975 if(!col.hasClass('sortable')){
2979 var sort = col.attr('sort');
2982 if(col.hasClass('glyphicon-arrow-up')){
2986 this.store.sortInfo = {field : sort, direction : dir};
2991 renderHeader : function()
3000 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3002 var config = cm.config[i];
3006 html: cm.getColumnHeader(i)
3009 if(typeof(config.dataIndex) != 'undefined'){
3010 c.sort = config.dataIndex;
3013 if(typeof(config.sortable) != 'undefined' && config.sortable){
3023 renderBody : function()
3033 renderFooter : function()
3045 Roo.log('ds onload');
3050 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3051 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3053 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3054 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3058 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3061 var tbody = this.el.select('tbody', true).first();
3065 if(this.store.getCount() > 0){
3066 this.store.data.each(function(d){
3072 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3073 var renderer = cm.getRenderer(i);
3074 var config = cm.config[i];
3078 if(typeof(renderer) !== 'undefined'){
3079 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3082 if(typeof(value) === 'object'){
3092 html: (typeof(value) === 'object') ? '' : value
3095 if(typeof(config.width) != 'undefined'){
3096 td.width = config.width;
3103 tbody.createChild(row);
3111 Roo.each(renders, function(r){
3112 _this.renderColumn(r);
3116 // if(this.loadMask){
3117 // this.maskEl.hide();
3121 onBeforeLoad : function()
3123 Roo.log('ds onBeforeLoad');
3127 // if(this.loadMask){
3128 // this.maskEl.show();
3134 this.el.select('tbody', true).first().dom.innerHTML = '';
3137 getSelectionModel : function(){
3139 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3141 return this.selModel;
3144 renderColumn : function(r)
3147 r.cfg.render(Roo.get(r.id));
3150 Roo.each(r.cfg.cn, function(c){
3155 _this.renderColumn(child);
3172 * @class Roo.bootstrap.TableCell
3173 * @extends Roo.bootstrap.Component
3174 * Bootstrap TableCell class
3175 * @cfg {String} html cell contain text
3176 * @cfg {String} cls cell class
3177 * @cfg {String} tag cell tag (td|th) default td
3178 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3179 * @cfg {String} align Aligns the content in a cell
3180 * @cfg {String} axis Categorizes cells
3181 * @cfg {String} bgcolor Specifies the background color of a cell
3182 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3183 * @cfg {Number} colspan Specifies the number of columns a cell should span
3184 * @cfg {String} headers Specifies one or more header cells a cell is related to
3185 * @cfg {Number} height Sets the height of a cell
3186 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3187 * @cfg {Number} rowspan Sets the number of rows a cell should span
3188 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3189 * @cfg {String} valign Vertical aligns the content in a cell
3190 * @cfg {Number} width Specifies the width of a cell
3193 * Create a new TableCell
3194 * @param {Object} config The config object
3197 Roo.bootstrap.TableCell = function(config){
3198 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3201 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3221 getAutoCreate : function(){
3222 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3242 cfg.align=this.align
3248 cfg.bgcolor=this.bgcolor
3251 cfg.charoff=this.charoff
3254 cfg.colspan=this.colspan
3257 cfg.headers=this.headers
3260 cfg.height=this.height
3263 cfg.nowrap=this.nowrap
3266 cfg.rowspan=this.rowspan
3269 cfg.scope=this.scope
3272 cfg.valign=this.valign
3275 cfg.width=this.width
3294 * @class Roo.bootstrap.TableRow
3295 * @extends Roo.bootstrap.Component
3296 * Bootstrap TableRow class
3297 * @cfg {String} cls row class
3298 * @cfg {String} align Aligns the content in a table row
3299 * @cfg {String} bgcolor Specifies a background color for a table row
3300 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3301 * @cfg {String} valign Vertical aligns the content in a table row
3304 * Create a new TableRow
3305 * @param {Object} config The config object
3308 Roo.bootstrap.TableRow = function(config){
3309 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3312 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3320 getAutoCreate : function(){
3321 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3331 cfg.align = this.align;
3334 cfg.bgcolor = this.bgcolor;
3337 cfg.charoff = this.charoff;
3340 cfg.valign = this.valign;
3358 * @class Roo.bootstrap.TableBody
3359 * @extends Roo.bootstrap.Component
3360 * Bootstrap TableBody class
3361 * @cfg {String} cls element class
3362 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3363 * @cfg {String} align Aligns the content inside the element
3364 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3365 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3368 * Create a new TableBody
3369 * @param {Object} config The config object
3372 Roo.bootstrap.TableBody = function(config){
3373 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3376 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3384 getAutoCreate : function(){
3385 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3399 cfg.align = this.align;
3402 cfg.charoff = this.charoff;
3405 cfg.valign = this.valign;
3412 // initEvents : function()
3419 // this.store = Roo.factory(this.store, Roo.data);
3420 // this.store.on('load', this.onLoad, this);
3422 // this.store.load();
3426 // onLoad: function ()
3428 // this.fireEvent('load', this);
3438 * Ext JS Library 1.1.1
3439 * Copyright(c) 2006-2007, Ext JS, LLC.
3441 * Originally Released Under LGPL - original licence link has changed is not relivant.
3444 * <script type="text/javascript">
3447 // as we use this in bootstrap.
3448 Roo.namespace('Roo.form');
3450 * @class Roo.form.Action
3451 * Internal Class used to handle form actions
3453 * @param {Roo.form.BasicForm} el The form element or its id
3454 * @param {Object} config Configuration options
3459 // define the action interface
3460 Roo.form.Action = function(form, options){
3462 this.options = options || {};
3465 * Client Validation Failed
3468 Roo.form.Action.CLIENT_INVALID = 'client';
3470 * Server Validation Failed
3473 Roo.form.Action.SERVER_INVALID = 'server';
3475 * Connect to Server Failed
3478 Roo.form.Action.CONNECT_FAILURE = 'connect';
3480 * Reading Data from Server Failed
3483 Roo.form.Action.LOAD_FAILURE = 'load';
3485 Roo.form.Action.prototype = {
3487 failureType : undefined,
3488 response : undefined,
3492 run : function(options){
3497 success : function(response){
3502 handleResponse : function(response){
3506 // default connection failure
3507 failure : function(response){
3509 this.response = response;
3510 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3511 this.form.afterAction(this, false);
3514 processResponse : function(response){
3515 this.response = response;
3516 if(!response.responseText){
3519 this.result = this.handleResponse(response);
3523 // utility functions used internally
3524 getUrl : function(appendParams){
3525 var url = this.options.url || this.form.url || this.form.el.dom.action;
3527 var p = this.getParams();
3529 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3535 getMethod : function(){
3536 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3539 getParams : function(){
3540 var bp = this.form.baseParams;
3541 var p = this.options.params;
3543 if(typeof p == "object"){
3544 p = Roo.urlEncode(Roo.applyIf(p, bp));
3545 }else if(typeof p == 'string' && bp){
3546 p += '&' + Roo.urlEncode(bp);
3549 p = Roo.urlEncode(bp);
3554 createCallback : function(){
3556 success: this.success,
3557 failure: this.failure,
3559 timeout: (this.form.timeout*1000),
3560 upload: this.form.fileUpload ? this.success : undefined
3565 Roo.form.Action.Submit = function(form, options){
3566 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3569 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3572 haveProgress : false,
3573 uploadComplete : false,
3575 // uploadProgress indicator.
3576 uploadProgress : function()
3578 if (!this.form.progressUrl) {
3582 if (!this.haveProgress) {
3583 Roo.MessageBox.progress("Uploading", "Uploading");
3585 if (this.uploadComplete) {
3586 Roo.MessageBox.hide();
3590 this.haveProgress = true;
3592 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3594 var c = new Roo.data.Connection();
3596 url : this.form.progressUrl,
3601 success : function(req){
3602 //console.log(data);
3606 rdata = Roo.decode(req.responseText)
3608 Roo.log("Invalid data from server..");
3612 if (!rdata || !rdata.success) {
3614 Roo.MessageBox.alert(Roo.encode(rdata));
3617 var data = rdata.data;
3619 if (this.uploadComplete) {
3620 Roo.MessageBox.hide();
3625 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3626 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3629 this.uploadProgress.defer(2000,this);
3632 failure: function(data) {
3633 Roo.log('progress url failed ');
3644 // run get Values on the form, so it syncs any secondary forms.
3645 this.form.getValues();
3647 var o = this.options;
3648 var method = this.getMethod();
3649 var isPost = method == 'POST';
3650 if(o.clientValidation === false || this.form.isValid()){
3652 if (this.form.progressUrl) {
3653 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3654 (new Date() * 1) + '' + Math.random());
3659 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3660 form:this.form.el.dom,
3661 url:this.getUrl(!isPost),
3663 params:isPost ? this.getParams() : null,
3664 isUpload: this.form.fileUpload
3667 this.uploadProgress();
3669 }else if (o.clientValidation !== false){ // client validation failed
3670 this.failureType = Roo.form.Action.CLIENT_INVALID;
3671 this.form.afterAction(this, false);
3675 success : function(response)
3677 this.uploadComplete= true;
3678 if (this.haveProgress) {
3679 Roo.MessageBox.hide();
3683 var result = this.processResponse(response);
3684 if(result === true || result.success){
3685 this.form.afterAction(this, true);
3689 this.form.markInvalid(result.errors);
3690 this.failureType = Roo.form.Action.SERVER_INVALID;
3692 this.form.afterAction(this, false);
3694 failure : function(response)
3696 this.uploadComplete= true;
3697 if (this.haveProgress) {
3698 Roo.MessageBox.hide();
3701 this.response = response;
3702 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3703 this.form.afterAction(this, false);
3706 handleResponse : function(response){
3707 if(this.form.errorReader){
3708 var rs = this.form.errorReader.read(response);
3711 for(var i = 0, len = rs.records.length; i < len; i++) {
3712 var r = rs.records[i];
3716 if(errors.length < 1){
3720 success : rs.success,
3726 ret = Roo.decode(response.responseText);
3730 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3740 Roo.form.Action.Load = function(form, options){
3741 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3742 this.reader = this.form.reader;
3745 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3750 Roo.Ajax.request(Roo.apply(
3751 this.createCallback(), {
3752 method:this.getMethod(),
3753 url:this.getUrl(false),
3754 params:this.getParams()
3758 success : function(response){
3760 var result = this.processResponse(response);
3761 if(result === true || !result.success || !result.data){
3762 this.failureType = Roo.form.Action.LOAD_FAILURE;
3763 this.form.afterAction(this, false);
3766 this.form.clearInvalid();
3767 this.form.setValues(result.data);
3768 this.form.afterAction(this, true);
3771 handleResponse : function(response){
3772 if(this.form.reader){
3773 var rs = this.form.reader.read(response);
3774 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3776 success : rs.success,
3780 return Roo.decode(response.responseText);
3784 Roo.form.Action.ACTION_TYPES = {
3785 'load' : Roo.form.Action.Load,
3786 'submit' : Roo.form.Action.Submit
3795 * @class Roo.bootstrap.Form
3796 * @extends Roo.bootstrap.Component
3797 * Bootstrap Form class
3798 * @cfg {String} method GET | POST (default POST)
3799 * @cfg {String} labelAlign top | left (default top)
3800 * @cfg {String} align left | right - for navbars
3805 * @param {Object} config The config object
3809 Roo.bootstrap.Form = function(config){
3810 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3813 * @event clientvalidation
3814 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3815 * @param {Form} this
3816 * @param {Boolean} valid true if the form has passed client-side validation
3818 clientvalidation: true,
3820 * @event beforeaction
3821 * Fires before any action is performed. Return false to cancel the action.
3822 * @param {Form} this
3823 * @param {Action} action The action to be performed
3827 * @event actionfailed
3828 * Fires when an action fails.
3829 * @param {Form} this
3830 * @param {Action} action The action that failed
3832 actionfailed : true,
3834 * @event actioncomplete
3835 * Fires when an action is completed.
3836 * @param {Form} this
3837 * @param {Action} action The action that completed
3839 actioncomplete : true
3844 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3847 * @cfg {String} method
3848 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3853 * The URL to use for form actions if one isn't supplied in the action options.
3856 * @cfg {Boolean} fileUpload
3857 * Set to true if this form is a file upload.
3861 * @cfg {Object} baseParams
3862 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3866 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3870 * @cfg {Sting} align (left|right) for navbar forms
3875 activeAction : null,
3878 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3879 * element by passing it or its id or mask the form itself by passing in true.
3882 waitMsgTarget : false,
3887 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3888 * element by passing it or its id or mask the form itself by passing in true.
3892 getAutoCreate : function(){
3896 method : this.method || 'POST',
3897 id : this.id || Roo.id(),
3900 if (this.parent().xtype.match(/^Nav/)) {
3901 cfg.cls = 'navbar-form navbar-' + this.align;
3905 if (this.labelAlign == 'left' ) {
3906 cfg.cls += ' form-horizontal';
3912 initEvents : function()
3914 this.el.on('submit', this.onSubmit, this);
3919 onSubmit : function(e){
3924 * Returns true if client-side validation on the form is successful.
3927 isValid : function(){
3928 var items = this.getItems();
3930 items.each(function(f){
3939 * Returns true if any fields in this form have changed since their original load.
3942 isDirty : function(){
3944 var items = this.getItems();
3945 items.each(function(f){
3955 * Performs a predefined action (submit or load) or custom actions you define on this form.
3956 * @param {String} actionName The name of the action type
3957 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3958 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3959 * accept other config options):
3961 Property Type Description
3962 ---------------- --------------- ----------------------------------------------------------------------------------
3963 url String The url for the action (defaults to the form's url)
3964 method String The form method to use (defaults to the form's method, or POST if not defined)
3965 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3966 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3967 validate the form on the client (defaults to false)
3969 * @return {BasicForm} this
3971 doAction : function(action, options){
3972 if(typeof action == 'string'){
3973 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3975 if(this.fireEvent('beforeaction', this, action) !== false){
3976 this.beforeAction(action);
3977 action.run.defer(100, action);
3983 beforeAction : function(action){
3984 var o = action.options;
3986 // not really supported yet.. ??
3988 //if(this.waitMsgTarget === true){
3989 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3990 //}else if(this.waitMsgTarget){
3991 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3992 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3994 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4000 afterAction : function(action, success){
4001 this.activeAction = null;
4002 var o = action.options;
4004 //if(this.waitMsgTarget === true){
4006 //}else if(this.waitMsgTarget){
4007 // this.waitMsgTarget.unmask();
4009 // Roo.MessageBox.updateProgress(1);
4010 // Roo.MessageBox.hide();
4017 Roo.callback(o.success, o.scope, [this, action]);
4018 this.fireEvent('actioncomplete', this, action);
4022 // failure condition..
4023 // we have a scenario where updates need confirming.
4024 // eg. if a locking scenario exists..
4025 // we look for { errors : { needs_confirm : true }} in the response.
4027 (typeof(action.result) != 'undefined') &&
4028 (typeof(action.result.errors) != 'undefined') &&
4029 (typeof(action.result.errors.needs_confirm) != 'undefined')
4032 Roo.log("not supported yet");
4035 Roo.MessageBox.confirm(
4036 "Change requires confirmation",
4037 action.result.errorMsg,
4042 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4052 Roo.callback(o.failure, o.scope, [this, action]);
4053 // show an error message if no failed handler is set..
4054 if (!this.hasListener('actionfailed')) {
4055 Roo.log("need to add dialog support");
4057 Roo.MessageBox.alert("Error",
4058 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4059 action.result.errorMsg :
4060 "Saving Failed, please check your entries or try again"
4065 this.fireEvent('actionfailed', this, action);
4070 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4071 * @param {String} id The value to search for
4074 findField : function(id){
4075 var items = this.getItems();
4076 var field = items.get(id);
4078 items.each(function(f){
4079 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4086 return field || null;
4089 * Mark fields in this form invalid in bulk.
4090 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4091 * @return {BasicForm} this
4093 markInvalid : function(errors){
4094 if(errors instanceof Array){
4095 for(var i = 0, len = errors.length; i < len; i++){
4096 var fieldError = errors[i];
4097 var f = this.findField(fieldError.id);
4099 f.markInvalid(fieldError.msg);
4105 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4106 field.markInvalid(errors[id]);
4110 //Roo.each(this.childForms || [], function (f) {
4111 // f.markInvalid(errors);
4118 * Set values for fields in this form in bulk.
4119 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4120 * @return {BasicForm} this
4122 setValues : function(values){
4123 if(values instanceof Array){ // array of objects
4124 for(var i = 0, len = values.length; i < len; i++){
4126 var f = this.findField(v.id);
4128 f.setValue(v.value);
4129 if(this.trackResetOnLoad){
4130 f.originalValue = f.getValue();
4134 }else{ // object hash
4137 if(typeof values[id] != 'function' && (field = this.findField(id))){
4139 if (field.setFromData &&
4141 field.displayField &&
4142 // combos' with local stores can
4143 // be queried via setValue()
4144 // to set their value..
4145 (field.store && !field.store.isLocal)
4149 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4150 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4151 field.setFromData(sd);
4154 field.setValue(values[id]);
4158 if(this.trackResetOnLoad){
4159 field.originalValue = field.getValue();
4165 //Roo.each(this.childForms || [], function (f) {
4166 // f.setValues(values);
4173 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4174 * they are returned as an array.
4175 * @param {Boolean} asString
4178 getValues : function(asString){
4179 //if (this.childForms) {
4180 // copy values from the child forms
4181 // Roo.each(this.childForms, function (f) {
4182 // this.setValues(f.getValues());
4188 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4189 if(asString === true){
4192 return Roo.urlDecode(fs);
4196 * Returns the fields in this form as an object with key/value pairs.
4197 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4200 getFieldValues : function(with_hidden)
4202 var items = this.getItems();
4204 items.each(function(f){
4208 var v = f.getValue();
4209 if (f.inputType =='radio') {
4210 if (typeof(ret[f.getName()]) == 'undefined') {
4211 ret[f.getName()] = ''; // empty..
4214 if (!f.el.dom.checked) {
4222 // not sure if this supported any more..
4223 if ((typeof(v) == 'object') && f.getRawValue) {
4224 v = f.getRawValue() ; // dates..
4226 // combo boxes where name != hiddenName...
4227 if (f.name != f.getName()) {
4228 ret[f.name] = f.getRawValue();
4230 ret[f.getName()] = v;
4237 * Clears all invalid messages in this form.
4238 * @return {BasicForm} this
4240 clearInvalid : function(){
4241 var items = this.getItems();
4243 items.each(function(f){
4254 * @return {BasicForm} this
4257 var items = this.getItems();
4258 items.each(function(f){
4262 Roo.each(this.childForms || [], function (f) {
4269 getItems : function()
4271 var r=new Roo.util.MixedCollection(false, function(o){
4272 return o.id || (o.id = Roo.id());
4274 var iter = function(el) {
4281 Roo.each(el.items,function(e) {
4300 * Ext JS Library 1.1.1
4301 * Copyright(c) 2006-2007, Ext JS, LLC.
4303 * Originally Released Under LGPL - original licence link has changed is not relivant.
4306 * <script type="text/javascript">
4309 * @class Roo.form.VTypes
4310 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4313 Roo.form.VTypes = function(){
4314 // closure these in so they are only created once.
4315 var alpha = /^[a-zA-Z_]+$/;
4316 var alphanum = /^[a-zA-Z0-9_]+$/;
4317 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4318 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4320 // All these messages and functions are configurable
4323 * The function used to validate email addresses
4324 * @param {String} value The email address
4326 'email' : function(v){
4327 return email.test(v);
4330 * The error text to display when the email validation function returns false
4333 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4335 * The keystroke filter mask to be applied on email input
4338 'emailMask' : /[a-z0-9_\.\-@]/i,
4341 * The function used to validate URLs
4342 * @param {String} value The URL
4344 'url' : function(v){
4348 * The error text to display when the url validation function returns false
4351 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4354 * The function used to validate alpha values
4355 * @param {String} value The value
4357 'alpha' : function(v){
4358 return alpha.test(v);
4361 * The error text to display when the alpha validation function returns false
4364 'alphaText' : 'This field should only contain letters and _',
4366 * The keystroke filter mask to be applied on alpha input
4369 'alphaMask' : /[a-z_]/i,
4372 * The function used to validate alphanumeric values
4373 * @param {String} value The value
4375 'alphanum' : function(v){
4376 return alphanum.test(v);
4379 * The error text to display when the alphanumeric validation function returns false
4382 'alphanumText' : 'This field should only contain letters, numbers and _',
4384 * The keystroke filter mask to be applied on alphanumeric input
4387 'alphanumMask' : /[a-z0-9_]/i
4397 * @class Roo.bootstrap.Input
4398 * @extends Roo.bootstrap.Component
4399 * Bootstrap Input class
4400 * @cfg {Boolean} disabled is it disabled
4401 * @cfg {String} fieldLabel - the label associated
4402 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4403 * @cfg {String} name name of the input
4404 * @cfg {string} fieldLabel - the label associated
4405 * @cfg {string} inputType - input / file submit ...
4406 * @cfg {string} placeholder - placeholder to put in text.
4407 * @cfg {string} before - input group add on before
4408 * @cfg {string} after - input group add on after
4409 * @cfg {string} size - (lg|sm) or leave empty..
4410 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4411 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4412 * @cfg {Number} md colspan out of 12 for computer-sized screens
4413 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4414 * @cfg {string} value default value of the input
4415 * @cfg {Number} labelWidth set the width of label (0-12)
4416 * @cfg {String} labelAlign (top|left)
4417 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4421 * Create a new Input
4422 * @param {Object} config The config object
4425 Roo.bootstrap.Input = function(config){
4426 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4431 * Fires when this field receives input focus.
4432 * @param {Roo.form.Field} this
4437 * Fires when this field loses input focus.
4438 * @param {Roo.form.Field} this
4443 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4444 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4445 * @param {Roo.form.Field} this
4446 * @param {Roo.EventObject} e The event object
4451 * Fires just before the field blurs if the field value has changed.
4452 * @param {Roo.form.Field} this
4453 * @param {Mixed} newValue The new value
4454 * @param {Mixed} oldValue The original value
4459 * Fires after the field has been marked as invalid.
4460 * @param {Roo.form.Field} this
4461 * @param {String} msg The validation message
4466 * Fires after the field has been validated with no errors.
4467 * @param {Roo.form.Field} this
4472 * Fires after the key up
4473 * @param {Roo.form.Field} this
4474 * @param {Roo.EventObject} e The event Object
4480 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4482 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4483 automatic validation (defaults to "keyup").
4485 validationEvent : "keyup",
4487 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4489 validateOnBlur : true,
4491 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4493 validationDelay : 250,
4495 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4497 focusClass : "x-form-focus", // not needed???
4501 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4503 invalidClass : "has-error",
4506 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4508 selectOnFocus : false,
4511 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4515 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4520 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4522 disableKeyFilter : false,
4525 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4529 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4533 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4535 blankText : "This field is required",
4538 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4542 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4544 maxLength : Number.MAX_VALUE,
4546 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4548 minLengthText : "The minimum length for this field is {0}",
4550 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4552 maxLengthText : "The maximum length for this field is {0}",
4556 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4557 * If available, this function will be called only after the basic validators all return true, and will be passed the
4558 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4562 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4563 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4564 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4568 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4591 parentLabelAlign : function()
4594 while (parent.parent()) {
4595 parent = parent.parent();
4596 if (typeof(parent.labelAlign) !='undefined') {
4597 return parent.labelAlign;
4604 getAutoCreate : function(){
4606 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4612 if(this.inputType != 'hidden'){
4613 cfg.cls = 'form-group' //input-group
4619 type : this.inputType,
4621 cls : 'form-control',
4622 placeholder : this.placeholder || ''
4626 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4627 input.maxLength = this.maxLength;
4630 if (this.disabled) {
4631 input.disabled=true;
4634 if (this.readOnly) {
4635 input.readonly=true;
4639 input.name = this.name;
4642 input.cls += ' input-' + this.size;
4645 ['xs','sm','md','lg'].map(function(size){
4646 if (settings[size]) {
4647 cfg.cls += ' col-' + size + '-' + settings[size];
4651 var inputblock = input;
4653 if (this.before || this.after) {
4656 cls : 'input-group',
4660 inputblock.cn.push({
4662 cls : 'input-group-addon',
4666 inputblock.cn.push(input);
4668 inputblock.cn.push({
4670 cls : 'input-group-addon',
4677 if (align ==='left' && this.fieldLabel.length) {
4678 Roo.log("left and has label");
4684 cls : 'control-label col-sm-' + this.labelWidth,
4685 html : this.fieldLabel
4689 cls : "col-sm-" + (12 - this.labelWidth),
4696 } else if ( this.fieldLabel.length) {
4702 //cls : 'input-group-addon',
4703 html : this.fieldLabel
4713 Roo.log(" no label && no align");
4722 Roo.log('input-parentType: ' + this.parentType);
4724 if (this.parentType === 'Navbar' && this.parent().bar) {
4725 cfg.cls += ' navbar-form';
4733 * return the real input element.
4735 inputEl: function ()
4737 return this.el.select('input.form-control',true).first();
4739 setDisabled : function(v)
4741 var i = this.inputEl().dom;
4743 i.removeAttribute('disabled');
4747 i.setAttribute('disabled','true');
4749 initEvents : function()
4752 this.inputEl().on("keydown" , this.fireKey, this);
4753 this.inputEl().on("focus", this.onFocus, this);
4754 this.inputEl().on("blur", this.onBlur, this);
4756 this.inputEl().relayEvent('keyup', this);
4758 // reference to original value for reset
4759 this.originalValue = this.getValue();
4760 //Roo.form.TextField.superclass.initEvents.call(this);
4761 if(this.validationEvent == 'keyup'){
4762 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4763 this.inputEl().on('keyup', this.filterValidation, this);
4765 else if(this.validationEvent !== false){
4766 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4769 if(this.selectOnFocus){
4770 this.on("focus", this.preFocus, this);
4773 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4774 this.inputEl().on("keypress", this.filterKeys, this);
4777 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4778 this.el.on("click", this.autoSize, this);
4781 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4782 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4786 filterValidation : function(e){
4787 if(!e.isNavKeyPress()){
4788 this.validationTask.delay(this.validationDelay);
4792 * Validates the field value
4793 * @return {Boolean} True if the value is valid, else false
4795 validate : function(){
4796 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4797 if(this.disabled || this.validateValue(this.getRawValue())){
4798 this.clearInvalid();
4806 * Validates a value according to the field's validation rules and marks the field as invalid
4807 * if the validation fails
4808 * @param {Mixed} value The value to validate
4809 * @return {Boolean} True if the value is valid, else false
4811 validateValue : function(value){
4812 if(value.length < 1) { // if it's blank
4813 if(this.allowBlank){
4814 this.clearInvalid();
4817 this.markInvalid(this.blankText);
4821 if(value.length < this.minLength){
4822 this.markInvalid(String.format(this.minLengthText, this.minLength));
4825 if(value.length > this.maxLength){
4826 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4830 var vt = Roo.form.VTypes;
4831 if(!vt[this.vtype](value, this)){
4832 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4836 if(typeof this.validator == "function"){
4837 var msg = this.validator(value);
4839 this.markInvalid(msg);
4843 if(this.regex && !this.regex.test(value)){
4844 this.markInvalid(this.regexText);
4853 fireKey : function(e){
4854 //Roo.log('field ' + e.getKey());
4855 if(e.isNavKeyPress()){
4856 this.fireEvent("specialkey", this, e);
4859 focus : function (selectText){
4861 this.inputEl().focus();
4862 if(selectText === true){
4863 this.inputEl().dom.select();
4869 onFocus : function(){
4870 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4871 // this.el.addClass(this.focusClass);
4874 this.hasFocus = true;
4875 this.startValue = this.getValue();
4876 this.fireEvent("focus", this);
4880 beforeBlur : Roo.emptyFn,
4884 onBlur : function(){
4886 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4887 //this.el.removeClass(this.focusClass);
4889 this.hasFocus = false;
4890 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4893 var v = this.getValue();
4894 if(String(v) !== String(this.startValue)){
4895 this.fireEvent('change', this, v, this.startValue);
4897 this.fireEvent("blur", this);
4901 * Resets the current field value to the originally loaded value and clears any validation messages
4904 this.setValue(this.originalValue);
4905 this.clearInvalid();
4908 * Returns the name of the field
4909 * @return {Mixed} name The name field
4911 getName: function(){
4915 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4916 * @return {Mixed} value The field value
4918 getValue : function(){
4919 return this.inputEl().getValue();
4922 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4923 * @return {Mixed} value The field value
4925 getRawValue : function(){
4926 var v = this.inputEl().getValue();
4932 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4933 * @param {Mixed} value The value to set
4935 setRawValue : function(v){
4936 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4939 selectText : function(start, end){
4940 var v = this.getRawValue();
4942 start = start === undefined ? 0 : start;
4943 end = end === undefined ? v.length : end;
4944 var d = this.inputEl().dom;
4945 if(d.setSelectionRange){
4946 d.setSelectionRange(start, end);
4947 }else if(d.createTextRange){
4948 var range = d.createTextRange();
4949 range.moveStart("character", start);
4950 range.moveEnd("character", v.length-end);
4957 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4958 * @param {Mixed} value The value to set
4960 setValue : function(v){
4963 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4969 processValue : function(value){
4970 if(this.stripCharsRe){
4971 var newValue = value.replace(this.stripCharsRe, '');
4972 if(newValue !== value){
4973 this.setRawValue(newValue);
4980 preFocus : function(){
4982 if(this.selectOnFocus){
4983 this.inputEl().dom.select();
4986 filterKeys : function(e){
4988 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4991 var c = e.getCharCode(), cc = String.fromCharCode(c);
4992 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4995 if(!this.maskRe.test(cc)){
5000 * Clear any invalid styles/messages for this field
5002 clearInvalid : function(){
5004 if(!this.el || this.preventMark){ // not rendered
5007 this.el.removeClass(this.invalidClass);
5009 switch(this.msgTarget){
5011 this.el.dom.qtip = '';
5014 this.el.dom.title = '';
5018 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5023 this.errorIcon.dom.qtip = '';
5024 this.errorIcon.hide();
5025 this.un('resize', this.alignErrorIcon, this);
5029 var t = Roo.getDom(this.msgTarget);
5031 t.style.display = 'none';
5035 this.fireEvent('valid', this);
5038 * Mark this field as invalid
5039 * @param {String} msg The validation message
5041 markInvalid : function(msg){
5042 if(!this.el || this.preventMark){ // not rendered
5045 this.el.addClass(this.invalidClass);
5047 msg = msg || this.invalidText;
5048 switch(this.msgTarget){
5050 this.el.dom.qtip = msg;
5051 this.el.dom.qclass = 'x-form-invalid-tip';
5052 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5053 Roo.QuickTips.enable();
5057 this.el.dom.title = msg;
5061 var elp = this.el.findParent('.x-form-element', 5, true);
5062 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5063 this.errorEl.setWidth(elp.getWidth(true)-20);
5065 this.errorEl.update(msg);
5066 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5069 if(!this.errorIcon){
5070 var elp = this.el.findParent('.x-form-element', 5, true);
5071 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5073 this.alignErrorIcon();
5074 this.errorIcon.dom.qtip = msg;
5075 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5076 this.errorIcon.show();
5077 this.on('resize', this.alignErrorIcon, this);
5080 var t = Roo.getDom(this.msgTarget);
5082 t.style.display = this.msgDisplay;
5086 this.fireEvent('invalid', this, msg);
5089 SafariOnKeyDown : function(event)
5091 // this is a workaround for a password hang bug on chrome/ webkit.
5093 var isSelectAll = false;
5095 if(this.inputEl().dom.selectionEnd > 0){
5096 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5098 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5099 event.preventDefault();
5104 if(isSelectAll){ // backspace and delete key
5106 event.preventDefault();
5107 // this is very hacky as keydown always get's upper case.
5109 var cc = String.fromCharCode(event.getCharCode());
5110 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5114 adjustWidth : function(tag, w){
5115 tag = tag.toLowerCase();
5116 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5117 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5121 if(tag == 'textarea'){
5124 }else if(Roo.isOpera){
5128 if(tag == 'textarea'){
5147 * @class Roo.bootstrap.TextArea
5148 * @extends Roo.bootstrap.Input
5149 * Bootstrap TextArea class
5150 * @cfg {Number} cols Specifies the visible width of a text area
5151 * @cfg {Number} rows Specifies the visible number of lines in a text area
5152 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5153 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5154 * @cfg {string} html text
5157 * Create a new TextArea
5158 * @param {Object} config The config object
5161 Roo.bootstrap.TextArea = function(config){
5162 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5166 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5176 getAutoCreate : function(){
5178 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5189 value : this.value || '',
5190 html: this.html || '',
5191 cls : 'form-control',
5192 placeholder : this.placeholder || ''
5196 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5197 input.maxLength = this.maxLength;
5201 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5205 input.cols = this.cols;
5208 if (this.readOnly) {
5209 input.readonly = true;
5213 input.name = this.name;
5217 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5221 ['xs','sm','md','lg'].map(function(size){
5222 if (settings[size]) {
5223 cfg.cls += ' col-' + size + '-' + settings[size];
5227 var inputblock = input;
5229 if (this.before || this.after) {
5232 cls : 'input-group',
5236 inputblock.cn.push({
5238 cls : 'input-group-addon',
5242 inputblock.cn.push(input);
5244 inputblock.cn.push({
5246 cls : 'input-group-addon',
5253 if (align ==='left' && this.fieldLabel.length) {
5254 Roo.log("left and has label");
5260 cls : 'control-label col-sm-' + this.labelWidth,
5261 html : this.fieldLabel
5265 cls : "col-sm-" + (12 - this.labelWidth),
5272 } else if ( this.fieldLabel.length) {
5278 //cls : 'input-group-addon',
5279 html : this.fieldLabel
5289 Roo.log(" no label && no align");
5299 if (this.disabled) {
5300 input.disabled=true;
5307 * return the real textarea element.
5309 inputEl: function ()
5311 return this.el.select('textarea.form-control',true).first();
5319 * trigger field - base class for combo..
5324 * @class Roo.bootstrap.TriggerField
5325 * @extends Roo.bootstrap.Input
5326 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5327 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5328 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5329 * for which you can provide a custom implementation. For example:
5331 var trigger = new Roo.bootstrap.TriggerField();
5332 trigger.onTriggerClick = myTriggerFn;
5333 trigger.applyTo('my-field');
5336 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5337 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5338 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5339 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5341 * Create a new TriggerField.
5342 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5343 * to the base TextField)
5345 Roo.bootstrap.TriggerField = function(config){
5346 this.mimicing = false;
5347 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5350 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5352 * @cfg {String} triggerClass A CSS class to apply to the trigger
5355 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5359 /** @cfg {Boolean} grow @hide */
5360 /** @cfg {Number} growMin @hide */
5361 /** @cfg {Number} growMax @hide */
5367 autoSize: Roo.emptyFn,
5374 actionMode : 'wrap',
5378 getAutoCreate : function(){
5380 var parent = this.parent();
5382 var align = this.parentLabelAlign();
5387 cls: 'form-group' //input-group
5394 type : this.inputType,
5395 cls : 'form-control',
5396 autocomplete: 'off',
5397 placeholder : this.placeholder || ''
5401 input.name = this.name;
5404 input.cls += ' input-' + this.size;
5407 if (this.disabled) {
5408 input.disabled=true;
5411 var inputblock = input;
5413 if (this.before || this.after) {
5416 cls : 'input-group',
5420 inputblock.cn.push({
5422 cls : 'input-group-addon',
5426 inputblock.cn.push(input);
5428 inputblock.cn.push({
5430 cls : 'input-group-addon',
5443 cls: 'form-hidden-field'
5451 Roo.log('multiple');
5459 cls: 'form-hidden-field'
5463 cls: 'select2-choices',
5467 cls: 'select2-search-field',
5480 cls: 'select2-container input-group',
5485 cls: 'typeahead typeahead-long dropdown-menu',
5486 style: 'display:none'
5494 cls : 'input-group-addon btn dropdown-toggle',
5502 cls: 'combobox-clear',
5516 combobox.cls += ' select2-container-multi';
5519 if (align ==='left' && this.fieldLabel.length) {
5521 Roo.log("left and has label");
5527 cls : 'control-label col-sm-' + this.labelWidth,
5528 html : this.fieldLabel
5532 cls : "col-sm-" + (12 - this.labelWidth),
5539 } else if ( this.fieldLabel.length) {
5545 //cls : 'input-group-addon',
5546 html : this.fieldLabel
5556 Roo.log(" no label && no align");
5563 ['xs','sm','md','lg'].map(function(size){
5564 if (settings[size]) {
5565 cfg.cls += ' col-' + size + '-' + settings[size];
5576 onResize : function(w, h){
5577 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5578 // if(typeof w == 'number'){
5579 // var x = w - this.trigger.getWidth();
5580 // this.inputEl().setWidth(this.adjustWidth('input', x));
5581 // this.trigger.setStyle('left', x+'px');
5586 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5589 getResizeEl : function(){
5590 return this.inputEl();
5594 getPositionEl : function(){
5595 return this.inputEl();
5599 alignErrorIcon : function(){
5600 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5604 initEvents : function(){
5606 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5607 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5609 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5610 if(this.hideTrigger){
5611 this.trigger.setDisplayed(false);
5613 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5617 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5620 //this.trigger.addClassOnOver('x-form-trigger-over');
5621 //this.trigger.addClassOnClick('x-form-trigger-click');
5624 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5629 initTrigger : function(){
5634 onDestroy : function(){
5636 this.trigger.removeAllListeners();
5637 // this.trigger.remove();
5640 // this.wrap.remove();
5642 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5646 onFocus : function(){
5647 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5650 this.wrap.addClass('x-trigger-wrap-focus');
5651 this.mimicing = true;
5652 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5653 if(this.monitorTab){
5654 this.el.on("keydown", this.checkTab, this);
5661 checkTab : function(e){
5662 if(e.getKey() == e.TAB){
5668 onBlur : function(){
5673 mimicBlur : function(e, t){
5675 if(!this.wrap.contains(t) && this.validateBlur()){
5682 triggerBlur : function(){
5683 this.mimicing = false;
5684 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5685 if(this.monitorTab){
5686 this.el.un("keydown", this.checkTab, this);
5688 //this.wrap.removeClass('x-trigger-wrap-focus');
5689 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5693 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5694 validateBlur : function(e, t){
5699 onDisable : function(){
5700 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5702 // this.wrap.addClass('x-item-disabled');
5707 onEnable : function(){
5708 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5710 // this.el.removeClass('x-item-disabled');
5715 onShow : function(){
5716 var ae = this.getActionEl();
5719 ae.dom.style.display = '';
5720 ae.dom.style.visibility = 'visible';
5726 onHide : function(){
5727 var ae = this.getActionEl();
5728 ae.dom.style.display = 'none';
5732 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5733 * by an implementing function.
5735 * @param {EventObject} e
5737 onTriggerClick : Roo.emptyFn
5741 * Ext JS Library 1.1.1
5742 * Copyright(c) 2006-2007, Ext JS, LLC.
5744 * Originally Released Under LGPL - original licence link has changed is not relivant.
5747 * <script type="text/javascript">
5752 * @class Roo.data.SortTypes
5754 * Defines the default sorting (casting?) comparison functions used when sorting data.
5756 Roo.data.SortTypes = {
5758 * Default sort that does nothing
5759 * @param {Mixed} s The value being converted
5760 * @return {Mixed} The comparison value
5767 * The regular expression used to strip tags
5771 stripTagsRE : /<\/?[^>]+>/gi,
5774 * Strips all HTML tags to sort on text only
5775 * @param {Mixed} s The value being converted
5776 * @return {String} The comparison value
5778 asText : function(s){
5779 return String(s).replace(this.stripTagsRE, "");
5783 * Strips all HTML tags to sort on text only - Case insensitive
5784 * @param {Mixed} s The value being converted
5785 * @return {String} The comparison value
5787 asUCText : function(s){
5788 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5792 * Case insensitive string
5793 * @param {Mixed} s The value being converted
5794 * @return {String} The comparison value
5796 asUCString : function(s) {
5797 return String(s).toUpperCase();
5802 * @param {Mixed} s The value being converted
5803 * @return {Number} The comparison value
5805 asDate : function(s) {
5809 if(s instanceof Date){
5812 return Date.parse(String(s));
5817 * @param {Mixed} s The value being converted
5818 * @return {Float} The comparison value
5820 asFloat : function(s) {
5821 var val = parseFloat(String(s).replace(/,/g, ""));
5822 if(isNaN(val)) val = 0;
5828 * @param {Mixed} s The value being converted
5829 * @return {Number} The comparison value
5831 asInt : function(s) {
5832 var val = parseInt(String(s).replace(/,/g, ""));
5833 if(isNaN(val)) val = 0;
5838 * Ext JS Library 1.1.1
5839 * Copyright(c) 2006-2007, Ext JS, LLC.
5841 * Originally Released Under LGPL - original licence link has changed is not relivant.
5844 * <script type="text/javascript">
5848 * @class Roo.data.Record
5849 * Instances of this class encapsulate both record <em>definition</em> information, and record
5850 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5851 * to access Records cached in an {@link Roo.data.Store} object.<br>
5853 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5854 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5857 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5859 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5860 * {@link #create}. The parameters are the same.
5861 * @param {Array} data An associative Array of data values keyed by the field name.
5862 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5863 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5864 * not specified an integer id is generated.
5866 Roo.data.Record = function(data, id){
5867 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5872 * Generate a constructor for a specific record layout.
5873 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5874 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5875 * Each field definition object may contain the following properties: <ul>
5876 * <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,
5877 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5878 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5879 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5880 * is being used, then this is a string containing the javascript expression to reference the data relative to
5881 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5882 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5883 * this may be omitted.</p></li>
5884 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5885 * <ul><li>auto (Default, implies no conversion)</li>
5890 * <li>date</li></ul></p></li>
5891 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5892 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5893 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5894 * by the Reader into an object that will be stored in the Record. It is passed the
5895 * following parameters:<ul>
5896 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5898 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5900 * <br>usage:<br><pre><code>
5901 var TopicRecord = Roo.data.Record.create(
5902 {name: 'title', mapping: 'topic_title'},
5903 {name: 'author', mapping: 'username'},
5904 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5905 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5906 {name: 'lastPoster', mapping: 'user2'},
5907 {name: 'excerpt', mapping: 'post_text'}
5910 var myNewRecord = new TopicRecord({
5911 title: 'Do my job please',
5914 lastPost: new Date(),
5915 lastPoster: 'Animal',
5916 excerpt: 'No way dude!'
5918 myStore.add(myNewRecord);
5923 Roo.data.Record.create = function(o){
5925 f.superclass.constructor.apply(this, arguments);
5927 Roo.extend(f, Roo.data.Record);
5928 var p = f.prototype;
5929 p.fields = new Roo.util.MixedCollection(false, function(field){
5932 for(var i = 0, len = o.length; i < len; i++){
5933 p.fields.add(new Roo.data.Field(o[i]));
5935 f.getField = function(name){
5936 return p.fields.get(name);
5941 Roo.data.Record.AUTO_ID = 1000;
5942 Roo.data.Record.EDIT = 'edit';
5943 Roo.data.Record.REJECT = 'reject';
5944 Roo.data.Record.COMMIT = 'commit';
5946 Roo.data.Record.prototype = {
5948 * Readonly flag - true if this record has been modified.
5957 join : function(store){
5962 * Set the named field to the specified value.
5963 * @param {String} name The name of the field to set.
5964 * @param {Object} value The value to set the field to.
5966 set : function(name, value){
5967 if(this.data[name] == value){
5974 if(typeof this.modified[name] == 'undefined'){
5975 this.modified[name] = this.data[name];
5977 this.data[name] = value;
5978 if(!this.editing && this.store){
5979 this.store.afterEdit(this);
5984 * Get the value of the named field.
5985 * @param {String} name The name of the field to get the value of.
5986 * @return {Object} The value of the field.
5988 get : function(name){
5989 return this.data[name];
5993 beginEdit : function(){
5994 this.editing = true;
5999 cancelEdit : function(){
6000 this.editing = false;
6001 delete this.modified;
6005 endEdit : function(){
6006 this.editing = false;
6007 if(this.dirty && this.store){
6008 this.store.afterEdit(this);
6013 * Usually called by the {@link Roo.data.Store} which owns the Record.
6014 * Rejects all changes made to the Record since either creation, or the last commit operation.
6015 * Modified fields are reverted to their original values.
6017 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6018 * of reject operations.
6020 reject : function(){
6021 var m = this.modified;
6023 if(typeof m[n] != "function"){
6024 this.data[n] = m[n];
6028 delete this.modified;
6029 this.editing = false;
6031 this.store.afterReject(this);
6036 * Usually called by the {@link Roo.data.Store} which owns the Record.
6037 * Commits all changes made to the Record since either creation, or the last commit operation.
6039 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6040 * of commit operations.
6042 commit : function(){
6044 delete this.modified;
6045 this.editing = false;
6047 this.store.afterCommit(this);
6052 hasError : function(){
6053 return this.error != null;
6057 clearError : function(){
6062 * Creates a copy of this record.
6063 * @param {String} id (optional) A new record id if you don't want to use this record's id
6066 copy : function(newId) {
6067 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6071 * Ext JS Library 1.1.1
6072 * Copyright(c) 2006-2007, Ext JS, LLC.
6074 * Originally Released Under LGPL - original licence link has changed is not relivant.
6077 * <script type="text/javascript">
6083 * @class Roo.data.Store
6084 * @extends Roo.util.Observable
6085 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6086 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6088 * 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
6089 * has no knowledge of the format of the data returned by the Proxy.<br>
6091 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6092 * instances from the data object. These records are cached and made available through accessor functions.
6094 * Creates a new Store.
6095 * @param {Object} config A config object containing the objects needed for the Store to access data,
6096 * and read the data into Records.
6098 Roo.data.Store = function(config){
6099 this.data = new Roo.util.MixedCollection(false);
6100 this.data.getKey = function(o){
6103 this.baseParams = {};
6110 "multisort" : "_multisort"
6113 if(config && config.data){
6114 this.inlineData = config.data;
6118 Roo.apply(this, config);
6120 if(this.reader){ // reader passed
6121 this.reader = Roo.factory(this.reader, Roo.data);
6122 this.reader.xmodule = this.xmodule || false;
6123 if(!this.recordType){
6124 this.recordType = this.reader.recordType;
6126 if(this.reader.onMetaChange){
6127 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6131 if(this.recordType){
6132 this.fields = this.recordType.prototype.fields;
6138 * @event datachanged
6139 * Fires when the data cache has changed, and a widget which is using this Store
6140 * as a Record cache should refresh its view.
6141 * @param {Store} this
6146 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6147 * @param {Store} this
6148 * @param {Object} meta The JSON metadata
6153 * Fires when Records have been added to the Store
6154 * @param {Store} this
6155 * @param {Roo.data.Record[]} records The array of Records added
6156 * @param {Number} index The index at which the record(s) were added
6161 * Fires when a Record has been removed from the Store
6162 * @param {Store} this
6163 * @param {Roo.data.Record} record The Record that was removed
6164 * @param {Number} index The index at which the record was removed
6169 * Fires when a Record has been updated
6170 * @param {Store} this
6171 * @param {Roo.data.Record} record The Record that was updated
6172 * @param {String} operation The update operation being performed. Value may be one of:
6174 Roo.data.Record.EDIT
6175 Roo.data.Record.REJECT
6176 Roo.data.Record.COMMIT
6182 * Fires when the data cache has been cleared.
6183 * @param {Store} this
6188 * Fires before a request is made for a new data object. If the beforeload handler returns false
6189 * the load action will be canceled.
6190 * @param {Store} this
6191 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6195 * @event beforeloadadd
6196 * Fires after a new set of Records has been loaded.
6197 * @param {Store} this
6198 * @param {Roo.data.Record[]} records The Records that were loaded
6199 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6201 beforeloadadd : true,
6204 * Fires after a new set of Records has been loaded, before they are added to the store.
6205 * @param {Store} this
6206 * @param {Roo.data.Record[]} records The Records that were loaded
6207 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6208 * @params {Object} return from reader
6212 * @event loadexception
6213 * Fires if an exception occurs in the Proxy during loading.
6214 * Called with the signature of the Proxy's "loadexception" event.
6215 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6218 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6219 * @param {Object} load options
6220 * @param {Object} jsonData from your request (normally this contains the Exception)
6222 loadexception : true
6226 this.proxy = Roo.factory(this.proxy, Roo.data);
6227 this.proxy.xmodule = this.xmodule || false;
6228 this.relayEvents(this.proxy, ["loadexception"]);
6230 this.sortToggle = {};
6231 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6233 Roo.data.Store.superclass.constructor.call(this);
6235 if(this.inlineData){
6236 this.loadData(this.inlineData);
6237 delete this.inlineData;
6241 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6243 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6244 * without a remote query - used by combo/forms at present.
6248 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6251 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6254 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6255 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6258 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6259 * on any HTTP request
6262 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6265 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6269 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6270 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6275 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6276 * loaded or when a record is removed. (defaults to false).
6278 pruneModifiedRecords : false,
6284 * Add Records to the Store and fires the add event.
6285 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6287 add : function(records){
6288 records = [].concat(records);
6289 for(var i = 0, len = records.length; i < len; i++){
6290 records[i].join(this);
6292 var index = this.data.length;
6293 this.data.addAll(records);
6294 this.fireEvent("add", this, records, index);
6298 * Remove a Record from the Store and fires the remove event.
6299 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6301 remove : function(record){
6302 var index = this.data.indexOf(record);
6303 this.data.removeAt(index);
6304 if(this.pruneModifiedRecords){
6305 this.modified.remove(record);
6307 this.fireEvent("remove", this, record, index);
6311 * Remove all Records from the Store and fires the clear event.
6313 removeAll : function(){
6315 if(this.pruneModifiedRecords){
6318 this.fireEvent("clear", this);
6322 * Inserts Records to the Store at the given index and fires the add event.
6323 * @param {Number} index The start index at which to insert the passed Records.
6324 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6326 insert : function(index, records){
6327 records = [].concat(records);
6328 for(var i = 0, len = records.length; i < len; i++){
6329 this.data.insert(index, records[i]);
6330 records[i].join(this);
6332 this.fireEvent("add", this, records, index);
6336 * Get the index within the cache of the passed Record.
6337 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6338 * @return {Number} The index of the passed Record. Returns -1 if not found.
6340 indexOf : function(record){
6341 return this.data.indexOf(record);
6345 * Get the index within the cache of the Record with the passed id.
6346 * @param {String} id The id of the Record to find.
6347 * @return {Number} The index of the Record. Returns -1 if not found.
6349 indexOfId : function(id){
6350 return this.data.indexOfKey(id);
6354 * Get the Record with the specified id.
6355 * @param {String} id The id of the Record to find.
6356 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6358 getById : function(id){
6359 return this.data.key(id);
6363 * Get the Record at the specified index.
6364 * @param {Number} index The index of the Record to find.
6365 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6367 getAt : function(index){
6368 return this.data.itemAt(index);
6372 * Returns a range of Records between specified indices.
6373 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6374 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6375 * @return {Roo.data.Record[]} An array of Records
6377 getRange : function(start, end){
6378 return this.data.getRange(start, end);
6382 storeOptions : function(o){
6383 o = Roo.apply({}, o);
6386 this.lastOptions = o;
6390 * Loads the Record cache from the configured Proxy using the configured Reader.
6392 * If using remote paging, then the first load call must specify the <em>start</em>
6393 * and <em>limit</em> properties in the options.params property to establish the initial
6394 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6396 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6397 * and this call will return before the new data has been loaded. Perform any post-processing
6398 * in a callback function, or in a "load" event handler.</strong>
6400 * @param {Object} options An object containing properties which control loading options:<ul>
6401 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6402 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6403 * passed the following arguments:<ul>
6404 * <li>r : Roo.data.Record[]</li>
6405 * <li>options: Options object from the load call</li>
6406 * <li>success: Boolean success indicator</li></ul></li>
6407 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6408 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6411 load : function(options){
6412 options = options || {};
6413 if(this.fireEvent("beforeload", this, options) !== false){
6414 this.storeOptions(options);
6415 var p = Roo.apply(options.params || {}, this.baseParams);
6416 // if meta was not loaded from remote source.. try requesting it.
6417 if (!this.reader.metaFromRemote) {
6420 if(this.sortInfo && this.remoteSort){
6421 var pn = this.paramNames;
6422 p[pn["sort"]] = this.sortInfo.field;
6423 p[pn["dir"]] = this.sortInfo.direction;
6425 if (this.multiSort) {
6426 var pn = this.paramNames;
6427 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6430 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6435 * Reloads the Record cache from the configured Proxy using the configured Reader and
6436 * the options from the last load operation performed.
6437 * @param {Object} options (optional) An object containing properties which may override the options
6438 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6439 * the most recently used options are reused).
6441 reload : function(options){
6442 this.load(Roo.applyIf(options||{}, this.lastOptions));
6446 // Called as a callback by the Reader during a load operation.
6447 loadRecords : function(o, options, success){
6448 if(!o || success === false){
6449 if(success !== false){
6450 this.fireEvent("load", this, [], options, o);
6452 if(options.callback){
6453 options.callback.call(options.scope || this, [], options, false);
6457 // if data returned failure - throw an exception.
6458 if (o.success === false) {
6459 // show a message if no listener is registered.
6460 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6461 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6463 // loadmask wil be hooked into this..
6464 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6467 var r = o.records, t = o.totalRecords || r.length;
6469 this.fireEvent("beforeloadadd", this, r, options, o);
6471 if(!options || options.add !== true){
6472 if(this.pruneModifiedRecords){
6475 for(var i = 0, len = r.length; i < len; i++){
6479 this.data = this.snapshot;
6480 delete this.snapshot;
6483 this.data.addAll(r);
6484 this.totalLength = t;
6486 this.fireEvent("datachanged", this);
6488 this.totalLength = Math.max(t, this.data.length+r.length);
6491 this.fireEvent("load", this, r, options, o);
6492 if(options.callback){
6493 options.callback.call(options.scope || this, r, options, true);
6499 * Loads data from a passed data block. A Reader which understands the format of the data
6500 * must have been configured in the constructor.
6501 * @param {Object} data The data block from which to read the Records. The format of the data expected
6502 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6503 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6505 loadData : function(o, append){
6506 var r = this.reader.readRecords(o);
6507 this.loadRecords(r, {add: append}, true);
6511 * Gets the number of cached records.
6513 * <em>If using paging, this may not be the total size of the dataset. If the data object
6514 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6515 * the data set size</em>
6517 getCount : function(){
6518 return this.data.length || 0;
6522 * Gets the total number of records in the dataset as returned by the server.
6524 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6525 * the dataset size</em>
6527 getTotalCount : function(){
6528 return this.totalLength || 0;
6532 * Returns the sort state of the Store as an object with two properties:
6534 field {String} The name of the field by which the Records are sorted
6535 direction {String} The sort order, "ASC" or "DESC"
6538 getSortState : function(){
6539 return this.sortInfo;
6543 applySort : function(){
6544 if(this.sortInfo && !this.remoteSort){
6545 var s = this.sortInfo, f = s.field;
6546 var st = this.fields.get(f).sortType;
6547 var fn = function(r1, r2){
6548 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6549 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6551 this.data.sort(s.direction, fn);
6552 if(this.snapshot && this.snapshot != this.data){
6553 this.snapshot.sort(s.direction, fn);
6559 * Sets the default sort column and order to be used by the next load operation.
6560 * @param {String} fieldName The name of the field to sort by.
6561 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6563 setDefaultSort : function(field, dir){
6564 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6569 * If remote sorting is used, the sort is performed on the server, and the cache is
6570 * reloaded. If local sorting is used, the cache is sorted internally.
6571 * @param {String} fieldName The name of the field to sort by.
6572 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6574 sort : function(fieldName, dir){
6575 var f = this.fields.get(fieldName);
6577 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6579 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6580 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6585 this.sortToggle[f.name] = dir;
6586 this.sortInfo = {field: f.name, direction: dir};
6587 if(!this.remoteSort){
6589 this.fireEvent("datachanged", this);
6591 this.load(this.lastOptions);
6596 * Calls the specified function for each of the Records in the cache.
6597 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6598 * Returning <em>false</em> aborts and exits the iteration.
6599 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6601 each : function(fn, scope){
6602 this.data.each(fn, scope);
6606 * Gets all records modified since the last commit. Modified records are persisted across load operations
6607 * (e.g., during paging).
6608 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6610 getModifiedRecords : function(){
6611 return this.modified;
6615 createFilterFn : function(property, value, anyMatch){
6616 if(!value.exec){ // not a regex
6617 value = String(value);
6618 if(value.length == 0){
6621 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6624 return value.test(r.data[property]);
6629 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6630 * @param {String} property A field on your records
6631 * @param {Number} start The record index to start at (defaults to 0)
6632 * @param {Number} end The last record index to include (defaults to length - 1)
6633 * @return {Number} The sum
6635 sum : function(property, start, end){
6636 var rs = this.data.items, v = 0;
6638 end = (end || end === 0) ? end : rs.length-1;
6640 for(var i = start; i <= end; i++){
6641 v += (rs[i].data[property] || 0);
6647 * Filter the records by a specified property.
6648 * @param {String} field A field on your records
6649 * @param {String/RegExp} value Either a string that the field
6650 * should start with or a RegExp to test against the field
6651 * @param {Boolean} anyMatch True to match any part not just the beginning
6653 filter : function(property, value, anyMatch){
6654 var fn = this.createFilterFn(property, value, anyMatch);
6655 return fn ? this.filterBy(fn) : this.clearFilter();
6659 * Filter by a function. The specified function will be called with each
6660 * record in this data source. If the function returns true the record is included,
6661 * otherwise it is filtered.
6662 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6663 * @param {Object} scope (optional) The scope of the function (defaults to this)
6665 filterBy : function(fn, scope){
6666 this.snapshot = this.snapshot || this.data;
6667 this.data = this.queryBy(fn, scope||this);
6668 this.fireEvent("datachanged", this);
6672 * Query the records by a specified property.
6673 * @param {String} field A field on your records
6674 * @param {String/RegExp} value Either a string that the field
6675 * should start with or a RegExp to test against the field
6676 * @param {Boolean} anyMatch True to match any part not just the beginning
6677 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6679 query : function(property, value, anyMatch){
6680 var fn = this.createFilterFn(property, value, anyMatch);
6681 return fn ? this.queryBy(fn) : this.data.clone();
6685 * Query by a function. The specified function will be called with each
6686 * record in this data source. If the function returns true the record is included
6688 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6689 * @param {Object} scope (optional) The scope of the function (defaults to this)
6690 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6692 queryBy : function(fn, scope){
6693 var data = this.snapshot || this.data;
6694 return data.filterBy(fn, scope||this);
6698 * Collects unique values for a particular dataIndex from this store.
6699 * @param {String} dataIndex The property to collect
6700 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6701 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6702 * @return {Array} An array of the unique values
6704 collect : function(dataIndex, allowNull, bypassFilter){
6705 var d = (bypassFilter === true && this.snapshot) ?
6706 this.snapshot.items : this.data.items;
6707 var v, sv, r = [], l = {};
6708 for(var i = 0, len = d.length; i < len; i++){
6709 v = d[i].data[dataIndex];
6711 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6720 * Revert to a view of the Record cache with no filtering applied.
6721 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6723 clearFilter : function(suppressEvent){
6724 if(this.snapshot && this.snapshot != this.data){
6725 this.data = this.snapshot;
6726 delete this.snapshot;
6727 if(suppressEvent !== true){
6728 this.fireEvent("datachanged", this);
6734 afterEdit : function(record){
6735 if(this.modified.indexOf(record) == -1){
6736 this.modified.push(record);
6738 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6742 afterReject : function(record){
6743 this.modified.remove(record);
6744 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6748 afterCommit : function(record){
6749 this.modified.remove(record);
6750 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6754 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6755 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6757 commitChanges : function(){
6758 var m = this.modified.slice(0);
6760 for(var i = 0, len = m.length; i < len; i++){
6766 * Cancel outstanding changes on all changed records.
6768 rejectChanges : function(){
6769 var m = this.modified.slice(0);
6771 for(var i = 0, len = m.length; i < len; i++){
6776 onMetaChange : function(meta, rtype, o){
6777 this.recordType = rtype;
6778 this.fields = rtype.prototype.fields;
6779 delete this.snapshot;
6780 this.sortInfo = meta.sortInfo || this.sortInfo;
6782 this.fireEvent('metachange', this, this.reader.meta);
6785 moveIndex : function(data, type)
6787 var index = this.indexOf(data);
6789 var newIndex = index + type;
6793 this.insert(newIndex, data);
6798 * Ext JS Library 1.1.1
6799 * Copyright(c) 2006-2007, Ext JS, LLC.
6801 * Originally Released Under LGPL - original licence link has changed is not relivant.
6804 * <script type="text/javascript">
6808 * @class Roo.data.SimpleStore
6809 * @extends Roo.data.Store
6810 * Small helper class to make creating Stores from Array data easier.
6811 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6812 * @cfg {Array} fields An array of field definition objects, or field name strings.
6813 * @cfg {Array} data The multi-dimensional array of data
6815 * @param {Object} config
6817 Roo.data.SimpleStore = function(config){
6818 Roo.data.SimpleStore.superclass.constructor.call(this, {
6820 reader: new Roo.data.ArrayReader({
6823 Roo.data.Record.create(config.fields)
6825 proxy : new Roo.data.MemoryProxy(config.data)
6829 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6831 * Ext JS Library 1.1.1
6832 * Copyright(c) 2006-2007, Ext JS, LLC.
6834 * Originally Released Under LGPL - original licence link has changed is not relivant.
6837 * <script type="text/javascript">
6842 * @extends Roo.data.Store
6843 * @class Roo.data.JsonStore
6844 * Small helper class to make creating Stores for JSON data easier. <br/>
6846 var store = new Roo.data.JsonStore({
6847 url: 'get-images.php',
6849 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6852 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6853 * JsonReader and HttpProxy (unless inline data is provided).</b>
6854 * @cfg {Array} fields An array of field definition objects, or field name strings.
6856 * @param {Object} config
6858 Roo.data.JsonStore = function(c){
6859 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6860 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6861 reader: new Roo.data.JsonReader(c, c.fields)
6864 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6866 * Ext JS Library 1.1.1
6867 * Copyright(c) 2006-2007, Ext JS, LLC.
6869 * Originally Released Under LGPL - original licence link has changed is not relivant.
6872 * <script type="text/javascript">
6876 Roo.data.Field = function(config){
6877 if(typeof config == "string"){
6878 config = {name: config};
6880 Roo.apply(this, config);
6886 var st = Roo.data.SortTypes;
6887 // named sortTypes are supported, here we look them up
6888 if(typeof this.sortType == "string"){
6889 this.sortType = st[this.sortType];
6892 // set default sortType for strings and dates
6896 this.sortType = st.asUCString;
6899 this.sortType = st.asDate;
6902 this.sortType = st.none;
6907 var stripRe = /[\$,%]/g;
6909 // prebuilt conversion function for this field, instead of
6910 // switching every time we're reading a value
6912 var cv, dateFormat = this.dateFormat;
6917 cv = function(v){ return v; };
6920 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6924 return v !== undefined && v !== null && v !== '' ?
6925 parseInt(String(v).replace(stripRe, ""), 10) : '';
6930 return v !== undefined && v !== null && v !== '' ?
6931 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6936 cv = function(v){ return v === true || v === "true" || v == 1; };
6943 if(v instanceof Date){
6947 if(dateFormat == "timestamp"){
6948 return new Date(v*1000);
6950 return Date.parseDate(v, dateFormat);
6952 var parsed = Date.parse(v);
6953 return parsed ? new Date(parsed) : null;
6962 Roo.data.Field.prototype = {
6970 * Ext JS Library 1.1.1
6971 * Copyright(c) 2006-2007, Ext JS, LLC.
6973 * Originally Released Under LGPL - original licence link has changed is not relivant.
6976 * <script type="text/javascript">
6979 // Base class for reading structured data from a data source. This class is intended to be
6980 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6983 * @class Roo.data.DataReader
6984 * Base class for reading structured data from a data source. This class is intended to be
6985 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6988 Roo.data.DataReader = function(meta, recordType){
6992 this.recordType = recordType instanceof Array ?
6993 Roo.data.Record.create(recordType) : recordType;
6996 Roo.data.DataReader.prototype = {
6998 * Create an empty record
6999 * @param {Object} data (optional) - overlay some values
7000 * @return {Roo.data.Record} record created.
7002 newRow : function(d) {
7004 this.recordType.prototype.fields.each(function(c) {
7006 case 'int' : da[c.name] = 0; break;
7007 case 'date' : da[c.name] = new Date(); break;
7008 case 'float' : da[c.name] = 0.0; break;
7009 case 'boolean' : da[c.name] = false; break;
7010 default : da[c.name] = ""; break;
7014 return new this.recordType(Roo.apply(da, d));
7019 * Ext JS Library 1.1.1
7020 * Copyright(c) 2006-2007, Ext JS, LLC.
7022 * Originally Released Under LGPL - original licence link has changed is not relivant.
7025 * <script type="text/javascript">
7029 * @class Roo.data.DataProxy
7030 * @extends Roo.data.Observable
7031 * This class is an abstract base class for implementations which provide retrieval of
7032 * unformatted data objects.<br>
7034 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7035 * (of the appropriate type which knows how to parse the data object) to provide a block of
7036 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7038 * Custom implementations must implement the load method as described in
7039 * {@link Roo.data.HttpProxy#load}.
7041 Roo.data.DataProxy = function(){
7045 * Fires before a network request is made to retrieve a data object.
7046 * @param {Object} This DataProxy object.
7047 * @param {Object} params The params parameter to the load function.
7052 * Fires before the load method's callback is called.
7053 * @param {Object} This DataProxy object.
7054 * @param {Object} o The data object.
7055 * @param {Object} arg The callback argument object passed to the load function.
7059 * @event loadexception
7060 * Fires if an Exception occurs during data retrieval.
7061 * @param {Object} This DataProxy object.
7062 * @param {Object} o The data object.
7063 * @param {Object} arg The callback argument object passed to the load function.
7064 * @param {Object} e The Exception.
7066 loadexception : true
7068 Roo.data.DataProxy.superclass.constructor.call(this);
7071 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7074 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7078 * Ext JS Library 1.1.1
7079 * Copyright(c) 2006-2007, Ext JS, LLC.
7081 * Originally Released Under LGPL - original licence link has changed is not relivant.
7084 * <script type="text/javascript">
7087 * @class Roo.data.MemoryProxy
7088 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7089 * to the Reader when its load method is called.
7091 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7093 Roo.data.MemoryProxy = function(data){
7097 Roo.data.MemoryProxy.superclass.constructor.call(this);
7101 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7103 * Load data from the requested source (in this case an in-memory
7104 * data object passed to the constructor), read the data object into
7105 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7106 * process that block using the passed callback.
7107 * @param {Object} params This parameter is not used by the MemoryProxy class.
7108 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7109 * object into a block of Roo.data.Records.
7110 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7111 * The function must be passed <ul>
7112 * <li>The Record block object</li>
7113 * <li>The "arg" argument from the load function</li>
7114 * <li>A boolean success indicator</li>
7116 * @param {Object} scope The scope in which to call the callback
7117 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7119 load : function(params, reader, callback, scope, arg){
7120 params = params || {};
7123 result = reader.readRecords(this.data);
7125 this.fireEvent("loadexception", this, arg, null, e);
7126 callback.call(scope, null, arg, false);
7129 callback.call(scope, result, arg, true);
7133 update : function(params, records){
7138 * Ext JS Library 1.1.1
7139 * Copyright(c) 2006-2007, Ext JS, LLC.
7141 * Originally Released Under LGPL - original licence link has changed is not relivant.
7144 * <script type="text/javascript">
7147 * @class Roo.data.HttpProxy
7148 * @extends Roo.data.DataProxy
7149 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7150 * configured to reference a certain URL.<br><br>
7152 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7153 * from which the running page was served.<br><br>
7155 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7157 * Be aware that to enable the browser to parse an XML document, the server must set
7158 * the Content-Type header in the HTTP response to "text/xml".
7160 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7161 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7162 * will be used to make the request.
7164 Roo.data.HttpProxy = function(conn){
7165 Roo.data.HttpProxy.superclass.constructor.call(this);
7166 // is conn a conn config or a real conn?
7168 this.useAjax = !conn || !conn.events;
7172 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7173 // thse are take from connection...
7176 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7179 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7180 * extra parameters to each request made by this object. (defaults to undefined)
7183 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7184 * to each request made by this object. (defaults to undefined)
7187 * @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)
7190 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7193 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7199 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7203 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7204 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7205 * a finer-grained basis than the DataProxy events.
7207 getConnection : function(){
7208 return this.useAjax ? Roo.Ajax : this.conn;
7212 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7213 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7214 * process that block using the passed callback.
7215 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7216 * for the request to the remote server.
7217 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7218 * object into a block of Roo.data.Records.
7219 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7220 * The function must be passed <ul>
7221 * <li>The Record block object</li>
7222 * <li>The "arg" argument from the load function</li>
7223 * <li>A boolean success indicator</li>
7225 * @param {Object} scope The scope in which to call the callback
7226 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7228 load : function(params, reader, callback, scope, arg){
7229 if(this.fireEvent("beforeload", this, params) !== false){
7231 params : params || {},
7233 callback : callback,
7238 callback : this.loadResponse,
7242 Roo.applyIf(o, this.conn);
7243 if(this.activeRequest){
7244 Roo.Ajax.abort(this.activeRequest);
7246 this.activeRequest = Roo.Ajax.request(o);
7248 this.conn.request(o);
7251 callback.call(scope||this, null, arg, false);
7256 loadResponse : function(o, success, response){
7257 delete this.activeRequest;
7259 this.fireEvent("loadexception", this, o, response);
7260 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7265 result = o.reader.read(response);
7267 this.fireEvent("loadexception", this, o, response, e);
7268 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7272 this.fireEvent("load", this, o, o.request.arg);
7273 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7277 update : function(dataSet){
7282 updateResponse : function(dataSet){
7287 * Ext JS Library 1.1.1
7288 * Copyright(c) 2006-2007, Ext JS, LLC.
7290 * Originally Released Under LGPL - original licence link has changed is not relivant.
7293 * <script type="text/javascript">
7297 * @class Roo.data.ScriptTagProxy
7298 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7299 * other than the originating domain of the running page.<br><br>
7301 * <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
7302 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7304 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7305 * source code that is used as the source inside a <script> tag.<br><br>
7307 * In order for the browser to process the returned data, the server must wrap the data object
7308 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7309 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7310 * depending on whether the callback name was passed:
7313 boolean scriptTag = false;
7314 String cb = request.getParameter("callback");
7317 response.setContentType("text/javascript");
7319 response.setContentType("application/x-json");
7321 Writer out = response.getWriter();
7323 out.write(cb + "(");
7325 out.print(dataBlock.toJsonString());
7332 * @param {Object} config A configuration object.
7334 Roo.data.ScriptTagProxy = function(config){
7335 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7336 Roo.apply(this, config);
7337 this.head = document.getElementsByTagName("head")[0];
7340 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7342 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7344 * @cfg {String} url The URL from which to request the data object.
7347 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7351 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7352 * the server the name of the callback function set up by the load call to process the returned data object.
7353 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7354 * javascript output which calls this named function passing the data object as its only parameter.
7356 callbackParam : "callback",
7358 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7359 * name to the request.
7364 * Load data from the configured URL, read the data object into
7365 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7366 * process that block using the passed callback.
7367 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7368 * for the request to the remote server.
7369 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7370 * object into a block of Roo.data.Records.
7371 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7372 * The function must be passed <ul>
7373 * <li>The Record block object</li>
7374 * <li>The "arg" argument from the load function</li>
7375 * <li>A boolean success indicator</li>
7377 * @param {Object} scope The scope in which to call the callback
7378 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7380 load : function(params, reader, callback, scope, arg){
7381 if(this.fireEvent("beforeload", this, params) !== false){
7383 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7386 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7388 url += "&_dc=" + (new Date().getTime());
7390 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7393 cb : "stcCallback"+transId,
7394 scriptId : "stcScript"+transId,
7398 callback : callback,
7404 window[trans.cb] = function(o){
7405 conn.handleResponse(o, trans);
7408 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7410 if(this.autoAbort !== false){
7414 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7416 var script = document.createElement("script");
7417 script.setAttribute("src", url);
7418 script.setAttribute("type", "text/javascript");
7419 script.setAttribute("id", trans.scriptId);
7420 this.head.appendChild(script);
7424 callback.call(scope||this, null, arg, false);
7429 isLoading : function(){
7430 return this.trans ? true : false;
7434 * Abort the current server request.
7437 if(this.isLoading()){
7438 this.destroyTrans(this.trans);
7443 destroyTrans : function(trans, isLoaded){
7444 this.head.removeChild(document.getElementById(trans.scriptId));
7445 clearTimeout(trans.timeoutId);
7447 window[trans.cb] = undefined;
7449 delete window[trans.cb];
7452 // if hasn't been loaded, wait for load to remove it to prevent script error
7453 window[trans.cb] = function(){
7454 window[trans.cb] = undefined;
7456 delete window[trans.cb];
7463 handleResponse : function(o, trans){
7465 this.destroyTrans(trans, true);
7468 result = trans.reader.readRecords(o);
7470 this.fireEvent("loadexception", this, o, trans.arg, e);
7471 trans.callback.call(trans.scope||window, null, trans.arg, false);
7474 this.fireEvent("load", this, o, trans.arg);
7475 trans.callback.call(trans.scope||window, result, trans.arg, true);
7479 handleFailure : function(trans){
7481 this.destroyTrans(trans, false);
7482 this.fireEvent("loadexception", this, null, trans.arg);
7483 trans.callback.call(trans.scope||window, null, trans.arg, false);
7487 * Ext JS Library 1.1.1
7488 * Copyright(c) 2006-2007, Ext JS, LLC.
7490 * Originally Released Under LGPL - original licence link has changed is not relivant.
7493 * <script type="text/javascript">
7497 * @class Roo.data.JsonReader
7498 * @extends Roo.data.DataReader
7499 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7500 * based on mappings in a provided Roo.data.Record constructor.
7502 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7503 * in the reply previously.
7508 var RecordDef = Roo.data.Record.create([
7509 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7510 {name: 'occupation'} // This field will use "occupation" as the mapping.
7512 var myReader = new Roo.data.JsonReader({
7513 totalProperty: "results", // The property which contains the total dataset size (optional)
7514 root: "rows", // The property which contains an Array of row objects
7515 id: "id" // The property within each row object that provides an ID for the record (optional)
7519 * This would consume a JSON file like this:
7521 { 'results': 2, 'rows': [
7522 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7523 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7526 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7527 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7528 * paged from the remote server.
7529 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7530 * @cfg {String} root name of the property which contains the Array of row objects.
7531 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7533 * Create a new JsonReader
7534 * @param {Object} meta Metadata configuration options
7535 * @param {Object} recordType Either an Array of field definition objects,
7536 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7538 Roo.data.JsonReader = function(meta, recordType){
7541 // set some defaults:
7543 totalProperty: 'total',
7544 successProperty : 'success',
7549 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7551 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7554 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7555 * Used by Store query builder to append _requestMeta to params.
7558 metaFromRemote : false,
7560 * This method is only used by a DataProxy which has retrieved data from a remote server.
7561 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7562 * @return {Object} data A data block which is used by an Roo.data.Store object as
7563 * a cache of Roo.data.Records.
7565 read : function(response){
7566 var json = response.responseText;
7568 var o = /* eval:var:o */ eval("("+json+")");
7570 throw {message: "JsonReader.read: Json object not found"};
7576 this.metaFromRemote = true;
7577 this.meta = o.metaData;
7578 this.recordType = Roo.data.Record.create(o.metaData.fields);
7579 this.onMetaChange(this.meta, this.recordType, o);
7581 return this.readRecords(o);
7584 // private function a store will implement
7585 onMetaChange : function(meta, recordType, o){
7592 simpleAccess: function(obj, subsc) {
7599 getJsonAccessor: function(){
7601 return function(expr) {
7603 return(re.test(expr))
7604 ? new Function("obj", "return obj." + expr)
7614 * Create a data block containing Roo.data.Records from an XML document.
7615 * @param {Object} o An object which contains an Array of row objects in the property specified
7616 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7617 * which contains the total size of the dataset.
7618 * @return {Object} data A data block which is used by an Roo.data.Store object as
7619 * a cache of Roo.data.Records.
7621 readRecords : function(o){
7623 * After any data loads, the raw JSON data is available for further custom processing.
7627 var s = this.meta, Record = this.recordType,
7628 f = Record.prototype.fields, fi = f.items, fl = f.length;
7630 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7632 if(s.totalProperty) {
7633 this.getTotal = this.getJsonAccessor(s.totalProperty);
7635 if(s.successProperty) {
7636 this.getSuccess = this.getJsonAccessor(s.successProperty);
7638 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7640 var g = this.getJsonAccessor(s.id);
7641 this.getId = function(rec) {
7643 return (r === undefined || r === "") ? null : r;
7646 this.getId = function(){return null;};
7649 for(var jj = 0; jj < fl; jj++){
7651 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7652 this.ef[jj] = this.getJsonAccessor(map);
7656 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7657 if(s.totalProperty){
7658 var vt = parseInt(this.getTotal(o), 10);
7663 if(s.successProperty){
7664 var vs = this.getSuccess(o);
7665 if(vs === false || vs === 'false'){
7670 for(var i = 0; i < c; i++){
7673 var id = this.getId(n);
7674 for(var j = 0; j < fl; j++){
7676 var v = this.ef[j](n);
7678 Roo.log('missing convert for ' + f.name);
7682 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7684 var record = new Record(values, id);
7686 records[i] = record;
7692 totalRecords : totalRecords
7697 * Ext JS Library 1.1.1
7698 * Copyright(c) 2006-2007, Ext JS, LLC.
7700 * Originally Released Under LGPL - original licence link has changed is not relivant.
7703 * <script type="text/javascript">
7707 * @class Roo.data.ArrayReader
7708 * @extends Roo.data.DataReader
7709 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7710 * Each element of that Array represents a row of data fields. The
7711 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7712 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7716 var RecordDef = Roo.data.Record.create([
7717 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7718 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7720 var myReader = new Roo.data.ArrayReader({
7721 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7725 * This would consume an Array like this:
7727 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7729 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7731 * Create a new JsonReader
7732 * @param {Object} meta Metadata configuration options.
7733 * @param {Object} recordType Either an Array of field definition objects
7734 * as specified to {@link Roo.data.Record#create},
7735 * or an {@link Roo.data.Record} object
7736 * created using {@link Roo.data.Record#create}.
7738 Roo.data.ArrayReader = function(meta, recordType){
7739 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7742 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7744 * Create a data block containing Roo.data.Records from an XML document.
7745 * @param {Object} o An Array of row objects which represents the dataset.
7746 * @return {Object} data A data block which is used by an Roo.data.Store object as
7747 * a cache of Roo.data.Records.
7749 readRecords : function(o){
7750 var sid = this.meta ? this.meta.id : null;
7751 var recordType = this.recordType, fields = recordType.prototype.fields;
7754 for(var i = 0; i < root.length; i++){
7757 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7758 for(var j = 0, jlen = fields.length; j < jlen; j++){
7759 var f = fields.items[j];
7760 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7761 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7765 var record = new recordType(values, id);
7767 records[records.length] = record;
7771 totalRecords : records.length
7780 * @class Roo.bootstrap.ComboBox
7781 * @extends Roo.bootstrap.TriggerField
7782 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7783 * @cfg {Boolean} append (true|false) default false
7785 * Create a new ComboBox.
7786 * @param {Object} config Configuration options
7788 Roo.bootstrap.ComboBox = function(config){
7789 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7793 * Fires when the dropdown list is expanded
7794 * @param {Roo.bootstrap.ComboBox} combo This combo box
7799 * Fires when the dropdown list is collapsed
7800 * @param {Roo.bootstrap.ComboBox} combo This combo box
7804 * @event beforeselect
7805 * Fires before a list item is selected. Return false to cancel the selection.
7806 * @param {Roo.bootstrap.ComboBox} combo This combo box
7807 * @param {Roo.data.Record} record The data record returned from the underlying store
7808 * @param {Number} index The index of the selected item in the dropdown list
7810 'beforeselect' : true,
7813 * Fires when a list item is selected
7814 * @param {Roo.bootstrap.ComboBox} combo This combo box
7815 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7816 * @param {Number} index The index of the selected item in the dropdown list
7820 * @event beforequery
7821 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7822 * The event object passed has these properties:
7823 * @param {Roo.bootstrap.ComboBox} combo This combo box
7824 * @param {String} query The query
7825 * @param {Boolean} forceAll true to force "all" query
7826 * @param {Boolean} cancel true to cancel the query
7827 * @param {Object} e The query event object
7829 'beforequery': true,
7832 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7833 * @param {Roo.bootstrap.ComboBox} combo This combo box
7838 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7839 * @param {Roo.bootstrap.ComboBox} combo This combo box
7840 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7845 * Fires when the remove value from the combobox array
7846 * @param {Roo.bootstrap.ComboBox} combo This combo box
7853 this.selectedIndex = -1;
7854 if(this.mode == 'local'){
7855 if(config.queryDelay === undefined){
7856 this.queryDelay = 10;
7858 if(config.minChars === undefined){
7864 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7867 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7868 * rendering into an Roo.Editor, defaults to false)
7871 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7872 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7875 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7878 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7879 * the dropdown list (defaults to undefined, with no header element)
7883 * @cfg {String/Roo.Template} tpl The template to use to render the output
7887 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7889 listWidth: undefined,
7891 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7892 * mode = 'remote' or 'text' if mode = 'local')
7894 displayField: undefined,
7896 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7897 * mode = 'remote' or 'value' if mode = 'local').
7898 * Note: use of a valueField requires the user make a selection
7899 * in order for a value to be mapped.
7901 valueField: undefined,
7905 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7906 * field's data value (defaults to the underlying DOM element's name)
7908 hiddenName: undefined,
7910 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7914 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7916 selectedClass: 'active',
7919 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7923 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7924 * anchor positions (defaults to 'tl-bl')
7926 listAlign: 'tl-bl?',
7928 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7932 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7933 * query specified by the allQuery config option (defaults to 'query')
7935 triggerAction: 'query',
7937 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7938 * (defaults to 4, does not apply if editable = false)
7942 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7943 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7947 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7948 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7952 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7953 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7957 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7958 * when editable = true (defaults to false)
7960 selectOnFocus:false,
7962 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7964 queryParam: 'query',
7966 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7967 * when mode = 'remote' (defaults to 'Loading...')
7969 loadingText: 'Loading...',
7971 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7975 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7979 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7980 * traditional select (defaults to true)
7984 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7988 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7992 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7993 * listWidth has a higher value)
7997 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7998 * allow the user to set arbitrary text into the field (defaults to false)
8000 forceSelection:false,
8002 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8003 * if typeAhead = true (defaults to 250)
8005 typeAheadDelay : 250,
8007 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8008 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8010 valueNotFoundText : undefined,
8012 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8017 * @cfg {Boolean} disableClear Disable showing of clear button.
8019 disableClear : false,
8021 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8023 alwaysQuery : false,
8026 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8040 // element that contains real text value.. (when hidden is used..)
8043 initEvents: function(){
8046 throw "can not find store for combo";
8048 this.store = Roo.factory(this.store, Roo.data);
8052 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8055 if(this.hiddenName){
8057 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8059 this.hiddenField.dom.value =
8060 this.hiddenValue !== undefined ? this.hiddenValue :
8061 this.value !== undefined ? this.value : '';
8063 // prevent input submission
8064 this.el.dom.removeAttribute('name');
8065 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8070 // this.el.dom.setAttribute('autocomplete', 'off');
8073 var cls = 'x-combo-list';
8074 this.list = this.el.select('ul.dropdown-menu',true).first();
8076 //this.list = new Roo.Layer({
8077 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8080 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8081 this.list.setWidth(lw);
8083 this.list.on('mouseover', this.onViewOver, this);
8084 this.list.on('mousemove', this.onViewMove, this);
8086 this.list.on('scroll', this.onViewScroll, this);
8089 this.list.swallowEvent('mousewheel');
8090 this.assetHeight = 0;
8093 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8094 this.assetHeight += this.header.getHeight();
8097 this.innerList = this.list.createChild({cls:cls+'-inner'});
8098 this.innerList.on('mouseover', this.onViewOver, this);
8099 this.innerList.on('mousemove', this.onViewMove, this);
8100 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8102 if(this.allowBlank && !this.pageSize && !this.disableClear){
8103 this.footer = this.list.createChild({cls:cls+'-ft'});
8104 this.pageTb = new Roo.Toolbar(this.footer);
8108 this.footer = this.list.createChild({cls:cls+'-ft'});
8109 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8110 {pageSize: this.pageSize});
8114 if (this.pageTb && this.allowBlank && !this.disableClear) {
8116 this.pageTb.add(new Roo.Toolbar.Fill(), {
8117 cls: 'x-btn-icon x-btn-clear',
8123 _this.onSelect(false, -1);
8128 this.assetHeight += this.footer.getHeight();
8133 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8136 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8137 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8139 //this.view.wrapEl.setDisplayed(false);
8140 this.view.on('click', this.onViewClick, this);
8144 this.store.on('beforeload', this.onBeforeLoad, this);
8145 this.store.on('load', this.onLoad, this);
8146 this.store.on('loadexception', this.onLoadException, this);
8149 this.resizer = new Roo.Resizable(this.list, {
8150 pinned:true, handles:'se'
8152 this.resizer.on('resize', function(r, w, h){
8153 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8155 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8156 this.restrictHeight();
8158 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8162 this.editable = true;
8163 this.setEditable(false);
8168 if (typeof(this.events.add.listeners) != 'undefined') {
8170 this.addicon = this.wrap.createChild(
8171 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8173 this.addicon.on('click', function(e) {
8174 this.fireEvent('add', this);
8177 if (typeof(this.events.edit.listeners) != 'undefined') {
8179 this.editicon = this.wrap.createChild(
8180 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8182 this.editicon.setStyle('margin-left', '40px');
8184 this.editicon.on('click', function(e) {
8186 // we fire even if inothing is selected..
8187 this.fireEvent('edit', this, this.lastData );
8193 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8195 this.inKeyMode = true;
8199 "down" : function(e){
8200 if(!this.isExpanded()){
8201 this.onTriggerClick();
8203 this.inKeyMode = true;
8208 "enter" : function(e){
8213 "esc" : function(e){
8217 "tab" : function(e){
8220 if(this.fireEvent("specialkey", this, e)){
8221 this.onViewClick(false);
8229 doRelay : function(foo, bar, hname){
8230 if(hname == 'down' || this.scope.isExpanded()){
8231 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8240 this.queryDelay = Math.max(this.queryDelay || 10,
8241 this.mode == 'local' ? 10 : 250);
8244 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8247 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8249 if(this.editable !== false){
8250 this.inputEl().on("keyup", this.onKeyUp, this);
8252 if(this.forceSelection){
8253 this.on('blur', this.doForce, this);
8257 this.choices = this.el.select('ul.select2-choices', true).first();
8258 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8262 onDestroy : function(){
8264 this.view.setStore(null);
8265 this.view.el.removeAllListeners();
8266 this.view.el.remove();
8267 this.view.purgeListeners();
8270 this.list.dom.innerHTML = '';
8273 this.store.un('beforeload', this.onBeforeLoad, this);
8274 this.store.un('load', this.onLoad, this);
8275 this.store.un('loadexception', this.onLoadException, this);
8277 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8281 fireKey : function(e){
8282 if(e.isNavKeyPress() && !this.list.isVisible()){
8283 this.fireEvent("specialkey", this, e);
8288 onResize: function(w, h){
8289 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8291 // if(typeof w != 'number'){
8292 // // we do not handle it!?!?
8295 // var tw = this.trigger.getWidth();
8296 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8297 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8299 // this.inputEl().setWidth( this.adjustWidth('input', x));
8301 // //this.trigger.setStyle('left', x+'px');
8303 // if(this.list && this.listWidth === undefined){
8304 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8305 // this.list.setWidth(lw);
8306 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8314 * Allow or prevent the user from directly editing the field text. If false is passed,
8315 * the user will only be able to select from the items defined in the dropdown list. This method
8316 * is the runtime equivalent of setting the 'editable' config option at config time.
8317 * @param {Boolean} value True to allow the user to directly edit the field text
8319 setEditable : function(value){
8320 if(value == this.editable){
8323 this.editable = value;
8325 this.inputEl().dom.setAttribute('readOnly', true);
8326 this.inputEl().on('mousedown', this.onTriggerClick, this);
8327 this.inputEl().addClass('x-combo-noedit');
8329 this.inputEl().dom.setAttribute('readOnly', false);
8330 this.inputEl().un('mousedown', this.onTriggerClick, this);
8331 this.inputEl().removeClass('x-combo-noedit');
8337 onBeforeLoad : function(combo,opts){
8342 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8344 this.restrictHeight();
8345 this.selectedIndex = -1;
8349 onLoad : function(){
8351 this.hasQuery = false;
8357 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8358 this.loading.hide();
8361 if(this.store.getCount() > 0){
8363 this.restrictHeight();
8364 if(this.lastQuery == this.allQuery){
8366 this.inputEl().dom.select();
8368 if(!this.selectByValue(this.value, true)){
8369 this.select(0, true);
8373 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8374 this.taTask.delay(this.typeAheadDelay);
8378 this.onEmptyResults();
8384 onLoadException : function()
8386 this.hasQuery = false;
8388 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8389 this.loading.hide();
8393 Roo.log(this.store.reader.jsonData);
8394 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8396 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8402 onTypeAhead : function(){
8403 if(this.store.getCount() > 0){
8404 var r = this.store.getAt(0);
8405 var newValue = r.data[this.displayField];
8406 var len = newValue.length;
8407 var selStart = this.getRawValue().length;
8409 if(selStart != len){
8410 this.setRawValue(newValue);
8411 this.selectText(selStart, newValue.length);
8417 onSelect : function(record, index){
8419 if(this.fireEvent('beforeselect', this, record, index) !== false){
8421 this.setFromData(index > -1 ? record.data : false);
8424 this.fireEvent('select', this, record, index);
8429 * Returns the currently selected field value or empty string if no value is set.
8430 * @return {String} value The selected value
8432 getValue : function(){
8435 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8438 if(this.valueField){
8439 return typeof this.value != 'undefined' ? this.value : '';
8441 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8446 * Clears any text/value currently set in the field
8448 clearValue : function(){
8449 if(this.hiddenField){
8450 this.hiddenField.dom.value = '';
8453 this.setRawValue('');
8454 this.lastSelectionText = '';
8459 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8460 * will be displayed in the field. If the value does not match the data value of an existing item,
8461 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8462 * Otherwise the field will be blank (although the value will still be set).
8463 * @param {String} value The value to match
8465 setValue : function(v){
8472 if(this.valueField){
8473 var r = this.findRecord(this.valueField, v);
8475 text = r.data[this.displayField];
8476 }else if(this.valueNotFoundText !== undefined){
8477 text = this.valueNotFoundText;
8480 this.lastSelectionText = text;
8481 if(this.hiddenField){
8482 this.hiddenField.dom.value = v;
8484 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8488 * @property {Object} the last set data for the element
8493 * Sets the value of the field based on a object which is related to the record format for the store.
8494 * @param {Object} value the value to set as. or false on reset?
8496 setFromData : function(o){
8503 var dv = ''; // display value
8504 var vv = ''; // value value..
8506 if (this.displayField) {
8507 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8509 // this is an error condition!!!
8510 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8513 if(this.valueField){
8514 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8517 if(this.hiddenField){
8518 this.hiddenField.dom.value = vv;
8520 this.lastSelectionText = dv;
8521 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8525 // no hidden field.. - we store the value in 'value', but still display
8526 // display field!!!!
8527 this.lastSelectionText = dv;
8528 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8535 // overridden so that last data is reset..
8536 this.setValue(this.originalValue);
8537 this.clearInvalid();
8538 this.lastData = false;
8540 this.view.clearSelections();
8544 findRecord : function(prop, value){
8546 if(this.store.getCount() > 0){
8547 this.store.each(function(r){
8548 if(r.data[prop] == value){
8560 // returns hidden if it's set..
8561 if (!this.rendered) {return ''};
8562 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8566 onViewMove : function(e, t){
8567 this.inKeyMode = false;
8571 onViewOver : function(e, t){
8572 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8575 var item = this.view.findItemFromChild(t);
8577 var index = this.view.indexOf(item);
8578 this.select(index, false);
8583 onViewClick : function(doFocus)
8585 var index = this.view.getSelectedIndexes()[0];
8586 var r = this.store.getAt(index);
8588 this.onSelect(r, index);
8590 if(doFocus !== false && !this.blockFocus){
8591 this.inputEl().focus();
8596 restrictHeight : function(){
8597 //this.innerList.dom.style.height = '';
8598 //var inner = this.innerList.dom;
8599 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8600 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8601 //this.list.beginUpdate();
8602 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8603 this.list.alignTo(this.inputEl(), this.listAlign);
8604 //this.list.endUpdate();
8608 onEmptyResults : function(){
8613 * Returns true if the dropdown list is expanded, else false.
8615 isExpanded : function(){
8616 return this.list.isVisible();
8620 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8621 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8622 * @param {String} value The data value of the item to select
8623 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8624 * selected item if it is not currently in view (defaults to true)
8625 * @return {Boolean} True if the value matched an item in the list, else false
8627 selectByValue : function(v, scrollIntoView){
8628 if(v !== undefined && v !== null){
8629 var r = this.findRecord(this.valueField || this.displayField, v);
8631 this.select(this.store.indexOf(r), scrollIntoView);
8639 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8640 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8641 * @param {Number} index The zero-based index of the list item to select
8642 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8643 * selected item if it is not currently in view (defaults to true)
8645 select : function(index, scrollIntoView){
8646 this.selectedIndex = index;
8647 this.view.select(index);
8648 if(scrollIntoView !== false){
8649 var el = this.view.getNode(index);
8651 //this.innerList.scrollChildIntoView(el, false);
8658 selectNext : function(){
8659 var ct = this.store.getCount();
8661 if(this.selectedIndex == -1){
8663 }else if(this.selectedIndex < ct-1){
8664 this.select(this.selectedIndex+1);
8670 selectPrev : function(){
8671 var ct = this.store.getCount();
8673 if(this.selectedIndex == -1){
8675 }else if(this.selectedIndex != 0){
8676 this.select(this.selectedIndex-1);
8682 onKeyUp : function(e){
8683 if(this.editable !== false && !e.isSpecialKey()){
8684 this.lastKey = e.getKey();
8685 this.dqTask.delay(this.queryDelay);
8690 validateBlur : function(){
8691 return !this.list || !this.list.isVisible();
8695 initQuery : function(){
8696 this.doQuery(this.getRawValue());
8700 doForce : function(){
8701 if(this.el.dom.value.length > 0){
8703 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8709 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8710 * query allowing the query action to be canceled if needed.
8711 * @param {String} query The SQL query to execute
8712 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8713 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8714 * saved in the current store (defaults to false)
8716 doQuery : function(q, forceAll){
8718 if(q === undefined || q === null){
8727 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8732 forceAll = qe.forceAll;
8733 if(forceAll === true || (q.length >= this.minChars)){
8735 this.hasQuery = true;
8737 if(this.lastQuery != q || this.alwaysQuery){
8739 if(this.mode == 'local'){
8740 this.selectedIndex = -1;
8742 this.store.clearFilter();
8744 this.store.filter(this.displayField, q);
8748 this.store.baseParams[this.queryParam] = q;
8750 var options = {params : this.getParams(q)};
8754 options.params.start = this.page * this.pageSize;
8757 this.store.load(options);
8761 this.selectedIndex = -1;
8766 this.loadNext = false;
8770 getParams : function(q){
8772 //p[this.queryParam] = q;
8776 p.limit = this.pageSize;
8782 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8784 collapse : function(){
8785 if(!this.isExpanded()){
8790 Roo.get(document).un('mousedown', this.collapseIf, this);
8791 Roo.get(document).un('mousewheel', this.collapseIf, this);
8792 if (!this.editable) {
8793 Roo.get(document).un('keydown', this.listKeyPress, this);
8795 this.fireEvent('collapse', this);
8799 collapseIf : function(e){
8800 var in_combo = e.within(this.el);
8801 var in_list = e.within(this.list);
8803 if (in_combo || in_list) {
8804 //e.stopPropagation();
8813 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8815 expand : function(){
8817 if(this.isExpanded() || !this.hasFocus){
8821 this.list.alignTo(this.inputEl(), this.listAlign);
8823 Roo.get(document).on('mousedown', this.collapseIf, this);
8824 Roo.get(document).on('mousewheel', this.collapseIf, this);
8825 if (!this.editable) {
8826 Roo.get(document).on('keydown', this.listKeyPress, this);
8829 this.fireEvent('expand', this);
8833 // Implements the default empty TriggerField.onTriggerClick function
8834 onTriggerClick : function()
8836 Roo.log('trigger click');
8843 this.loadNext = false;
8845 if(this.isExpanded()){
8847 if (!this.blockFocus) {
8848 this.inputEl().focus();
8852 this.hasFocus = true;
8853 if(this.triggerAction == 'all') {
8854 this.doQuery(this.allQuery, true);
8856 this.doQuery(this.getRawValue());
8858 if (!this.blockFocus) {
8859 this.inputEl().focus();
8863 listKeyPress : function(e)
8865 //Roo.log('listkeypress');
8866 // scroll to first matching element based on key pres..
8867 if (e.isSpecialKey()) {
8870 var k = String.fromCharCode(e.getKey()).toUpperCase();
8873 var csel = this.view.getSelectedNodes();
8874 var cselitem = false;
8876 var ix = this.view.indexOf(csel[0]);
8877 cselitem = this.store.getAt(ix);
8878 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8884 this.store.each(function(v) {
8886 // start at existing selection.
8887 if (cselitem.id == v.id) {
8893 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8894 match = this.store.indexOf(v);
8900 if (match === false) {
8901 return true; // no more action?
8904 this.view.select(match);
8905 var sn = Roo.get(this.view.getSelectedNodes()[0])
8906 //sn.scrollIntoView(sn.dom.parentNode, false);
8909 onViewScroll : function(e, t){
8911 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8915 this.hasQuery = true;
8917 this.loading = this.list.select('.loading', true).first();
8919 if(this.loading === null){
8920 this.list.createChild({
8922 cls: 'loading select2-more-results select2-active',
8923 html: 'Loading more results...'
8926 this.loading = this.list.select('.loading', true).first();
8928 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8930 this.loading.hide();
8933 this.loading.show();
8938 this.loadNext = true;
8940 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8945 addItem : function(o)
8947 var dv = ''; // display value
8949 if (this.displayField) {
8950 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8952 // this is an error condition!!!
8953 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8960 var choice = this.choices.createChild({
8962 cls: 'select2-search-choice',
8971 cls: 'select2-search-choice-close',
8976 }, this.searchField);
8978 var close = choice.select('a.select2-search-choice-close', true).first()
8980 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8987 this.inputEl().dom.value = '';
8991 onRemoveItem : function(e, _self, o)
8993 Roo.log('remove item');
8994 var index = this.item.indexOf(o.data) * 1;
8997 Roo.log('not this item?!');
9001 this.item.splice(index, 1);
9006 this.fireEvent('remove', this);
9010 syncValue : function()
9012 if(!this.item.length){
9019 Roo.each(this.item, function(i){
9020 if(_this.valueField){
9021 value.push(i[_this.valueField]);
9028 this.value = value.join(',');
9030 if(this.hiddenField){
9031 this.hiddenField.dom.value = this.value;
9035 clearItem : function()
9043 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9053 * @cfg {Boolean} grow
9057 * @cfg {Number} growMin
9061 * @cfg {Number} growMax
9071 * Ext JS Library 1.1.1
9072 * Copyright(c) 2006-2007, Ext JS, LLC.
9074 * Originally Released Under LGPL - original licence link has changed is not relivant.
9077 * <script type="text/javascript">
9082 * @extends Roo.util.Observable
9083 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9084 * This class also supports single and multi selection modes. <br>
9085 * Create a data model bound view:
9087 var store = new Roo.data.Store(...);
9089 var view = new Roo.View({
9091 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9094 selectedClass: "ydataview-selected",
9098 // listen for node click?
9099 view.on("click", function(vw, index, node, e){
9100 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9104 dataModel.load("foobar.xml");
9106 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9108 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9109 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9111 * Note: old style constructor is still suported (container, template, config)
9115 * @param {Object} config The config object
9118 Roo.View = function(config, depreciated_tpl, depreciated_config){
9120 if (typeof(depreciated_tpl) == 'undefined') {
9121 // new way.. - universal constructor.
9122 Roo.apply(this, config);
9123 this.el = Roo.get(this.el);
9126 this.el = Roo.get(config);
9127 this.tpl = depreciated_tpl;
9128 Roo.apply(this, depreciated_config);
9130 this.wrapEl = this.el.wrap().wrap();
9131 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9134 if(typeof(this.tpl) == "string"){
9135 this.tpl = new Roo.Template(this.tpl);
9137 // support xtype ctors..
9138 this.tpl = new Roo.factory(this.tpl, Roo);
9150 * @event beforeclick
9151 * Fires before a click is processed. Returns false to cancel the default action.
9152 * @param {Roo.View} this
9153 * @param {Number} index The index of the target node
9154 * @param {HTMLElement} node The target node
9155 * @param {Roo.EventObject} e The raw event object
9157 "beforeclick" : true,
9160 * Fires when a template node is clicked.
9161 * @param {Roo.View} this
9162 * @param {Number} index The index of the target node
9163 * @param {HTMLElement} node The target node
9164 * @param {Roo.EventObject} e The raw event object
9169 * Fires when a template node is double clicked.
9170 * @param {Roo.View} this
9171 * @param {Number} index The index of the target node
9172 * @param {HTMLElement} node The target node
9173 * @param {Roo.EventObject} e The raw event object
9177 * @event contextmenu
9178 * Fires when a template node is right clicked.
9179 * @param {Roo.View} this
9180 * @param {Number} index The index of the target node
9181 * @param {HTMLElement} node The target node
9182 * @param {Roo.EventObject} e The raw event object
9184 "contextmenu" : true,
9186 * @event selectionchange
9187 * Fires when the selected nodes change.
9188 * @param {Roo.View} this
9189 * @param {Array} selections Array of the selected nodes
9191 "selectionchange" : true,
9194 * @event beforeselect
9195 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9196 * @param {Roo.View} this
9197 * @param {HTMLElement} node The node to be selected
9198 * @param {Array} selections Array of currently selected nodes
9200 "beforeselect" : true,
9202 * @event preparedata
9203 * Fires on every row to render, to allow you to change the data.
9204 * @param {Roo.View} this
9205 * @param {Object} data to be rendered (change this)
9207 "preparedata" : true
9215 "click": this.onClick,
9216 "dblclick": this.onDblClick,
9217 "contextmenu": this.onContextMenu,
9221 this.selections = [];
9223 this.cmp = new Roo.CompositeElementLite([]);
9225 this.store = Roo.factory(this.store, Roo.data);
9226 this.setStore(this.store, true);
9229 if ( this.footer && this.footer.xtype) {
9231 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9233 this.footer.dataSource = this.store
9234 this.footer.container = fctr;
9235 this.footer = Roo.factory(this.footer, Roo);
9236 fctr.insertFirst(this.el);
9238 // this is a bit insane - as the paging toolbar seems to detach the el..
9239 // dom.parentNode.parentNode.parentNode
9240 // they get detached?
9244 Roo.View.superclass.constructor.call(this);
9249 Roo.extend(Roo.View, Roo.util.Observable, {
9252 * @cfg {Roo.data.Store} store Data store to load data from.
9257 * @cfg {String|Roo.Element} el The container element.
9262 * @cfg {String|Roo.Template} tpl The template used by this View
9266 * @cfg {String} dataName the named area of the template to use as the data area
9267 * Works with domtemplates roo-name="name"
9271 * @cfg {String} selectedClass The css class to add to selected nodes
9273 selectedClass : "x-view-selected",
9275 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9280 * @cfg {String} text to display on mask (default Loading)
9284 * @cfg {Boolean} multiSelect Allow multiple selection
9286 multiSelect : false,
9288 * @cfg {Boolean} singleSelect Allow single selection
9290 singleSelect: false,
9293 * @cfg {Boolean} toggleSelect - selecting
9295 toggleSelect : false,
9298 * Returns the element this view is bound to.
9299 * @return {Roo.Element}
9308 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9310 refresh : function(){
9314 // if we are using something like 'domtemplate', then
9315 // the what gets used is:
9316 // t.applySubtemplate(NAME, data, wrapping data..)
9317 // the outer template then get' applied with
9318 // the store 'extra data'
9319 // and the body get's added to the
9320 // roo-name="data" node?
9321 // <span class='roo-tpl-{name}'></span> ?????
9325 this.clearSelections();
9328 var records = this.store.getRange();
9329 if(records.length < 1) {
9331 // is this valid?? = should it render a template??
9333 this.el.update(this.emptyText);
9337 if (this.dataName) {
9338 this.el.update(t.apply(this.store.meta)); //????
9339 el = this.el.child('.roo-tpl-' + this.dataName);
9342 for(var i = 0, len = records.length; i < len; i++){
9343 var data = this.prepareData(records[i].data, i, records[i]);
9344 this.fireEvent("preparedata", this, data, i, records[i]);
9345 html[html.length] = Roo.util.Format.trim(
9347 t.applySubtemplate(this.dataName, data, this.store.meta) :
9354 el.update(html.join(""));
9355 this.nodes = el.dom.childNodes;
9356 this.updateIndexes(0);
9361 * Function to override to reformat the data that is sent to
9362 * the template for each node.
9363 * DEPRICATED - use the preparedata event handler.
9364 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9365 * a JSON object for an UpdateManager bound view).
9367 prepareData : function(data, index, record)
9369 this.fireEvent("preparedata", this, data, index, record);
9373 onUpdate : function(ds, record){
9374 Roo.log('on update');
9375 this.clearSelections();
9376 var index = this.store.indexOf(record);
9377 var n = this.nodes[index];
9378 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9379 n.parentNode.removeChild(n);
9380 this.updateIndexes(index, index);
9386 onAdd : function(ds, records, index)
9388 Roo.log(['on Add', ds, records, index] );
9389 this.clearSelections();
9390 if(this.nodes.length == 0){
9394 var n = this.nodes[index];
9395 for(var i = 0, len = records.length; i < len; i++){
9396 var d = this.prepareData(records[i].data, i, records[i]);
9398 this.tpl.insertBefore(n, d);
9401 this.tpl.append(this.el, d);
9404 this.updateIndexes(index);
9407 onRemove : function(ds, record, index){
9408 Roo.log('onRemove');
9409 this.clearSelections();
9410 var el = this.dataName ?
9411 this.el.child('.roo-tpl-' + this.dataName) :
9414 el.dom.removeChild(this.nodes[index]);
9415 this.updateIndexes(index);
9419 * Refresh an individual node.
9420 * @param {Number} index
9422 refreshNode : function(index){
9423 this.onUpdate(this.store, this.store.getAt(index));
9426 updateIndexes : function(startIndex, endIndex){
9427 var ns = this.nodes;
9428 startIndex = startIndex || 0;
9429 endIndex = endIndex || ns.length - 1;
9430 for(var i = startIndex; i <= endIndex; i++){
9431 ns[i].nodeIndex = i;
9436 * Changes the data store this view uses and refresh the view.
9437 * @param {Store} store
9439 setStore : function(store, initial){
9440 if(!initial && this.store){
9441 this.store.un("datachanged", this.refresh);
9442 this.store.un("add", this.onAdd);
9443 this.store.un("remove", this.onRemove);
9444 this.store.un("update", this.onUpdate);
9445 this.store.un("clear", this.refresh);
9446 this.store.un("beforeload", this.onBeforeLoad);
9447 this.store.un("load", this.onLoad);
9448 this.store.un("loadexception", this.onLoad);
9452 store.on("datachanged", this.refresh, this);
9453 store.on("add", this.onAdd, this);
9454 store.on("remove", this.onRemove, this);
9455 store.on("update", this.onUpdate, this);
9456 store.on("clear", this.refresh, this);
9457 store.on("beforeload", this.onBeforeLoad, this);
9458 store.on("load", this.onLoad, this);
9459 store.on("loadexception", this.onLoad, this);
9467 * onbeforeLoad - masks the loading area.
9470 onBeforeLoad : function(store,opts)
9472 Roo.log('onBeforeLoad');
9476 this.el.mask(this.mask ? this.mask : "Loading" );
9478 onLoad : function ()
9485 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9486 * @param {HTMLElement} node
9487 * @return {HTMLElement} The template node
9489 findItemFromChild : function(node){
9490 var el = this.dataName ?
9491 this.el.child('.roo-tpl-' + this.dataName,true) :
9494 if(!node || node.parentNode == el){
9497 var p = node.parentNode;
9498 while(p && p != el){
9499 if(p.parentNode == el){
9508 onClick : function(e){
9509 var item = this.findItemFromChild(e.getTarget());
9511 var index = this.indexOf(item);
9512 if(this.onItemClick(item, index, e) !== false){
9513 this.fireEvent("click", this, index, item, e);
9516 this.clearSelections();
9521 onContextMenu : function(e){
9522 var item = this.findItemFromChild(e.getTarget());
9524 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9529 onDblClick : function(e){
9530 var item = this.findItemFromChild(e.getTarget());
9532 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9536 onItemClick : function(item, index, e)
9538 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9541 if (this.toggleSelect) {
9542 var m = this.isSelected(item) ? 'unselect' : 'select';
9545 _t[m](item, true, false);
9548 if(this.multiSelect || this.singleSelect){
9549 if(this.multiSelect && e.shiftKey && this.lastSelection){
9550 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9552 this.select(item, this.multiSelect && e.ctrlKey);
9553 this.lastSelection = item;
9561 * Get the number of selected nodes.
9564 getSelectionCount : function(){
9565 return this.selections.length;
9569 * Get the currently selected nodes.
9570 * @return {Array} An array of HTMLElements
9572 getSelectedNodes : function(){
9573 return this.selections;
9577 * Get the indexes of the selected nodes.
9580 getSelectedIndexes : function(){
9581 var indexes = [], s = this.selections;
9582 for(var i = 0, len = s.length; i < len; i++){
9583 indexes.push(s[i].nodeIndex);
9589 * Clear all selections
9590 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9592 clearSelections : function(suppressEvent){
9593 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9594 this.cmp.elements = this.selections;
9595 this.cmp.removeClass(this.selectedClass);
9596 this.selections = [];
9598 this.fireEvent("selectionchange", this, this.selections);
9604 * Returns true if the passed node is selected
9605 * @param {HTMLElement/Number} node The node or node index
9608 isSelected : function(node){
9609 var s = this.selections;
9613 node = this.getNode(node);
9614 return s.indexOf(node) !== -1;
9619 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9620 * @param {Boolean} keepExisting (optional) true to keep existing selections
9621 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623 select : function(nodeInfo, keepExisting, suppressEvent){
9624 if(nodeInfo instanceof Array){
9626 this.clearSelections(true);
9628 for(var i = 0, len = nodeInfo.length; i < len; i++){
9629 this.select(nodeInfo[i], true, true);
9633 var node = this.getNode(nodeInfo);
9634 if(!node || this.isSelected(node)){
9635 return; // already selected.
9638 this.clearSelections(true);
9640 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9641 Roo.fly(node).addClass(this.selectedClass);
9642 this.selections.push(node);
9644 this.fireEvent("selectionchange", this, this.selections);
9652 * @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
9653 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9654 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9656 unselect : function(nodeInfo, keepExisting, suppressEvent)
9658 if(nodeInfo instanceof Array){
9659 Roo.each(this.selections, function(s) {
9660 this.unselect(s, nodeInfo);
9664 var node = this.getNode(nodeInfo);
9665 if(!node || !this.isSelected(node)){
9666 Roo.log("not selected");
9667 return; // not selected.
9671 Roo.each(this.selections, function(s) {
9673 Roo.fly(node).removeClass(this.selectedClass);
9680 this.selections= ns;
9681 this.fireEvent("selectionchange", this, this.selections);
9685 * Gets a template node.
9686 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9687 * @return {HTMLElement} The node or null if it wasn't found
9689 getNode : function(nodeInfo){
9690 if(typeof nodeInfo == "string"){
9691 return document.getElementById(nodeInfo);
9692 }else if(typeof nodeInfo == "number"){
9693 return this.nodes[nodeInfo];
9699 * Gets a range template nodes.
9700 * @param {Number} startIndex
9701 * @param {Number} endIndex
9702 * @return {Array} An array of nodes
9704 getNodes : function(start, end){
9705 var ns = this.nodes;
9707 end = typeof end == "undefined" ? ns.length - 1 : end;
9710 for(var i = start; i <= end; i++){
9714 for(var i = start; i >= end; i--){
9722 * Finds the index of the passed node
9723 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9724 * @return {Number} The index of the node or -1
9726 indexOf : function(node){
9727 node = this.getNode(node);
9728 if(typeof node.nodeIndex == "number"){
9729 return node.nodeIndex;
9731 var ns = this.nodes;
9732 for(var i = 0, len = ns.length; i < len; i++){
9743 * based on jquery fullcalendar
9747 Roo.bootstrap = Roo.bootstrap || {};
9749 * @class Roo.bootstrap.Calendar
9750 * @extends Roo.bootstrap.Component
9751 * Bootstrap Calendar class
9752 * @cfg {Boolean} loadMask (true|false) default false
9755 * Create a new Container
9756 * @param {Object} config The config object
9761 Roo.bootstrap.Calendar = function(config){
9762 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9766 * Fires when a date is selected
9767 * @param {DatePicker} this
9768 * @param {Date} date The selected date
9772 * @event monthchange
9773 * Fires when the displayed month changes
9774 * @param {DatePicker} this
9775 * @param {Date} date The selected month
9777 'monthchange': true,
9780 * Fires when mouse over an event
9781 * @param {Calendar} this
9782 * @param {event} Event
9787 * Fires when the mouse leaves an
9788 * @param {Calendar} this
9794 * Fires when the mouse click an
9795 * @param {Calendar} this
9804 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9807 * @cfg {Number} startDay
9808 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9814 getAutoCreate : function(){
9817 var fc_button = function(name, corner, style, content ) {
9818 return Roo.apply({},{
9820 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9822 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9825 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9833 style : 'width:100%',
9840 cls : 'fc-header-left',
9842 fc_button('prev', 'left', 'arrow', '‹' ),
9843 fc_button('next', 'right', 'arrow', '›' ),
9844 { tag: 'span', cls: 'fc-header-space' },
9845 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9853 cls : 'fc-header-center',
9857 cls: 'fc-header-title',
9860 html : 'month / year'
9868 cls : 'fc-header-right',
9870 /* fc_button('month', 'left', '', 'month' ),
9871 fc_button('week', '', '', 'week' ),
9872 fc_button('day', 'right', '', 'day' )
9884 var cal_heads = function() {
9886 // fixme - handle this.
9888 for (var i =0; i < Date.dayNames.length; i++) {
9889 var d = Date.dayNames[i];
9892 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9893 html : d.substring(0,3)
9897 ret[0].cls += ' fc-first';
9898 ret[6].cls += ' fc-last';
9901 var cal_cell = function(n) {
9904 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9909 cls: 'fc-day-number',
9913 cls: 'fc-day-content',
9917 style: 'position: relative;' // height: 17px;
9929 var cal_rows = function() {
9932 for (var r = 0; r < 6; r++) {
9939 for (var i =0; i < Date.dayNames.length; i++) {
9940 var d = Date.dayNames[i];
9941 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9944 row.cn[0].cls+=' fc-first';
9945 row.cn[0].cn[0].style = 'min-height:90px';
9946 row.cn[6].cls+=' fc-last';
9950 ret[0].cls += ' fc-first';
9951 ret[4].cls += ' fc-prev-last';
9952 ret[5].cls += ' fc-last';
9959 cls: 'fc-border-separate',
9960 style : 'width:100%',
9968 cls : 'fc-first fc-last',
9987 style : "position: relative;",
9990 cls : 'fc-view fc-view-month fc-grid',
9991 style : 'position: relative',
9992 unselectable : 'on',
9995 cls : 'fc-event-container',
9996 style : 'position:absolute;z-index:8;top:0;left:0;'
10014 initEvents : function()
10017 throw "can not find store for calendar";
10023 style: "text-align:center",
10027 style: "background-color:white;width:50%;margin:250 auto",
10031 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10042 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10044 var size = this.el.select('.fc-content', true).first().getSize();
10045 this.maskEl.setSize(size.width, size.height);
10046 this.maskEl.enableDisplayMode("block");
10047 if(!this.loadMask){
10048 this.maskEl.hide();
10051 this.store = Roo.factory(this.store, Roo.data);
10052 this.store.on('load', this.onLoad, this);
10053 this.store.on('beforeload', this.onBeforeLoad, this);
10057 this.cells = this.el.select('.fc-day',true);
10058 //Roo.log(this.cells);
10059 this.textNodes = this.el.query('.fc-day-number');
10060 this.cells.addClassOnOver('fc-state-hover');
10062 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10063 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10064 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10065 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10067 this.on('monthchange', this.onMonthChange, this);
10069 this.update(new Date().clearTime());
10072 resize : function() {
10073 var sz = this.el.getSize();
10075 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10076 this.el.select('.fc-day-content div',true).setHeight(34);
10081 showPrevMonth : function(e){
10082 this.update(this.activeDate.add("mo", -1));
10084 showToday : function(e){
10085 this.update(new Date().clearTime());
10088 showNextMonth : function(e){
10089 this.update(this.activeDate.add("mo", 1));
10093 showPrevYear : function(){
10094 this.update(this.activeDate.add("y", -1));
10098 showNextYear : function(){
10099 this.update(this.activeDate.add("y", 1));
10104 update : function(date)
10106 var vd = this.activeDate;
10107 this.activeDate = date;
10108 // if(vd && this.el){
10109 // var t = date.getTime();
10110 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10111 // Roo.log('using add remove');
10113 // this.fireEvent('monthchange', this, date);
10115 // this.cells.removeClass("fc-state-highlight");
10116 // this.cells.each(function(c){
10117 // if(c.dateValue == t){
10118 // c.addClass("fc-state-highlight");
10119 // setTimeout(function(){
10120 // try{c.dom.firstChild.focus();}catch(e){}
10130 var days = date.getDaysInMonth();
10132 var firstOfMonth = date.getFirstDateOfMonth();
10133 var startingPos = firstOfMonth.getDay()-this.startDay;
10135 if(startingPos < this.startDay){
10139 var pm = date.add(Date.MONTH, -1);
10140 var prevStart = pm.getDaysInMonth()-startingPos;
10142 this.cells = this.el.select('.fc-day',true);
10143 this.textNodes = this.el.query('.fc-day-number');
10144 this.cells.addClassOnOver('fc-state-hover');
10146 var cells = this.cells.elements;
10147 var textEls = this.textNodes;
10149 Roo.each(cells, function(cell){
10150 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10153 days += startingPos;
10155 // convert everything to numbers so it's fast
10156 var day = 86400000;
10157 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10160 //Roo.log(prevStart);
10162 var today = new Date().clearTime().getTime();
10163 var sel = date.clearTime().getTime();
10164 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10165 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10166 var ddMatch = this.disabledDatesRE;
10167 var ddText = this.disabledDatesText;
10168 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10169 var ddaysText = this.disabledDaysText;
10170 var format = this.format;
10172 var setCellClass = function(cal, cell){
10174 //Roo.log('set Cell Class');
10176 var t = d.getTime();
10180 cell.dateValue = t;
10182 cell.className += " fc-today";
10183 cell.className += " fc-state-highlight";
10184 cell.title = cal.todayText;
10187 // disable highlight in other month..
10188 //cell.className += " fc-state-highlight";
10193 cell.className = " fc-state-disabled";
10194 cell.title = cal.minText;
10198 cell.className = " fc-state-disabled";
10199 cell.title = cal.maxText;
10203 if(ddays.indexOf(d.getDay()) != -1){
10204 cell.title = ddaysText;
10205 cell.className = " fc-state-disabled";
10208 if(ddMatch && format){
10209 var fvalue = d.dateFormat(format);
10210 if(ddMatch.test(fvalue)){
10211 cell.title = ddText.replace("%0", fvalue);
10212 cell.className = " fc-state-disabled";
10216 if (!cell.initialClassName) {
10217 cell.initialClassName = cell.dom.className;
10220 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10225 for(; i < startingPos; i++) {
10226 textEls[i].innerHTML = (++prevStart);
10227 d.setDate(d.getDate()+1);
10229 cells[i].className = "fc-past fc-other-month";
10230 setCellClass(this, cells[i]);
10235 for(; i < days; i++){
10236 intDay = i - startingPos + 1;
10237 textEls[i].innerHTML = (intDay);
10238 d.setDate(d.getDate()+1);
10240 cells[i].className = ''; // "x-date-active";
10241 setCellClass(this, cells[i]);
10245 for(; i < 42; i++) {
10246 textEls[i].innerHTML = (++extraDays);
10247 d.setDate(d.getDate()+1);
10249 cells[i].className = "fc-future fc-other-month";
10250 setCellClass(this, cells[i]);
10253 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10255 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10257 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10258 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10260 if(totalRows != 6){
10261 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10262 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10265 this.fireEvent('monthchange', this, date);
10269 if(!this.internalRender){
10270 var main = this.el.dom.firstChild;
10271 var w = main.offsetWidth;
10272 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10273 Roo.fly(main).setWidth(w);
10274 this.internalRender = true;
10275 // opera does not respect the auto grow header center column
10276 // then, after it gets a width opera refuses to recalculate
10277 // without a second pass
10278 if(Roo.isOpera && !this.secondPass){
10279 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10280 this.secondPass = true;
10281 this.update.defer(10, this, [date]);
10288 findCell : function(dt) {
10289 dt = dt.clearTime().getTime();
10291 this.cells.each(function(c){
10292 //Roo.log("check " +c.dateValue + '?=' + dt);
10293 if(c.dateValue == dt){
10303 findCells : function(ev) {
10304 var s = ev.start.clone().clearTime().getTime();
10306 var e= ev.end.clone().clearTime().getTime();
10309 this.cells.each(function(c){
10310 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10312 if(c.dateValue > e){
10315 if(c.dateValue < s){
10324 findBestRow: function(cells)
10328 for (var i =0 ; i < cells.length;i++) {
10329 ret = Math.max(cells[i].rows || 0,ret);
10336 addItem : function(ev)
10338 // look for vertical location slot in
10339 var cells = this.findCells(ev);
10341 ev.row = this.findBestRow(cells);
10343 // work out the location.
10347 for(var i =0; i < cells.length; i++) {
10355 if (crow.start.getY() == cells[i].getY()) {
10357 crow.end = cells[i];
10373 for (var i = 0; i < cells.length;i++) {
10374 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10378 this.calevents.push(ev);
10381 clearEvents: function() {
10383 if(!this.calevents){
10387 Roo.each(this.cells.elements, function(c){
10391 Roo.each(this.calevents, function(e) {
10392 Roo.each(e.els, function(el) {
10393 el.un('mouseenter' ,this.onEventEnter, this);
10394 el.un('mouseleave' ,this.onEventLeave, this);
10401 renderEvents: function()
10403 // first make sure there is enough space..
10405 this.cells.each(function(c) {
10407 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10410 for (var e = 0; e < this.calevents.length; e++) {
10411 var ev = this.calevents[e];
10412 var cells = ev.cells;
10413 var rows = ev.rows;
10415 for(var i =0; i < rows.length; i++) {
10418 // how many rows should it span..
10421 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10422 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10424 unselectable : "on",
10427 cls: 'fc-event-inner',
10431 // cls: 'fc-event-time',
10432 // html : cells.length > 1 ? '' : ev.time
10436 cls: 'fc-event-title',
10437 html : String.format('{0}', ev.title)
10444 cls: 'ui-resizable-handle ui-resizable-e',
10445 html : '  '
10451 cfg.cls += ' fc-event-start';
10453 if ((i+1) == rows.length) {
10454 cfg.cls += ' fc-event-end';
10457 var ctr = this.el.select('.fc-event-container',true).first();
10458 var cg = ctr.createChild(cfg);
10460 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10461 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10462 cg.on('click', this.onEventClick, this, ev);
10466 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10467 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10469 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10470 cg.setWidth(ebox.right - sbox.x -2);
10478 onEventEnter: function (e, el,event,d) {
10479 this.fireEvent('evententer', this, el, event);
10482 onEventLeave: function (e, el,event,d) {
10483 this.fireEvent('eventleave', this, el, event);
10486 onEventClick: function (e, el,event,d) {
10487 this.fireEvent('eventclick', this, el, event);
10490 onMonthChange: function () {
10494 onLoad: function ()
10496 this.calevents = [];
10499 if(this.store.getCount() > 0){
10500 this.store.data.each(function(d){
10503 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10504 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10505 time : d.data.start_time,
10506 title : d.data.title,
10507 description : d.data.description,
10508 venue : d.data.venue
10513 this.renderEvents();
10516 this.maskEl.hide();
10520 onBeforeLoad: function()
10522 this.clearEvents();
10525 this.maskEl.show();
10539 * @class Roo.bootstrap.Popover
10540 * @extends Roo.bootstrap.Component
10541 * Bootstrap Popover class
10542 * @cfg {String} html contents of the popover (or false to use children..)
10543 * @cfg {String} title of popover (or false to hide)
10544 * @cfg {String} placement how it is placed
10545 * @cfg {String} trigger click || hover (or false to trigger manually)
10546 * @cfg {String} over what (parent or false to trigger manually.)
10549 * Create a new Popover
10550 * @param {Object} config The config object
10553 Roo.bootstrap.Popover = function(config){
10554 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10557 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10559 title: 'Fill in a title',
10562 placement : 'right',
10563 trigger : 'hover', // hover
10567 can_build_overlaid : false,
10569 getChildContainer : function()
10571 return this.el.select('.popover-content',true).first();
10574 getAutoCreate : function(){
10575 Roo.log('make popover?');
10577 cls : 'popover roo-dynamic',
10578 style: 'display:block',
10584 cls : 'popover-inner',
10588 cls: 'popover-title',
10592 cls : 'popover-content',
10603 setTitle: function(str)
10605 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10607 setContent: function(str)
10609 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10611 // as it get's added to the bottom of the page.
10612 onRender : function(ct, position)
10614 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10616 var cfg = Roo.apply({}, this.getAutoCreate());
10620 cfg.cls += ' ' + this.cls;
10623 cfg.style = this.style;
10625 Roo.log("adding to ")
10626 this.el = Roo.get(document.body).createChild(cfg, position);
10632 initEvents : function()
10634 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10635 this.el.enableDisplayMode('block');
10637 if (this.over === false) {
10640 if (this.triggers === false) {
10643 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10644 var triggers = this.trigger ? this.trigger.split(' ') : [];
10645 Roo.each(triggers, function(trigger) {
10647 if (trigger == 'click') {
10648 on_el.on('click', this.toggle, this);
10649 } else if (trigger != 'manual') {
10650 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10651 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10653 on_el.on(eventIn ,this.enter, this);
10654 on_el.on(eventOut, this.leave, this);
10665 toggle : function () {
10666 this.hoverState == 'in' ? this.leave() : this.enter();
10669 enter : function () {
10672 clearTimeout(this.timeout);
10674 this.hoverState = 'in'
10676 if (!this.delay || !this.delay.show) {
10681 this.timeout = setTimeout(function () {
10682 if (_t.hoverState == 'in') {
10685 }, this.delay.show)
10687 leave : function() {
10688 clearTimeout(this.timeout);
10690 this.hoverState = 'out'
10692 if (!this.delay || !this.delay.hide) {
10697 this.timeout = setTimeout(function () {
10698 if (_t.hoverState == 'out') {
10701 }, this.delay.hide)
10704 show : function (on_el)
10707 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10710 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10711 if (this.html !== false) {
10712 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10714 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10715 if (!this.title.length) {
10716 this.el.select('.popover-title',true).hide();
10719 var placement = typeof this.placement == 'function' ?
10720 this.placement.call(this, this.el, on_el) :
10723 var autoToken = /\s?auto?\s?/i;
10724 var autoPlace = autoToken.test(placement);
10726 placement = placement.replace(autoToken, '') || 'top';
10730 //this.el.setXY([0,0]);
10732 this.el.dom.style.display='block';
10733 this.el.addClass(placement);
10735 //this.el.appendTo(on_el);
10737 var p = this.getPosition();
10738 var box = this.el.getBox();
10743 var align = Roo.bootstrap.Popover.alignment[placement]
10744 this.el.alignTo(on_el, align[0],align[1]);
10745 //var arrow = this.el.select('.arrow',true).first();
10746 //arrow.set(align[2],
10748 this.el.addClass('in');
10749 this.hoverState = null;
10751 if (this.el.hasClass('fade')) {
10758 this.el.setXY([0,0]);
10759 this.el.removeClass('in');
10766 Roo.bootstrap.Popover.alignment = {
10767 'left' : ['r-l', [-10,0], 'right'],
10768 'right' : ['l-r', [10,0], 'left'],
10769 'bottom' : ['t-b', [0,10], 'top'],
10770 'top' : [ 'b-t', [0,-10], 'bottom']
10781 * @class Roo.bootstrap.Progress
10782 * @extends Roo.bootstrap.Component
10783 * Bootstrap Progress class
10784 * @cfg {Boolean} striped striped of the progress bar
10785 * @cfg {Boolean} active animated of the progress bar
10789 * Create a new Progress
10790 * @param {Object} config The config object
10793 Roo.bootstrap.Progress = function(config){
10794 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10797 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10802 getAutoCreate : function(){
10810 cfg.cls += ' progress-striped';
10814 cfg.cls += ' active';
10833 * @class Roo.bootstrap.ProgressBar
10834 * @extends Roo.bootstrap.Component
10835 * Bootstrap ProgressBar class
10836 * @cfg {Number} aria_valuenow aria-value now
10837 * @cfg {Number} aria_valuemin aria-value min
10838 * @cfg {Number} aria_valuemax aria-value max
10839 * @cfg {String} label label for the progress bar
10840 * @cfg {String} panel (success | info | warning | danger )
10841 * @cfg {String} role role of the progress bar
10842 * @cfg {String} sr_only text
10846 * Create a new ProgressBar
10847 * @param {Object} config The config object
10850 Roo.bootstrap.ProgressBar = function(config){
10851 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10854 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10858 aria_valuemax : 100,
10864 getAutoCreate : function()
10869 cls: 'progress-bar',
10870 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10882 cfg.role = this.role;
10885 if(this.aria_valuenow){
10886 cfg['aria-valuenow'] = this.aria_valuenow;
10889 if(this.aria_valuemin){
10890 cfg['aria-valuemin'] = this.aria_valuemin;
10893 if(this.aria_valuemax){
10894 cfg['aria-valuemax'] = this.aria_valuemax;
10897 if(this.label && !this.sr_only){
10898 cfg.html = this.label;
10902 cfg.cls += ' progress-bar-' + this.panel;
10908 update : function(aria_valuenow)
10910 this.aria_valuenow = aria_valuenow;
10912 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10927 * @class Roo.bootstrap.TabPanel
10928 * @extends Roo.bootstrap.Component
10929 * Bootstrap TabPanel class
10930 * @cfg {Boolean} active panel active
10931 * @cfg {String} html panel content
10932 * @cfg {String} tabId tab relate id
10936 * Create a new TabPanel
10937 * @param {Object} config The config object
10940 Roo.bootstrap.TabPanel = function(config){
10941 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10944 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10950 getAutoCreate : function(){
10954 html: this.html || ''
10958 cfg.cls += ' active';
10962 cfg.tabId = this.tabId;
10980 * @class Roo.bootstrap.DateField
10981 * @extends Roo.bootstrap.Input
10982 * Bootstrap DateField class
10983 * @cfg {Number} weekStart default 0
10984 * @cfg {Number} weekStart default 0
10985 * @cfg {Number} viewMode default empty, (months|years)
10986 * @cfg {Number} minViewMode default empty, (months|years)
10987 * @cfg {Number} startDate default -Infinity
10988 * @cfg {Number} endDate default Infinity
10989 * @cfg {Boolean} todayHighlight default false
10990 * @cfg {Boolean} todayBtn default false
10991 * @cfg {Boolean} calendarWeeks default false
10992 * @cfg {Object} daysOfWeekDisabled default empty
10994 * @cfg {Boolean} keyboardNavigation default true
10995 * @cfg {String} language default en
10998 * Create a new DateField
10999 * @param {Object} config The config object
11002 Roo.bootstrap.DateField = function(config){
11003 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11007 * Fires when this field show.
11008 * @param {Roo.bootstrap.DateField} this
11009 * @param {Mixed} date The date value
11014 * Fires when this field hide.
11015 * @param {Roo.bootstrap.DateField} this
11016 * @param {Mixed} date The date value
11021 * Fires when select a date.
11022 * @param {Roo.bootstrap.DateField} this
11023 * @param {Mixed} date The date value
11029 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11032 * @cfg {String} format
11033 * The default date format string which can be overriden for localization support. The format must be
11034 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11038 * @cfg {String} altFormats
11039 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11040 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11042 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11050 todayHighlight : false,
11056 keyboardNavigation: true,
11058 calendarWeeks: false,
11060 startDate: -Infinity,
11064 daysOfWeekDisabled: [],
11068 UTCDate: function()
11070 return new Date(Date.UTC.apply(Date, arguments));
11073 UTCToday: function()
11075 var today = new Date();
11076 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11079 getDate: function() {
11080 var d = this.getUTCDate();
11081 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11084 getUTCDate: function() {
11088 setDate: function(d) {
11089 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11092 setUTCDate: function(d) {
11094 this.setValue(this.formatDate(this.date));
11097 onRender: function(ct, position)
11100 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11102 this.language = this.language || 'en';
11103 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11104 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11106 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11107 this.format = this.format || 'm/d/y';
11108 this.isInline = false;
11109 this.isInput = true;
11110 this.component = this.el.select('.add-on', true).first() || false;
11111 this.component = (this.component && this.component.length === 0) ? false : this.component;
11112 this.hasInput = this.component && this.inputEL().length;
11114 if (typeof(this.minViewMode === 'string')) {
11115 switch (this.minViewMode) {
11117 this.minViewMode = 1;
11120 this.minViewMode = 2;
11123 this.minViewMode = 0;
11128 if (typeof(this.viewMode === 'string')) {
11129 switch (this.viewMode) {
11142 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11144 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11146 this.picker().on('mousedown', this.onMousedown, this);
11147 this.picker().on('click', this.onClick, this);
11149 this.picker().addClass('datepicker-dropdown');
11151 this.startViewMode = this.viewMode;
11154 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11155 if(!this.calendarWeeks){
11160 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11161 v.attr('colspan', function(i, val){
11162 return parseInt(val) + 1;
11167 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11169 this.setStartDate(this.startDate);
11170 this.setEndDate(this.endDate);
11172 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11179 if(this.isInline) {
11184 picker : function()
11186 return this.el.select('.datepicker', true).first();
11189 fillDow: function()
11191 var dowCnt = this.weekStart;
11200 if(this.calendarWeeks){
11208 while (dowCnt < this.weekStart + 7) {
11212 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11216 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11219 fillMonths: function()
11222 var months = this.picker().select('>.datepicker-months td', true).first();
11224 months.dom.innerHTML = '';
11230 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11233 months.createChild(month);
11238 update: function(){
11240 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11242 if (this.date < this.startDate) {
11243 this.viewDate = new Date(this.startDate);
11244 } else if (this.date > this.endDate) {
11245 this.viewDate = new Date(this.endDate);
11247 this.viewDate = new Date(this.date);
11254 var d = new Date(this.viewDate),
11255 year = d.getUTCFullYear(),
11256 month = d.getUTCMonth(),
11257 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11258 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11259 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11260 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11261 currentDate = this.date && this.date.valueOf(),
11262 today = this.UTCToday();
11264 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11266 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11268 // this.picker.select('>tfoot th.today').
11269 // .text(dates[this.language].today)
11270 // .toggle(this.todayBtn !== false);
11272 this.updateNavArrows();
11275 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11277 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11279 prevMonth.setUTCDate(day);
11281 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11283 var nextMonth = new Date(prevMonth);
11285 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11287 nextMonth = nextMonth.valueOf();
11289 var fillMonths = false;
11291 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11293 while(prevMonth.valueOf() < nextMonth) {
11296 if (prevMonth.getUTCDay() === this.weekStart) {
11298 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11306 if(this.calendarWeeks){
11307 // ISO 8601: First week contains first thursday.
11308 // ISO also states week starts on Monday, but we can be more abstract here.
11310 // Start of current week: based on weekstart/current date
11311 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11312 // Thursday of this week
11313 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11314 // First Thursday of year, year from thursday
11315 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11316 // Calendar week: ms between thursdays, div ms per day, div 7 days
11317 calWeek = (th - yth) / 864e5 / 7 + 1;
11319 fillMonths.cn.push({
11327 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11329 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11332 if (this.todayHighlight &&
11333 prevMonth.getUTCFullYear() == today.getFullYear() &&
11334 prevMonth.getUTCMonth() == today.getMonth() &&
11335 prevMonth.getUTCDate() == today.getDate()) {
11336 clsName += ' today';
11339 if (currentDate && prevMonth.valueOf() === currentDate) {
11340 clsName += ' active';
11343 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11344 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11345 clsName += ' disabled';
11348 fillMonths.cn.push({
11350 cls: 'day ' + clsName,
11351 html: prevMonth.getDate()
11354 prevMonth.setDate(prevMonth.getDate()+1);
11357 var currentYear = this.date && this.date.getUTCFullYear();
11358 var currentMonth = this.date && this.date.getUTCMonth();
11360 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11362 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11363 v.removeClass('active');
11365 if(currentYear === year && k === currentMonth){
11366 v.addClass('active');
11369 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11370 v.addClass('disabled');
11376 year = parseInt(year/10, 10) * 10;
11378 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11380 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11383 for (var i = -1; i < 11; i++) {
11384 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11386 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11394 showMode: function(dir) {
11396 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11398 Roo.each(this.picker().select('>div',true).elements, function(v){
11399 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11402 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11407 if(this.isInline) return;
11409 this.picker().removeClass(['bottom', 'top']);
11411 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11413 * place to the top of element!
11417 this.picker().addClass('top');
11418 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11423 this.picker().addClass('bottom');
11425 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11428 parseDate : function(value){
11429 if(!value || value instanceof Date){
11432 var v = Date.parseDate(value, this.format);
11433 if (!v && this.useIso) {
11434 v = Date.parseDate(value, 'Y-m-d');
11436 if(!v && this.altFormats){
11437 if(!this.altFormatsArray){
11438 this.altFormatsArray = this.altFormats.split("|");
11440 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11441 v = Date.parseDate(value, this.altFormatsArray[i]);
11447 formatDate : function(date, fmt){
11448 return (!date || !(date instanceof Date)) ?
11449 date : date.dateFormat(fmt || this.format);
11452 onFocus : function()
11454 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11458 onBlur : function()
11460 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11466 this.picker().show();
11470 this.fireEvent('show', this, this.date);
11475 if(this.isInline) return;
11476 this.picker().hide();
11477 this.viewMode = this.startViewMode;
11480 this.fireEvent('hide', this, this.date);
11484 onMousedown: function(e){
11485 e.stopPropagation();
11486 e.preventDefault();
11489 keyup: function(e){
11490 Roo.bootstrap.DateField.superclass.keyup.call(this);
11495 setValue: function(v){
11496 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11498 this.fireEvent('select', this, this.date);
11502 fireKey: function(e){
11503 if (!this.picker().isVisible()){
11504 if (e.keyCode == 27) // allow escape to hide and re-show picker
11508 var dateChanged = false,
11510 newDate, newViewDate;
11514 e.preventDefault();
11518 if (!this.keyboardNavigation) break;
11519 dir = e.keyCode == 37 ? -1 : 1;
11522 newDate = this.moveYear(this.date, dir);
11523 newViewDate = this.moveYear(this.viewDate, dir);
11524 } else if (e.shiftKey){
11525 newDate = this.moveMonth(this.date, dir);
11526 newViewDate = this.moveMonth(this.viewDate, dir);
11528 newDate = new Date(this.date);
11529 newDate.setUTCDate(this.date.getUTCDate() + dir);
11530 newViewDate = new Date(this.viewDate);
11531 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11533 if (this.dateWithinRange(newDate)){
11534 this.date = newDate;
11535 this.viewDate = newViewDate;
11536 this.setValue(this.formatDate(this.date));
11538 e.preventDefault();
11539 dateChanged = true;
11544 if (!this.keyboardNavigation) break;
11545 dir = e.keyCode == 38 ? -1 : 1;
11547 newDate = this.moveYear(this.date, dir);
11548 newViewDate = this.moveYear(this.viewDate, dir);
11549 } else if (e.shiftKey){
11550 newDate = this.moveMonth(this.date, dir);
11551 newViewDate = this.moveMonth(this.viewDate, dir);
11553 newDate = new Date(this.date);
11554 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11555 newViewDate = new Date(this.viewDate);
11556 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11558 if (this.dateWithinRange(newDate)){
11559 this.date = newDate;
11560 this.viewDate = newViewDate;
11561 this.setValue(this.formatDate(this.date));
11563 e.preventDefault();
11564 dateChanged = true;
11568 this.setValue(this.formatDate(this.date));
11570 e.preventDefault();
11573 this.setValue(this.formatDate(this.date));
11580 onClick: function(e) {
11581 e.stopPropagation();
11582 e.preventDefault();
11584 var target = e.getTarget();
11586 if(target.nodeName.toLowerCase() === 'i'){
11587 target = Roo.get(target).dom.parentNode;
11590 var nodeName = target.nodeName;
11591 var className = target.className;
11592 var html = target.innerHTML;
11594 switch(nodeName.toLowerCase()) {
11596 switch(className) {
11602 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11603 switch(this.viewMode){
11605 this.viewDate = this.moveMonth(this.viewDate, dir);
11609 this.viewDate = this.moveYear(this.viewDate, dir);
11615 var date = new Date();
11616 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11618 this.setValue(this.formatDate(this.date));
11624 if (className.indexOf('disabled') === -1) {
11625 this.viewDate.setUTCDate(1);
11626 if (className.indexOf('month') !== -1) {
11627 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11629 var year = parseInt(html, 10) || 0;
11630 this.viewDate.setUTCFullYear(year);
11639 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11640 var day = parseInt(html, 10) || 1;
11641 var year = this.viewDate.getUTCFullYear(),
11642 month = this.viewDate.getUTCMonth();
11644 if (className.indexOf('old') !== -1) {
11651 } else if (className.indexOf('new') !== -1) {
11659 this.date = this.UTCDate(year, month, day,0,0,0,0);
11660 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11662 this.setValue(this.formatDate(this.date));
11669 setStartDate: function(startDate){
11670 this.startDate = startDate || -Infinity;
11671 if (this.startDate !== -Infinity) {
11672 this.startDate = this.parseDate(this.startDate);
11675 this.updateNavArrows();
11678 setEndDate: function(endDate){
11679 this.endDate = endDate || Infinity;
11680 if (this.endDate !== Infinity) {
11681 this.endDate = this.parseDate(this.endDate);
11684 this.updateNavArrows();
11687 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11688 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11689 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11690 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11692 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11693 return parseInt(d, 10);
11696 this.updateNavArrows();
11699 updateNavArrows: function() {
11700 var d = new Date(this.viewDate),
11701 year = d.getUTCFullYear(),
11702 month = d.getUTCMonth();
11704 Roo.each(this.picker().select('.prev', true).elements, function(v){
11706 switch (this.viewMode) {
11709 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11715 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11722 Roo.each(this.picker().select('.next', true).elements, function(v){
11724 switch (this.viewMode) {
11727 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11733 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11741 moveMonth: function(date, dir){
11742 if (!dir) return date;
11743 var new_date = new Date(date.valueOf()),
11744 day = new_date.getUTCDate(),
11745 month = new_date.getUTCMonth(),
11746 mag = Math.abs(dir),
11748 dir = dir > 0 ? 1 : -1;
11751 // If going back one month, make sure month is not current month
11752 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11754 return new_date.getUTCMonth() == month;
11756 // If going forward one month, make sure month is as expected
11757 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11759 return new_date.getUTCMonth() != new_month;
11761 new_month = month + dir;
11762 new_date.setUTCMonth(new_month);
11763 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11764 if (new_month < 0 || new_month > 11)
11765 new_month = (new_month + 12) % 12;
11767 // For magnitudes >1, move one month at a time...
11768 for (var i=0; i<mag; i++)
11769 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11770 new_date = this.moveMonth(new_date, dir);
11771 // ...then reset the day, keeping it in the new month
11772 new_month = new_date.getUTCMonth();
11773 new_date.setUTCDate(day);
11775 return new_month != new_date.getUTCMonth();
11778 // Common date-resetting loop -- if date is beyond end of month, make it
11781 new_date.setUTCDate(--day);
11782 new_date.setUTCMonth(new_month);
11787 moveYear: function(date, dir){
11788 return this.moveMonth(date, dir*12);
11791 dateWithinRange: function(date){
11792 return date >= this.startDate && date <= this.endDate;
11796 remove: function() {
11797 this.picker().remove();
11802 Roo.apply(Roo.bootstrap.DateField, {
11813 html: '<i class="icon-arrow-left"/>'
11823 html: '<i class="icon-arrow-right"/>'
11865 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11866 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11867 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11868 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11869 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11882 navFnc: 'FullYear',
11887 navFnc: 'FullYear',
11892 Roo.apply(Roo.bootstrap.DateField, {
11896 cls: 'datepicker dropdown-menu',
11900 cls: 'datepicker-days',
11904 cls: 'table-condensed',
11906 Roo.bootstrap.DateField.head,
11910 Roo.bootstrap.DateField.footer
11917 cls: 'datepicker-months',
11921 cls: 'table-condensed',
11923 Roo.bootstrap.DateField.head,
11924 Roo.bootstrap.DateField.content,
11925 Roo.bootstrap.DateField.footer
11932 cls: 'datepicker-years',
11936 cls: 'table-condensed',
11938 Roo.bootstrap.DateField.head,
11939 Roo.bootstrap.DateField.content,
11940 Roo.bootstrap.DateField.footer
11959 * @class Roo.bootstrap.TimeField
11960 * @extends Roo.bootstrap.Input
11961 * Bootstrap DateField class
11965 * Create a new TimeField
11966 * @param {Object} config The config object
11969 Roo.bootstrap.TimeField = function(config){
11970 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11974 * Fires when this field show.
11975 * @param {Roo.bootstrap.DateField} this
11976 * @param {Mixed} date The date value
11981 * Fires when this field hide.
11982 * @param {Roo.bootstrap.DateField} this
11983 * @param {Mixed} date The date value
11988 * Fires when select a date.
11989 * @param {Roo.bootstrap.DateField} this
11990 * @param {Mixed} date The date value
11996 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11999 * @cfg {String} format
12000 * The default time format string which can be overriden for localization support. The format must be
12001 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12005 onRender: function(ct, position)
12008 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12010 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12012 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12014 this.pop = this.picker().select('>.datepicker-time',true).first();
12015 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12017 this.picker().on('mousedown', this.onMousedown, this);
12018 this.picker().on('click', this.onClick, this);
12020 this.picker().addClass('datepicker-dropdown');
12025 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12026 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12027 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12028 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12029 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12030 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12034 fireKey: function(e){
12035 if (!this.picker().isVisible()){
12036 if (e.keyCode == 27) // allow escape to hide and re-show picker
12041 e.preventDefault();
12049 this.onTogglePeriod();
12052 this.onIncrementMinutes();
12055 this.onDecrementMinutes();
12064 onClick: function(e) {
12065 e.stopPropagation();
12066 e.preventDefault();
12069 picker : function()
12071 return this.el.select('.datepicker', true).first();
12074 fillTime: function()
12076 var time = this.pop.select('tbody', true).first();
12078 time.dom.innerHTML = '';
12093 cls: 'hours-up glyphicon glyphicon-chevron-up'
12113 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12134 cls: 'timepicker-hour',
12149 cls: 'timepicker-minute',
12164 cls: 'btn btn-primary period',
12186 cls: 'hours-down glyphicon glyphicon-chevron-down'
12206 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12224 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12231 var hours = this.time.getHours();
12232 var minutes = this.time.getMinutes();
12245 hours = hours - 12;
12249 hours = '0' + hours;
12253 minutes = '0' + minutes;
12256 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12257 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12258 this.pop.select('button', true).first().dom.innerHTML = period;
12264 this.picker().removeClass(['bottom', 'top']);
12266 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12268 * place to the top of element!
12272 this.picker().addClass('top');
12273 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12278 this.picker().addClass('bottom');
12280 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12283 onFocus : function()
12285 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12289 onBlur : function()
12291 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12297 this.picker().show();
12302 this.fireEvent('show', this, this.date);
12307 this.picker().hide();
12310 this.fireEvent('hide', this, this.date);
12313 setTime : function()
12316 this.setValue(this.time.format(this.format));
12318 this.fireEvent('select', this, this.date);
12323 onMousedown: function(e){
12324 e.stopPropagation();
12325 e.preventDefault();
12328 onIncrementHours: function()
12330 Roo.log('onIncrementHours');
12331 this.time = this.time.add(Date.HOUR, 1);
12336 onDecrementHours: function()
12338 Roo.log('onDecrementHours');
12339 this.time = this.time.add(Date.HOUR, -1);
12343 onIncrementMinutes: function()
12345 Roo.log('onIncrementMinutes');
12346 this.time = this.time.add(Date.MINUTE, 1);
12350 onDecrementMinutes: function()
12352 Roo.log('onDecrementMinutes');
12353 this.time = this.time.add(Date.MINUTE, -1);
12357 onTogglePeriod: function()
12359 Roo.log('onTogglePeriod');
12360 this.time = this.time.add(Date.HOUR, 12);
12367 Roo.apply(Roo.bootstrap.TimeField, {
12397 cls: 'btn btn-info ok',
12409 Roo.apply(Roo.bootstrap.TimeField, {
12413 cls: 'datepicker dropdown-menu',
12417 cls: 'datepicker-time',
12421 cls: 'table-condensed',
12423 Roo.bootstrap.TimeField.content,
12424 Roo.bootstrap.TimeField.footer
12443 * @class Roo.bootstrap.CheckBox
12444 * @extends Roo.bootstrap.Input
12445 * Bootstrap CheckBox class
12447 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12448 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12449 * @cfg {String} boxLabel The text that appears beside the checkbox
12450 * @cfg {Boolean} checked initnal the element
12453 * Create a new CheckBox
12454 * @param {Object} config The config object
12457 Roo.bootstrap.CheckBox = function(config){
12458 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12463 * Fires when the element is checked or unchecked.
12464 * @param {Roo.bootstrap.CheckBox} this This input
12465 * @param {Boolean} checked The new checked value
12471 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12473 inputType: 'checkbox',
12479 getAutoCreate : function()
12481 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12487 cfg.cls = 'form-group' //input-group
12492 type : this.inputType,
12493 value : (!this.checked) ? this.valueOff : this.inputValue,
12495 placeholder : this.placeholder || ''
12499 if (this.disabled) {
12500 input.disabled=true;
12504 input.checked = this.checked;
12508 input.name = this.name;
12512 input.cls += ' input-' + this.size;
12516 ['xs','sm','md','lg'].map(function(size){
12517 if (settings[size]) {
12518 cfg.cls += ' col-' + size + '-' + settings[size];
12522 var inputblock = input;
12524 if (this.before || this.after) {
12527 cls : 'input-group',
12531 inputblock.cn.push({
12533 cls : 'input-group-addon',
12537 inputblock.cn.push(input);
12539 inputblock.cn.push({
12541 cls : 'input-group-addon',
12548 if (align ==='left' && this.fieldLabel.length) {
12549 Roo.log("left and has label");
12555 cls : 'control-label col-md-' + this.labelWidth,
12556 html : this.fieldLabel
12560 cls : "col-md-" + (12 - this.labelWidth),
12567 } else if ( this.fieldLabel.length) {
12572 tag: this.boxLabel ? 'span' : 'label',
12574 cls: 'control-label box-input-label',
12575 //cls : 'input-group-addon',
12576 html : this.fieldLabel
12586 Roo.log(" no label && no align");
12601 html: this.boxLabel
12610 * return the real input element.
12612 inputEl: function ()
12614 return this.el.select('input.form-box',true).first();
12617 initEvents : function()
12619 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12621 this.inputEl().on('click', this.onClick, this);
12625 onClick : function()
12627 this.setChecked(!this.checked);
12630 setChecked : function(state,suppressEvent)
12632 this.checked = state;
12634 this.inputEl().dom.checked = state;
12636 if(suppressEvent !== true){
12637 this.fireEvent('check', this, state);
12640 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12644 setValue : function(v,suppressEvent)
12646 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12660 * @class Roo.bootstrap.Radio
12661 * @extends Roo.bootstrap.CheckBox
12662 * Bootstrap Radio class
12665 * Create a new Radio
12666 * @param {Object} config The config object
12669 Roo.bootstrap.Radio = function(config){
12670 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12674 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12676 inputType: 'radio',
12680 getAutoCreate : function()
12682 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12688 cfg.cls = 'form-group' //input-group
12693 type : this.inputType,
12694 value : (!this.checked) ? this.valueOff : this.inputValue,
12696 placeholder : this.placeholder || ''
12700 if (this.disabled) {
12701 input.disabled=true;
12705 input.checked = this.checked;
12709 input.name = this.name;
12713 input.cls += ' input-' + this.size;
12717 ['xs','sm','md','lg'].map(function(size){
12718 if (settings[size]) {
12719 cfg.cls += ' col-' + size + '-' + settings[size];
12723 var inputblock = input;
12725 if (this.before || this.after) {
12728 cls : 'input-group',
12732 inputblock.cn.push({
12734 cls : 'input-group-addon',
12738 inputblock.cn.push(input);
12740 inputblock.cn.push({
12742 cls : 'input-group-addon',
12749 if (align ==='left' && this.fieldLabel.length) {
12750 Roo.log("left and has label");
12756 cls : 'control-label col-md-' + this.labelWidth,
12757 html : this.fieldLabel
12761 cls : "col-md-" + (12 - this.labelWidth),
12768 } else if ( this.fieldLabel.length) {
12775 cls: 'control-label box-input-label',
12776 //cls : 'input-group-addon',
12777 html : this.fieldLabel
12787 Roo.log(" no label && no align");
12802 html: this.boxLabel
12810 onClick : function()
12812 this.setChecked(true);
12815 setChecked : function(state,suppressEvent)
12818 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12819 v.dom.checked = false;
12823 this.checked = state;
12824 this.inputEl().dom.checked = state;
12826 if(suppressEvent !== true){
12827 this.fireEvent('check', this, state);
12830 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12834 getGroupValue : function()
12837 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12838 if(v.dom.checked == true){
12839 value = v.dom.value;
12847 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12848 * @return {Mixed} value The field value
12850 getValue : function(){
12851 return this.getGroupValue();
12857 //<script type="text/javascript">
12860 * Based Ext JS Library 1.1.1
12861 * Copyright(c) 2006-2007, Ext JS, LLC.
12867 * @class Roo.HtmlEditorCore
12868 * @extends Roo.Component
12869 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12871 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12874 Roo.HtmlEditorCore = function(config){
12877 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12880 * @event initialize
12881 * Fires when the editor is fully initialized (including the iframe)
12882 * @param {Roo.HtmlEditorCore} this
12887 * Fires when the editor is first receives the focus. Any insertion must wait
12888 * until after this event.
12889 * @param {Roo.HtmlEditorCore} this
12893 * @event beforesync
12894 * Fires before the textarea is updated with content from the editor iframe. Return false
12895 * to cancel the sync.
12896 * @param {Roo.HtmlEditorCore} this
12897 * @param {String} html
12901 * @event beforepush
12902 * Fires before the iframe editor is updated with content from the textarea. Return false
12903 * to cancel the push.
12904 * @param {Roo.HtmlEditorCore} this
12905 * @param {String} html
12910 * Fires when the textarea is updated with content from the editor iframe.
12911 * @param {Roo.HtmlEditorCore} this
12912 * @param {String} html
12917 * Fires when the iframe editor is updated with content from the textarea.
12918 * @param {Roo.HtmlEditorCore} this
12919 * @param {String} html
12924 * @event editorevent
12925 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12926 * @param {Roo.HtmlEditorCore} this
12934 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12938 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12944 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12949 * @cfg {Number} height (in pixels)
12953 * @cfg {Number} width (in pixels)
12958 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12961 stylesheets: false,
12966 // private properties
12967 validationEvent : false,
12969 initialized : false,
12971 sourceEditMode : false,
12972 onFocus : Roo.emptyFn,
12974 hideMode:'offsets',
12982 * Protected method that will not generally be called directly. It
12983 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12984 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12986 getDocMarkup : function(){
12989 Roo.log(this.stylesheets);
12991 // inherit styels from page...??
12992 if (this.stylesheets === false) {
12994 Roo.get(document.head).select('style').each(function(node) {
12995 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12998 Roo.get(document.head).select('link').each(function(node) {
12999 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13002 } else if (!this.stylesheets.length) {
13004 st = '<style type="text/css">' +
13005 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13008 Roo.each(this.stylesheets, function(s) {
13009 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13014 st += '<style type="text/css">' +
13015 'IMG { cursor: pointer } ' +
13019 return '<html><head>' + st +
13020 //<style type="text/css">' +
13021 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13023 ' </head><body class="roo-htmleditor-body"></body></html>';
13027 onRender : function(ct, position)
13030 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13031 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13034 this.el.dom.style.border = '0 none';
13035 this.el.dom.setAttribute('tabIndex', -1);
13036 this.el.addClass('x-hidden hide');
13040 if(Roo.isIE){ // fix IE 1px bogus margin
13041 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13045 this.frameId = Roo.id();
13049 var iframe = this.owner.wrap.createChild({
13051 cls: 'form-control', // bootstrap..
13053 name: this.frameId,
13054 frameBorder : 'no',
13055 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13060 this.iframe = iframe.dom;
13062 this.assignDocWin();
13064 this.doc.designMode = 'on';
13067 this.doc.write(this.getDocMarkup());
13071 var task = { // must defer to wait for browser to be ready
13073 //console.log("run task?" + this.doc.readyState);
13074 this.assignDocWin();
13075 if(this.doc.body || this.doc.readyState == 'complete'){
13077 this.doc.designMode="on";
13081 Roo.TaskMgr.stop(task);
13082 this.initEditor.defer(10, this);
13089 Roo.TaskMgr.start(task);
13096 onResize : function(w, h)
13098 Roo.log('resize: ' +w + ',' + h );
13099 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13103 if(typeof w == 'number'){
13105 this.iframe.style.width = w + 'px';
13107 if(typeof h == 'number'){
13109 this.iframe.style.height = h + 'px';
13111 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13118 * Toggles the editor between standard and source edit mode.
13119 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13121 toggleSourceEdit : function(sourceEditMode){
13123 this.sourceEditMode = sourceEditMode === true;
13125 if(this.sourceEditMode){
13127 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13130 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13131 //this.iframe.className = '';
13134 //this.setSize(this.owner.wrap.getSize());
13135 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13142 * Protected method that will not generally be called directly. If you need/want
13143 * custom HTML cleanup, this is the method you should override.
13144 * @param {String} html The HTML to be cleaned
13145 * return {String} The cleaned HTML
13147 cleanHtml : function(html){
13148 html = String(html);
13149 if(html.length > 5){
13150 if(Roo.isSafari){ // strip safari nonsense
13151 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13154 if(html == ' '){
13161 * HTML Editor -> Textarea
13162 * Protected method that will not generally be called directly. Syncs the contents
13163 * of the editor iframe with the textarea.
13165 syncValue : function(){
13166 if(this.initialized){
13167 var bd = (this.doc.body || this.doc.documentElement);
13168 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13169 var html = bd.innerHTML;
13171 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13172 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13174 html = '<div style="'+m[0]+'">' + html + '</div>';
13177 html = this.cleanHtml(html);
13178 // fix up the special chars.. normaly like back quotes in word...
13179 // however we do not want to do this with chinese..
13180 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13181 var cc = b.charCodeAt();
13183 (cc >= 0x4E00 && cc < 0xA000 ) ||
13184 (cc >= 0x3400 && cc < 0x4E00 ) ||
13185 (cc >= 0xf900 && cc < 0xfb00 )
13191 if(this.owner.fireEvent('beforesync', this, html) !== false){
13192 this.el.dom.value = html;
13193 this.owner.fireEvent('sync', this, html);
13199 * Protected method that will not generally be called directly. Pushes the value of the textarea
13200 * into the iframe editor.
13202 pushValue : function(){
13203 if(this.initialized){
13204 var v = this.el.dom.value.trim();
13206 // if(v.length < 1){
13210 if(this.owner.fireEvent('beforepush', this, v) !== false){
13211 var d = (this.doc.body || this.doc.documentElement);
13213 this.cleanUpPaste();
13214 this.el.dom.value = d.innerHTML;
13215 this.owner.fireEvent('push', this, v);
13221 deferFocus : function(){
13222 this.focus.defer(10, this);
13226 focus : function(){
13227 if(this.win && !this.sourceEditMode){
13234 assignDocWin: function()
13236 var iframe = this.iframe;
13239 this.doc = iframe.contentWindow.document;
13240 this.win = iframe.contentWindow;
13242 if (!Roo.get(this.frameId)) {
13245 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13246 this.win = Roo.get(this.frameId).dom.contentWindow;
13251 initEditor : function(){
13252 //console.log("INIT EDITOR");
13253 this.assignDocWin();
13257 this.doc.designMode="on";
13259 this.doc.write(this.getDocMarkup());
13262 var dbody = (this.doc.body || this.doc.documentElement);
13263 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13264 // this copies styles from the containing element into thsi one..
13265 // not sure why we need all of this..
13266 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13267 ss['background-attachment'] = 'fixed'; // w3c
13268 dbody.bgProperties = 'fixed'; // ie
13269 Roo.DomHelper.applyStyles(dbody, ss);
13270 Roo.EventManager.on(this.doc, {
13271 //'mousedown': this.onEditorEvent,
13272 'mouseup': this.onEditorEvent,
13273 'dblclick': this.onEditorEvent,
13274 'click': this.onEditorEvent,
13275 'keyup': this.onEditorEvent,
13280 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13282 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13283 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13285 this.initialized = true;
13287 this.owner.fireEvent('initialize', this);
13292 onDestroy : function(){
13298 //for (var i =0; i < this.toolbars.length;i++) {
13299 // // fixme - ask toolbars for heights?
13300 // this.toolbars[i].onDestroy();
13303 //this.wrap.dom.innerHTML = '';
13304 //this.wrap.remove();
13309 onFirstFocus : function(){
13311 this.assignDocWin();
13314 this.activated = true;
13317 if(Roo.isGecko){ // prevent silly gecko errors
13319 var s = this.win.getSelection();
13320 if(!s.focusNode || s.focusNode.nodeType != 3){
13321 var r = s.getRangeAt(0);
13322 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13327 this.execCmd('useCSS', true);
13328 this.execCmd('styleWithCSS', false);
13331 this.owner.fireEvent('activate', this);
13335 adjustFont: function(btn){
13336 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13337 //if(Roo.isSafari){ // safari
13340 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13341 if(Roo.isSafari){ // safari
13342 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13343 v = (v < 10) ? 10 : v;
13344 v = (v > 48) ? 48 : v;
13345 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13350 v = Math.max(1, v+adjust);
13352 this.execCmd('FontSize', v );
13355 onEditorEvent : function(e){
13356 this.owner.fireEvent('editorevent', this, e);
13357 // this.updateToolbar();
13358 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13361 insertTag : function(tg)
13363 // could be a bit smarter... -> wrap the current selected tRoo..
13364 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13366 range = this.createRange(this.getSelection());
13367 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13368 wrappingNode.appendChild(range.extractContents());
13369 range.insertNode(wrappingNode);
13376 this.execCmd("formatblock", tg);
13380 insertText : function(txt)
13384 var range = this.createRange();
13385 range.deleteContents();
13386 //alert(Sender.getAttribute('label'));
13388 range.insertNode(this.doc.createTextNode(txt));
13394 * Executes a Midas editor command on the editor document and performs necessary focus and
13395 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13396 * @param {String} cmd The Midas command
13397 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13399 relayCmd : function(cmd, value){
13401 this.execCmd(cmd, value);
13402 this.owner.fireEvent('editorevent', this);
13403 //this.updateToolbar();
13404 this.owner.deferFocus();
13408 * Executes a Midas editor command directly on the editor document.
13409 * For visual commands, you should use {@link #relayCmd} instead.
13410 * <b>This should only be called after the editor is initialized.</b>
13411 * @param {String} cmd The Midas command
13412 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13414 execCmd : function(cmd, value){
13415 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13422 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13424 * @param {String} text | dom node..
13426 insertAtCursor : function(text)
13431 if(!this.activated){
13437 var r = this.doc.selection.createRange();
13448 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13452 // from jquery ui (MIT licenced)
13454 var win = this.win;
13456 if (win.getSelection && win.getSelection().getRangeAt) {
13457 range = win.getSelection().getRangeAt(0);
13458 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13459 range.insertNode(node);
13460 } else if (win.document.selection && win.document.selection.createRange) {
13461 // no firefox support
13462 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13463 win.document.selection.createRange().pasteHTML(txt);
13465 // no firefox support
13466 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13467 this.execCmd('InsertHTML', txt);
13476 mozKeyPress : function(e){
13478 var c = e.getCharCode(), cmd;
13481 c = String.fromCharCode(c).toLowerCase();
13495 this.cleanUpPaste.defer(100, this);
13503 e.preventDefault();
13511 fixKeys : function(){ // load time branching for fastest keydown performance
13513 return function(e){
13514 var k = e.getKey(), r;
13517 r = this.doc.selection.createRange();
13520 r.pasteHTML('    ');
13527 r = this.doc.selection.createRange();
13529 var target = r.parentElement();
13530 if(!target || target.tagName.toLowerCase() != 'li'){
13532 r.pasteHTML('<br />');
13538 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13539 this.cleanUpPaste.defer(100, this);
13545 }else if(Roo.isOpera){
13546 return function(e){
13547 var k = e.getKey();
13551 this.execCmd('InsertHTML','    ');
13554 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13555 this.cleanUpPaste.defer(100, this);
13560 }else if(Roo.isSafari){
13561 return function(e){
13562 var k = e.getKey();
13566 this.execCmd('InsertText','\t');
13570 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13571 this.cleanUpPaste.defer(100, this);
13579 getAllAncestors: function()
13581 var p = this.getSelectedNode();
13584 a.push(p); // push blank onto stack..
13585 p = this.getParentElement();
13589 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13593 a.push(this.doc.body);
13597 lastSelNode : false,
13600 getSelection : function()
13602 this.assignDocWin();
13603 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13606 getSelectedNode: function()
13608 // this may only work on Gecko!!!
13610 // should we cache this!!!!
13615 var range = this.createRange(this.getSelection()).cloneRange();
13618 var parent = range.parentElement();
13620 var testRange = range.duplicate();
13621 testRange.moveToElementText(parent);
13622 if (testRange.inRange(range)) {
13625 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13628 parent = parent.parentElement;
13633 // is ancestor a text element.
13634 var ac = range.commonAncestorContainer;
13635 if (ac.nodeType == 3) {
13636 ac = ac.parentNode;
13639 var ar = ac.childNodes;
13642 var other_nodes = [];
13643 var has_other_nodes = false;
13644 for (var i=0;i<ar.length;i++) {
13645 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13648 // fullly contained node.
13650 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13655 // probably selected..
13656 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13657 other_nodes.push(ar[i]);
13661 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13666 has_other_nodes = true;
13668 if (!nodes.length && other_nodes.length) {
13669 nodes= other_nodes;
13671 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13677 createRange: function(sel)
13679 // this has strange effects when using with
13680 // top toolbar - not sure if it's a great idea.
13681 //this.editor.contentWindow.focus();
13682 if (typeof sel != "undefined") {
13684 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13686 return this.doc.createRange();
13689 return this.doc.createRange();
13692 getParentElement: function()
13695 this.assignDocWin();
13696 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13698 var range = this.createRange(sel);
13701 var p = range.commonAncestorContainer;
13702 while (p.nodeType == 3) { // text node
13713 * Range intersection.. the hard stuff...
13717 * [ -- selected range --- ]
13721 * if end is before start or hits it. fail.
13722 * if start is after end or hits it fail.
13724 * if either hits (but other is outside. - then it's not
13730 // @see http://www.thismuchiknow.co.uk/?p=64.
13731 rangeIntersectsNode : function(range, node)
13733 var nodeRange = node.ownerDocument.createRange();
13735 nodeRange.selectNode(node);
13737 nodeRange.selectNodeContents(node);
13740 var rangeStartRange = range.cloneRange();
13741 rangeStartRange.collapse(true);
13743 var rangeEndRange = range.cloneRange();
13744 rangeEndRange.collapse(false);
13746 var nodeStartRange = nodeRange.cloneRange();
13747 nodeStartRange.collapse(true);
13749 var nodeEndRange = nodeRange.cloneRange();
13750 nodeEndRange.collapse(false);
13752 return rangeStartRange.compareBoundaryPoints(
13753 Range.START_TO_START, nodeEndRange) == -1 &&
13754 rangeEndRange.compareBoundaryPoints(
13755 Range.START_TO_START, nodeStartRange) == 1;
13759 rangeCompareNode : function(range, node)
13761 var nodeRange = node.ownerDocument.createRange();
13763 nodeRange.selectNode(node);
13765 nodeRange.selectNodeContents(node);
13769 range.collapse(true);
13771 nodeRange.collapse(true);
13773 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13774 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13776 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13778 var nodeIsBefore = ss == 1;
13779 var nodeIsAfter = ee == -1;
13781 if (nodeIsBefore && nodeIsAfter)
13783 if (!nodeIsBefore && nodeIsAfter)
13784 return 1; //right trailed.
13786 if (nodeIsBefore && !nodeIsAfter)
13787 return 2; // left trailed.
13792 // private? - in a new class?
13793 cleanUpPaste : function()
13795 // cleans up the whole document..
13796 Roo.log('cleanuppaste');
13798 this.cleanUpChildren(this.doc.body);
13799 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13800 if (clean != this.doc.body.innerHTML) {
13801 this.doc.body.innerHTML = clean;
13806 cleanWordChars : function(input) {// change the chars to hex code
13807 var he = Roo.HtmlEditorCore;
13809 var output = input;
13810 Roo.each(he.swapCodes, function(sw) {
13811 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13813 output = output.replace(swapper, sw[1]);
13820 cleanUpChildren : function (n)
13822 if (!n.childNodes.length) {
13825 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13826 this.cleanUpChild(n.childNodes[i]);
13833 cleanUpChild : function (node)
13836 //console.log(node);
13837 if (node.nodeName == "#text") {
13838 // clean up silly Windows -- stuff?
13841 if (node.nodeName == "#comment") {
13842 node.parentNode.removeChild(node);
13843 // clean up silly Windows -- stuff?
13847 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13849 node.parentNode.removeChild(node);
13854 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13856 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13857 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13859 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13860 // remove_keep_children = true;
13863 if (remove_keep_children) {
13864 this.cleanUpChildren(node);
13865 // inserts everything just before this node...
13866 while (node.childNodes.length) {
13867 var cn = node.childNodes[0];
13868 node.removeChild(cn);
13869 node.parentNode.insertBefore(cn, node);
13871 node.parentNode.removeChild(node);
13875 if (!node.attributes || !node.attributes.length) {
13876 this.cleanUpChildren(node);
13880 function cleanAttr(n,v)
13883 if (v.match(/^\./) || v.match(/^\//)) {
13886 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13889 if (v.match(/^#/)) {
13892 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13893 node.removeAttribute(n);
13897 function cleanStyle(n,v)
13899 if (v.match(/expression/)) { //XSS?? should we even bother..
13900 node.removeAttribute(n);
13903 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13904 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13907 var parts = v.split(/;/);
13910 Roo.each(parts, function(p) {
13911 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13915 var l = p.split(':').shift().replace(/\s+/g,'');
13916 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13918 if ( cblack.indexOf(l) > -1) {
13919 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13920 //node.removeAttribute(n);
13924 // only allow 'c whitelisted system attributes'
13925 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13926 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13927 //node.removeAttribute(n);
13937 if (clean.length) {
13938 node.setAttribute(n, clean.join(';'));
13940 node.removeAttribute(n);
13946 for (var i = node.attributes.length-1; i > -1 ; i--) {
13947 var a = node.attributes[i];
13950 if (a.name.toLowerCase().substr(0,2)=='on') {
13951 node.removeAttribute(a.name);
13954 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13955 node.removeAttribute(a.name);
13958 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13959 cleanAttr(a.name,a.value); // fixme..
13962 if (a.name == 'style') {
13963 cleanStyle(a.name,a.value);
13966 /// clean up MS crap..
13967 // tecnically this should be a list of valid class'es..
13970 if (a.name == 'class') {
13971 if (a.value.match(/^Mso/)) {
13972 node.className = '';
13975 if (a.value.match(/body/)) {
13976 node.className = '';
13987 this.cleanUpChildren(node);
13993 // hide stuff that is not compatible
14007 * @event specialkey
14011 * @cfg {String} fieldClass @hide
14014 * @cfg {String} focusClass @hide
14017 * @cfg {String} autoCreate @hide
14020 * @cfg {String} inputType @hide
14023 * @cfg {String} invalidClass @hide
14026 * @cfg {String} invalidText @hide
14029 * @cfg {String} msgFx @hide
14032 * @cfg {String} validateOnBlur @hide
14036 Roo.HtmlEditorCore.white = [
14037 'area', 'br', 'img', 'input', 'hr', 'wbr',
14039 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14040 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14041 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14042 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14043 'table', 'ul', 'xmp',
14045 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14048 'dir', 'menu', 'ol', 'ul', 'dl',
14054 Roo.HtmlEditorCore.black = [
14055 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14057 'base', 'basefont', 'bgsound', 'blink', 'body',
14058 'frame', 'frameset', 'head', 'html', 'ilayer',
14059 'iframe', 'layer', 'link', 'meta', 'object',
14060 'script', 'style' ,'title', 'xml' // clean later..
14062 Roo.HtmlEditorCore.clean = [
14063 'script', 'style', 'title', 'xml'
14065 Roo.HtmlEditorCore.remove = [
14070 Roo.HtmlEditorCore.ablack = [
14074 Roo.HtmlEditorCore.aclean = [
14075 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14079 Roo.HtmlEditorCore.pwhite= [
14080 'http', 'https', 'mailto'
14083 // white listed style attributes.
14084 Roo.HtmlEditorCore.cwhite= [
14085 // 'text-align', /// default is to allow most things..
14091 // black listed style attributes.
14092 Roo.HtmlEditorCore.cblack= [
14093 // 'font-size' -- this can be set by the project
14097 Roo.HtmlEditorCore.swapCodes =[
14116 * @class Roo.bootstrap.HtmlEditor
14117 * @extends Roo.bootstrap.TextArea
14118 * Bootstrap HtmlEditor class
14121 * Create a new HtmlEditor
14122 * @param {Object} config The config object
14125 Roo.bootstrap.HtmlEditor = function(config){
14126 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14127 if (!this.toolbars) {
14128 this.toolbars = [];
14130 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14133 * @event initialize
14134 * Fires when the editor is fully initialized (including the iframe)
14135 * @param {HtmlEditor} this
14140 * Fires when the editor is first receives the focus. Any insertion must wait
14141 * until after this event.
14142 * @param {HtmlEditor} this
14146 * @event beforesync
14147 * Fires before the textarea is updated with content from the editor iframe. Return false
14148 * to cancel the sync.
14149 * @param {HtmlEditor} this
14150 * @param {String} html
14154 * @event beforepush
14155 * Fires before the iframe editor is updated with content from the textarea. Return false
14156 * to cancel the push.
14157 * @param {HtmlEditor} this
14158 * @param {String} html
14163 * Fires when the textarea is updated with content from the editor iframe.
14164 * @param {HtmlEditor} this
14165 * @param {String} html
14170 * Fires when the iframe editor is updated with content from the textarea.
14171 * @param {HtmlEditor} this
14172 * @param {String} html
14176 * @event editmodechange
14177 * Fires when the editor switches edit modes
14178 * @param {HtmlEditor} this
14179 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14181 editmodechange: true,
14183 * @event editorevent
14184 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14185 * @param {HtmlEditor} this
14189 * @event firstfocus
14190 * Fires when on first focus - needed by toolbars..
14191 * @param {HtmlEditor} this
14196 * Auto save the htmlEditor value as a file into Events
14197 * @param {HtmlEditor} this
14201 * @event savedpreview
14202 * preview the saved version of htmlEditor
14203 * @param {HtmlEditor} this
14210 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14214 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14219 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14224 * @cfg {Number} height (in pixels)
14228 * @cfg {Number} width (in pixels)
14233 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14236 stylesheets: false,
14241 // private properties
14242 validationEvent : false,
14244 initialized : false,
14247 onFocus : Roo.emptyFn,
14249 hideMode:'offsets',
14252 tbContainer : false,
14254 toolbarContainer :function() {
14255 return this.wrap.select('.x-html-editor-tb',true).first();
14259 * Protected method that will not generally be called directly. It
14260 * is called when the editor creates its toolbar. Override this method if you need to
14261 * add custom toolbar buttons.
14262 * @param {HtmlEditor} editor
14264 createToolbar : function(){
14266 Roo.log("create toolbars");
14268 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14269 this.toolbars[0].render(this.toolbarContainer());
14273 // if (!editor.toolbars || !editor.toolbars.length) {
14274 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14277 // for (var i =0 ; i < editor.toolbars.length;i++) {
14278 // editor.toolbars[i] = Roo.factory(
14279 // typeof(editor.toolbars[i]) == 'string' ?
14280 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14281 // Roo.bootstrap.HtmlEditor);
14282 // editor.toolbars[i].init(editor);
14288 onRender : function(ct, position)
14290 // Roo.log("Call onRender: " + this.xtype);
14292 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14294 this.wrap = this.inputEl().wrap({
14295 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14298 this.editorcore.onRender(ct, position);
14300 if (this.resizable) {
14301 this.resizeEl = new Roo.Resizable(this.wrap, {
14305 minHeight : this.height,
14306 height: this.height,
14307 handles : this.resizable,
14310 resize : function(r, w, h) {
14311 _t.onResize(w,h); // -something
14317 this.createToolbar(this);
14320 if(!this.width && this.resizable){
14321 this.setSize(this.wrap.getSize());
14323 if (this.resizeEl) {
14324 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14325 // should trigger onReize..
14331 onResize : function(w, h)
14333 Roo.log('resize: ' +w + ',' + h );
14334 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14338 if(this.inputEl() ){
14339 if(typeof w == 'number'){
14340 var aw = w - this.wrap.getFrameWidth('lr');
14341 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14344 if(typeof h == 'number'){
14345 var tbh = -11; // fixme it needs to tool bar size!
14346 for (var i =0; i < this.toolbars.length;i++) {
14347 // fixme - ask toolbars for heights?
14348 tbh += this.toolbars[i].el.getHeight();
14349 //if (this.toolbars[i].footer) {
14350 // tbh += this.toolbars[i].footer.el.getHeight();
14358 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14359 ah -= 5; // knock a few pixes off for look..
14360 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14364 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14365 this.editorcore.onResize(ew,eh);
14370 * Toggles the editor between standard and source edit mode.
14371 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14373 toggleSourceEdit : function(sourceEditMode)
14375 this.editorcore.toggleSourceEdit(sourceEditMode);
14377 if(this.editorcore.sourceEditMode){
14378 Roo.log('editor - showing textarea');
14381 // Roo.log(this.syncValue());
14383 this.inputEl().removeClass('hide');
14384 this.inputEl().dom.removeAttribute('tabIndex');
14385 this.inputEl().focus();
14387 Roo.log('editor - hiding textarea');
14389 // Roo.log(this.pushValue());
14392 this.inputEl().addClass('hide');
14393 this.inputEl().dom.setAttribute('tabIndex', -1);
14394 //this.deferFocus();
14397 if(this.resizable){
14398 this.setSize(this.wrap.getSize());
14401 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14404 // private (for BoxComponent)
14405 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14407 // private (for BoxComponent)
14408 getResizeEl : function(){
14412 // private (for BoxComponent)
14413 getPositionEl : function(){
14418 initEvents : function(){
14419 this.originalValue = this.getValue();
14423 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14426 // markInvalid : Roo.emptyFn,
14428 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14431 // clearInvalid : Roo.emptyFn,
14433 setValue : function(v){
14434 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14435 this.editorcore.pushValue();
14440 deferFocus : function(){
14441 this.focus.defer(10, this);
14445 focus : function(){
14446 this.editorcore.focus();
14452 onDestroy : function(){
14458 for (var i =0; i < this.toolbars.length;i++) {
14459 // fixme - ask toolbars for heights?
14460 this.toolbars[i].onDestroy();
14463 this.wrap.dom.innerHTML = '';
14464 this.wrap.remove();
14469 onFirstFocus : function(){
14470 //Roo.log("onFirstFocus");
14471 this.editorcore.onFirstFocus();
14472 for (var i =0; i < this.toolbars.length;i++) {
14473 this.toolbars[i].onFirstFocus();
14479 syncValue : function()
14481 this.editorcore.syncValue();
14484 pushValue : function()
14486 this.editorcore.pushValue();
14490 // hide stuff that is not compatible
14504 * @event specialkey
14508 * @cfg {String} fieldClass @hide
14511 * @cfg {String} focusClass @hide
14514 * @cfg {String} autoCreate @hide
14517 * @cfg {String} inputType @hide
14520 * @cfg {String} invalidClass @hide
14523 * @cfg {String} invalidText @hide
14526 * @cfg {String} msgFx @hide
14529 * @cfg {String} validateOnBlur @hide
14540 * @class Roo.bootstrap.HtmlEditorToolbar1
14545 new Roo.bootstrap.HtmlEditor({
14548 new Roo.bootstrap.HtmlEditorToolbar1({
14549 disable : { fonts: 1 , format: 1, ..., ... , ...],
14555 * @cfg {Object} disable List of elements to disable..
14556 * @cfg {Array} btns List of additional buttons.
14560 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14563 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14566 Roo.apply(this, config);
14568 // default disabled, based on 'good practice'..
14569 this.disable = this.disable || {};
14570 Roo.applyIf(this.disable, {
14573 specialElements : true
14575 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14577 this.editor = config.editor;
14578 this.editorcore = config.editor.editorcore;
14580 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14582 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14583 // dont call parent... till later.
14585 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14591 editorcore : false,
14596 "h1","h2","h3","h4","h5","h6",
14598 "abbr", "acronym", "address", "cite", "samp", "var",
14602 onRender : function(ct, position)
14604 // Roo.log("Call onRender: " + this.xtype);
14606 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14608 this.el.dom.style.marginBottom = '0';
14610 var editorcore = this.editorcore;
14611 var editor= this.editor;
14614 var btn = function(id,cmd , toggle, handler){
14616 var event = toggle ? 'toggle' : 'click';
14621 xns: Roo.bootstrap,
14624 enableToggle:toggle !== false,
14626 pressed : toggle ? false : null,
14629 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14630 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14639 xns: Roo.bootstrap,
14640 glyphicon : 'font',
14644 xns: Roo.bootstrap,
14648 Roo.each(this.formats, function(f) {
14649 style.menu.items.push({
14651 xns: Roo.bootstrap,
14652 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14657 editorcore.insertTag(this.tagname);
14664 children.push(style);
14667 btn('bold',false,true);
14668 btn('italic',false,true);
14669 btn('align-left', 'justifyleft',true);
14670 btn('align-center', 'justifycenter',true);
14671 btn('align-right' , 'justifyright',true);
14672 btn('link', false, false, function(btn) {
14673 //Roo.log("create link?");
14674 var url = prompt(this.createLinkText, this.defaultLinkValue);
14675 if(url && url != 'http:/'+'/'){
14676 this.editorcore.relayCmd('createlink', url);
14679 btn('list','insertunorderedlist',true);
14680 btn('pencil', false,true, function(btn){
14683 this.toggleSourceEdit(btn.pressed);
14689 xns: Roo.bootstrap,
14694 xns: Roo.bootstrap,
14699 cog.menu.items.push({
14701 xns: Roo.bootstrap,
14702 html : Clean styles,
14707 editorcore.insertTag(this.tagname);
14716 this.xtype = 'Navbar';
14718 for(var i=0;i< children.length;i++) {
14720 this.buttons.add(this.addxtypeChild(children[i]));
14724 editor.on('editorevent', this.updateToolbar, this);
14726 onBtnClick : function(id)
14728 this.editorcore.relayCmd(id);
14729 this.editorcore.focus();
14733 * Protected method that will not generally be called directly. It triggers
14734 * a toolbar update by reading the markup state of the current selection in the editor.
14736 updateToolbar: function(){
14738 if(!this.editorcore.activated){
14739 this.editor.onFirstFocus(); // is this neeed?
14743 var btns = this.buttons;
14744 var doc = this.editorcore.doc;
14745 btns.get('bold').setActive(doc.queryCommandState('bold'));
14746 btns.get('italic').setActive(doc.queryCommandState('italic'));
14747 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14749 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14750 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14751 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14753 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14754 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14757 var ans = this.editorcore.getAllAncestors();
14758 if (this.formatCombo) {
14761 var store = this.formatCombo.store;
14762 this.formatCombo.setValue("");
14763 for (var i =0; i < ans.length;i++) {
14764 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14766 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14774 // hides menus... - so this cant be on a menu...
14775 Roo.bootstrap.MenuMgr.hideAll();
14777 Roo.bootstrap.MenuMgr.hideAll();
14778 //this.editorsyncValue();
14780 onFirstFocus: function() {
14781 this.buttons.each(function(item){
14785 toggleSourceEdit : function(sourceEditMode){
14788 if(sourceEditMode){
14789 Roo.log("disabling buttons");
14790 this.buttons.each( function(item){
14791 if(item.cmd != 'pencil'){
14797 Roo.log("enabling buttons");
14798 if(this.editorcore.initialized){
14799 this.buttons.each( function(item){
14805 Roo.log("calling toggole on editor");
14806 // tell the editor that it's been pressed..
14807 this.editor.toggleSourceEdit(sourceEditMode);
14817 * @class Roo.bootstrap.Table.AbstractSelectionModel
14818 * @extends Roo.util.Observable
14819 * Abstract base class for grid SelectionModels. It provides the interface that should be
14820 * implemented by descendant classes. This class should not be directly instantiated.
14823 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14824 this.locked = false;
14825 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14829 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14830 /** @ignore Called by the grid automatically. Do not call directly. */
14831 init : function(grid){
14837 * Locks the selections.
14840 this.locked = true;
14844 * Unlocks the selections.
14846 unlock : function(){
14847 this.locked = false;
14851 * Returns true if the selections are locked.
14852 * @return {Boolean}
14854 isLocked : function(){
14855 return this.locked;
14859 * @class Roo.bootstrap.Table.ColumnModel
14860 * @extends Roo.util.Observable
14861 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14862 * the columns in the table.
14865 * @param {Object} config An Array of column config objects. See this class's
14866 * config objects for details.
14868 Roo.bootstrap.Table.ColumnModel = function(config){
14870 * The config passed into the constructor
14872 this.config = config;
14875 // if no id, create one
14876 // if the column does not have a dataIndex mapping,
14877 // map it to the order it is in the config
14878 for(var i = 0, len = config.length; i < len; i++){
14880 if(typeof c.dataIndex == "undefined"){
14883 if(typeof c.renderer == "string"){
14884 c.renderer = Roo.util.Format[c.renderer];
14886 if(typeof c.id == "undefined"){
14889 // if(c.editor && c.editor.xtype){
14890 // c.editor = Roo.factory(c.editor, Roo.grid);
14892 // if(c.editor && c.editor.isFormField){
14893 // c.editor = new Roo.grid.GridEditor(c.editor);
14896 this.lookup[c.id] = c;
14900 * The width of columns which have no width specified (defaults to 100)
14903 this.defaultWidth = 100;
14906 * Default sortable of columns which have no sortable specified (defaults to false)
14909 this.defaultSortable = false;
14913 * @event widthchange
14914 * Fires when the width of a column changes.
14915 * @param {ColumnModel} this
14916 * @param {Number} columnIndex The column index
14917 * @param {Number} newWidth The new width
14919 "widthchange": true,
14921 * @event headerchange
14922 * Fires when the text of a header changes.
14923 * @param {ColumnModel} this
14924 * @param {Number} columnIndex The column index
14925 * @param {Number} newText The new header text
14927 "headerchange": true,
14929 * @event hiddenchange
14930 * Fires when a column is hidden or "unhidden".
14931 * @param {ColumnModel} this
14932 * @param {Number} columnIndex The column index
14933 * @param {Boolean} hidden true if hidden, false otherwise
14935 "hiddenchange": true,
14937 * @event columnmoved
14938 * Fires when a column is moved.
14939 * @param {ColumnModel} this
14940 * @param {Number} oldIndex
14941 * @param {Number} newIndex
14943 "columnmoved" : true,
14945 * @event columlockchange
14946 * Fires when a column's locked state is changed
14947 * @param {ColumnModel} this
14948 * @param {Number} colIndex
14949 * @param {Boolean} locked true if locked
14951 "columnlockchange" : true
14953 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14955 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14957 * @cfg {String} header The header text to display in the Grid view.
14960 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14961 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14962 * specified, the column's index is used as an index into the Record's data Array.
14965 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14966 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14969 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14970 * Defaults to the value of the {@link #defaultSortable} property.
14971 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14974 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14977 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14980 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14983 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14986 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14987 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14988 * default renderer uses the raw data value.
14991 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14995 * Returns the id of the column at the specified index.
14996 * @param {Number} index The column index
14997 * @return {String} the id
14999 getColumnId : function(index){
15000 return this.config[index].id;
15004 * Returns the column for a specified id.
15005 * @param {String} id The column id
15006 * @return {Object} the column
15008 getColumnById : function(id){
15009 return this.lookup[id];
15014 * Returns the column for a specified dataIndex.
15015 * @param {String} dataIndex The column dataIndex
15016 * @return {Object|Boolean} the column or false if not found
15018 getColumnByDataIndex: function(dataIndex){
15019 var index = this.findColumnIndex(dataIndex);
15020 return index > -1 ? this.config[index] : false;
15024 * Returns the index for a specified column id.
15025 * @param {String} id The column id
15026 * @return {Number} the index, or -1 if not found
15028 getIndexById : function(id){
15029 for(var i = 0, len = this.config.length; i < len; i++){
15030 if(this.config[i].id == id){
15038 * Returns the index for a specified column dataIndex.
15039 * @param {String} dataIndex The column dataIndex
15040 * @return {Number} the index, or -1 if not found
15043 findColumnIndex : function(dataIndex){
15044 for(var i = 0, len = this.config.length; i < len; i++){
15045 if(this.config[i].dataIndex == dataIndex){
15053 moveColumn : function(oldIndex, newIndex){
15054 var c = this.config[oldIndex];
15055 this.config.splice(oldIndex, 1);
15056 this.config.splice(newIndex, 0, c);
15057 this.dataMap = null;
15058 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15061 isLocked : function(colIndex){
15062 return this.config[colIndex].locked === true;
15065 setLocked : function(colIndex, value, suppressEvent){
15066 if(this.isLocked(colIndex) == value){
15069 this.config[colIndex].locked = value;
15070 if(!suppressEvent){
15071 this.fireEvent("columnlockchange", this, colIndex, value);
15075 getTotalLockedWidth : function(){
15076 var totalWidth = 0;
15077 for(var i = 0; i < this.config.length; i++){
15078 if(this.isLocked(i) && !this.isHidden(i)){
15079 this.totalWidth += this.getColumnWidth(i);
15085 getLockedCount : function(){
15086 for(var i = 0, len = this.config.length; i < len; i++){
15087 if(!this.isLocked(i)){
15094 * Returns the number of columns.
15097 getColumnCount : function(visibleOnly){
15098 if(visibleOnly === true){
15100 for(var i = 0, len = this.config.length; i < len; i++){
15101 if(!this.isHidden(i)){
15107 return this.config.length;
15111 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15112 * @param {Function} fn
15113 * @param {Object} scope (optional)
15114 * @return {Array} result
15116 getColumnsBy : function(fn, scope){
15118 for(var i = 0, len = this.config.length; i < len; i++){
15119 var c = this.config[i];
15120 if(fn.call(scope||this, c, i) === true){
15128 * Returns true if the specified column is sortable.
15129 * @param {Number} col The column index
15130 * @return {Boolean}
15132 isSortable : function(col){
15133 if(typeof this.config[col].sortable == "undefined"){
15134 return this.defaultSortable;
15136 return this.config[col].sortable;
15140 * Returns the rendering (formatting) function defined for the column.
15141 * @param {Number} col The column index.
15142 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15144 getRenderer : function(col){
15145 if(!this.config[col].renderer){
15146 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15148 return this.config[col].renderer;
15152 * Sets the rendering (formatting) function for a column.
15153 * @param {Number} col The column index
15154 * @param {Function} fn The function to use to process the cell's raw data
15155 * to return HTML markup for the grid view. The render function is called with
15156 * the following parameters:<ul>
15157 * <li>Data value.</li>
15158 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15159 * <li>css A CSS style string to apply to the table cell.</li>
15160 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15161 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15162 * <li>Row index</li>
15163 * <li>Column index</li>
15164 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15166 setRenderer : function(col, fn){
15167 this.config[col].renderer = fn;
15171 * Returns the width for the specified column.
15172 * @param {Number} col The column index
15175 getColumnWidth : function(col){
15176 return this.config[col].width * 1 || this.defaultWidth;
15180 * Sets the width for a column.
15181 * @param {Number} col The column index
15182 * @param {Number} width The new width
15184 setColumnWidth : function(col, width, suppressEvent){
15185 this.config[col].width = width;
15186 this.totalWidth = null;
15187 if(!suppressEvent){
15188 this.fireEvent("widthchange", this, col, width);
15193 * Returns the total width of all columns.
15194 * @param {Boolean} includeHidden True to include hidden column widths
15197 getTotalWidth : function(includeHidden){
15198 if(!this.totalWidth){
15199 this.totalWidth = 0;
15200 for(var i = 0, len = this.config.length; i < len; i++){
15201 if(includeHidden || !this.isHidden(i)){
15202 this.totalWidth += this.getColumnWidth(i);
15206 return this.totalWidth;
15210 * Returns the header for the specified column.
15211 * @param {Number} col The column index
15214 getColumnHeader : function(col){
15215 return this.config[col].header;
15219 * Sets the header for a column.
15220 * @param {Number} col The column index
15221 * @param {String} header The new header
15223 setColumnHeader : function(col, header){
15224 this.config[col].header = header;
15225 this.fireEvent("headerchange", this, col, header);
15229 * Returns the tooltip for the specified column.
15230 * @param {Number} col The column index
15233 getColumnTooltip : function(col){
15234 return this.config[col].tooltip;
15237 * Sets the tooltip for a column.
15238 * @param {Number} col The column index
15239 * @param {String} tooltip The new tooltip
15241 setColumnTooltip : function(col, tooltip){
15242 this.config[col].tooltip = tooltip;
15246 * Returns the dataIndex for the specified column.
15247 * @param {Number} col The column index
15250 getDataIndex : function(col){
15251 return this.config[col].dataIndex;
15255 * Sets the dataIndex for a column.
15256 * @param {Number} col The column index
15257 * @param {Number} dataIndex The new dataIndex
15259 setDataIndex : function(col, dataIndex){
15260 this.config[col].dataIndex = dataIndex;
15266 * Returns true if the cell is editable.
15267 * @param {Number} colIndex The column index
15268 * @param {Number} rowIndex The row index
15269 * @return {Boolean}
15271 isCellEditable : function(colIndex, rowIndex){
15272 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15276 * Returns the editor defined for the cell/column.
15277 * return false or null to disable editing.
15278 * @param {Number} colIndex The column index
15279 * @param {Number} rowIndex The row index
15282 getCellEditor : function(colIndex, rowIndex){
15283 return this.config[colIndex].editor;
15287 * Sets if a column is editable.
15288 * @param {Number} col The column index
15289 * @param {Boolean} editable True if the column is editable
15291 setEditable : function(col, editable){
15292 this.config[col].editable = editable;
15297 * Returns true if the column is hidden.
15298 * @param {Number} colIndex The column index
15299 * @return {Boolean}
15301 isHidden : function(colIndex){
15302 return this.config[colIndex].hidden;
15307 * Returns true if the column width cannot be changed
15309 isFixed : function(colIndex){
15310 return this.config[colIndex].fixed;
15314 * Returns true if the column can be resized
15315 * @return {Boolean}
15317 isResizable : function(colIndex){
15318 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15321 * Sets if a column is hidden.
15322 * @param {Number} colIndex The column index
15323 * @param {Boolean} hidden True if the column is hidden
15325 setHidden : function(colIndex, hidden){
15326 this.config[colIndex].hidden = hidden;
15327 this.totalWidth = null;
15328 this.fireEvent("hiddenchange", this, colIndex, hidden);
15332 * Sets the editor for a column.
15333 * @param {Number} col The column index
15334 * @param {Object} editor The editor object
15336 setEditor : function(col, editor){
15337 this.config[col].editor = editor;
15341 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15342 if(typeof value == "string" && value.length < 1){
15348 // Alias for backwards compatibility
15349 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15352 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15353 * @class Roo.bootstrap.Table.RowSelectionModel
15354 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15355 * It supports multiple selections and keyboard selection/navigation.
15357 * @param {Object} config
15360 Roo.bootstrap.Table.RowSelectionModel = function(config){
15361 Roo.apply(this, config);
15362 this.selections = new Roo.util.MixedCollection(false, function(o){
15367 this.lastActive = false;
15371 * @event selectionchange
15372 * Fires when the selection changes
15373 * @param {SelectionModel} this
15375 "selectionchange" : true,
15377 * @event afterselectionchange
15378 * Fires after the selection changes (eg. by key press or clicking)
15379 * @param {SelectionModel} this
15381 "afterselectionchange" : true,
15383 * @event beforerowselect
15384 * Fires when a row is selected being selected, return false to cancel.
15385 * @param {SelectionModel} this
15386 * @param {Number} rowIndex The selected index
15387 * @param {Boolean} keepExisting False if other selections will be cleared
15389 "beforerowselect" : true,
15392 * Fires when a row is selected.
15393 * @param {SelectionModel} this
15394 * @param {Number} rowIndex The selected index
15395 * @param {Roo.data.Record} r The record
15397 "rowselect" : true,
15399 * @event rowdeselect
15400 * Fires when a row is deselected.
15401 * @param {SelectionModel} this
15402 * @param {Number} rowIndex The selected index
15404 "rowdeselect" : true
15406 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15407 this.locked = false;
15410 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15412 * @cfg {Boolean} singleSelect
15413 * True to allow selection of only one row at a time (defaults to false)
15415 singleSelect : false,
15418 initEvents : function(){
15420 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15421 this.grid.on("mousedown", this.handleMouseDown, this);
15422 }else{ // allow click to work like normal
15423 this.grid.on("rowclick", this.handleDragableRowClick, this);
15426 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15427 "up" : function(e){
15429 this.selectPrevious(e.shiftKey);
15430 }else if(this.last !== false && this.lastActive !== false){
15431 var last = this.last;
15432 this.selectRange(this.last, this.lastActive-1);
15433 this.grid.getView().focusRow(this.lastActive);
15434 if(last !== false){
15438 this.selectFirstRow();
15440 this.fireEvent("afterselectionchange", this);
15442 "down" : function(e){
15444 this.selectNext(e.shiftKey);
15445 }else if(this.last !== false && this.lastActive !== false){
15446 var last = this.last;
15447 this.selectRange(this.last, this.lastActive+1);
15448 this.grid.getView().focusRow(this.lastActive);
15449 if(last !== false){
15453 this.selectFirstRow();
15455 this.fireEvent("afterselectionchange", this);
15460 var view = this.grid.view;
15461 view.on("refresh", this.onRefresh, this);
15462 view.on("rowupdated", this.onRowUpdated, this);
15463 view.on("rowremoved", this.onRemove, this);
15467 onRefresh : function(){
15468 var ds = this.grid.dataSource, i, v = this.grid.view;
15469 var s = this.selections;
15470 s.each(function(r){
15471 if((i = ds.indexOfId(r.id)) != -1){
15480 onRemove : function(v, index, r){
15481 this.selections.remove(r);
15485 onRowUpdated : function(v, index, r){
15486 if(this.isSelected(r)){
15487 v.onRowSelect(index);
15493 * @param {Array} records The records to select
15494 * @param {Boolean} keepExisting (optional) True to keep existing selections
15496 selectRecords : function(records, keepExisting){
15498 this.clearSelections();
15500 var ds = this.grid.dataSource;
15501 for(var i = 0, len = records.length; i < len; i++){
15502 this.selectRow(ds.indexOf(records[i]), true);
15507 * Gets the number of selected rows.
15510 getCount : function(){
15511 return this.selections.length;
15515 * Selects the first row in the grid.
15517 selectFirstRow : function(){
15522 * Select the last row.
15523 * @param {Boolean} keepExisting (optional) True to keep existing selections
15525 selectLastRow : function(keepExisting){
15526 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15530 * Selects the row immediately following the last selected row.
15531 * @param {Boolean} keepExisting (optional) True to keep existing selections
15533 selectNext : function(keepExisting){
15534 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15535 this.selectRow(this.last+1, keepExisting);
15536 this.grid.getView().focusRow(this.last);
15541 * Selects the row that precedes the last selected row.
15542 * @param {Boolean} keepExisting (optional) True to keep existing selections
15544 selectPrevious : function(keepExisting){
15546 this.selectRow(this.last-1, keepExisting);
15547 this.grid.getView().focusRow(this.last);
15552 * Returns the selected records
15553 * @return {Array} Array of selected records
15555 getSelections : function(){
15556 return [].concat(this.selections.items);
15560 * Returns the first selected record.
15563 getSelected : function(){
15564 return this.selections.itemAt(0);
15569 * Clears all selections.
15571 clearSelections : function(fast){
15572 if(this.locked) return;
15574 var ds = this.grid.dataSource;
15575 var s = this.selections;
15576 s.each(function(r){
15577 this.deselectRow(ds.indexOfId(r.id));
15581 this.selections.clear();
15588 * Selects all rows.
15590 selectAll : function(){
15591 if(this.locked) return;
15592 this.selections.clear();
15593 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15594 this.selectRow(i, true);
15599 * Returns True if there is a selection.
15600 * @return {Boolean}
15602 hasSelection : function(){
15603 return this.selections.length > 0;
15607 * Returns True if the specified row is selected.
15608 * @param {Number/Record} record The record or index of the record to check
15609 * @return {Boolean}
15611 isSelected : function(index){
15612 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15613 return (r && this.selections.key(r.id) ? true : false);
15617 * Returns True if the specified record id is selected.
15618 * @param {String} id The id of record to check
15619 * @return {Boolean}
15621 isIdSelected : function(id){
15622 return (this.selections.key(id) ? true : false);
15626 handleMouseDown : function(e, t){
15627 var view = this.grid.getView(), rowIndex;
15628 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15631 if(e.shiftKey && this.last !== false){
15632 var last = this.last;
15633 this.selectRange(last, rowIndex, e.ctrlKey);
15634 this.last = last; // reset the last
15635 view.focusRow(rowIndex);
15637 var isSelected = this.isSelected(rowIndex);
15638 if(e.button !== 0 && isSelected){
15639 view.focusRow(rowIndex);
15640 }else if(e.ctrlKey && isSelected){
15641 this.deselectRow(rowIndex);
15642 }else if(!isSelected){
15643 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15644 view.focusRow(rowIndex);
15647 this.fireEvent("afterselectionchange", this);
15650 handleDragableRowClick : function(grid, rowIndex, e)
15652 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15653 this.selectRow(rowIndex, false);
15654 grid.view.focusRow(rowIndex);
15655 this.fireEvent("afterselectionchange", this);
15660 * Selects multiple rows.
15661 * @param {Array} rows Array of the indexes of the row to select
15662 * @param {Boolean} keepExisting (optional) True to keep existing selections
15664 selectRows : function(rows, keepExisting){
15666 this.clearSelections();
15668 for(var i = 0, len = rows.length; i < len; i++){
15669 this.selectRow(rows[i], true);
15674 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15675 * @param {Number} startRow The index of the first row in the range
15676 * @param {Number} endRow The index of the last row in the range
15677 * @param {Boolean} keepExisting (optional) True to retain existing selections
15679 selectRange : function(startRow, endRow, keepExisting){
15680 if(this.locked) return;
15682 this.clearSelections();
15684 if(startRow <= endRow){
15685 for(var i = startRow; i <= endRow; i++){
15686 this.selectRow(i, true);
15689 for(var i = startRow; i >= endRow; i--){
15690 this.selectRow(i, true);
15696 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15697 * @param {Number} startRow The index of the first row in the range
15698 * @param {Number} endRow The index of the last row in the range
15700 deselectRange : function(startRow, endRow, preventViewNotify){
15701 if(this.locked) return;
15702 for(var i = startRow; i <= endRow; i++){
15703 this.deselectRow(i, preventViewNotify);
15709 * @param {Number} row The index of the row to select
15710 * @param {Boolean} keepExisting (optional) True to keep existing selections
15712 selectRow : function(index, keepExisting, preventViewNotify){
15713 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15714 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15715 if(!keepExisting || this.singleSelect){
15716 this.clearSelections();
15718 var r = this.grid.dataSource.getAt(index);
15719 this.selections.add(r);
15720 this.last = this.lastActive = index;
15721 if(!preventViewNotify){
15722 this.grid.getView().onRowSelect(index);
15724 this.fireEvent("rowselect", this, index, r);
15725 this.fireEvent("selectionchange", this);
15731 * @param {Number} row The index of the row to deselect
15733 deselectRow : function(index, preventViewNotify){
15734 if(this.locked) return;
15735 if(this.last == index){
15738 if(this.lastActive == index){
15739 this.lastActive = false;
15741 var r = this.grid.dataSource.getAt(index);
15742 this.selections.remove(r);
15743 if(!preventViewNotify){
15744 this.grid.getView().onRowDeselect(index);
15746 this.fireEvent("rowdeselect", this, index);
15747 this.fireEvent("selectionchange", this);
15751 restoreLast : function(){
15753 this.last = this._last;
15758 acceptsNav : function(row, col, cm){
15759 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15763 onEditorKey : function(field, e){
15764 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15769 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15771 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15773 }else if(k == e.ENTER && !e.ctrlKey){
15777 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15779 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15781 }else if(k == e.ESC){
15785 g.startEditing(newCell[0], newCell[1]);
15796 * @class Roo.bootstrap.MessageBar
15797 * @extends Roo.bootstrap.Component
15798 * Bootstrap MessageBar class
15799 * @cfg {String} html contents of the MessageBar
15800 * @cfg {String} weight (info | success | warning | danger) default info
15801 * @cfg {String} beforeClass insert the bar before the given class
15802 * @cfg {Boolean} closable (true | false) default false
15803 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15806 * Create a new Element
15807 * @param {Object} config The config object
15810 Roo.bootstrap.MessageBar = function(config){
15811 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15814 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15820 beforeClass: 'bootstrap-sticky-wrap',
15822 getAutoCreate : function(){
15826 cls: 'alert alert-dismissable alert-' + this.weight,
15831 html: this.html || ''
15837 cfg.cls += ' alert-messages-fixed';
15851 onRender : function(ct, position)
15853 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15856 var cfg = Roo.apply({}, this.getAutoCreate());
15860 cfg.cls += ' ' + this.cls;
15863 cfg.style = this.style;
15865 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15867 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15870 this.el.select('>button.close').on('click', this.hide, this);
15876 if (!this.rendered) {
15882 this.fireEvent('show', this);
15888 if (!this.rendered) {
15894 this.fireEvent('hide', this);
15897 update : function()
15899 // var e = this.el.dom.firstChild;
15901 // if(this.closable){
15902 // e = e.nextSibling;
15905 // e.data = this.html || '';
15907 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';