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 var sortable = e.attr('')
3055 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3056 e.addClass('glyphicon', 'glyphicon-arrow-up');
3060 e.addClass('glyphicon', 'glyphicon-arrow-down');
3063 var tbody = this.el.select('tbody', true).first();
3067 if(this.store.getCount() > 0){
3068 this.store.data.each(function(d){
3074 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3075 var renderer = cm.getRenderer(i);
3076 var config = cm.config[i];
3080 if(typeof(renderer) !== 'undefined'){
3081 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3084 if(typeof(value) === 'object'){
3094 html: (typeof(value) === 'object') ? '' : value
3097 if(typeof(config.width) != 'undefined'){
3098 td.width = config.width;
3105 tbody.createChild(row);
3113 Roo.each(renders, function(r){
3114 _this.renderColumn(r);
3118 // if(this.loadMask){
3119 // this.maskEl.hide();
3123 onBeforeLoad : function()
3125 Roo.log('ds onBeforeLoad');
3129 // if(this.loadMask){
3130 // this.maskEl.show();
3136 this.el.select('tbody', true).first().dom.innerHTML = '';
3139 getSelectionModel : function(){
3141 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3143 return this.selModel;
3146 renderColumn : function(r)
3149 r.cfg.render(Roo.get(r.id));
3152 Roo.each(r.cfg.cn, function(c){
3157 _this.renderColumn(child);
3174 * @class Roo.bootstrap.TableCell
3175 * @extends Roo.bootstrap.Component
3176 * Bootstrap TableCell class
3177 * @cfg {String} html cell contain text
3178 * @cfg {String} cls cell class
3179 * @cfg {String} tag cell tag (td|th) default td
3180 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3181 * @cfg {String} align Aligns the content in a cell
3182 * @cfg {String} axis Categorizes cells
3183 * @cfg {String} bgcolor Specifies the background color of a cell
3184 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3185 * @cfg {Number} colspan Specifies the number of columns a cell should span
3186 * @cfg {String} headers Specifies one or more header cells a cell is related to
3187 * @cfg {Number} height Sets the height of a cell
3188 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3189 * @cfg {Number} rowspan Sets the number of rows a cell should span
3190 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3191 * @cfg {String} valign Vertical aligns the content in a cell
3192 * @cfg {Number} width Specifies the width of a cell
3195 * Create a new TableCell
3196 * @param {Object} config The config object
3199 Roo.bootstrap.TableCell = function(config){
3200 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3203 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3223 getAutoCreate : function(){
3224 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3244 cfg.align=this.align
3250 cfg.bgcolor=this.bgcolor
3253 cfg.charoff=this.charoff
3256 cfg.colspan=this.colspan
3259 cfg.headers=this.headers
3262 cfg.height=this.height
3265 cfg.nowrap=this.nowrap
3268 cfg.rowspan=this.rowspan
3271 cfg.scope=this.scope
3274 cfg.valign=this.valign
3277 cfg.width=this.width
3296 * @class Roo.bootstrap.TableRow
3297 * @extends Roo.bootstrap.Component
3298 * Bootstrap TableRow class
3299 * @cfg {String} cls row class
3300 * @cfg {String} align Aligns the content in a table row
3301 * @cfg {String} bgcolor Specifies a background color for a table row
3302 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3303 * @cfg {String} valign Vertical aligns the content in a table row
3306 * Create a new TableRow
3307 * @param {Object} config The config object
3310 Roo.bootstrap.TableRow = function(config){
3311 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3314 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3322 getAutoCreate : function(){
3323 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3333 cfg.align = this.align;
3336 cfg.bgcolor = this.bgcolor;
3339 cfg.charoff = this.charoff;
3342 cfg.valign = this.valign;
3360 * @class Roo.bootstrap.TableBody
3361 * @extends Roo.bootstrap.Component
3362 * Bootstrap TableBody class
3363 * @cfg {String} cls element class
3364 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3365 * @cfg {String} align Aligns the content inside the element
3366 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3367 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3370 * Create a new TableBody
3371 * @param {Object} config The config object
3374 Roo.bootstrap.TableBody = function(config){
3375 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3378 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3386 getAutoCreate : function(){
3387 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3401 cfg.align = this.align;
3404 cfg.charoff = this.charoff;
3407 cfg.valign = this.valign;
3414 // initEvents : function()
3421 // this.store = Roo.factory(this.store, Roo.data);
3422 // this.store.on('load', this.onLoad, this);
3424 // this.store.load();
3428 // onLoad: function ()
3430 // this.fireEvent('load', this);
3440 * Ext JS Library 1.1.1
3441 * Copyright(c) 2006-2007, Ext JS, LLC.
3443 * Originally Released Under LGPL - original licence link has changed is not relivant.
3446 * <script type="text/javascript">
3449 // as we use this in bootstrap.
3450 Roo.namespace('Roo.form');
3452 * @class Roo.form.Action
3453 * Internal Class used to handle form actions
3455 * @param {Roo.form.BasicForm} el The form element or its id
3456 * @param {Object} config Configuration options
3461 // define the action interface
3462 Roo.form.Action = function(form, options){
3464 this.options = options || {};
3467 * Client Validation Failed
3470 Roo.form.Action.CLIENT_INVALID = 'client';
3472 * Server Validation Failed
3475 Roo.form.Action.SERVER_INVALID = 'server';
3477 * Connect to Server Failed
3480 Roo.form.Action.CONNECT_FAILURE = 'connect';
3482 * Reading Data from Server Failed
3485 Roo.form.Action.LOAD_FAILURE = 'load';
3487 Roo.form.Action.prototype = {
3489 failureType : undefined,
3490 response : undefined,
3494 run : function(options){
3499 success : function(response){
3504 handleResponse : function(response){
3508 // default connection failure
3509 failure : function(response){
3511 this.response = response;
3512 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3513 this.form.afterAction(this, false);
3516 processResponse : function(response){
3517 this.response = response;
3518 if(!response.responseText){
3521 this.result = this.handleResponse(response);
3525 // utility functions used internally
3526 getUrl : function(appendParams){
3527 var url = this.options.url || this.form.url || this.form.el.dom.action;
3529 var p = this.getParams();
3531 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3537 getMethod : function(){
3538 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3541 getParams : function(){
3542 var bp = this.form.baseParams;
3543 var p = this.options.params;
3545 if(typeof p == "object"){
3546 p = Roo.urlEncode(Roo.applyIf(p, bp));
3547 }else if(typeof p == 'string' && bp){
3548 p += '&' + Roo.urlEncode(bp);
3551 p = Roo.urlEncode(bp);
3556 createCallback : function(){
3558 success: this.success,
3559 failure: this.failure,
3561 timeout: (this.form.timeout*1000),
3562 upload: this.form.fileUpload ? this.success : undefined
3567 Roo.form.Action.Submit = function(form, options){
3568 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3571 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3574 haveProgress : false,
3575 uploadComplete : false,
3577 // uploadProgress indicator.
3578 uploadProgress : function()
3580 if (!this.form.progressUrl) {
3584 if (!this.haveProgress) {
3585 Roo.MessageBox.progress("Uploading", "Uploading");
3587 if (this.uploadComplete) {
3588 Roo.MessageBox.hide();
3592 this.haveProgress = true;
3594 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3596 var c = new Roo.data.Connection();
3598 url : this.form.progressUrl,
3603 success : function(req){
3604 //console.log(data);
3608 rdata = Roo.decode(req.responseText)
3610 Roo.log("Invalid data from server..");
3614 if (!rdata || !rdata.success) {
3616 Roo.MessageBox.alert(Roo.encode(rdata));
3619 var data = rdata.data;
3621 if (this.uploadComplete) {
3622 Roo.MessageBox.hide();
3627 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3628 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3631 this.uploadProgress.defer(2000,this);
3634 failure: function(data) {
3635 Roo.log('progress url failed ');
3646 // run get Values on the form, so it syncs any secondary forms.
3647 this.form.getValues();
3649 var o = this.options;
3650 var method = this.getMethod();
3651 var isPost = method == 'POST';
3652 if(o.clientValidation === false || this.form.isValid()){
3654 if (this.form.progressUrl) {
3655 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3656 (new Date() * 1) + '' + Math.random());
3661 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3662 form:this.form.el.dom,
3663 url:this.getUrl(!isPost),
3665 params:isPost ? this.getParams() : null,
3666 isUpload: this.form.fileUpload
3669 this.uploadProgress();
3671 }else if (o.clientValidation !== false){ // client validation failed
3672 this.failureType = Roo.form.Action.CLIENT_INVALID;
3673 this.form.afterAction(this, false);
3677 success : function(response)
3679 this.uploadComplete= true;
3680 if (this.haveProgress) {
3681 Roo.MessageBox.hide();
3685 var result = this.processResponse(response);
3686 if(result === true || result.success){
3687 this.form.afterAction(this, true);
3691 this.form.markInvalid(result.errors);
3692 this.failureType = Roo.form.Action.SERVER_INVALID;
3694 this.form.afterAction(this, false);
3696 failure : function(response)
3698 this.uploadComplete= true;
3699 if (this.haveProgress) {
3700 Roo.MessageBox.hide();
3703 this.response = response;
3704 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3705 this.form.afterAction(this, false);
3708 handleResponse : function(response){
3709 if(this.form.errorReader){
3710 var rs = this.form.errorReader.read(response);
3713 for(var i = 0, len = rs.records.length; i < len; i++) {
3714 var r = rs.records[i];
3718 if(errors.length < 1){
3722 success : rs.success,
3728 ret = Roo.decode(response.responseText);
3732 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3742 Roo.form.Action.Load = function(form, options){
3743 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3744 this.reader = this.form.reader;
3747 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3752 Roo.Ajax.request(Roo.apply(
3753 this.createCallback(), {
3754 method:this.getMethod(),
3755 url:this.getUrl(false),
3756 params:this.getParams()
3760 success : function(response){
3762 var result = this.processResponse(response);
3763 if(result === true || !result.success || !result.data){
3764 this.failureType = Roo.form.Action.LOAD_FAILURE;
3765 this.form.afterAction(this, false);
3768 this.form.clearInvalid();
3769 this.form.setValues(result.data);
3770 this.form.afterAction(this, true);
3773 handleResponse : function(response){
3774 if(this.form.reader){
3775 var rs = this.form.reader.read(response);
3776 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3778 success : rs.success,
3782 return Roo.decode(response.responseText);
3786 Roo.form.Action.ACTION_TYPES = {
3787 'load' : Roo.form.Action.Load,
3788 'submit' : Roo.form.Action.Submit
3797 * @class Roo.bootstrap.Form
3798 * @extends Roo.bootstrap.Component
3799 * Bootstrap Form class
3800 * @cfg {String} method GET | POST (default POST)
3801 * @cfg {String} labelAlign top | left (default top)
3802 * @cfg {String} align left | right - for navbars
3807 * @param {Object} config The config object
3811 Roo.bootstrap.Form = function(config){
3812 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3815 * @event clientvalidation
3816 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3817 * @param {Form} this
3818 * @param {Boolean} valid true if the form has passed client-side validation
3820 clientvalidation: true,
3822 * @event beforeaction
3823 * Fires before any action is performed. Return false to cancel the action.
3824 * @param {Form} this
3825 * @param {Action} action The action to be performed
3829 * @event actionfailed
3830 * Fires when an action fails.
3831 * @param {Form} this
3832 * @param {Action} action The action that failed
3834 actionfailed : true,
3836 * @event actioncomplete
3837 * Fires when an action is completed.
3838 * @param {Form} this
3839 * @param {Action} action The action that completed
3841 actioncomplete : true
3846 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3849 * @cfg {String} method
3850 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3855 * The URL to use for form actions if one isn't supplied in the action options.
3858 * @cfg {Boolean} fileUpload
3859 * Set to true if this form is a file upload.
3863 * @cfg {Object} baseParams
3864 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3868 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3872 * @cfg {Sting} align (left|right) for navbar forms
3877 activeAction : null,
3880 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3881 * element by passing it or its id or mask the form itself by passing in true.
3884 waitMsgTarget : false,
3889 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3890 * element by passing it or its id or mask the form itself by passing in true.
3894 getAutoCreate : function(){
3898 method : this.method || 'POST',
3899 id : this.id || Roo.id(),
3902 if (this.parent().xtype.match(/^Nav/)) {
3903 cfg.cls = 'navbar-form navbar-' + this.align;
3907 if (this.labelAlign == 'left' ) {
3908 cfg.cls += ' form-horizontal';
3914 initEvents : function()
3916 this.el.on('submit', this.onSubmit, this);
3921 onSubmit : function(e){
3926 * Returns true if client-side validation on the form is successful.
3929 isValid : function(){
3930 var items = this.getItems();
3932 items.each(function(f){
3941 * Returns true if any fields in this form have changed since their original load.
3944 isDirty : function(){
3946 var items = this.getItems();
3947 items.each(function(f){
3957 * Performs a predefined action (submit or load) or custom actions you define on this form.
3958 * @param {String} actionName The name of the action type
3959 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3960 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3961 * accept other config options):
3963 Property Type Description
3964 ---------------- --------------- ----------------------------------------------------------------------------------
3965 url String The url for the action (defaults to the form's url)
3966 method String The form method to use (defaults to the form's method, or POST if not defined)
3967 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3968 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3969 validate the form on the client (defaults to false)
3971 * @return {BasicForm} this
3973 doAction : function(action, options){
3974 if(typeof action == 'string'){
3975 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3977 if(this.fireEvent('beforeaction', this, action) !== false){
3978 this.beforeAction(action);
3979 action.run.defer(100, action);
3985 beforeAction : function(action){
3986 var o = action.options;
3988 // not really supported yet.. ??
3990 //if(this.waitMsgTarget === true){
3991 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3992 //}else if(this.waitMsgTarget){
3993 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3994 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3996 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4002 afterAction : function(action, success){
4003 this.activeAction = null;
4004 var o = action.options;
4006 //if(this.waitMsgTarget === true){
4008 //}else if(this.waitMsgTarget){
4009 // this.waitMsgTarget.unmask();
4011 // Roo.MessageBox.updateProgress(1);
4012 // Roo.MessageBox.hide();
4019 Roo.callback(o.success, o.scope, [this, action]);
4020 this.fireEvent('actioncomplete', this, action);
4024 // failure condition..
4025 // we have a scenario where updates need confirming.
4026 // eg. if a locking scenario exists..
4027 // we look for { errors : { needs_confirm : true }} in the response.
4029 (typeof(action.result) != 'undefined') &&
4030 (typeof(action.result.errors) != 'undefined') &&
4031 (typeof(action.result.errors.needs_confirm) != 'undefined')
4034 Roo.log("not supported yet");
4037 Roo.MessageBox.confirm(
4038 "Change requires confirmation",
4039 action.result.errorMsg,
4044 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4054 Roo.callback(o.failure, o.scope, [this, action]);
4055 // show an error message if no failed handler is set..
4056 if (!this.hasListener('actionfailed')) {
4057 Roo.log("need to add dialog support");
4059 Roo.MessageBox.alert("Error",
4060 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4061 action.result.errorMsg :
4062 "Saving Failed, please check your entries or try again"
4067 this.fireEvent('actionfailed', this, action);
4072 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4073 * @param {String} id The value to search for
4076 findField : function(id){
4077 var items = this.getItems();
4078 var field = items.get(id);
4080 items.each(function(f){
4081 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4088 return field || null;
4091 * Mark fields in this form invalid in bulk.
4092 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4093 * @return {BasicForm} this
4095 markInvalid : function(errors){
4096 if(errors instanceof Array){
4097 for(var i = 0, len = errors.length; i < len; i++){
4098 var fieldError = errors[i];
4099 var f = this.findField(fieldError.id);
4101 f.markInvalid(fieldError.msg);
4107 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4108 field.markInvalid(errors[id]);
4112 //Roo.each(this.childForms || [], function (f) {
4113 // f.markInvalid(errors);
4120 * Set values for fields in this form in bulk.
4121 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4122 * @return {BasicForm} this
4124 setValues : function(values){
4125 if(values instanceof Array){ // array of objects
4126 for(var i = 0, len = values.length; i < len; i++){
4128 var f = this.findField(v.id);
4130 f.setValue(v.value);
4131 if(this.trackResetOnLoad){
4132 f.originalValue = f.getValue();
4136 }else{ // object hash
4139 if(typeof values[id] != 'function' && (field = this.findField(id))){
4141 if (field.setFromData &&
4143 field.displayField &&
4144 // combos' with local stores can
4145 // be queried via setValue()
4146 // to set their value..
4147 (field.store && !field.store.isLocal)
4151 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4152 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4153 field.setFromData(sd);
4156 field.setValue(values[id]);
4160 if(this.trackResetOnLoad){
4161 field.originalValue = field.getValue();
4167 //Roo.each(this.childForms || [], function (f) {
4168 // f.setValues(values);
4175 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4176 * they are returned as an array.
4177 * @param {Boolean} asString
4180 getValues : function(asString){
4181 //if (this.childForms) {
4182 // copy values from the child forms
4183 // Roo.each(this.childForms, function (f) {
4184 // this.setValues(f.getValues());
4190 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4191 if(asString === true){
4194 return Roo.urlDecode(fs);
4198 * Returns the fields in this form as an object with key/value pairs.
4199 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4202 getFieldValues : function(with_hidden)
4204 var items = this.getItems();
4206 items.each(function(f){
4210 var v = f.getValue();
4211 if (f.inputType =='radio') {
4212 if (typeof(ret[f.getName()]) == 'undefined') {
4213 ret[f.getName()] = ''; // empty..
4216 if (!f.el.dom.checked) {
4224 // not sure if this supported any more..
4225 if ((typeof(v) == 'object') && f.getRawValue) {
4226 v = f.getRawValue() ; // dates..
4228 // combo boxes where name != hiddenName...
4229 if (f.name != f.getName()) {
4230 ret[f.name] = f.getRawValue();
4232 ret[f.getName()] = v;
4239 * Clears all invalid messages in this form.
4240 * @return {BasicForm} this
4242 clearInvalid : function(){
4243 var items = this.getItems();
4245 items.each(function(f){
4256 * @return {BasicForm} this
4259 var items = this.getItems();
4260 items.each(function(f){
4264 Roo.each(this.childForms || [], function (f) {
4271 getItems : function()
4273 var r=new Roo.util.MixedCollection(false, function(o){
4274 return o.id || (o.id = Roo.id());
4276 var iter = function(el) {
4283 Roo.each(el.items,function(e) {
4302 * Ext JS Library 1.1.1
4303 * Copyright(c) 2006-2007, Ext JS, LLC.
4305 * Originally Released Under LGPL - original licence link has changed is not relivant.
4308 * <script type="text/javascript">
4311 * @class Roo.form.VTypes
4312 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4315 Roo.form.VTypes = function(){
4316 // closure these in so they are only created once.
4317 var alpha = /^[a-zA-Z_]+$/;
4318 var alphanum = /^[a-zA-Z0-9_]+$/;
4319 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4320 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4322 // All these messages and functions are configurable
4325 * The function used to validate email addresses
4326 * @param {String} value The email address
4328 'email' : function(v){
4329 return email.test(v);
4332 * The error text to display when the email validation function returns false
4335 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4337 * The keystroke filter mask to be applied on email input
4340 'emailMask' : /[a-z0-9_\.\-@]/i,
4343 * The function used to validate URLs
4344 * @param {String} value The URL
4346 'url' : function(v){
4350 * The error text to display when the url validation function returns false
4353 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4356 * The function used to validate alpha values
4357 * @param {String} value The value
4359 'alpha' : function(v){
4360 return alpha.test(v);
4363 * The error text to display when the alpha validation function returns false
4366 'alphaText' : 'This field should only contain letters and _',
4368 * The keystroke filter mask to be applied on alpha input
4371 'alphaMask' : /[a-z_]/i,
4374 * The function used to validate alphanumeric values
4375 * @param {String} value The value
4377 'alphanum' : function(v){
4378 return alphanum.test(v);
4381 * The error text to display when the alphanumeric validation function returns false
4384 'alphanumText' : 'This field should only contain letters, numbers and _',
4386 * The keystroke filter mask to be applied on alphanumeric input
4389 'alphanumMask' : /[a-z0-9_]/i
4399 * @class Roo.bootstrap.Input
4400 * @extends Roo.bootstrap.Component
4401 * Bootstrap Input class
4402 * @cfg {Boolean} disabled is it disabled
4403 * @cfg {String} fieldLabel - the label associated
4404 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4405 * @cfg {String} name name of the input
4406 * @cfg {string} fieldLabel - the label associated
4407 * @cfg {string} inputType - input / file submit ...
4408 * @cfg {string} placeholder - placeholder to put in text.
4409 * @cfg {string} before - input group add on before
4410 * @cfg {string} after - input group add on after
4411 * @cfg {string} size - (lg|sm) or leave empty..
4412 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4413 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4414 * @cfg {Number} md colspan out of 12 for computer-sized screens
4415 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4416 * @cfg {string} value default value of the input
4417 * @cfg {Number} labelWidth set the width of label (0-12)
4418 * @cfg {String} labelAlign (top|left)
4419 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4423 * Create a new Input
4424 * @param {Object} config The config object
4427 Roo.bootstrap.Input = function(config){
4428 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4433 * Fires when this field receives input focus.
4434 * @param {Roo.form.Field} this
4439 * Fires when this field loses input focus.
4440 * @param {Roo.form.Field} this
4445 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4446 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4447 * @param {Roo.form.Field} this
4448 * @param {Roo.EventObject} e The event object
4453 * Fires just before the field blurs if the field value has changed.
4454 * @param {Roo.form.Field} this
4455 * @param {Mixed} newValue The new value
4456 * @param {Mixed} oldValue The original value
4461 * Fires after the field has been marked as invalid.
4462 * @param {Roo.form.Field} this
4463 * @param {String} msg The validation message
4468 * Fires after the field has been validated with no errors.
4469 * @param {Roo.form.Field} this
4474 * Fires after the key up
4475 * @param {Roo.form.Field} this
4476 * @param {Roo.EventObject} e The event Object
4482 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4484 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4485 automatic validation (defaults to "keyup").
4487 validationEvent : "keyup",
4489 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4491 validateOnBlur : true,
4493 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4495 validationDelay : 250,
4497 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4499 focusClass : "x-form-focus", // not needed???
4503 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4505 invalidClass : "has-error",
4508 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4510 selectOnFocus : false,
4513 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4517 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4522 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4524 disableKeyFilter : false,
4527 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4531 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4535 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4537 blankText : "This field is required",
4540 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4544 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4546 maxLength : Number.MAX_VALUE,
4548 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4550 minLengthText : "The minimum length for this field is {0}",
4552 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4554 maxLengthText : "The maximum length for this field is {0}",
4558 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4559 * If available, this function will be called only after the basic validators all return true, and will be passed the
4560 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4564 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4565 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4566 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4570 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4593 parentLabelAlign : function()
4596 while (parent.parent()) {
4597 parent = parent.parent();
4598 if (typeof(parent.labelAlign) !='undefined') {
4599 return parent.labelAlign;
4606 getAutoCreate : function(){
4608 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4614 if(this.inputType != 'hidden'){
4615 cfg.cls = 'form-group' //input-group
4621 type : this.inputType,
4623 cls : 'form-control',
4624 placeholder : this.placeholder || ''
4628 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4629 input.maxLength = this.maxLength;
4632 if (this.disabled) {
4633 input.disabled=true;
4636 if (this.readOnly) {
4637 input.readonly=true;
4641 input.name = this.name;
4644 input.cls += ' input-' + this.size;
4647 ['xs','sm','md','lg'].map(function(size){
4648 if (settings[size]) {
4649 cfg.cls += ' col-' + size + '-' + settings[size];
4653 var inputblock = input;
4655 if (this.before || this.after) {
4658 cls : 'input-group',
4662 inputblock.cn.push({
4664 cls : 'input-group-addon',
4668 inputblock.cn.push(input);
4670 inputblock.cn.push({
4672 cls : 'input-group-addon',
4679 if (align ==='left' && this.fieldLabel.length) {
4680 Roo.log("left and has label");
4686 cls : 'control-label col-sm-' + this.labelWidth,
4687 html : this.fieldLabel
4691 cls : "col-sm-" + (12 - this.labelWidth),
4698 } else if ( this.fieldLabel.length) {
4704 //cls : 'input-group-addon',
4705 html : this.fieldLabel
4715 Roo.log(" no label && no align");
4724 Roo.log('input-parentType: ' + this.parentType);
4726 if (this.parentType === 'Navbar' && this.parent().bar) {
4727 cfg.cls += ' navbar-form';
4735 * return the real input element.
4737 inputEl: function ()
4739 return this.el.select('input.form-control',true).first();
4741 setDisabled : function(v)
4743 var i = this.inputEl().dom;
4745 i.removeAttribute('disabled');
4749 i.setAttribute('disabled','true');
4751 initEvents : function()
4754 this.inputEl().on("keydown" , this.fireKey, this);
4755 this.inputEl().on("focus", this.onFocus, this);
4756 this.inputEl().on("blur", this.onBlur, this);
4758 this.inputEl().relayEvent('keyup', this);
4760 // reference to original value for reset
4761 this.originalValue = this.getValue();
4762 //Roo.form.TextField.superclass.initEvents.call(this);
4763 if(this.validationEvent == 'keyup'){
4764 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4765 this.inputEl().on('keyup', this.filterValidation, this);
4767 else if(this.validationEvent !== false){
4768 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4771 if(this.selectOnFocus){
4772 this.on("focus", this.preFocus, this);
4775 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4776 this.inputEl().on("keypress", this.filterKeys, this);
4779 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4780 this.el.on("click", this.autoSize, this);
4783 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4784 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4788 filterValidation : function(e){
4789 if(!e.isNavKeyPress()){
4790 this.validationTask.delay(this.validationDelay);
4794 * Validates the field value
4795 * @return {Boolean} True if the value is valid, else false
4797 validate : function(){
4798 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4799 if(this.disabled || this.validateValue(this.getRawValue())){
4800 this.clearInvalid();
4808 * Validates a value according to the field's validation rules and marks the field as invalid
4809 * if the validation fails
4810 * @param {Mixed} value The value to validate
4811 * @return {Boolean} True if the value is valid, else false
4813 validateValue : function(value){
4814 if(value.length < 1) { // if it's blank
4815 if(this.allowBlank){
4816 this.clearInvalid();
4819 this.markInvalid(this.blankText);
4823 if(value.length < this.minLength){
4824 this.markInvalid(String.format(this.minLengthText, this.minLength));
4827 if(value.length > this.maxLength){
4828 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4832 var vt = Roo.form.VTypes;
4833 if(!vt[this.vtype](value, this)){
4834 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4838 if(typeof this.validator == "function"){
4839 var msg = this.validator(value);
4841 this.markInvalid(msg);
4845 if(this.regex && !this.regex.test(value)){
4846 this.markInvalid(this.regexText);
4855 fireKey : function(e){
4856 //Roo.log('field ' + e.getKey());
4857 if(e.isNavKeyPress()){
4858 this.fireEvent("specialkey", this, e);
4861 focus : function (selectText){
4863 this.inputEl().focus();
4864 if(selectText === true){
4865 this.inputEl().dom.select();
4871 onFocus : function(){
4872 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4873 // this.el.addClass(this.focusClass);
4876 this.hasFocus = true;
4877 this.startValue = this.getValue();
4878 this.fireEvent("focus", this);
4882 beforeBlur : Roo.emptyFn,
4886 onBlur : function(){
4888 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4889 //this.el.removeClass(this.focusClass);
4891 this.hasFocus = false;
4892 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4895 var v = this.getValue();
4896 if(String(v) !== String(this.startValue)){
4897 this.fireEvent('change', this, v, this.startValue);
4899 this.fireEvent("blur", this);
4903 * Resets the current field value to the originally loaded value and clears any validation messages
4906 this.setValue(this.originalValue);
4907 this.clearInvalid();
4910 * Returns the name of the field
4911 * @return {Mixed} name The name field
4913 getName: function(){
4917 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4918 * @return {Mixed} value The field value
4920 getValue : function(){
4921 return this.inputEl().getValue();
4924 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4925 * @return {Mixed} value The field value
4927 getRawValue : function(){
4928 var v = this.inputEl().getValue();
4934 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4935 * @param {Mixed} value The value to set
4937 setRawValue : function(v){
4938 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4941 selectText : function(start, end){
4942 var v = this.getRawValue();
4944 start = start === undefined ? 0 : start;
4945 end = end === undefined ? v.length : end;
4946 var d = this.inputEl().dom;
4947 if(d.setSelectionRange){
4948 d.setSelectionRange(start, end);
4949 }else if(d.createTextRange){
4950 var range = d.createTextRange();
4951 range.moveStart("character", start);
4952 range.moveEnd("character", v.length-end);
4959 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4960 * @param {Mixed} value The value to set
4962 setValue : function(v){
4965 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4971 processValue : function(value){
4972 if(this.stripCharsRe){
4973 var newValue = value.replace(this.stripCharsRe, '');
4974 if(newValue !== value){
4975 this.setRawValue(newValue);
4982 preFocus : function(){
4984 if(this.selectOnFocus){
4985 this.inputEl().dom.select();
4988 filterKeys : function(e){
4990 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4993 var c = e.getCharCode(), cc = String.fromCharCode(c);
4994 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4997 if(!this.maskRe.test(cc)){
5002 * Clear any invalid styles/messages for this field
5004 clearInvalid : function(){
5006 if(!this.el || this.preventMark){ // not rendered
5009 this.el.removeClass(this.invalidClass);
5011 switch(this.msgTarget){
5013 this.el.dom.qtip = '';
5016 this.el.dom.title = '';
5020 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5025 this.errorIcon.dom.qtip = '';
5026 this.errorIcon.hide();
5027 this.un('resize', this.alignErrorIcon, this);
5031 var t = Roo.getDom(this.msgTarget);
5033 t.style.display = 'none';
5037 this.fireEvent('valid', this);
5040 * Mark this field as invalid
5041 * @param {String} msg The validation message
5043 markInvalid : function(msg){
5044 if(!this.el || this.preventMark){ // not rendered
5047 this.el.addClass(this.invalidClass);
5049 msg = msg || this.invalidText;
5050 switch(this.msgTarget){
5052 this.el.dom.qtip = msg;
5053 this.el.dom.qclass = 'x-form-invalid-tip';
5054 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5055 Roo.QuickTips.enable();
5059 this.el.dom.title = msg;
5063 var elp = this.el.findParent('.x-form-element', 5, true);
5064 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5065 this.errorEl.setWidth(elp.getWidth(true)-20);
5067 this.errorEl.update(msg);
5068 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5071 if(!this.errorIcon){
5072 var elp = this.el.findParent('.x-form-element', 5, true);
5073 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5075 this.alignErrorIcon();
5076 this.errorIcon.dom.qtip = msg;
5077 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5078 this.errorIcon.show();
5079 this.on('resize', this.alignErrorIcon, this);
5082 var t = Roo.getDom(this.msgTarget);
5084 t.style.display = this.msgDisplay;
5088 this.fireEvent('invalid', this, msg);
5091 SafariOnKeyDown : function(event)
5093 // this is a workaround for a password hang bug on chrome/ webkit.
5095 var isSelectAll = false;
5097 if(this.inputEl().dom.selectionEnd > 0){
5098 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5100 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5101 event.preventDefault();
5106 if(isSelectAll){ // backspace and delete key
5108 event.preventDefault();
5109 // this is very hacky as keydown always get's upper case.
5111 var cc = String.fromCharCode(event.getCharCode());
5112 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5116 adjustWidth : function(tag, w){
5117 tag = tag.toLowerCase();
5118 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5119 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5123 if(tag == 'textarea'){
5126 }else if(Roo.isOpera){
5130 if(tag == 'textarea'){
5149 * @class Roo.bootstrap.TextArea
5150 * @extends Roo.bootstrap.Input
5151 * Bootstrap TextArea class
5152 * @cfg {Number} cols Specifies the visible width of a text area
5153 * @cfg {Number} rows Specifies the visible number of lines in a text area
5154 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5155 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5156 * @cfg {string} html text
5159 * Create a new TextArea
5160 * @param {Object} config The config object
5163 Roo.bootstrap.TextArea = function(config){
5164 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5168 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5178 getAutoCreate : function(){
5180 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5191 value : this.value || '',
5192 html: this.html || '',
5193 cls : 'form-control',
5194 placeholder : this.placeholder || ''
5198 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5199 input.maxLength = this.maxLength;
5203 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5207 input.cols = this.cols;
5210 if (this.readOnly) {
5211 input.readonly = true;
5215 input.name = this.name;
5219 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5223 ['xs','sm','md','lg'].map(function(size){
5224 if (settings[size]) {
5225 cfg.cls += ' col-' + size + '-' + settings[size];
5229 var inputblock = input;
5231 if (this.before || this.after) {
5234 cls : 'input-group',
5238 inputblock.cn.push({
5240 cls : 'input-group-addon',
5244 inputblock.cn.push(input);
5246 inputblock.cn.push({
5248 cls : 'input-group-addon',
5255 if (align ==='left' && this.fieldLabel.length) {
5256 Roo.log("left and has label");
5262 cls : 'control-label col-sm-' + this.labelWidth,
5263 html : this.fieldLabel
5267 cls : "col-sm-" + (12 - this.labelWidth),
5274 } else if ( this.fieldLabel.length) {
5280 //cls : 'input-group-addon',
5281 html : this.fieldLabel
5291 Roo.log(" no label && no align");
5301 if (this.disabled) {
5302 input.disabled=true;
5309 * return the real textarea element.
5311 inputEl: function ()
5313 return this.el.select('textarea.form-control',true).first();
5321 * trigger field - base class for combo..
5326 * @class Roo.bootstrap.TriggerField
5327 * @extends Roo.bootstrap.Input
5328 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5329 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5330 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5331 * for which you can provide a custom implementation. For example:
5333 var trigger = new Roo.bootstrap.TriggerField();
5334 trigger.onTriggerClick = myTriggerFn;
5335 trigger.applyTo('my-field');
5338 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5339 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5340 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5341 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5343 * Create a new TriggerField.
5344 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5345 * to the base TextField)
5347 Roo.bootstrap.TriggerField = function(config){
5348 this.mimicing = false;
5349 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5352 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5354 * @cfg {String} triggerClass A CSS class to apply to the trigger
5357 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5361 /** @cfg {Boolean} grow @hide */
5362 /** @cfg {Number} growMin @hide */
5363 /** @cfg {Number} growMax @hide */
5369 autoSize: Roo.emptyFn,
5376 actionMode : 'wrap',
5380 getAutoCreate : function(){
5382 var parent = this.parent();
5384 var align = this.parentLabelAlign();
5389 cls: 'form-group' //input-group
5396 type : this.inputType,
5397 cls : 'form-control',
5398 autocomplete: 'off',
5399 placeholder : this.placeholder || ''
5403 input.name = this.name;
5406 input.cls += ' input-' + this.size;
5409 if (this.disabled) {
5410 input.disabled=true;
5413 var inputblock = input;
5415 if (this.before || this.after) {
5418 cls : 'input-group',
5422 inputblock.cn.push({
5424 cls : 'input-group-addon',
5428 inputblock.cn.push(input);
5430 inputblock.cn.push({
5432 cls : 'input-group-addon',
5445 cls: 'form-hidden-field'
5453 Roo.log('multiple');
5461 cls: 'form-hidden-field'
5465 cls: 'select2-choices',
5469 cls: 'select2-search-field',
5482 cls: 'select2-container input-group',
5487 cls: 'typeahead typeahead-long dropdown-menu',
5488 style: 'display:none'
5496 cls : 'input-group-addon btn dropdown-toggle',
5504 cls: 'combobox-clear',
5518 combobox.cls += ' select2-container-multi';
5521 if (align ==='left' && this.fieldLabel.length) {
5523 Roo.log("left and has label");
5529 cls : 'control-label col-sm-' + this.labelWidth,
5530 html : this.fieldLabel
5534 cls : "col-sm-" + (12 - this.labelWidth),
5541 } else if ( this.fieldLabel.length) {
5547 //cls : 'input-group-addon',
5548 html : this.fieldLabel
5558 Roo.log(" no label && no align");
5565 ['xs','sm','md','lg'].map(function(size){
5566 if (settings[size]) {
5567 cfg.cls += ' col-' + size + '-' + settings[size];
5578 onResize : function(w, h){
5579 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5580 // if(typeof w == 'number'){
5581 // var x = w - this.trigger.getWidth();
5582 // this.inputEl().setWidth(this.adjustWidth('input', x));
5583 // this.trigger.setStyle('left', x+'px');
5588 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5591 getResizeEl : function(){
5592 return this.inputEl();
5596 getPositionEl : function(){
5597 return this.inputEl();
5601 alignErrorIcon : function(){
5602 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5606 initEvents : function(){
5608 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5609 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5611 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5612 if(this.hideTrigger){
5613 this.trigger.setDisplayed(false);
5615 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5619 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5622 //this.trigger.addClassOnOver('x-form-trigger-over');
5623 //this.trigger.addClassOnClick('x-form-trigger-click');
5626 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5631 initTrigger : function(){
5636 onDestroy : function(){
5638 this.trigger.removeAllListeners();
5639 // this.trigger.remove();
5642 // this.wrap.remove();
5644 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5648 onFocus : function(){
5649 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5652 this.wrap.addClass('x-trigger-wrap-focus');
5653 this.mimicing = true;
5654 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5655 if(this.monitorTab){
5656 this.el.on("keydown", this.checkTab, this);
5663 checkTab : function(e){
5664 if(e.getKey() == e.TAB){
5670 onBlur : function(){
5675 mimicBlur : function(e, t){
5677 if(!this.wrap.contains(t) && this.validateBlur()){
5684 triggerBlur : function(){
5685 this.mimicing = false;
5686 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5687 if(this.monitorTab){
5688 this.el.un("keydown", this.checkTab, this);
5690 //this.wrap.removeClass('x-trigger-wrap-focus');
5691 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5695 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5696 validateBlur : function(e, t){
5701 onDisable : function(){
5702 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5704 // this.wrap.addClass('x-item-disabled');
5709 onEnable : function(){
5710 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5712 // this.el.removeClass('x-item-disabled');
5717 onShow : function(){
5718 var ae = this.getActionEl();
5721 ae.dom.style.display = '';
5722 ae.dom.style.visibility = 'visible';
5728 onHide : function(){
5729 var ae = this.getActionEl();
5730 ae.dom.style.display = 'none';
5734 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5735 * by an implementing function.
5737 * @param {EventObject} e
5739 onTriggerClick : Roo.emptyFn
5743 * Ext JS Library 1.1.1
5744 * Copyright(c) 2006-2007, Ext JS, LLC.
5746 * Originally Released Under LGPL - original licence link has changed is not relivant.
5749 * <script type="text/javascript">
5754 * @class Roo.data.SortTypes
5756 * Defines the default sorting (casting?) comparison functions used when sorting data.
5758 Roo.data.SortTypes = {
5760 * Default sort that does nothing
5761 * @param {Mixed} s The value being converted
5762 * @return {Mixed} The comparison value
5769 * The regular expression used to strip tags
5773 stripTagsRE : /<\/?[^>]+>/gi,
5776 * Strips all HTML tags to sort on text only
5777 * @param {Mixed} s The value being converted
5778 * @return {String} The comparison value
5780 asText : function(s){
5781 return String(s).replace(this.stripTagsRE, "");
5785 * Strips all HTML tags to sort on text only - Case insensitive
5786 * @param {Mixed} s The value being converted
5787 * @return {String} The comparison value
5789 asUCText : function(s){
5790 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5794 * Case insensitive string
5795 * @param {Mixed} s The value being converted
5796 * @return {String} The comparison value
5798 asUCString : function(s) {
5799 return String(s).toUpperCase();
5804 * @param {Mixed} s The value being converted
5805 * @return {Number} The comparison value
5807 asDate : function(s) {
5811 if(s instanceof Date){
5814 return Date.parse(String(s));
5819 * @param {Mixed} s The value being converted
5820 * @return {Float} The comparison value
5822 asFloat : function(s) {
5823 var val = parseFloat(String(s).replace(/,/g, ""));
5824 if(isNaN(val)) val = 0;
5830 * @param {Mixed} s The value being converted
5831 * @return {Number} The comparison value
5833 asInt : function(s) {
5834 var val = parseInt(String(s).replace(/,/g, ""));
5835 if(isNaN(val)) val = 0;
5840 * Ext JS Library 1.1.1
5841 * Copyright(c) 2006-2007, Ext JS, LLC.
5843 * Originally Released Under LGPL - original licence link has changed is not relivant.
5846 * <script type="text/javascript">
5850 * @class Roo.data.Record
5851 * Instances of this class encapsulate both record <em>definition</em> information, and record
5852 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5853 * to access Records cached in an {@link Roo.data.Store} object.<br>
5855 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5856 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5859 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5861 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5862 * {@link #create}. The parameters are the same.
5863 * @param {Array} data An associative Array of data values keyed by the field name.
5864 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5865 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5866 * not specified an integer id is generated.
5868 Roo.data.Record = function(data, id){
5869 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5874 * Generate a constructor for a specific record layout.
5875 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5876 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5877 * Each field definition object may contain the following properties: <ul>
5878 * <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,
5879 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5880 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5881 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5882 * is being used, then this is a string containing the javascript expression to reference the data relative to
5883 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5884 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5885 * this may be omitted.</p></li>
5886 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5887 * <ul><li>auto (Default, implies no conversion)</li>
5892 * <li>date</li></ul></p></li>
5893 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5894 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5895 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5896 * by the Reader into an object that will be stored in the Record. It is passed the
5897 * following parameters:<ul>
5898 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5900 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5902 * <br>usage:<br><pre><code>
5903 var TopicRecord = Roo.data.Record.create(
5904 {name: 'title', mapping: 'topic_title'},
5905 {name: 'author', mapping: 'username'},
5906 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5907 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5908 {name: 'lastPoster', mapping: 'user2'},
5909 {name: 'excerpt', mapping: 'post_text'}
5912 var myNewRecord = new TopicRecord({
5913 title: 'Do my job please',
5916 lastPost: new Date(),
5917 lastPoster: 'Animal',
5918 excerpt: 'No way dude!'
5920 myStore.add(myNewRecord);
5925 Roo.data.Record.create = function(o){
5927 f.superclass.constructor.apply(this, arguments);
5929 Roo.extend(f, Roo.data.Record);
5930 var p = f.prototype;
5931 p.fields = new Roo.util.MixedCollection(false, function(field){
5934 for(var i = 0, len = o.length; i < len; i++){
5935 p.fields.add(new Roo.data.Field(o[i]));
5937 f.getField = function(name){
5938 return p.fields.get(name);
5943 Roo.data.Record.AUTO_ID = 1000;
5944 Roo.data.Record.EDIT = 'edit';
5945 Roo.data.Record.REJECT = 'reject';
5946 Roo.data.Record.COMMIT = 'commit';
5948 Roo.data.Record.prototype = {
5950 * Readonly flag - true if this record has been modified.
5959 join : function(store){
5964 * Set the named field to the specified value.
5965 * @param {String} name The name of the field to set.
5966 * @param {Object} value The value to set the field to.
5968 set : function(name, value){
5969 if(this.data[name] == value){
5976 if(typeof this.modified[name] == 'undefined'){
5977 this.modified[name] = this.data[name];
5979 this.data[name] = value;
5980 if(!this.editing && this.store){
5981 this.store.afterEdit(this);
5986 * Get the value of the named field.
5987 * @param {String} name The name of the field to get the value of.
5988 * @return {Object} The value of the field.
5990 get : function(name){
5991 return this.data[name];
5995 beginEdit : function(){
5996 this.editing = true;
6001 cancelEdit : function(){
6002 this.editing = false;
6003 delete this.modified;
6007 endEdit : function(){
6008 this.editing = false;
6009 if(this.dirty && this.store){
6010 this.store.afterEdit(this);
6015 * Usually called by the {@link Roo.data.Store} which owns the Record.
6016 * Rejects all changes made to the Record since either creation, or the last commit operation.
6017 * Modified fields are reverted to their original values.
6019 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6020 * of reject operations.
6022 reject : function(){
6023 var m = this.modified;
6025 if(typeof m[n] != "function"){
6026 this.data[n] = m[n];
6030 delete this.modified;
6031 this.editing = false;
6033 this.store.afterReject(this);
6038 * Usually called by the {@link Roo.data.Store} which owns the Record.
6039 * Commits all changes made to the Record since either creation, or the last commit operation.
6041 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6042 * of commit operations.
6044 commit : function(){
6046 delete this.modified;
6047 this.editing = false;
6049 this.store.afterCommit(this);
6054 hasError : function(){
6055 return this.error != null;
6059 clearError : function(){
6064 * Creates a copy of this record.
6065 * @param {String} id (optional) A new record id if you don't want to use this record's id
6068 copy : function(newId) {
6069 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6073 * Ext JS Library 1.1.1
6074 * Copyright(c) 2006-2007, Ext JS, LLC.
6076 * Originally Released Under LGPL - original licence link has changed is not relivant.
6079 * <script type="text/javascript">
6085 * @class Roo.data.Store
6086 * @extends Roo.util.Observable
6087 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6088 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6090 * 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
6091 * has no knowledge of the format of the data returned by the Proxy.<br>
6093 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6094 * instances from the data object. These records are cached and made available through accessor functions.
6096 * Creates a new Store.
6097 * @param {Object} config A config object containing the objects needed for the Store to access data,
6098 * and read the data into Records.
6100 Roo.data.Store = function(config){
6101 this.data = new Roo.util.MixedCollection(false);
6102 this.data.getKey = function(o){
6105 this.baseParams = {};
6112 "multisort" : "_multisort"
6115 if(config && config.data){
6116 this.inlineData = config.data;
6120 Roo.apply(this, config);
6122 if(this.reader){ // reader passed
6123 this.reader = Roo.factory(this.reader, Roo.data);
6124 this.reader.xmodule = this.xmodule || false;
6125 if(!this.recordType){
6126 this.recordType = this.reader.recordType;
6128 if(this.reader.onMetaChange){
6129 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6133 if(this.recordType){
6134 this.fields = this.recordType.prototype.fields;
6140 * @event datachanged
6141 * Fires when the data cache has changed, and a widget which is using this Store
6142 * as a Record cache should refresh its view.
6143 * @param {Store} this
6148 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6149 * @param {Store} this
6150 * @param {Object} meta The JSON metadata
6155 * Fires when Records have been added to the Store
6156 * @param {Store} this
6157 * @param {Roo.data.Record[]} records The array of Records added
6158 * @param {Number} index The index at which the record(s) were added
6163 * Fires when a Record has been removed from the Store
6164 * @param {Store} this
6165 * @param {Roo.data.Record} record The Record that was removed
6166 * @param {Number} index The index at which the record was removed
6171 * Fires when a Record has been updated
6172 * @param {Store} this
6173 * @param {Roo.data.Record} record The Record that was updated
6174 * @param {String} operation The update operation being performed. Value may be one of:
6176 Roo.data.Record.EDIT
6177 Roo.data.Record.REJECT
6178 Roo.data.Record.COMMIT
6184 * Fires when the data cache has been cleared.
6185 * @param {Store} this
6190 * Fires before a request is made for a new data object. If the beforeload handler returns false
6191 * the load action will be canceled.
6192 * @param {Store} this
6193 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6197 * @event beforeloadadd
6198 * Fires after a new set of Records has been loaded.
6199 * @param {Store} this
6200 * @param {Roo.data.Record[]} records The Records that were loaded
6201 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6203 beforeloadadd : true,
6206 * Fires after a new set of Records has been loaded, before they are added to the store.
6207 * @param {Store} this
6208 * @param {Roo.data.Record[]} records The Records that were loaded
6209 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6210 * @params {Object} return from reader
6214 * @event loadexception
6215 * Fires if an exception occurs in the Proxy during loading.
6216 * Called with the signature of the Proxy's "loadexception" event.
6217 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6220 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6221 * @param {Object} load options
6222 * @param {Object} jsonData from your request (normally this contains the Exception)
6224 loadexception : true
6228 this.proxy = Roo.factory(this.proxy, Roo.data);
6229 this.proxy.xmodule = this.xmodule || false;
6230 this.relayEvents(this.proxy, ["loadexception"]);
6232 this.sortToggle = {};
6233 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6235 Roo.data.Store.superclass.constructor.call(this);
6237 if(this.inlineData){
6238 this.loadData(this.inlineData);
6239 delete this.inlineData;
6243 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6245 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6246 * without a remote query - used by combo/forms at present.
6250 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6253 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6256 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6257 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6260 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6261 * on any HTTP request
6264 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6267 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6271 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6272 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6277 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6278 * loaded or when a record is removed. (defaults to false).
6280 pruneModifiedRecords : false,
6286 * Add Records to the Store and fires the add event.
6287 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6289 add : function(records){
6290 records = [].concat(records);
6291 for(var i = 0, len = records.length; i < len; i++){
6292 records[i].join(this);
6294 var index = this.data.length;
6295 this.data.addAll(records);
6296 this.fireEvent("add", this, records, index);
6300 * Remove a Record from the Store and fires the remove event.
6301 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6303 remove : function(record){
6304 var index = this.data.indexOf(record);
6305 this.data.removeAt(index);
6306 if(this.pruneModifiedRecords){
6307 this.modified.remove(record);
6309 this.fireEvent("remove", this, record, index);
6313 * Remove all Records from the Store and fires the clear event.
6315 removeAll : function(){
6317 if(this.pruneModifiedRecords){
6320 this.fireEvent("clear", this);
6324 * Inserts Records to the Store at the given index and fires the add event.
6325 * @param {Number} index The start index at which to insert the passed Records.
6326 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6328 insert : function(index, records){
6329 records = [].concat(records);
6330 for(var i = 0, len = records.length; i < len; i++){
6331 this.data.insert(index, records[i]);
6332 records[i].join(this);
6334 this.fireEvent("add", this, records, index);
6338 * Get the index within the cache of the passed Record.
6339 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6340 * @return {Number} The index of the passed Record. Returns -1 if not found.
6342 indexOf : function(record){
6343 return this.data.indexOf(record);
6347 * Get the index within the cache of the Record with the passed id.
6348 * @param {String} id The id of the Record to find.
6349 * @return {Number} The index of the Record. Returns -1 if not found.
6351 indexOfId : function(id){
6352 return this.data.indexOfKey(id);
6356 * Get the Record with the specified id.
6357 * @param {String} id The id of the Record to find.
6358 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6360 getById : function(id){
6361 return this.data.key(id);
6365 * Get the Record at the specified index.
6366 * @param {Number} index The index of the Record to find.
6367 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6369 getAt : function(index){
6370 return this.data.itemAt(index);
6374 * Returns a range of Records between specified indices.
6375 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6376 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6377 * @return {Roo.data.Record[]} An array of Records
6379 getRange : function(start, end){
6380 return this.data.getRange(start, end);
6384 storeOptions : function(o){
6385 o = Roo.apply({}, o);
6388 this.lastOptions = o;
6392 * Loads the Record cache from the configured Proxy using the configured Reader.
6394 * If using remote paging, then the first load call must specify the <em>start</em>
6395 * and <em>limit</em> properties in the options.params property to establish the initial
6396 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6398 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6399 * and this call will return before the new data has been loaded. Perform any post-processing
6400 * in a callback function, or in a "load" event handler.</strong>
6402 * @param {Object} options An object containing properties which control loading options:<ul>
6403 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6404 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6405 * passed the following arguments:<ul>
6406 * <li>r : Roo.data.Record[]</li>
6407 * <li>options: Options object from the load call</li>
6408 * <li>success: Boolean success indicator</li></ul></li>
6409 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6410 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6413 load : function(options){
6414 options = options || {};
6415 if(this.fireEvent("beforeload", this, options) !== false){
6416 this.storeOptions(options);
6417 var p = Roo.apply(options.params || {}, this.baseParams);
6418 // if meta was not loaded from remote source.. try requesting it.
6419 if (!this.reader.metaFromRemote) {
6422 if(this.sortInfo && this.remoteSort){
6423 var pn = this.paramNames;
6424 p[pn["sort"]] = this.sortInfo.field;
6425 p[pn["dir"]] = this.sortInfo.direction;
6427 if (this.multiSort) {
6428 var pn = this.paramNames;
6429 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6432 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6437 * Reloads the Record cache from the configured Proxy using the configured Reader and
6438 * the options from the last load operation performed.
6439 * @param {Object} options (optional) An object containing properties which may override the options
6440 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6441 * the most recently used options are reused).
6443 reload : function(options){
6444 this.load(Roo.applyIf(options||{}, this.lastOptions));
6448 // Called as a callback by the Reader during a load operation.
6449 loadRecords : function(o, options, success){
6450 if(!o || success === false){
6451 if(success !== false){
6452 this.fireEvent("load", this, [], options, o);
6454 if(options.callback){
6455 options.callback.call(options.scope || this, [], options, false);
6459 // if data returned failure - throw an exception.
6460 if (o.success === false) {
6461 // show a message if no listener is registered.
6462 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6463 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6465 // loadmask wil be hooked into this..
6466 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6469 var r = o.records, t = o.totalRecords || r.length;
6471 this.fireEvent("beforeloadadd", this, r, options, o);
6473 if(!options || options.add !== true){
6474 if(this.pruneModifiedRecords){
6477 for(var i = 0, len = r.length; i < len; i++){
6481 this.data = this.snapshot;
6482 delete this.snapshot;
6485 this.data.addAll(r);
6486 this.totalLength = t;
6488 this.fireEvent("datachanged", this);
6490 this.totalLength = Math.max(t, this.data.length+r.length);
6493 this.fireEvent("load", this, r, options, o);
6494 if(options.callback){
6495 options.callback.call(options.scope || this, r, options, true);
6501 * Loads data from a passed data block. A Reader which understands the format of the data
6502 * must have been configured in the constructor.
6503 * @param {Object} data The data block from which to read the Records. The format of the data expected
6504 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6505 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6507 loadData : function(o, append){
6508 var r = this.reader.readRecords(o);
6509 this.loadRecords(r, {add: append}, true);
6513 * Gets the number of cached records.
6515 * <em>If using paging, this may not be the total size of the dataset. If the data object
6516 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6517 * the data set size</em>
6519 getCount : function(){
6520 return this.data.length || 0;
6524 * Gets the total number of records in the dataset as returned by the server.
6526 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6527 * the dataset size</em>
6529 getTotalCount : function(){
6530 return this.totalLength || 0;
6534 * Returns the sort state of the Store as an object with two properties:
6536 field {String} The name of the field by which the Records are sorted
6537 direction {String} The sort order, "ASC" or "DESC"
6540 getSortState : function(){
6541 return this.sortInfo;
6545 applySort : function(){
6546 if(this.sortInfo && !this.remoteSort){
6547 var s = this.sortInfo, f = s.field;
6548 var st = this.fields.get(f).sortType;
6549 var fn = function(r1, r2){
6550 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6551 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6553 this.data.sort(s.direction, fn);
6554 if(this.snapshot && this.snapshot != this.data){
6555 this.snapshot.sort(s.direction, fn);
6561 * Sets the default sort column and order to be used by the next load operation.
6562 * @param {String} fieldName The name of the field to sort by.
6563 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6565 setDefaultSort : function(field, dir){
6566 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6571 * If remote sorting is used, the sort is performed on the server, and the cache is
6572 * reloaded. If local sorting is used, the cache is sorted internally.
6573 * @param {String} fieldName The name of the field to sort by.
6574 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6576 sort : function(fieldName, dir){
6577 var f = this.fields.get(fieldName);
6579 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6581 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6582 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6587 this.sortToggle[f.name] = dir;
6588 this.sortInfo = {field: f.name, direction: dir};
6589 if(!this.remoteSort){
6591 this.fireEvent("datachanged", this);
6593 this.load(this.lastOptions);
6598 * Calls the specified function for each of the Records in the cache.
6599 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6600 * Returning <em>false</em> aborts and exits the iteration.
6601 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6603 each : function(fn, scope){
6604 this.data.each(fn, scope);
6608 * Gets all records modified since the last commit. Modified records are persisted across load operations
6609 * (e.g., during paging).
6610 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6612 getModifiedRecords : function(){
6613 return this.modified;
6617 createFilterFn : function(property, value, anyMatch){
6618 if(!value.exec){ // not a regex
6619 value = String(value);
6620 if(value.length == 0){
6623 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6626 return value.test(r.data[property]);
6631 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6632 * @param {String} property A field on your records
6633 * @param {Number} start The record index to start at (defaults to 0)
6634 * @param {Number} end The last record index to include (defaults to length - 1)
6635 * @return {Number} The sum
6637 sum : function(property, start, end){
6638 var rs = this.data.items, v = 0;
6640 end = (end || end === 0) ? end : rs.length-1;
6642 for(var i = start; i <= end; i++){
6643 v += (rs[i].data[property] || 0);
6649 * Filter the records by a specified property.
6650 * @param {String} field A field on your records
6651 * @param {String/RegExp} value Either a string that the field
6652 * should start with or a RegExp to test against the field
6653 * @param {Boolean} anyMatch True to match any part not just the beginning
6655 filter : function(property, value, anyMatch){
6656 var fn = this.createFilterFn(property, value, anyMatch);
6657 return fn ? this.filterBy(fn) : this.clearFilter();
6661 * Filter by a function. The specified function will be called with each
6662 * record in this data source. If the function returns true the record is included,
6663 * otherwise it is filtered.
6664 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6665 * @param {Object} scope (optional) The scope of the function (defaults to this)
6667 filterBy : function(fn, scope){
6668 this.snapshot = this.snapshot || this.data;
6669 this.data = this.queryBy(fn, scope||this);
6670 this.fireEvent("datachanged", this);
6674 * Query the records by a specified property.
6675 * @param {String} field A field on your records
6676 * @param {String/RegExp} value Either a string that the field
6677 * should start with or a RegExp to test against the field
6678 * @param {Boolean} anyMatch True to match any part not just the beginning
6679 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6681 query : function(property, value, anyMatch){
6682 var fn = this.createFilterFn(property, value, anyMatch);
6683 return fn ? this.queryBy(fn) : this.data.clone();
6687 * Query by a function. The specified function will be called with each
6688 * record in this data source. If the function returns true the record is included
6690 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6691 * @param {Object} scope (optional) The scope of the function (defaults to this)
6692 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6694 queryBy : function(fn, scope){
6695 var data = this.snapshot || this.data;
6696 return data.filterBy(fn, scope||this);
6700 * Collects unique values for a particular dataIndex from this store.
6701 * @param {String} dataIndex The property to collect
6702 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6703 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6704 * @return {Array} An array of the unique values
6706 collect : function(dataIndex, allowNull, bypassFilter){
6707 var d = (bypassFilter === true && this.snapshot) ?
6708 this.snapshot.items : this.data.items;
6709 var v, sv, r = [], l = {};
6710 for(var i = 0, len = d.length; i < len; i++){
6711 v = d[i].data[dataIndex];
6713 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6722 * Revert to a view of the Record cache with no filtering applied.
6723 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6725 clearFilter : function(suppressEvent){
6726 if(this.snapshot && this.snapshot != this.data){
6727 this.data = this.snapshot;
6728 delete this.snapshot;
6729 if(suppressEvent !== true){
6730 this.fireEvent("datachanged", this);
6736 afterEdit : function(record){
6737 if(this.modified.indexOf(record) == -1){
6738 this.modified.push(record);
6740 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6744 afterReject : function(record){
6745 this.modified.remove(record);
6746 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6750 afterCommit : function(record){
6751 this.modified.remove(record);
6752 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6756 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6757 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6759 commitChanges : function(){
6760 var m = this.modified.slice(0);
6762 for(var i = 0, len = m.length; i < len; i++){
6768 * Cancel outstanding changes on all changed records.
6770 rejectChanges : function(){
6771 var m = this.modified.slice(0);
6773 for(var i = 0, len = m.length; i < len; i++){
6778 onMetaChange : function(meta, rtype, o){
6779 this.recordType = rtype;
6780 this.fields = rtype.prototype.fields;
6781 delete this.snapshot;
6782 this.sortInfo = meta.sortInfo || this.sortInfo;
6784 this.fireEvent('metachange', this, this.reader.meta);
6787 moveIndex : function(data, type)
6789 var index = this.indexOf(data);
6791 var newIndex = index + type;
6795 this.insert(newIndex, data);
6800 * Ext JS Library 1.1.1
6801 * Copyright(c) 2006-2007, Ext JS, LLC.
6803 * Originally Released Under LGPL - original licence link has changed is not relivant.
6806 * <script type="text/javascript">
6810 * @class Roo.data.SimpleStore
6811 * @extends Roo.data.Store
6812 * Small helper class to make creating Stores from Array data easier.
6813 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6814 * @cfg {Array} fields An array of field definition objects, or field name strings.
6815 * @cfg {Array} data The multi-dimensional array of data
6817 * @param {Object} config
6819 Roo.data.SimpleStore = function(config){
6820 Roo.data.SimpleStore.superclass.constructor.call(this, {
6822 reader: new Roo.data.ArrayReader({
6825 Roo.data.Record.create(config.fields)
6827 proxy : new Roo.data.MemoryProxy(config.data)
6831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6833 * Ext JS Library 1.1.1
6834 * Copyright(c) 2006-2007, Ext JS, LLC.
6836 * Originally Released Under LGPL - original licence link has changed is not relivant.
6839 * <script type="text/javascript">
6844 * @extends Roo.data.Store
6845 * @class Roo.data.JsonStore
6846 * Small helper class to make creating Stores for JSON data easier. <br/>
6848 var store = new Roo.data.JsonStore({
6849 url: 'get-images.php',
6851 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6854 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6855 * JsonReader and HttpProxy (unless inline data is provided).</b>
6856 * @cfg {Array} fields An array of field definition objects, or field name strings.
6858 * @param {Object} config
6860 Roo.data.JsonStore = function(c){
6861 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6862 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6863 reader: new Roo.data.JsonReader(c, c.fields)
6866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6868 * Ext JS Library 1.1.1
6869 * Copyright(c) 2006-2007, Ext JS, LLC.
6871 * Originally Released Under LGPL - original licence link has changed is not relivant.
6874 * <script type="text/javascript">
6878 Roo.data.Field = function(config){
6879 if(typeof config == "string"){
6880 config = {name: config};
6882 Roo.apply(this, config);
6888 var st = Roo.data.SortTypes;
6889 // named sortTypes are supported, here we look them up
6890 if(typeof this.sortType == "string"){
6891 this.sortType = st[this.sortType];
6894 // set default sortType for strings and dates
6898 this.sortType = st.asUCString;
6901 this.sortType = st.asDate;
6904 this.sortType = st.none;
6909 var stripRe = /[\$,%]/g;
6911 // prebuilt conversion function for this field, instead of
6912 // switching every time we're reading a value
6914 var cv, dateFormat = this.dateFormat;
6919 cv = function(v){ return v; };
6922 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6926 return v !== undefined && v !== null && v !== '' ?
6927 parseInt(String(v).replace(stripRe, ""), 10) : '';
6932 return v !== undefined && v !== null && v !== '' ?
6933 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6938 cv = function(v){ return v === true || v === "true" || v == 1; };
6945 if(v instanceof Date){
6949 if(dateFormat == "timestamp"){
6950 return new Date(v*1000);
6952 return Date.parseDate(v, dateFormat);
6954 var parsed = Date.parse(v);
6955 return parsed ? new Date(parsed) : null;
6964 Roo.data.Field.prototype = {
6972 * Ext JS Library 1.1.1
6973 * Copyright(c) 2006-2007, Ext JS, LLC.
6975 * Originally Released Under LGPL - original licence link has changed is not relivant.
6978 * <script type="text/javascript">
6981 // Base class for reading structured data from a data source. This class is intended to be
6982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6985 * @class Roo.data.DataReader
6986 * Base class for reading structured data from a data source. This class is intended to be
6987 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6990 Roo.data.DataReader = function(meta, recordType){
6994 this.recordType = recordType instanceof Array ?
6995 Roo.data.Record.create(recordType) : recordType;
6998 Roo.data.DataReader.prototype = {
7000 * Create an empty record
7001 * @param {Object} data (optional) - overlay some values
7002 * @return {Roo.data.Record} record created.
7004 newRow : function(d) {
7006 this.recordType.prototype.fields.each(function(c) {
7008 case 'int' : da[c.name] = 0; break;
7009 case 'date' : da[c.name] = new Date(); break;
7010 case 'float' : da[c.name] = 0.0; break;
7011 case 'boolean' : da[c.name] = false; break;
7012 default : da[c.name] = ""; break;
7016 return new this.recordType(Roo.apply(da, d));
7021 * Ext JS Library 1.1.1
7022 * Copyright(c) 2006-2007, Ext JS, LLC.
7024 * Originally Released Under LGPL - original licence link has changed is not relivant.
7027 * <script type="text/javascript">
7031 * @class Roo.data.DataProxy
7032 * @extends Roo.data.Observable
7033 * This class is an abstract base class for implementations which provide retrieval of
7034 * unformatted data objects.<br>
7036 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7037 * (of the appropriate type which knows how to parse the data object) to provide a block of
7038 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7040 * Custom implementations must implement the load method as described in
7041 * {@link Roo.data.HttpProxy#load}.
7043 Roo.data.DataProxy = function(){
7047 * Fires before a network request is made to retrieve a data object.
7048 * @param {Object} This DataProxy object.
7049 * @param {Object} params The params parameter to the load function.
7054 * Fires before the load method's callback is called.
7055 * @param {Object} This DataProxy object.
7056 * @param {Object} o The data object.
7057 * @param {Object} arg The callback argument object passed to the load function.
7061 * @event loadexception
7062 * Fires if an Exception occurs during data retrieval.
7063 * @param {Object} This DataProxy object.
7064 * @param {Object} o The data object.
7065 * @param {Object} arg The callback argument object passed to the load function.
7066 * @param {Object} e The Exception.
7068 loadexception : true
7070 Roo.data.DataProxy.superclass.constructor.call(this);
7073 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7076 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7080 * Ext JS Library 1.1.1
7081 * Copyright(c) 2006-2007, Ext JS, LLC.
7083 * Originally Released Under LGPL - original licence link has changed is not relivant.
7086 * <script type="text/javascript">
7089 * @class Roo.data.MemoryProxy
7090 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7091 * to the Reader when its load method is called.
7093 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7095 Roo.data.MemoryProxy = function(data){
7099 Roo.data.MemoryProxy.superclass.constructor.call(this);
7103 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7105 * Load data from the requested source (in this case an in-memory
7106 * data object passed to the constructor), read the data object into
7107 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7108 * process that block using the passed callback.
7109 * @param {Object} params This parameter is not used by the MemoryProxy class.
7110 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7111 * object into a block of Roo.data.Records.
7112 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7113 * The function must be passed <ul>
7114 * <li>The Record block object</li>
7115 * <li>The "arg" argument from the load function</li>
7116 * <li>A boolean success indicator</li>
7118 * @param {Object} scope The scope in which to call the callback
7119 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7121 load : function(params, reader, callback, scope, arg){
7122 params = params || {};
7125 result = reader.readRecords(this.data);
7127 this.fireEvent("loadexception", this, arg, null, e);
7128 callback.call(scope, null, arg, false);
7131 callback.call(scope, result, arg, true);
7135 update : function(params, records){
7140 * Ext JS Library 1.1.1
7141 * Copyright(c) 2006-2007, Ext JS, LLC.
7143 * Originally Released Under LGPL - original licence link has changed is not relivant.
7146 * <script type="text/javascript">
7149 * @class Roo.data.HttpProxy
7150 * @extends Roo.data.DataProxy
7151 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7152 * configured to reference a certain URL.<br><br>
7154 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7155 * from which the running page was served.<br><br>
7157 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7159 * Be aware that to enable the browser to parse an XML document, the server must set
7160 * the Content-Type header in the HTTP response to "text/xml".
7162 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7163 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7164 * will be used to make the request.
7166 Roo.data.HttpProxy = function(conn){
7167 Roo.data.HttpProxy.superclass.constructor.call(this);
7168 // is conn a conn config or a real conn?
7170 this.useAjax = !conn || !conn.events;
7174 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7175 // thse are take from connection...
7178 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7181 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7182 * extra parameters to each request made by this object. (defaults to undefined)
7185 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7186 * to each request made by this object. (defaults to undefined)
7189 * @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)
7192 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7195 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7201 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7205 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7206 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7207 * a finer-grained basis than the DataProxy events.
7209 getConnection : function(){
7210 return this.useAjax ? Roo.Ajax : this.conn;
7214 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7215 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7216 * process that block using the passed callback.
7217 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7218 * for the request to the remote server.
7219 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7220 * object into a block of Roo.data.Records.
7221 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7222 * The function must be passed <ul>
7223 * <li>The Record block object</li>
7224 * <li>The "arg" argument from the load function</li>
7225 * <li>A boolean success indicator</li>
7227 * @param {Object} scope The scope in which to call the callback
7228 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7230 load : function(params, reader, callback, scope, arg){
7231 if(this.fireEvent("beforeload", this, params) !== false){
7233 params : params || {},
7235 callback : callback,
7240 callback : this.loadResponse,
7244 Roo.applyIf(o, this.conn);
7245 if(this.activeRequest){
7246 Roo.Ajax.abort(this.activeRequest);
7248 this.activeRequest = Roo.Ajax.request(o);
7250 this.conn.request(o);
7253 callback.call(scope||this, null, arg, false);
7258 loadResponse : function(o, success, response){
7259 delete this.activeRequest;
7261 this.fireEvent("loadexception", this, o, response);
7262 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7267 result = o.reader.read(response);
7269 this.fireEvent("loadexception", this, o, response, e);
7270 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7274 this.fireEvent("load", this, o, o.request.arg);
7275 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7279 update : function(dataSet){
7284 updateResponse : function(dataSet){
7289 * Ext JS Library 1.1.1
7290 * Copyright(c) 2006-2007, Ext JS, LLC.
7292 * Originally Released Under LGPL - original licence link has changed is not relivant.
7295 * <script type="text/javascript">
7299 * @class Roo.data.ScriptTagProxy
7300 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7301 * other than the originating domain of the running page.<br><br>
7303 * <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
7304 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7306 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7307 * source code that is used as the source inside a <script> tag.<br><br>
7309 * In order for the browser to process the returned data, the server must wrap the data object
7310 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7311 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7312 * depending on whether the callback name was passed:
7315 boolean scriptTag = false;
7316 String cb = request.getParameter("callback");
7319 response.setContentType("text/javascript");
7321 response.setContentType("application/x-json");
7323 Writer out = response.getWriter();
7325 out.write(cb + "(");
7327 out.print(dataBlock.toJsonString());
7334 * @param {Object} config A configuration object.
7336 Roo.data.ScriptTagProxy = function(config){
7337 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7338 Roo.apply(this, config);
7339 this.head = document.getElementsByTagName("head")[0];
7342 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7344 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7346 * @cfg {String} url The URL from which to request the data object.
7349 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7353 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7354 * the server the name of the callback function set up by the load call to process the returned data object.
7355 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7356 * javascript output which calls this named function passing the data object as its only parameter.
7358 callbackParam : "callback",
7360 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7361 * name to the request.
7366 * Load data from the configured URL, read the data object into
7367 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7368 * process that block using the passed callback.
7369 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7370 * for the request to the remote server.
7371 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7372 * object into a block of Roo.data.Records.
7373 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7374 * The function must be passed <ul>
7375 * <li>The Record block object</li>
7376 * <li>The "arg" argument from the load function</li>
7377 * <li>A boolean success indicator</li>
7379 * @param {Object} scope The scope in which to call the callback
7380 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7382 load : function(params, reader, callback, scope, arg){
7383 if(this.fireEvent("beforeload", this, params) !== false){
7385 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7388 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7390 url += "&_dc=" + (new Date().getTime());
7392 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7395 cb : "stcCallback"+transId,
7396 scriptId : "stcScript"+transId,
7400 callback : callback,
7406 window[trans.cb] = function(o){
7407 conn.handleResponse(o, trans);
7410 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7412 if(this.autoAbort !== false){
7416 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7418 var script = document.createElement("script");
7419 script.setAttribute("src", url);
7420 script.setAttribute("type", "text/javascript");
7421 script.setAttribute("id", trans.scriptId);
7422 this.head.appendChild(script);
7426 callback.call(scope||this, null, arg, false);
7431 isLoading : function(){
7432 return this.trans ? true : false;
7436 * Abort the current server request.
7439 if(this.isLoading()){
7440 this.destroyTrans(this.trans);
7445 destroyTrans : function(trans, isLoaded){
7446 this.head.removeChild(document.getElementById(trans.scriptId));
7447 clearTimeout(trans.timeoutId);
7449 window[trans.cb] = undefined;
7451 delete window[trans.cb];
7454 // if hasn't been loaded, wait for load to remove it to prevent script error
7455 window[trans.cb] = function(){
7456 window[trans.cb] = undefined;
7458 delete window[trans.cb];
7465 handleResponse : function(o, trans){
7467 this.destroyTrans(trans, true);
7470 result = trans.reader.readRecords(o);
7472 this.fireEvent("loadexception", this, o, trans.arg, e);
7473 trans.callback.call(trans.scope||window, null, trans.arg, false);
7476 this.fireEvent("load", this, o, trans.arg);
7477 trans.callback.call(trans.scope||window, result, trans.arg, true);
7481 handleFailure : function(trans){
7483 this.destroyTrans(trans, false);
7484 this.fireEvent("loadexception", this, null, trans.arg);
7485 trans.callback.call(trans.scope||window, null, trans.arg, false);
7489 * Ext JS Library 1.1.1
7490 * Copyright(c) 2006-2007, Ext JS, LLC.
7492 * Originally Released Under LGPL - original licence link has changed is not relivant.
7495 * <script type="text/javascript">
7499 * @class Roo.data.JsonReader
7500 * @extends Roo.data.DataReader
7501 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7502 * based on mappings in a provided Roo.data.Record constructor.
7504 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7505 * in the reply previously.
7510 var RecordDef = Roo.data.Record.create([
7511 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7512 {name: 'occupation'} // This field will use "occupation" as the mapping.
7514 var myReader = new Roo.data.JsonReader({
7515 totalProperty: "results", // The property which contains the total dataset size (optional)
7516 root: "rows", // The property which contains an Array of row objects
7517 id: "id" // The property within each row object that provides an ID for the record (optional)
7521 * This would consume a JSON file like this:
7523 { 'results': 2, 'rows': [
7524 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7525 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7528 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7529 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7530 * paged from the remote server.
7531 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7532 * @cfg {String} root name of the property which contains the Array of row objects.
7533 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7535 * Create a new JsonReader
7536 * @param {Object} meta Metadata configuration options
7537 * @param {Object} recordType Either an Array of field definition objects,
7538 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7540 Roo.data.JsonReader = function(meta, recordType){
7543 // set some defaults:
7545 totalProperty: 'total',
7546 successProperty : 'success',
7551 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7553 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7556 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7557 * Used by Store query builder to append _requestMeta to params.
7560 metaFromRemote : false,
7562 * This method is only used by a DataProxy which has retrieved data from a remote server.
7563 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7564 * @return {Object} data A data block which is used by an Roo.data.Store object as
7565 * a cache of Roo.data.Records.
7567 read : function(response){
7568 var json = response.responseText;
7570 var o = /* eval:var:o */ eval("("+json+")");
7572 throw {message: "JsonReader.read: Json object not found"};
7578 this.metaFromRemote = true;
7579 this.meta = o.metaData;
7580 this.recordType = Roo.data.Record.create(o.metaData.fields);
7581 this.onMetaChange(this.meta, this.recordType, o);
7583 return this.readRecords(o);
7586 // private function a store will implement
7587 onMetaChange : function(meta, recordType, o){
7594 simpleAccess: function(obj, subsc) {
7601 getJsonAccessor: function(){
7603 return function(expr) {
7605 return(re.test(expr))
7606 ? new Function("obj", "return obj." + expr)
7616 * Create a data block containing Roo.data.Records from an XML document.
7617 * @param {Object} o An object which contains an Array of row objects in the property specified
7618 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7619 * which contains the total size of the dataset.
7620 * @return {Object} data A data block which is used by an Roo.data.Store object as
7621 * a cache of Roo.data.Records.
7623 readRecords : function(o){
7625 * After any data loads, the raw JSON data is available for further custom processing.
7629 var s = this.meta, Record = this.recordType,
7630 f = Record.prototype.fields, fi = f.items, fl = f.length;
7632 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7634 if(s.totalProperty) {
7635 this.getTotal = this.getJsonAccessor(s.totalProperty);
7637 if(s.successProperty) {
7638 this.getSuccess = this.getJsonAccessor(s.successProperty);
7640 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7642 var g = this.getJsonAccessor(s.id);
7643 this.getId = function(rec) {
7645 return (r === undefined || r === "") ? null : r;
7648 this.getId = function(){return null;};
7651 for(var jj = 0; jj < fl; jj++){
7653 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7654 this.ef[jj] = this.getJsonAccessor(map);
7658 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7659 if(s.totalProperty){
7660 var vt = parseInt(this.getTotal(o), 10);
7665 if(s.successProperty){
7666 var vs = this.getSuccess(o);
7667 if(vs === false || vs === 'false'){
7672 for(var i = 0; i < c; i++){
7675 var id = this.getId(n);
7676 for(var j = 0; j < fl; j++){
7678 var v = this.ef[j](n);
7680 Roo.log('missing convert for ' + f.name);
7684 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7686 var record = new Record(values, id);
7688 records[i] = record;
7694 totalRecords : totalRecords
7699 * Ext JS Library 1.1.1
7700 * Copyright(c) 2006-2007, Ext JS, LLC.
7702 * Originally Released Under LGPL - original licence link has changed is not relivant.
7705 * <script type="text/javascript">
7709 * @class Roo.data.ArrayReader
7710 * @extends Roo.data.DataReader
7711 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7712 * Each element of that Array represents a row of data fields. The
7713 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7714 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7718 var RecordDef = Roo.data.Record.create([
7719 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7720 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7722 var myReader = new Roo.data.ArrayReader({
7723 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7727 * This would consume an Array like this:
7729 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7731 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7733 * Create a new JsonReader
7734 * @param {Object} meta Metadata configuration options.
7735 * @param {Object} recordType Either an Array of field definition objects
7736 * as specified to {@link Roo.data.Record#create},
7737 * or an {@link Roo.data.Record} object
7738 * created using {@link Roo.data.Record#create}.
7740 Roo.data.ArrayReader = function(meta, recordType){
7741 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7744 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7746 * Create a data block containing Roo.data.Records from an XML document.
7747 * @param {Object} o An Array of row objects which represents the dataset.
7748 * @return {Object} data A data block which is used by an Roo.data.Store object as
7749 * a cache of Roo.data.Records.
7751 readRecords : function(o){
7752 var sid = this.meta ? this.meta.id : null;
7753 var recordType = this.recordType, fields = recordType.prototype.fields;
7756 for(var i = 0; i < root.length; i++){
7759 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7760 for(var j = 0, jlen = fields.length; j < jlen; j++){
7761 var f = fields.items[j];
7762 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7763 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7767 var record = new recordType(values, id);
7769 records[records.length] = record;
7773 totalRecords : records.length
7782 * @class Roo.bootstrap.ComboBox
7783 * @extends Roo.bootstrap.TriggerField
7784 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7785 * @cfg {Boolean} append (true|false) default false
7787 * Create a new ComboBox.
7788 * @param {Object} config Configuration options
7790 Roo.bootstrap.ComboBox = function(config){
7791 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7795 * Fires when the dropdown list is expanded
7796 * @param {Roo.bootstrap.ComboBox} combo This combo box
7801 * Fires when the dropdown list is collapsed
7802 * @param {Roo.bootstrap.ComboBox} combo This combo box
7806 * @event beforeselect
7807 * Fires before a list item is selected. Return false to cancel the selection.
7808 * @param {Roo.bootstrap.ComboBox} combo This combo box
7809 * @param {Roo.data.Record} record The data record returned from the underlying store
7810 * @param {Number} index The index of the selected item in the dropdown list
7812 'beforeselect' : true,
7815 * Fires when a list item is selected
7816 * @param {Roo.bootstrap.ComboBox} combo This combo box
7817 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7818 * @param {Number} index The index of the selected item in the dropdown list
7822 * @event beforequery
7823 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7824 * The event object passed has these properties:
7825 * @param {Roo.bootstrap.ComboBox} combo This combo box
7826 * @param {String} query The query
7827 * @param {Boolean} forceAll true to force "all" query
7828 * @param {Boolean} cancel true to cancel the query
7829 * @param {Object} e The query event object
7831 'beforequery': true,
7834 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7835 * @param {Roo.bootstrap.ComboBox} combo This combo box
7840 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7841 * @param {Roo.bootstrap.ComboBox} combo This combo box
7842 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7847 * Fires when the remove value from the combobox array
7848 * @param {Roo.bootstrap.ComboBox} combo This combo box
7855 this.selectedIndex = -1;
7856 if(this.mode == 'local'){
7857 if(config.queryDelay === undefined){
7858 this.queryDelay = 10;
7860 if(config.minChars === undefined){
7866 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7869 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7870 * rendering into an Roo.Editor, defaults to false)
7873 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7874 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7877 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7880 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7881 * the dropdown list (defaults to undefined, with no header element)
7885 * @cfg {String/Roo.Template} tpl The template to use to render the output
7889 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7891 listWidth: undefined,
7893 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7894 * mode = 'remote' or 'text' if mode = 'local')
7896 displayField: undefined,
7898 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7899 * mode = 'remote' or 'value' if mode = 'local').
7900 * Note: use of a valueField requires the user make a selection
7901 * in order for a value to be mapped.
7903 valueField: undefined,
7907 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7908 * field's data value (defaults to the underlying DOM element's name)
7910 hiddenName: undefined,
7912 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7916 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7918 selectedClass: 'active',
7921 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7925 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7926 * anchor positions (defaults to 'tl-bl')
7928 listAlign: 'tl-bl?',
7930 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7934 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7935 * query specified by the allQuery config option (defaults to 'query')
7937 triggerAction: 'query',
7939 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7940 * (defaults to 4, does not apply if editable = false)
7944 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7945 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7949 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7950 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7954 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7955 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7959 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7960 * when editable = true (defaults to false)
7962 selectOnFocus:false,
7964 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7966 queryParam: 'query',
7968 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7969 * when mode = 'remote' (defaults to 'Loading...')
7971 loadingText: 'Loading...',
7973 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7977 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7981 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7982 * traditional select (defaults to true)
7986 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7990 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7994 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7995 * listWidth has a higher value)
7999 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8000 * allow the user to set arbitrary text into the field (defaults to false)
8002 forceSelection:false,
8004 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8005 * if typeAhead = true (defaults to 250)
8007 typeAheadDelay : 250,
8009 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8010 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8012 valueNotFoundText : undefined,
8014 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8019 * @cfg {Boolean} disableClear Disable showing of clear button.
8021 disableClear : false,
8023 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8025 alwaysQuery : false,
8028 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8042 // element that contains real text value.. (when hidden is used..)
8045 initEvents: function(){
8048 throw "can not find store for combo";
8050 this.store = Roo.factory(this.store, Roo.data);
8054 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8057 if(this.hiddenName){
8059 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8061 this.hiddenField.dom.value =
8062 this.hiddenValue !== undefined ? this.hiddenValue :
8063 this.value !== undefined ? this.value : '';
8065 // prevent input submission
8066 this.el.dom.removeAttribute('name');
8067 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8072 // this.el.dom.setAttribute('autocomplete', 'off');
8075 var cls = 'x-combo-list';
8076 this.list = this.el.select('ul.dropdown-menu',true).first();
8078 //this.list = new Roo.Layer({
8079 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8082 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8083 this.list.setWidth(lw);
8085 this.list.on('mouseover', this.onViewOver, this);
8086 this.list.on('mousemove', this.onViewMove, this);
8088 this.list.on('scroll', this.onViewScroll, this);
8091 this.list.swallowEvent('mousewheel');
8092 this.assetHeight = 0;
8095 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8096 this.assetHeight += this.header.getHeight();
8099 this.innerList = this.list.createChild({cls:cls+'-inner'});
8100 this.innerList.on('mouseover', this.onViewOver, this);
8101 this.innerList.on('mousemove', this.onViewMove, this);
8102 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8104 if(this.allowBlank && !this.pageSize && !this.disableClear){
8105 this.footer = this.list.createChild({cls:cls+'-ft'});
8106 this.pageTb = new Roo.Toolbar(this.footer);
8110 this.footer = this.list.createChild({cls:cls+'-ft'});
8111 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8112 {pageSize: this.pageSize});
8116 if (this.pageTb && this.allowBlank && !this.disableClear) {
8118 this.pageTb.add(new Roo.Toolbar.Fill(), {
8119 cls: 'x-btn-icon x-btn-clear',
8125 _this.onSelect(false, -1);
8130 this.assetHeight += this.footer.getHeight();
8135 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8138 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8139 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8141 //this.view.wrapEl.setDisplayed(false);
8142 this.view.on('click', this.onViewClick, this);
8146 this.store.on('beforeload', this.onBeforeLoad, this);
8147 this.store.on('load', this.onLoad, this);
8148 this.store.on('loadexception', this.onLoadException, this);
8151 this.resizer = new Roo.Resizable(this.list, {
8152 pinned:true, handles:'se'
8154 this.resizer.on('resize', function(r, w, h){
8155 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8157 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8158 this.restrictHeight();
8160 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8164 this.editable = true;
8165 this.setEditable(false);
8170 if (typeof(this.events.add.listeners) != 'undefined') {
8172 this.addicon = this.wrap.createChild(
8173 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8175 this.addicon.on('click', function(e) {
8176 this.fireEvent('add', this);
8179 if (typeof(this.events.edit.listeners) != 'undefined') {
8181 this.editicon = this.wrap.createChild(
8182 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8184 this.editicon.setStyle('margin-left', '40px');
8186 this.editicon.on('click', function(e) {
8188 // we fire even if inothing is selected..
8189 this.fireEvent('edit', this, this.lastData );
8195 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8197 this.inKeyMode = true;
8201 "down" : function(e){
8202 if(!this.isExpanded()){
8203 this.onTriggerClick();
8205 this.inKeyMode = true;
8210 "enter" : function(e){
8215 "esc" : function(e){
8219 "tab" : function(e){
8222 if(this.fireEvent("specialkey", this, e)){
8223 this.onViewClick(false);
8231 doRelay : function(foo, bar, hname){
8232 if(hname == 'down' || this.scope.isExpanded()){
8233 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8242 this.queryDelay = Math.max(this.queryDelay || 10,
8243 this.mode == 'local' ? 10 : 250);
8246 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8249 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8251 if(this.editable !== false){
8252 this.inputEl().on("keyup", this.onKeyUp, this);
8254 if(this.forceSelection){
8255 this.on('blur', this.doForce, this);
8259 this.choices = this.el.select('ul.select2-choices', true).first();
8260 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8264 onDestroy : function(){
8266 this.view.setStore(null);
8267 this.view.el.removeAllListeners();
8268 this.view.el.remove();
8269 this.view.purgeListeners();
8272 this.list.dom.innerHTML = '';
8275 this.store.un('beforeload', this.onBeforeLoad, this);
8276 this.store.un('load', this.onLoad, this);
8277 this.store.un('loadexception', this.onLoadException, this);
8279 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8283 fireKey : function(e){
8284 if(e.isNavKeyPress() && !this.list.isVisible()){
8285 this.fireEvent("specialkey", this, e);
8290 onResize: function(w, h){
8291 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8293 // if(typeof w != 'number'){
8294 // // we do not handle it!?!?
8297 // var tw = this.trigger.getWidth();
8298 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8299 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8301 // this.inputEl().setWidth( this.adjustWidth('input', x));
8303 // //this.trigger.setStyle('left', x+'px');
8305 // if(this.list && this.listWidth === undefined){
8306 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8307 // this.list.setWidth(lw);
8308 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8316 * Allow or prevent the user from directly editing the field text. If false is passed,
8317 * the user will only be able to select from the items defined in the dropdown list. This method
8318 * is the runtime equivalent of setting the 'editable' config option at config time.
8319 * @param {Boolean} value True to allow the user to directly edit the field text
8321 setEditable : function(value){
8322 if(value == this.editable){
8325 this.editable = value;
8327 this.inputEl().dom.setAttribute('readOnly', true);
8328 this.inputEl().on('mousedown', this.onTriggerClick, this);
8329 this.inputEl().addClass('x-combo-noedit');
8331 this.inputEl().dom.setAttribute('readOnly', false);
8332 this.inputEl().un('mousedown', this.onTriggerClick, this);
8333 this.inputEl().removeClass('x-combo-noedit');
8339 onBeforeLoad : function(combo,opts){
8344 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8346 this.restrictHeight();
8347 this.selectedIndex = -1;
8351 onLoad : function(){
8353 this.hasQuery = false;
8359 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8360 this.loading.hide();
8363 if(this.store.getCount() > 0){
8365 this.restrictHeight();
8366 if(this.lastQuery == this.allQuery){
8368 this.inputEl().dom.select();
8370 if(!this.selectByValue(this.value, true)){
8371 this.select(0, true);
8375 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8376 this.taTask.delay(this.typeAheadDelay);
8380 this.onEmptyResults();
8386 onLoadException : function()
8388 this.hasQuery = false;
8390 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8391 this.loading.hide();
8395 Roo.log(this.store.reader.jsonData);
8396 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8398 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8404 onTypeAhead : function(){
8405 if(this.store.getCount() > 0){
8406 var r = this.store.getAt(0);
8407 var newValue = r.data[this.displayField];
8408 var len = newValue.length;
8409 var selStart = this.getRawValue().length;
8411 if(selStart != len){
8412 this.setRawValue(newValue);
8413 this.selectText(selStart, newValue.length);
8419 onSelect : function(record, index){
8421 if(this.fireEvent('beforeselect', this, record, index) !== false){
8423 this.setFromData(index > -1 ? record.data : false);
8426 this.fireEvent('select', this, record, index);
8431 * Returns the currently selected field value or empty string if no value is set.
8432 * @return {String} value The selected value
8434 getValue : function(){
8437 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8440 if(this.valueField){
8441 return typeof this.value != 'undefined' ? this.value : '';
8443 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8448 * Clears any text/value currently set in the field
8450 clearValue : function(){
8451 if(this.hiddenField){
8452 this.hiddenField.dom.value = '';
8455 this.setRawValue('');
8456 this.lastSelectionText = '';
8461 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8462 * will be displayed in the field. If the value does not match the data value of an existing item,
8463 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8464 * Otherwise the field will be blank (although the value will still be set).
8465 * @param {String} value The value to match
8467 setValue : function(v){
8474 if(this.valueField){
8475 var r = this.findRecord(this.valueField, v);
8477 text = r.data[this.displayField];
8478 }else if(this.valueNotFoundText !== undefined){
8479 text = this.valueNotFoundText;
8482 this.lastSelectionText = text;
8483 if(this.hiddenField){
8484 this.hiddenField.dom.value = v;
8486 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8490 * @property {Object} the last set data for the element
8495 * Sets the value of the field based on a object which is related to the record format for the store.
8496 * @param {Object} value the value to set as. or false on reset?
8498 setFromData : function(o){
8505 var dv = ''; // display value
8506 var vv = ''; // value value..
8508 if (this.displayField) {
8509 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8511 // this is an error condition!!!
8512 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8515 if(this.valueField){
8516 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8519 if(this.hiddenField){
8520 this.hiddenField.dom.value = vv;
8522 this.lastSelectionText = dv;
8523 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8527 // no hidden field.. - we store the value in 'value', but still display
8528 // display field!!!!
8529 this.lastSelectionText = dv;
8530 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8537 // overridden so that last data is reset..
8538 this.setValue(this.originalValue);
8539 this.clearInvalid();
8540 this.lastData = false;
8542 this.view.clearSelections();
8546 findRecord : function(prop, value){
8548 if(this.store.getCount() > 0){
8549 this.store.each(function(r){
8550 if(r.data[prop] == value){
8562 // returns hidden if it's set..
8563 if (!this.rendered) {return ''};
8564 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8568 onViewMove : function(e, t){
8569 this.inKeyMode = false;
8573 onViewOver : function(e, t){
8574 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8577 var item = this.view.findItemFromChild(t);
8579 var index = this.view.indexOf(item);
8580 this.select(index, false);
8585 onViewClick : function(doFocus)
8587 var index = this.view.getSelectedIndexes()[0];
8588 var r = this.store.getAt(index);
8590 this.onSelect(r, index);
8592 if(doFocus !== false && !this.blockFocus){
8593 this.inputEl().focus();
8598 restrictHeight : function(){
8599 //this.innerList.dom.style.height = '';
8600 //var inner = this.innerList.dom;
8601 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8602 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8603 //this.list.beginUpdate();
8604 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8605 this.list.alignTo(this.inputEl(), this.listAlign);
8606 //this.list.endUpdate();
8610 onEmptyResults : function(){
8615 * Returns true if the dropdown list is expanded, else false.
8617 isExpanded : function(){
8618 return this.list.isVisible();
8622 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8623 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8624 * @param {String} value The data value of the item to select
8625 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8626 * selected item if it is not currently in view (defaults to true)
8627 * @return {Boolean} True if the value matched an item in the list, else false
8629 selectByValue : function(v, scrollIntoView){
8630 if(v !== undefined && v !== null){
8631 var r = this.findRecord(this.valueField || this.displayField, v);
8633 this.select(this.store.indexOf(r), scrollIntoView);
8641 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8642 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8643 * @param {Number} index The zero-based index of the list item to select
8644 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8645 * selected item if it is not currently in view (defaults to true)
8647 select : function(index, scrollIntoView){
8648 this.selectedIndex = index;
8649 this.view.select(index);
8650 if(scrollIntoView !== false){
8651 var el = this.view.getNode(index);
8653 //this.innerList.scrollChildIntoView(el, false);
8660 selectNext : function(){
8661 var ct = this.store.getCount();
8663 if(this.selectedIndex == -1){
8665 }else if(this.selectedIndex < ct-1){
8666 this.select(this.selectedIndex+1);
8672 selectPrev : function(){
8673 var ct = this.store.getCount();
8675 if(this.selectedIndex == -1){
8677 }else if(this.selectedIndex != 0){
8678 this.select(this.selectedIndex-1);
8684 onKeyUp : function(e){
8685 if(this.editable !== false && !e.isSpecialKey()){
8686 this.lastKey = e.getKey();
8687 this.dqTask.delay(this.queryDelay);
8692 validateBlur : function(){
8693 return !this.list || !this.list.isVisible();
8697 initQuery : function(){
8698 this.doQuery(this.getRawValue());
8702 doForce : function(){
8703 if(this.el.dom.value.length > 0){
8705 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8711 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8712 * query allowing the query action to be canceled if needed.
8713 * @param {String} query The SQL query to execute
8714 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8715 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8716 * saved in the current store (defaults to false)
8718 doQuery : function(q, forceAll){
8720 if(q === undefined || q === null){
8729 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8734 forceAll = qe.forceAll;
8735 if(forceAll === true || (q.length >= this.minChars)){
8737 this.hasQuery = true;
8739 if(this.lastQuery != q || this.alwaysQuery){
8741 if(this.mode == 'local'){
8742 this.selectedIndex = -1;
8744 this.store.clearFilter();
8746 this.store.filter(this.displayField, q);
8750 this.store.baseParams[this.queryParam] = q;
8752 var options = {params : this.getParams(q)};
8756 options.params.start = this.page * this.pageSize;
8759 this.store.load(options);
8763 this.selectedIndex = -1;
8768 this.loadNext = false;
8772 getParams : function(q){
8774 //p[this.queryParam] = q;
8778 p.limit = this.pageSize;
8784 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8786 collapse : function(){
8787 if(!this.isExpanded()){
8792 Roo.get(document).un('mousedown', this.collapseIf, this);
8793 Roo.get(document).un('mousewheel', this.collapseIf, this);
8794 if (!this.editable) {
8795 Roo.get(document).un('keydown', this.listKeyPress, this);
8797 this.fireEvent('collapse', this);
8801 collapseIf : function(e){
8802 var in_combo = e.within(this.el);
8803 var in_list = e.within(this.list);
8805 if (in_combo || in_list) {
8806 //e.stopPropagation();
8815 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8817 expand : function(){
8819 if(this.isExpanded() || !this.hasFocus){
8823 this.list.alignTo(this.inputEl(), this.listAlign);
8825 Roo.get(document).on('mousedown', this.collapseIf, this);
8826 Roo.get(document).on('mousewheel', this.collapseIf, this);
8827 if (!this.editable) {
8828 Roo.get(document).on('keydown', this.listKeyPress, this);
8831 this.fireEvent('expand', this);
8835 // Implements the default empty TriggerField.onTriggerClick function
8836 onTriggerClick : function()
8838 Roo.log('trigger click');
8845 this.loadNext = false;
8847 if(this.isExpanded()){
8849 if (!this.blockFocus) {
8850 this.inputEl().focus();
8854 this.hasFocus = true;
8855 if(this.triggerAction == 'all') {
8856 this.doQuery(this.allQuery, true);
8858 this.doQuery(this.getRawValue());
8860 if (!this.blockFocus) {
8861 this.inputEl().focus();
8865 listKeyPress : function(e)
8867 //Roo.log('listkeypress');
8868 // scroll to first matching element based on key pres..
8869 if (e.isSpecialKey()) {
8872 var k = String.fromCharCode(e.getKey()).toUpperCase();
8875 var csel = this.view.getSelectedNodes();
8876 var cselitem = false;
8878 var ix = this.view.indexOf(csel[0]);
8879 cselitem = this.store.getAt(ix);
8880 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8886 this.store.each(function(v) {
8888 // start at existing selection.
8889 if (cselitem.id == v.id) {
8895 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8896 match = this.store.indexOf(v);
8902 if (match === false) {
8903 return true; // no more action?
8906 this.view.select(match);
8907 var sn = Roo.get(this.view.getSelectedNodes()[0])
8908 //sn.scrollIntoView(sn.dom.parentNode, false);
8911 onViewScroll : function(e, t){
8913 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8917 this.hasQuery = true;
8919 this.loading = this.list.select('.loading', true).first();
8921 if(this.loading === null){
8922 this.list.createChild({
8924 cls: 'loading select2-more-results select2-active',
8925 html: 'Loading more results...'
8928 this.loading = this.list.select('.loading', true).first();
8930 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8932 this.loading.hide();
8935 this.loading.show();
8940 this.loadNext = true;
8942 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8947 addItem : function(o)
8949 var dv = ''; // display value
8951 if (this.displayField) {
8952 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8954 // this is an error condition!!!
8955 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8962 var choice = this.choices.createChild({
8964 cls: 'select2-search-choice',
8973 cls: 'select2-search-choice-close',
8978 }, this.searchField);
8980 var close = choice.select('a.select2-search-choice-close', true).first()
8982 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8989 this.inputEl().dom.value = '';
8993 onRemoveItem : function(e, _self, o)
8995 Roo.log('remove item');
8996 var index = this.item.indexOf(o.data) * 1;
8999 Roo.log('not this item?!');
9003 this.item.splice(index, 1);
9008 this.fireEvent('remove', this);
9012 syncValue : function()
9014 if(!this.item.length){
9021 Roo.each(this.item, function(i){
9022 if(_this.valueField){
9023 value.push(i[_this.valueField]);
9030 this.value = value.join(',');
9032 if(this.hiddenField){
9033 this.hiddenField.dom.value = this.value;
9037 clearItem : function()
9045 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9055 * @cfg {Boolean} grow
9059 * @cfg {Number} growMin
9063 * @cfg {Number} growMax
9073 * Ext JS Library 1.1.1
9074 * Copyright(c) 2006-2007, Ext JS, LLC.
9076 * Originally Released Under LGPL - original licence link has changed is not relivant.
9079 * <script type="text/javascript">
9084 * @extends Roo.util.Observable
9085 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9086 * This class also supports single and multi selection modes. <br>
9087 * Create a data model bound view:
9089 var store = new Roo.data.Store(...);
9091 var view = new Roo.View({
9093 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9096 selectedClass: "ydataview-selected",
9100 // listen for node click?
9101 view.on("click", function(vw, index, node, e){
9102 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9106 dataModel.load("foobar.xml");
9108 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9110 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9111 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9113 * Note: old style constructor is still suported (container, template, config)
9117 * @param {Object} config The config object
9120 Roo.View = function(config, depreciated_tpl, depreciated_config){
9122 if (typeof(depreciated_tpl) == 'undefined') {
9123 // new way.. - universal constructor.
9124 Roo.apply(this, config);
9125 this.el = Roo.get(this.el);
9128 this.el = Roo.get(config);
9129 this.tpl = depreciated_tpl;
9130 Roo.apply(this, depreciated_config);
9132 this.wrapEl = this.el.wrap().wrap();
9133 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9136 if(typeof(this.tpl) == "string"){
9137 this.tpl = new Roo.Template(this.tpl);
9139 // support xtype ctors..
9140 this.tpl = new Roo.factory(this.tpl, Roo);
9152 * @event beforeclick
9153 * Fires before a click is processed. Returns false to cancel the default action.
9154 * @param {Roo.View} this
9155 * @param {Number} index The index of the target node
9156 * @param {HTMLElement} node The target node
9157 * @param {Roo.EventObject} e The raw event object
9159 "beforeclick" : true,
9162 * Fires when a template node is clicked.
9163 * @param {Roo.View} this
9164 * @param {Number} index The index of the target node
9165 * @param {HTMLElement} node The target node
9166 * @param {Roo.EventObject} e The raw event object
9171 * Fires when a template node is double clicked.
9172 * @param {Roo.View} this
9173 * @param {Number} index The index of the target node
9174 * @param {HTMLElement} node The target node
9175 * @param {Roo.EventObject} e The raw event object
9179 * @event contextmenu
9180 * Fires when a template node is right clicked.
9181 * @param {Roo.View} this
9182 * @param {Number} index The index of the target node
9183 * @param {HTMLElement} node The target node
9184 * @param {Roo.EventObject} e The raw event object
9186 "contextmenu" : true,
9188 * @event selectionchange
9189 * Fires when the selected nodes change.
9190 * @param {Roo.View} this
9191 * @param {Array} selections Array of the selected nodes
9193 "selectionchange" : true,
9196 * @event beforeselect
9197 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9198 * @param {Roo.View} this
9199 * @param {HTMLElement} node The node to be selected
9200 * @param {Array} selections Array of currently selected nodes
9202 "beforeselect" : true,
9204 * @event preparedata
9205 * Fires on every row to render, to allow you to change the data.
9206 * @param {Roo.View} this
9207 * @param {Object} data to be rendered (change this)
9209 "preparedata" : true
9217 "click": this.onClick,
9218 "dblclick": this.onDblClick,
9219 "contextmenu": this.onContextMenu,
9223 this.selections = [];
9225 this.cmp = new Roo.CompositeElementLite([]);
9227 this.store = Roo.factory(this.store, Roo.data);
9228 this.setStore(this.store, true);
9231 if ( this.footer && this.footer.xtype) {
9233 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9235 this.footer.dataSource = this.store
9236 this.footer.container = fctr;
9237 this.footer = Roo.factory(this.footer, Roo);
9238 fctr.insertFirst(this.el);
9240 // this is a bit insane - as the paging toolbar seems to detach the el..
9241 // dom.parentNode.parentNode.parentNode
9242 // they get detached?
9246 Roo.View.superclass.constructor.call(this);
9251 Roo.extend(Roo.View, Roo.util.Observable, {
9254 * @cfg {Roo.data.Store} store Data store to load data from.
9259 * @cfg {String|Roo.Element} el The container element.
9264 * @cfg {String|Roo.Template} tpl The template used by this View
9268 * @cfg {String} dataName the named area of the template to use as the data area
9269 * Works with domtemplates roo-name="name"
9273 * @cfg {String} selectedClass The css class to add to selected nodes
9275 selectedClass : "x-view-selected",
9277 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9282 * @cfg {String} text to display on mask (default Loading)
9286 * @cfg {Boolean} multiSelect Allow multiple selection
9288 multiSelect : false,
9290 * @cfg {Boolean} singleSelect Allow single selection
9292 singleSelect: false,
9295 * @cfg {Boolean} toggleSelect - selecting
9297 toggleSelect : false,
9300 * Returns the element this view is bound to.
9301 * @return {Roo.Element}
9310 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9312 refresh : function(){
9316 // if we are using something like 'domtemplate', then
9317 // the what gets used is:
9318 // t.applySubtemplate(NAME, data, wrapping data..)
9319 // the outer template then get' applied with
9320 // the store 'extra data'
9321 // and the body get's added to the
9322 // roo-name="data" node?
9323 // <span class='roo-tpl-{name}'></span> ?????
9327 this.clearSelections();
9330 var records = this.store.getRange();
9331 if(records.length < 1) {
9333 // is this valid?? = should it render a template??
9335 this.el.update(this.emptyText);
9339 if (this.dataName) {
9340 this.el.update(t.apply(this.store.meta)); //????
9341 el = this.el.child('.roo-tpl-' + this.dataName);
9344 for(var i = 0, len = records.length; i < len; i++){
9345 var data = this.prepareData(records[i].data, i, records[i]);
9346 this.fireEvent("preparedata", this, data, i, records[i]);
9347 html[html.length] = Roo.util.Format.trim(
9349 t.applySubtemplate(this.dataName, data, this.store.meta) :
9356 el.update(html.join(""));
9357 this.nodes = el.dom.childNodes;
9358 this.updateIndexes(0);
9363 * Function to override to reformat the data that is sent to
9364 * the template for each node.
9365 * DEPRICATED - use the preparedata event handler.
9366 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9367 * a JSON object for an UpdateManager bound view).
9369 prepareData : function(data, index, record)
9371 this.fireEvent("preparedata", this, data, index, record);
9375 onUpdate : function(ds, record){
9376 Roo.log('on update');
9377 this.clearSelections();
9378 var index = this.store.indexOf(record);
9379 var n = this.nodes[index];
9380 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9381 n.parentNode.removeChild(n);
9382 this.updateIndexes(index, index);
9388 onAdd : function(ds, records, index)
9390 Roo.log(['on Add', ds, records, index] );
9391 this.clearSelections();
9392 if(this.nodes.length == 0){
9396 var n = this.nodes[index];
9397 for(var i = 0, len = records.length; i < len; i++){
9398 var d = this.prepareData(records[i].data, i, records[i]);
9400 this.tpl.insertBefore(n, d);
9403 this.tpl.append(this.el, d);
9406 this.updateIndexes(index);
9409 onRemove : function(ds, record, index){
9410 Roo.log('onRemove');
9411 this.clearSelections();
9412 var el = this.dataName ?
9413 this.el.child('.roo-tpl-' + this.dataName) :
9416 el.dom.removeChild(this.nodes[index]);
9417 this.updateIndexes(index);
9421 * Refresh an individual node.
9422 * @param {Number} index
9424 refreshNode : function(index){
9425 this.onUpdate(this.store, this.store.getAt(index));
9428 updateIndexes : function(startIndex, endIndex){
9429 var ns = this.nodes;
9430 startIndex = startIndex || 0;
9431 endIndex = endIndex || ns.length - 1;
9432 for(var i = startIndex; i <= endIndex; i++){
9433 ns[i].nodeIndex = i;
9438 * Changes the data store this view uses and refresh the view.
9439 * @param {Store} store
9441 setStore : function(store, initial){
9442 if(!initial && this.store){
9443 this.store.un("datachanged", this.refresh);
9444 this.store.un("add", this.onAdd);
9445 this.store.un("remove", this.onRemove);
9446 this.store.un("update", this.onUpdate);
9447 this.store.un("clear", this.refresh);
9448 this.store.un("beforeload", this.onBeforeLoad);
9449 this.store.un("load", this.onLoad);
9450 this.store.un("loadexception", this.onLoad);
9454 store.on("datachanged", this.refresh, this);
9455 store.on("add", this.onAdd, this);
9456 store.on("remove", this.onRemove, this);
9457 store.on("update", this.onUpdate, this);
9458 store.on("clear", this.refresh, this);
9459 store.on("beforeload", this.onBeforeLoad, this);
9460 store.on("load", this.onLoad, this);
9461 store.on("loadexception", this.onLoad, this);
9469 * onbeforeLoad - masks the loading area.
9472 onBeforeLoad : function(store,opts)
9474 Roo.log('onBeforeLoad');
9478 this.el.mask(this.mask ? this.mask : "Loading" );
9480 onLoad : function ()
9487 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9488 * @param {HTMLElement} node
9489 * @return {HTMLElement} The template node
9491 findItemFromChild : function(node){
9492 var el = this.dataName ?
9493 this.el.child('.roo-tpl-' + this.dataName,true) :
9496 if(!node || node.parentNode == el){
9499 var p = node.parentNode;
9500 while(p && p != el){
9501 if(p.parentNode == el){
9510 onClick : function(e){
9511 var item = this.findItemFromChild(e.getTarget());
9513 var index = this.indexOf(item);
9514 if(this.onItemClick(item, index, e) !== false){
9515 this.fireEvent("click", this, index, item, e);
9518 this.clearSelections();
9523 onContextMenu : function(e){
9524 var item = this.findItemFromChild(e.getTarget());
9526 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9531 onDblClick : function(e){
9532 var item = this.findItemFromChild(e.getTarget());
9534 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9538 onItemClick : function(item, index, e)
9540 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9543 if (this.toggleSelect) {
9544 var m = this.isSelected(item) ? 'unselect' : 'select';
9547 _t[m](item, true, false);
9550 if(this.multiSelect || this.singleSelect){
9551 if(this.multiSelect && e.shiftKey && this.lastSelection){
9552 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9554 this.select(item, this.multiSelect && e.ctrlKey);
9555 this.lastSelection = item;
9563 * Get the number of selected nodes.
9566 getSelectionCount : function(){
9567 return this.selections.length;
9571 * Get the currently selected nodes.
9572 * @return {Array} An array of HTMLElements
9574 getSelectedNodes : function(){
9575 return this.selections;
9579 * Get the indexes of the selected nodes.
9582 getSelectedIndexes : function(){
9583 var indexes = [], s = this.selections;
9584 for(var i = 0, len = s.length; i < len; i++){
9585 indexes.push(s[i].nodeIndex);
9591 * Clear all selections
9592 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9594 clearSelections : function(suppressEvent){
9595 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9596 this.cmp.elements = this.selections;
9597 this.cmp.removeClass(this.selectedClass);
9598 this.selections = [];
9600 this.fireEvent("selectionchange", this, this.selections);
9606 * Returns true if the passed node is selected
9607 * @param {HTMLElement/Number} node The node or node index
9610 isSelected : function(node){
9611 var s = this.selections;
9615 node = this.getNode(node);
9616 return s.indexOf(node) !== -1;
9621 * @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
9622 * @param {Boolean} keepExisting (optional) true to keep existing selections
9623 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9625 select : function(nodeInfo, keepExisting, suppressEvent){
9626 if(nodeInfo instanceof Array){
9628 this.clearSelections(true);
9630 for(var i = 0, len = nodeInfo.length; i < len; i++){
9631 this.select(nodeInfo[i], true, true);
9635 var node = this.getNode(nodeInfo);
9636 if(!node || this.isSelected(node)){
9637 return; // already selected.
9640 this.clearSelections(true);
9642 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9643 Roo.fly(node).addClass(this.selectedClass);
9644 this.selections.push(node);
9646 this.fireEvent("selectionchange", this, this.selections);
9654 * @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
9655 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9656 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9658 unselect : function(nodeInfo, keepExisting, suppressEvent)
9660 if(nodeInfo instanceof Array){
9661 Roo.each(this.selections, function(s) {
9662 this.unselect(s, nodeInfo);
9666 var node = this.getNode(nodeInfo);
9667 if(!node || !this.isSelected(node)){
9668 Roo.log("not selected");
9669 return; // not selected.
9673 Roo.each(this.selections, function(s) {
9675 Roo.fly(node).removeClass(this.selectedClass);
9682 this.selections= ns;
9683 this.fireEvent("selectionchange", this, this.selections);
9687 * Gets a template node.
9688 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9689 * @return {HTMLElement} The node or null if it wasn't found
9691 getNode : function(nodeInfo){
9692 if(typeof nodeInfo == "string"){
9693 return document.getElementById(nodeInfo);
9694 }else if(typeof nodeInfo == "number"){
9695 return this.nodes[nodeInfo];
9701 * Gets a range template nodes.
9702 * @param {Number} startIndex
9703 * @param {Number} endIndex
9704 * @return {Array} An array of nodes
9706 getNodes : function(start, end){
9707 var ns = this.nodes;
9709 end = typeof end == "undefined" ? ns.length - 1 : end;
9712 for(var i = start; i <= end; i++){
9716 for(var i = start; i >= end; i--){
9724 * Finds the index of the passed node
9725 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9726 * @return {Number} The index of the node or -1
9728 indexOf : function(node){
9729 node = this.getNode(node);
9730 if(typeof node.nodeIndex == "number"){
9731 return node.nodeIndex;
9733 var ns = this.nodes;
9734 for(var i = 0, len = ns.length; i < len; i++){
9745 * based on jquery fullcalendar
9749 Roo.bootstrap = Roo.bootstrap || {};
9751 * @class Roo.bootstrap.Calendar
9752 * @extends Roo.bootstrap.Component
9753 * Bootstrap Calendar class
9754 * @cfg {Boolean} loadMask (true|false) default false
9757 * Create a new Container
9758 * @param {Object} config The config object
9763 Roo.bootstrap.Calendar = function(config){
9764 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9768 * Fires when a date is selected
9769 * @param {DatePicker} this
9770 * @param {Date} date The selected date
9774 * @event monthchange
9775 * Fires when the displayed month changes
9776 * @param {DatePicker} this
9777 * @param {Date} date The selected month
9779 'monthchange': true,
9782 * Fires when mouse over an event
9783 * @param {Calendar} this
9784 * @param {event} Event
9789 * Fires when the mouse leaves an
9790 * @param {Calendar} this
9796 * Fires when the mouse click an
9797 * @param {Calendar} this
9806 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9809 * @cfg {Number} startDay
9810 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9816 getAutoCreate : function(){
9819 var fc_button = function(name, corner, style, content ) {
9820 return Roo.apply({},{
9822 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9824 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9827 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9835 style : 'width:100%',
9842 cls : 'fc-header-left',
9844 fc_button('prev', 'left', 'arrow', '‹' ),
9845 fc_button('next', 'right', 'arrow', '›' ),
9846 { tag: 'span', cls: 'fc-header-space' },
9847 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9855 cls : 'fc-header-center',
9859 cls: 'fc-header-title',
9862 html : 'month / year'
9870 cls : 'fc-header-right',
9872 /* fc_button('month', 'left', '', 'month' ),
9873 fc_button('week', '', '', 'week' ),
9874 fc_button('day', 'right', '', 'day' )
9886 var cal_heads = function() {
9888 // fixme - handle this.
9890 for (var i =0; i < Date.dayNames.length; i++) {
9891 var d = Date.dayNames[i];
9894 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9895 html : d.substring(0,3)
9899 ret[0].cls += ' fc-first';
9900 ret[6].cls += ' fc-last';
9903 var cal_cell = function(n) {
9906 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9911 cls: 'fc-day-number',
9915 cls: 'fc-day-content',
9919 style: 'position: relative;' // height: 17px;
9931 var cal_rows = function() {
9934 for (var r = 0; r < 6; r++) {
9941 for (var i =0; i < Date.dayNames.length; i++) {
9942 var d = Date.dayNames[i];
9943 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9946 row.cn[0].cls+=' fc-first';
9947 row.cn[0].cn[0].style = 'min-height:90px';
9948 row.cn[6].cls+=' fc-last';
9952 ret[0].cls += ' fc-first';
9953 ret[4].cls += ' fc-prev-last';
9954 ret[5].cls += ' fc-last';
9961 cls: 'fc-border-separate',
9962 style : 'width:100%',
9970 cls : 'fc-first fc-last',
9989 style : "position: relative;",
9992 cls : 'fc-view fc-view-month fc-grid',
9993 style : 'position: relative',
9994 unselectable : 'on',
9997 cls : 'fc-event-container',
9998 style : 'position:absolute;z-index:8;top:0;left:0;'
10016 initEvents : function()
10019 throw "can not find store for calendar";
10025 style: "text-align:center",
10029 style: "background-color:white;width:50%;margin:250 auto",
10033 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10044 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10046 var size = this.el.select('.fc-content', true).first().getSize();
10047 this.maskEl.setSize(size.width, size.height);
10048 this.maskEl.enableDisplayMode("block");
10049 if(!this.loadMask){
10050 this.maskEl.hide();
10053 this.store = Roo.factory(this.store, Roo.data);
10054 this.store.on('load', this.onLoad, this);
10055 this.store.on('beforeload', this.onBeforeLoad, this);
10059 this.cells = this.el.select('.fc-day',true);
10060 //Roo.log(this.cells);
10061 this.textNodes = this.el.query('.fc-day-number');
10062 this.cells.addClassOnOver('fc-state-hover');
10064 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10065 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10066 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10067 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10069 this.on('monthchange', this.onMonthChange, this);
10071 this.update(new Date().clearTime());
10074 resize : function() {
10075 var sz = this.el.getSize();
10077 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10078 this.el.select('.fc-day-content div',true).setHeight(34);
10083 showPrevMonth : function(e){
10084 this.update(this.activeDate.add("mo", -1));
10086 showToday : function(e){
10087 this.update(new Date().clearTime());
10090 showNextMonth : function(e){
10091 this.update(this.activeDate.add("mo", 1));
10095 showPrevYear : function(){
10096 this.update(this.activeDate.add("y", -1));
10100 showNextYear : function(){
10101 this.update(this.activeDate.add("y", 1));
10106 update : function(date)
10108 var vd = this.activeDate;
10109 this.activeDate = date;
10110 // if(vd && this.el){
10111 // var t = date.getTime();
10112 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10113 // Roo.log('using add remove');
10115 // this.fireEvent('monthchange', this, date);
10117 // this.cells.removeClass("fc-state-highlight");
10118 // this.cells.each(function(c){
10119 // if(c.dateValue == t){
10120 // c.addClass("fc-state-highlight");
10121 // setTimeout(function(){
10122 // try{c.dom.firstChild.focus();}catch(e){}
10132 var days = date.getDaysInMonth();
10134 var firstOfMonth = date.getFirstDateOfMonth();
10135 var startingPos = firstOfMonth.getDay()-this.startDay;
10137 if(startingPos < this.startDay){
10141 var pm = date.add(Date.MONTH, -1);
10142 var prevStart = pm.getDaysInMonth()-startingPos;
10144 this.cells = this.el.select('.fc-day',true);
10145 this.textNodes = this.el.query('.fc-day-number');
10146 this.cells.addClassOnOver('fc-state-hover');
10148 var cells = this.cells.elements;
10149 var textEls = this.textNodes;
10151 Roo.each(cells, function(cell){
10152 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10155 days += startingPos;
10157 // convert everything to numbers so it's fast
10158 var day = 86400000;
10159 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10162 //Roo.log(prevStart);
10164 var today = new Date().clearTime().getTime();
10165 var sel = date.clearTime().getTime();
10166 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10167 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10168 var ddMatch = this.disabledDatesRE;
10169 var ddText = this.disabledDatesText;
10170 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10171 var ddaysText = this.disabledDaysText;
10172 var format = this.format;
10174 var setCellClass = function(cal, cell){
10176 //Roo.log('set Cell Class');
10178 var t = d.getTime();
10182 cell.dateValue = t;
10184 cell.className += " fc-today";
10185 cell.className += " fc-state-highlight";
10186 cell.title = cal.todayText;
10189 // disable highlight in other month..
10190 //cell.className += " fc-state-highlight";
10195 cell.className = " fc-state-disabled";
10196 cell.title = cal.minText;
10200 cell.className = " fc-state-disabled";
10201 cell.title = cal.maxText;
10205 if(ddays.indexOf(d.getDay()) != -1){
10206 cell.title = ddaysText;
10207 cell.className = " fc-state-disabled";
10210 if(ddMatch && format){
10211 var fvalue = d.dateFormat(format);
10212 if(ddMatch.test(fvalue)){
10213 cell.title = ddText.replace("%0", fvalue);
10214 cell.className = " fc-state-disabled";
10218 if (!cell.initialClassName) {
10219 cell.initialClassName = cell.dom.className;
10222 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10227 for(; i < startingPos; i++) {
10228 textEls[i].innerHTML = (++prevStart);
10229 d.setDate(d.getDate()+1);
10231 cells[i].className = "fc-past fc-other-month";
10232 setCellClass(this, cells[i]);
10237 for(; i < days; i++){
10238 intDay = i - startingPos + 1;
10239 textEls[i].innerHTML = (intDay);
10240 d.setDate(d.getDate()+1);
10242 cells[i].className = ''; // "x-date-active";
10243 setCellClass(this, cells[i]);
10247 for(; i < 42; i++) {
10248 textEls[i].innerHTML = (++extraDays);
10249 d.setDate(d.getDate()+1);
10251 cells[i].className = "fc-future fc-other-month";
10252 setCellClass(this, cells[i]);
10255 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10257 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10259 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10260 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10262 if(totalRows != 6){
10263 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10264 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10267 this.fireEvent('monthchange', this, date);
10271 if(!this.internalRender){
10272 var main = this.el.dom.firstChild;
10273 var w = main.offsetWidth;
10274 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10275 Roo.fly(main).setWidth(w);
10276 this.internalRender = true;
10277 // opera does not respect the auto grow header center column
10278 // then, after it gets a width opera refuses to recalculate
10279 // without a second pass
10280 if(Roo.isOpera && !this.secondPass){
10281 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10282 this.secondPass = true;
10283 this.update.defer(10, this, [date]);
10290 findCell : function(dt) {
10291 dt = dt.clearTime().getTime();
10293 this.cells.each(function(c){
10294 //Roo.log("check " +c.dateValue + '?=' + dt);
10295 if(c.dateValue == dt){
10305 findCells : function(ev) {
10306 var s = ev.start.clone().clearTime().getTime();
10308 var e= ev.end.clone().clearTime().getTime();
10311 this.cells.each(function(c){
10312 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10314 if(c.dateValue > e){
10317 if(c.dateValue < s){
10326 findBestRow: function(cells)
10330 for (var i =0 ; i < cells.length;i++) {
10331 ret = Math.max(cells[i].rows || 0,ret);
10338 addItem : function(ev)
10340 // look for vertical location slot in
10341 var cells = this.findCells(ev);
10343 ev.row = this.findBestRow(cells);
10345 // work out the location.
10349 for(var i =0; i < cells.length; i++) {
10357 if (crow.start.getY() == cells[i].getY()) {
10359 crow.end = cells[i];
10375 for (var i = 0; i < cells.length;i++) {
10376 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10380 this.calevents.push(ev);
10383 clearEvents: function() {
10385 if(!this.calevents){
10389 Roo.each(this.cells.elements, function(c){
10393 Roo.each(this.calevents, function(e) {
10394 Roo.each(e.els, function(el) {
10395 el.un('mouseenter' ,this.onEventEnter, this);
10396 el.un('mouseleave' ,this.onEventLeave, this);
10403 renderEvents: function()
10405 // first make sure there is enough space..
10407 this.cells.each(function(c) {
10409 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10412 for (var e = 0; e < this.calevents.length; e++) {
10413 var ev = this.calevents[e];
10414 var cells = ev.cells;
10415 var rows = ev.rows;
10417 for(var i =0; i < rows.length; i++) {
10420 // how many rows should it span..
10423 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10424 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10426 unselectable : "on",
10429 cls: 'fc-event-inner',
10433 // cls: 'fc-event-time',
10434 // html : cells.length > 1 ? '' : ev.time
10438 cls: 'fc-event-title',
10439 html : String.format('{0}', ev.title)
10446 cls: 'ui-resizable-handle ui-resizable-e',
10447 html : '  '
10453 cfg.cls += ' fc-event-start';
10455 if ((i+1) == rows.length) {
10456 cfg.cls += ' fc-event-end';
10459 var ctr = this.el.select('.fc-event-container',true).first();
10460 var cg = ctr.createChild(cfg);
10462 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10463 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10464 cg.on('click', this.onEventClick, this, ev);
10468 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10469 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10471 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10472 cg.setWidth(ebox.right - sbox.x -2);
10480 onEventEnter: function (e, el,event,d) {
10481 this.fireEvent('evententer', this, el, event);
10484 onEventLeave: function (e, el,event,d) {
10485 this.fireEvent('eventleave', this, el, event);
10488 onEventClick: function (e, el,event,d) {
10489 this.fireEvent('eventclick', this, el, event);
10492 onMonthChange: function () {
10496 onLoad: function ()
10498 this.calevents = [];
10501 if(this.store.getCount() > 0){
10502 this.store.data.each(function(d){
10505 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10506 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10507 time : d.data.start_time,
10508 title : d.data.title,
10509 description : d.data.description,
10510 venue : d.data.venue
10515 this.renderEvents();
10518 this.maskEl.hide();
10522 onBeforeLoad: function()
10524 this.clearEvents();
10527 this.maskEl.show();
10541 * @class Roo.bootstrap.Popover
10542 * @extends Roo.bootstrap.Component
10543 * Bootstrap Popover class
10544 * @cfg {String} html contents of the popover (or false to use children..)
10545 * @cfg {String} title of popover (or false to hide)
10546 * @cfg {String} placement how it is placed
10547 * @cfg {String} trigger click || hover (or false to trigger manually)
10548 * @cfg {String} over what (parent or false to trigger manually.)
10551 * Create a new Popover
10552 * @param {Object} config The config object
10555 Roo.bootstrap.Popover = function(config){
10556 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10559 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10561 title: 'Fill in a title',
10564 placement : 'right',
10565 trigger : 'hover', // hover
10569 can_build_overlaid : false,
10571 getChildContainer : function()
10573 return this.el.select('.popover-content',true).first();
10576 getAutoCreate : function(){
10577 Roo.log('make popover?');
10579 cls : 'popover roo-dynamic',
10580 style: 'display:block',
10586 cls : 'popover-inner',
10590 cls: 'popover-title',
10594 cls : 'popover-content',
10605 setTitle: function(str)
10607 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10609 setContent: function(str)
10611 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10613 // as it get's added to the bottom of the page.
10614 onRender : function(ct, position)
10616 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10618 var cfg = Roo.apply({}, this.getAutoCreate());
10622 cfg.cls += ' ' + this.cls;
10625 cfg.style = this.style;
10627 Roo.log("adding to ")
10628 this.el = Roo.get(document.body).createChild(cfg, position);
10634 initEvents : function()
10636 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10637 this.el.enableDisplayMode('block');
10639 if (this.over === false) {
10642 if (this.triggers === false) {
10645 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10646 var triggers = this.trigger ? this.trigger.split(' ') : [];
10647 Roo.each(triggers, function(trigger) {
10649 if (trigger == 'click') {
10650 on_el.on('click', this.toggle, this);
10651 } else if (trigger != 'manual') {
10652 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10653 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10655 on_el.on(eventIn ,this.enter, this);
10656 on_el.on(eventOut, this.leave, this);
10667 toggle : function () {
10668 this.hoverState == 'in' ? this.leave() : this.enter();
10671 enter : function () {
10674 clearTimeout(this.timeout);
10676 this.hoverState = 'in'
10678 if (!this.delay || !this.delay.show) {
10683 this.timeout = setTimeout(function () {
10684 if (_t.hoverState == 'in') {
10687 }, this.delay.show)
10689 leave : function() {
10690 clearTimeout(this.timeout);
10692 this.hoverState = 'out'
10694 if (!this.delay || !this.delay.hide) {
10699 this.timeout = setTimeout(function () {
10700 if (_t.hoverState == 'out') {
10703 }, this.delay.hide)
10706 show : function (on_el)
10709 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10712 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10713 if (this.html !== false) {
10714 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10716 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10717 if (!this.title.length) {
10718 this.el.select('.popover-title',true).hide();
10721 var placement = typeof this.placement == 'function' ?
10722 this.placement.call(this, this.el, on_el) :
10725 var autoToken = /\s?auto?\s?/i;
10726 var autoPlace = autoToken.test(placement);
10728 placement = placement.replace(autoToken, '') || 'top';
10732 //this.el.setXY([0,0]);
10734 this.el.dom.style.display='block';
10735 this.el.addClass(placement);
10737 //this.el.appendTo(on_el);
10739 var p = this.getPosition();
10740 var box = this.el.getBox();
10745 var align = Roo.bootstrap.Popover.alignment[placement]
10746 this.el.alignTo(on_el, align[0],align[1]);
10747 //var arrow = this.el.select('.arrow',true).first();
10748 //arrow.set(align[2],
10750 this.el.addClass('in');
10751 this.hoverState = null;
10753 if (this.el.hasClass('fade')) {
10760 this.el.setXY([0,0]);
10761 this.el.removeClass('in');
10768 Roo.bootstrap.Popover.alignment = {
10769 'left' : ['r-l', [-10,0], 'right'],
10770 'right' : ['l-r', [10,0], 'left'],
10771 'bottom' : ['t-b', [0,10], 'top'],
10772 'top' : [ 'b-t', [0,-10], 'bottom']
10783 * @class Roo.bootstrap.Progress
10784 * @extends Roo.bootstrap.Component
10785 * Bootstrap Progress class
10786 * @cfg {Boolean} striped striped of the progress bar
10787 * @cfg {Boolean} active animated of the progress bar
10791 * Create a new Progress
10792 * @param {Object} config The config object
10795 Roo.bootstrap.Progress = function(config){
10796 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10799 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10804 getAutoCreate : function(){
10812 cfg.cls += ' progress-striped';
10816 cfg.cls += ' active';
10835 * @class Roo.bootstrap.ProgressBar
10836 * @extends Roo.bootstrap.Component
10837 * Bootstrap ProgressBar class
10838 * @cfg {Number} aria_valuenow aria-value now
10839 * @cfg {Number} aria_valuemin aria-value min
10840 * @cfg {Number} aria_valuemax aria-value max
10841 * @cfg {String} label label for the progress bar
10842 * @cfg {String} panel (success | info | warning | danger )
10843 * @cfg {String} role role of the progress bar
10844 * @cfg {String} sr_only text
10848 * Create a new ProgressBar
10849 * @param {Object} config The config object
10852 Roo.bootstrap.ProgressBar = function(config){
10853 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10856 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10860 aria_valuemax : 100,
10866 getAutoCreate : function()
10871 cls: 'progress-bar',
10872 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10884 cfg.role = this.role;
10887 if(this.aria_valuenow){
10888 cfg['aria-valuenow'] = this.aria_valuenow;
10891 if(this.aria_valuemin){
10892 cfg['aria-valuemin'] = this.aria_valuemin;
10895 if(this.aria_valuemax){
10896 cfg['aria-valuemax'] = this.aria_valuemax;
10899 if(this.label && !this.sr_only){
10900 cfg.html = this.label;
10904 cfg.cls += ' progress-bar-' + this.panel;
10910 update : function(aria_valuenow)
10912 this.aria_valuenow = aria_valuenow;
10914 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10929 * @class Roo.bootstrap.TabPanel
10930 * @extends Roo.bootstrap.Component
10931 * Bootstrap TabPanel class
10932 * @cfg {Boolean} active panel active
10933 * @cfg {String} html panel content
10934 * @cfg {String} tabId tab relate id
10938 * Create a new TabPanel
10939 * @param {Object} config The config object
10942 Roo.bootstrap.TabPanel = function(config){
10943 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10946 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10952 getAutoCreate : function(){
10956 html: this.html || ''
10960 cfg.cls += ' active';
10964 cfg.tabId = this.tabId;
10982 * @class Roo.bootstrap.DateField
10983 * @extends Roo.bootstrap.Input
10984 * Bootstrap DateField class
10985 * @cfg {Number} weekStart default 0
10986 * @cfg {Number} weekStart default 0
10987 * @cfg {Number} viewMode default empty, (months|years)
10988 * @cfg {Number} minViewMode default empty, (months|years)
10989 * @cfg {Number} startDate default -Infinity
10990 * @cfg {Number} endDate default Infinity
10991 * @cfg {Boolean} todayHighlight default false
10992 * @cfg {Boolean} todayBtn default false
10993 * @cfg {Boolean} calendarWeeks default false
10994 * @cfg {Object} daysOfWeekDisabled default empty
10996 * @cfg {Boolean} keyboardNavigation default true
10997 * @cfg {String} language default en
11000 * Create a new DateField
11001 * @param {Object} config The config object
11004 Roo.bootstrap.DateField = function(config){
11005 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11009 * Fires when this field show.
11010 * @param {Roo.bootstrap.DateField} this
11011 * @param {Mixed} date The date value
11016 * Fires when this field hide.
11017 * @param {Roo.bootstrap.DateField} this
11018 * @param {Mixed} date The date value
11023 * Fires when select a date.
11024 * @param {Roo.bootstrap.DateField} this
11025 * @param {Mixed} date The date value
11031 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11034 * @cfg {String} format
11035 * The default date format string which can be overriden for localization support. The format must be
11036 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11040 * @cfg {String} altFormats
11041 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11042 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11044 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11052 todayHighlight : false,
11058 keyboardNavigation: true,
11060 calendarWeeks: false,
11062 startDate: -Infinity,
11066 daysOfWeekDisabled: [],
11070 UTCDate: function()
11072 return new Date(Date.UTC.apply(Date, arguments));
11075 UTCToday: function()
11077 var today = new Date();
11078 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11081 getDate: function() {
11082 var d = this.getUTCDate();
11083 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11086 getUTCDate: function() {
11090 setDate: function(d) {
11091 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11094 setUTCDate: function(d) {
11096 this.setValue(this.formatDate(this.date));
11099 onRender: function(ct, position)
11102 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11104 this.language = this.language || 'en';
11105 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11106 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11108 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11109 this.format = this.format || 'm/d/y';
11110 this.isInline = false;
11111 this.isInput = true;
11112 this.component = this.el.select('.add-on', true).first() || false;
11113 this.component = (this.component && this.component.length === 0) ? false : this.component;
11114 this.hasInput = this.component && this.inputEL().length;
11116 if (typeof(this.minViewMode === 'string')) {
11117 switch (this.minViewMode) {
11119 this.minViewMode = 1;
11122 this.minViewMode = 2;
11125 this.minViewMode = 0;
11130 if (typeof(this.viewMode === 'string')) {
11131 switch (this.viewMode) {
11144 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11146 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11148 this.picker().on('mousedown', this.onMousedown, this);
11149 this.picker().on('click', this.onClick, this);
11151 this.picker().addClass('datepicker-dropdown');
11153 this.startViewMode = this.viewMode;
11156 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11157 if(!this.calendarWeeks){
11162 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11163 v.attr('colspan', function(i, val){
11164 return parseInt(val) + 1;
11169 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11171 this.setStartDate(this.startDate);
11172 this.setEndDate(this.endDate);
11174 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11181 if(this.isInline) {
11186 picker : function()
11188 return this.el.select('.datepicker', true).first();
11191 fillDow: function()
11193 var dowCnt = this.weekStart;
11202 if(this.calendarWeeks){
11210 while (dowCnt < this.weekStart + 7) {
11214 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11218 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11221 fillMonths: function()
11224 var months = this.picker().select('>.datepicker-months td', true).first();
11226 months.dom.innerHTML = '';
11232 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11235 months.createChild(month);
11240 update: function(){
11242 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11244 if (this.date < this.startDate) {
11245 this.viewDate = new Date(this.startDate);
11246 } else if (this.date > this.endDate) {
11247 this.viewDate = new Date(this.endDate);
11249 this.viewDate = new Date(this.date);
11256 var d = new Date(this.viewDate),
11257 year = d.getUTCFullYear(),
11258 month = d.getUTCMonth(),
11259 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11260 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11261 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11262 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11263 currentDate = this.date && this.date.valueOf(),
11264 today = this.UTCToday();
11266 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11268 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11270 // this.picker.select('>tfoot th.today').
11271 // .text(dates[this.language].today)
11272 // .toggle(this.todayBtn !== false);
11274 this.updateNavArrows();
11277 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11279 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11281 prevMonth.setUTCDate(day);
11283 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11285 var nextMonth = new Date(prevMonth);
11287 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11289 nextMonth = nextMonth.valueOf();
11291 var fillMonths = false;
11293 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11295 while(prevMonth.valueOf() < nextMonth) {
11298 if (prevMonth.getUTCDay() === this.weekStart) {
11300 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11308 if(this.calendarWeeks){
11309 // ISO 8601: First week contains first thursday.
11310 // ISO also states week starts on Monday, but we can be more abstract here.
11312 // Start of current week: based on weekstart/current date
11313 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11314 // Thursday of this week
11315 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11316 // First Thursday of year, year from thursday
11317 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11318 // Calendar week: ms between thursdays, div ms per day, div 7 days
11319 calWeek = (th - yth) / 864e5 / 7 + 1;
11321 fillMonths.cn.push({
11329 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11331 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11334 if (this.todayHighlight &&
11335 prevMonth.getUTCFullYear() == today.getFullYear() &&
11336 prevMonth.getUTCMonth() == today.getMonth() &&
11337 prevMonth.getUTCDate() == today.getDate()) {
11338 clsName += ' today';
11341 if (currentDate && prevMonth.valueOf() === currentDate) {
11342 clsName += ' active';
11345 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11346 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11347 clsName += ' disabled';
11350 fillMonths.cn.push({
11352 cls: 'day ' + clsName,
11353 html: prevMonth.getDate()
11356 prevMonth.setDate(prevMonth.getDate()+1);
11359 var currentYear = this.date && this.date.getUTCFullYear();
11360 var currentMonth = this.date && this.date.getUTCMonth();
11362 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11364 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11365 v.removeClass('active');
11367 if(currentYear === year && k === currentMonth){
11368 v.addClass('active');
11371 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11372 v.addClass('disabled');
11378 year = parseInt(year/10, 10) * 10;
11380 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11382 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11385 for (var i = -1; i < 11; i++) {
11386 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11388 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11396 showMode: function(dir) {
11398 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11400 Roo.each(this.picker().select('>div',true).elements, function(v){
11401 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11404 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11409 if(this.isInline) return;
11411 this.picker().removeClass(['bottom', 'top']);
11413 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11415 * place to the top of element!
11419 this.picker().addClass('top');
11420 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11425 this.picker().addClass('bottom');
11427 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11430 parseDate : function(value){
11431 if(!value || value instanceof Date){
11434 var v = Date.parseDate(value, this.format);
11435 if (!v && this.useIso) {
11436 v = Date.parseDate(value, 'Y-m-d');
11438 if(!v && this.altFormats){
11439 if(!this.altFormatsArray){
11440 this.altFormatsArray = this.altFormats.split("|");
11442 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11443 v = Date.parseDate(value, this.altFormatsArray[i]);
11449 formatDate : function(date, fmt){
11450 return (!date || !(date instanceof Date)) ?
11451 date : date.dateFormat(fmt || this.format);
11454 onFocus : function()
11456 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11460 onBlur : function()
11462 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11468 this.picker().show();
11472 this.fireEvent('show', this, this.date);
11477 if(this.isInline) return;
11478 this.picker().hide();
11479 this.viewMode = this.startViewMode;
11482 this.fireEvent('hide', this, this.date);
11486 onMousedown: function(e){
11487 e.stopPropagation();
11488 e.preventDefault();
11491 keyup: function(e){
11492 Roo.bootstrap.DateField.superclass.keyup.call(this);
11497 setValue: function(v){
11498 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11500 this.fireEvent('select', this, this.date);
11504 fireKey: function(e){
11505 if (!this.picker().isVisible()){
11506 if (e.keyCode == 27) // allow escape to hide and re-show picker
11510 var dateChanged = false,
11512 newDate, newViewDate;
11516 e.preventDefault();
11520 if (!this.keyboardNavigation) break;
11521 dir = e.keyCode == 37 ? -1 : 1;
11524 newDate = this.moveYear(this.date, dir);
11525 newViewDate = this.moveYear(this.viewDate, dir);
11526 } else if (e.shiftKey){
11527 newDate = this.moveMonth(this.date, dir);
11528 newViewDate = this.moveMonth(this.viewDate, dir);
11530 newDate = new Date(this.date);
11531 newDate.setUTCDate(this.date.getUTCDate() + dir);
11532 newViewDate = new Date(this.viewDate);
11533 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11535 if (this.dateWithinRange(newDate)){
11536 this.date = newDate;
11537 this.viewDate = newViewDate;
11538 this.setValue(this.formatDate(this.date));
11540 e.preventDefault();
11541 dateChanged = true;
11546 if (!this.keyboardNavigation) break;
11547 dir = e.keyCode == 38 ? -1 : 1;
11549 newDate = this.moveYear(this.date, dir);
11550 newViewDate = this.moveYear(this.viewDate, dir);
11551 } else if (e.shiftKey){
11552 newDate = this.moveMonth(this.date, dir);
11553 newViewDate = this.moveMonth(this.viewDate, dir);
11555 newDate = new Date(this.date);
11556 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11557 newViewDate = new Date(this.viewDate);
11558 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11560 if (this.dateWithinRange(newDate)){
11561 this.date = newDate;
11562 this.viewDate = newViewDate;
11563 this.setValue(this.formatDate(this.date));
11565 e.preventDefault();
11566 dateChanged = true;
11570 this.setValue(this.formatDate(this.date));
11572 e.preventDefault();
11575 this.setValue(this.formatDate(this.date));
11582 onClick: function(e) {
11583 e.stopPropagation();
11584 e.preventDefault();
11586 var target = e.getTarget();
11588 if(target.nodeName.toLowerCase() === 'i'){
11589 target = Roo.get(target).dom.parentNode;
11592 var nodeName = target.nodeName;
11593 var className = target.className;
11594 var html = target.innerHTML;
11596 switch(nodeName.toLowerCase()) {
11598 switch(className) {
11604 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11605 switch(this.viewMode){
11607 this.viewDate = this.moveMonth(this.viewDate, dir);
11611 this.viewDate = this.moveYear(this.viewDate, dir);
11617 var date = new Date();
11618 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11620 this.setValue(this.formatDate(this.date));
11626 if (className.indexOf('disabled') === -1) {
11627 this.viewDate.setUTCDate(1);
11628 if (className.indexOf('month') !== -1) {
11629 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11631 var year = parseInt(html, 10) || 0;
11632 this.viewDate.setUTCFullYear(year);
11641 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11642 var day = parseInt(html, 10) || 1;
11643 var year = this.viewDate.getUTCFullYear(),
11644 month = this.viewDate.getUTCMonth();
11646 if (className.indexOf('old') !== -1) {
11653 } else if (className.indexOf('new') !== -1) {
11661 this.date = this.UTCDate(year, month, day,0,0,0,0);
11662 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11664 this.setValue(this.formatDate(this.date));
11671 setStartDate: function(startDate){
11672 this.startDate = startDate || -Infinity;
11673 if (this.startDate !== -Infinity) {
11674 this.startDate = this.parseDate(this.startDate);
11677 this.updateNavArrows();
11680 setEndDate: function(endDate){
11681 this.endDate = endDate || Infinity;
11682 if (this.endDate !== Infinity) {
11683 this.endDate = this.parseDate(this.endDate);
11686 this.updateNavArrows();
11689 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11690 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11691 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11692 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11694 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11695 return parseInt(d, 10);
11698 this.updateNavArrows();
11701 updateNavArrows: function() {
11702 var d = new Date(this.viewDate),
11703 year = d.getUTCFullYear(),
11704 month = d.getUTCMonth();
11706 Roo.each(this.picker().select('.prev', true).elements, function(v){
11708 switch (this.viewMode) {
11711 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11717 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11724 Roo.each(this.picker().select('.next', true).elements, function(v){
11726 switch (this.viewMode) {
11729 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11735 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11743 moveMonth: function(date, dir){
11744 if (!dir) return date;
11745 var new_date = new Date(date.valueOf()),
11746 day = new_date.getUTCDate(),
11747 month = new_date.getUTCMonth(),
11748 mag = Math.abs(dir),
11750 dir = dir > 0 ? 1 : -1;
11753 // If going back one month, make sure month is not current month
11754 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11756 return new_date.getUTCMonth() == month;
11758 // If going forward one month, make sure month is as expected
11759 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11761 return new_date.getUTCMonth() != new_month;
11763 new_month = month + dir;
11764 new_date.setUTCMonth(new_month);
11765 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11766 if (new_month < 0 || new_month > 11)
11767 new_month = (new_month + 12) % 12;
11769 // For magnitudes >1, move one month at a time...
11770 for (var i=0; i<mag; i++)
11771 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11772 new_date = this.moveMonth(new_date, dir);
11773 // ...then reset the day, keeping it in the new month
11774 new_month = new_date.getUTCMonth();
11775 new_date.setUTCDate(day);
11777 return new_month != new_date.getUTCMonth();
11780 // Common date-resetting loop -- if date is beyond end of month, make it
11783 new_date.setUTCDate(--day);
11784 new_date.setUTCMonth(new_month);
11789 moveYear: function(date, dir){
11790 return this.moveMonth(date, dir*12);
11793 dateWithinRange: function(date){
11794 return date >= this.startDate && date <= this.endDate;
11798 remove: function() {
11799 this.picker().remove();
11804 Roo.apply(Roo.bootstrap.DateField, {
11815 html: '<i class="icon-arrow-left"/>'
11825 html: '<i class="icon-arrow-right"/>'
11867 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11868 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11869 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11870 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11871 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11884 navFnc: 'FullYear',
11889 navFnc: 'FullYear',
11894 Roo.apply(Roo.bootstrap.DateField, {
11898 cls: 'datepicker dropdown-menu',
11902 cls: 'datepicker-days',
11906 cls: 'table-condensed',
11908 Roo.bootstrap.DateField.head,
11912 Roo.bootstrap.DateField.footer
11919 cls: 'datepicker-months',
11923 cls: 'table-condensed',
11925 Roo.bootstrap.DateField.head,
11926 Roo.bootstrap.DateField.content,
11927 Roo.bootstrap.DateField.footer
11934 cls: 'datepicker-years',
11938 cls: 'table-condensed',
11940 Roo.bootstrap.DateField.head,
11941 Roo.bootstrap.DateField.content,
11942 Roo.bootstrap.DateField.footer
11961 * @class Roo.bootstrap.TimeField
11962 * @extends Roo.bootstrap.Input
11963 * Bootstrap DateField class
11967 * Create a new TimeField
11968 * @param {Object} config The config object
11971 Roo.bootstrap.TimeField = function(config){
11972 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11976 * Fires when this field show.
11977 * @param {Roo.bootstrap.DateField} this
11978 * @param {Mixed} date The date value
11983 * Fires when this field hide.
11984 * @param {Roo.bootstrap.DateField} this
11985 * @param {Mixed} date The date value
11990 * Fires when select a date.
11991 * @param {Roo.bootstrap.DateField} this
11992 * @param {Mixed} date The date value
11998 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
12001 * @cfg {String} format
12002 * The default time format string which can be overriden for localization support. The format must be
12003 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12007 onRender: function(ct, position)
12010 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12012 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12014 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12016 this.pop = this.picker().select('>.datepicker-time',true).first();
12017 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12019 this.picker().on('mousedown', this.onMousedown, this);
12020 this.picker().on('click', this.onClick, this);
12022 this.picker().addClass('datepicker-dropdown');
12027 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12028 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12029 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12030 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12031 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12032 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12036 fireKey: function(e){
12037 if (!this.picker().isVisible()){
12038 if (e.keyCode == 27) // allow escape to hide and re-show picker
12043 e.preventDefault();
12051 this.onTogglePeriod();
12054 this.onIncrementMinutes();
12057 this.onDecrementMinutes();
12066 onClick: function(e) {
12067 e.stopPropagation();
12068 e.preventDefault();
12071 picker : function()
12073 return this.el.select('.datepicker', true).first();
12076 fillTime: function()
12078 var time = this.pop.select('tbody', true).first();
12080 time.dom.innerHTML = '';
12095 cls: 'hours-up glyphicon glyphicon-chevron-up'
12115 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12136 cls: 'timepicker-hour',
12151 cls: 'timepicker-minute',
12166 cls: 'btn btn-primary period',
12188 cls: 'hours-down glyphicon glyphicon-chevron-down'
12208 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12226 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12233 var hours = this.time.getHours();
12234 var minutes = this.time.getMinutes();
12247 hours = hours - 12;
12251 hours = '0' + hours;
12255 minutes = '0' + minutes;
12258 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12259 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12260 this.pop.select('button', true).first().dom.innerHTML = period;
12266 this.picker().removeClass(['bottom', 'top']);
12268 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12270 * place to the top of element!
12274 this.picker().addClass('top');
12275 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12280 this.picker().addClass('bottom');
12282 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12285 onFocus : function()
12287 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12291 onBlur : function()
12293 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12299 this.picker().show();
12304 this.fireEvent('show', this, this.date);
12309 this.picker().hide();
12312 this.fireEvent('hide', this, this.date);
12315 setTime : function()
12318 this.setValue(this.time.format(this.format));
12320 this.fireEvent('select', this, this.date);
12325 onMousedown: function(e){
12326 e.stopPropagation();
12327 e.preventDefault();
12330 onIncrementHours: function()
12332 Roo.log('onIncrementHours');
12333 this.time = this.time.add(Date.HOUR, 1);
12338 onDecrementHours: function()
12340 Roo.log('onDecrementHours');
12341 this.time = this.time.add(Date.HOUR, -1);
12345 onIncrementMinutes: function()
12347 Roo.log('onIncrementMinutes');
12348 this.time = this.time.add(Date.MINUTE, 1);
12352 onDecrementMinutes: function()
12354 Roo.log('onDecrementMinutes');
12355 this.time = this.time.add(Date.MINUTE, -1);
12359 onTogglePeriod: function()
12361 Roo.log('onTogglePeriod');
12362 this.time = this.time.add(Date.HOUR, 12);
12369 Roo.apply(Roo.bootstrap.TimeField, {
12399 cls: 'btn btn-info ok',
12411 Roo.apply(Roo.bootstrap.TimeField, {
12415 cls: 'datepicker dropdown-menu',
12419 cls: 'datepicker-time',
12423 cls: 'table-condensed',
12425 Roo.bootstrap.TimeField.content,
12426 Roo.bootstrap.TimeField.footer
12445 * @class Roo.bootstrap.CheckBox
12446 * @extends Roo.bootstrap.Input
12447 * Bootstrap CheckBox class
12449 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12450 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12451 * @cfg {String} boxLabel The text that appears beside the checkbox
12452 * @cfg {Boolean} checked initnal the element
12455 * Create a new CheckBox
12456 * @param {Object} config The config object
12459 Roo.bootstrap.CheckBox = function(config){
12460 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12465 * Fires when the element is checked or unchecked.
12466 * @param {Roo.bootstrap.CheckBox} this This input
12467 * @param {Boolean} checked The new checked value
12473 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12475 inputType: 'checkbox',
12481 getAutoCreate : function()
12483 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12489 cfg.cls = 'form-group' //input-group
12494 type : this.inputType,
12495 value : (!this.checked) ? this.valueOff : this.inputValue,
12497 placeholder : this.placeholder || ''
12501 if (this.disabled) {
12502 input.disabled=true;
12506 input.checked = this.checked;
12510 input.name = this.name;
12514 input.cls += ' input-' + this.size;
12518 ['xs','sm','md','lg'].map(function(size){
12519 if (settings[size]) {
12520 cfg.cls += ' col-' + size + '-' + settings[size];
12524 var inputblock = input;
12526 if (this.before || this.after) {
12529 cls : 'input-group',
12533 inputblock.cn.push({
12535 cls : 'input-group-addon',
12539 inputblock.cn.push(input);
12541 inputblock.cn.push({
12543 cls : 'input-group-addon',
12550 if (align ==='left' && this.fieldLabel.length) {
12551 Roo.log("left and has label");
12557 cls : 'control-label col-md-' + this.labelWidth,
12558 html : this.fieldLabel
12562 cls : "col-md-" + (12 - this.labelWidth),
12569 } else if ( this.fieldLabel.length) {
12574 tag: this.boxLabel ? 'span' : 'label',
12576 cls: 'control-label box-input-label',
12577 //cls : 'input-group-addon',
12578 html : this.fieldLabel
12588 Roo.log(" no label && no align");
12603 html: this.boxLabel
12612 * return the real input element.
12614 inputEl: function ()
12616 return this.el.select('input.form-box',true).first();
12619 initEvents : function()
12621 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12623 this.inputEl().on('click', this.onClick, this);
12627 onClick : function()
12629 this.setChecked(!this.checked);
12632 setChecked : function(state,suppressEvent)
12634 this.checked = state;
12636 this.inputEl().dom.checked = state;
12638 if(suppressEvent !== true){
12639 this.fireEvent('check', this, state);
12642 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12646 setValue : function(v,suppressEvent)
12648 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12662 * @class Roo.bootstrap.Radio
12663 * @extends Roo.bootstrap.CheckBox
12664 * Bootstrap Radio class
12667 * Create a new Radio
12668 * @param {Object} config The config object
12671 Roo.bootstrap.Radio = function(config){
12672 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12676 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12678 inputType: 'radio',
12682 getAutoCreate : function()
12684 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12690 cfg.cls = 'form-group' //input-group
12695 type : this.inputType,
12696 value : (!this.checked) ? this.valueOff : this.inputValue,
12698 placeholder : this.placeholder || ''
12702 if (this.disabled) {
12703 input.disabled=true;
12707 input.checked = this.checked;
12711 input.name = this.name;
12715 input.cls += ' input-' + this.size;
12719 ['xs','sm','md','lg'].map(function(size){
12720 if (settings[size]) {
12721 cfg.cls += ' col-' + size + '-' + settings[size];
12725 var inputblock = input;
12727 if (this.before || this.after) {
12730 cls : 'input-group',
12734 inputblock.cn.push({
12736 cls : 'input-group-addon',
12740 inputblock.cn.push(input);
12742 inputblock.cn.push({
12744 cls : 'input-group-addon',
12751 if (align ==='left' && this.fieldLabel.length) {
12752 Roo.log("left and has label");
12758 cls : 'control-label col-md-' + this.labelWidth,
12759 html : this.fieldLabel
12763 cls : "col-md-" + (12 - this.labelWidth),
12770 } else if ( this.fieldLabel.length) {
12777 cls: 'control-label box-input-label',
12778 //cls : 'input-group-addon',
12779 html : this.fieldLabel
12789 Roo.log(" no label && no align");
12804 html: this.boxLabel
12812 onClick : function()
12814 this.setChecked(true);
12817 setChecked : function(state,suppressEvent)
12820 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12821 v.dom.checked = false;
12825 this.checked = state;
12826 this.inputEl().dom.checked = state;
12828 if(suppressEvent !== true){
12829 this.fireEvent('check', this, state);
12832 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12836 getGroupValue : function()
12839 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12840 if(v.dom.checked == true){
12841 value = v.dom.value;
12849 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12850 * @return {Mixed} value The field value
12852 getValue : function(){
12853 return this.getGroupValue();
12859 //<script type="text/javascript">
12862 * Based Ext JS Library 1.1.1
12863 * Copyright(c) 2006-2007, Ext JS, LLC.
12869 * @class Roo.HtmlEditorCore
12870 * @extends Roo.Component
12871 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12873 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12876 Roo.HtmlEditorCore = function(config){
12879 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12882 * @event initialize
12883 * Fires when the editor is fully initialized (including the iframe)
12884 * @param {Roo.HtmlEditorCore} this
12889 * Fires when the editor is first receives the focus. Any insertion must wait
12890 * until after this event.
12891 * @param {Roo.HtmlEditorCore} this
12895 * @event beforesync
12896 * Fires before the textarea is updated with content from the editor iframe. Return false
12897 * to cancel the sync.
12898 * @param {Roo.HtmlEditorCore} this
12899 * @param {String} html
12903 * @event beforepush
12904 * Fires before the iframe editor is updated with content from the textarea. Return false
12905 * to cancel the push.
12906 * @param {Roo.HtmlEditorCore} this
12907 * @param {String} html
12912 * Fires when the textarea is updated with content from the editor iframe.
12913 * @param {Roo.HtmlEditorCore} this
12914 * @param {String} html
12919 * Fires when the iframe editor is updated with content from the textarea.
12920 * @param {Roo.HtmlEditorCore} this
12921 * @param {String} html
12926 * @event editorevent
12927 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12928 * @param {Roo.HtmlEditorCore} this
12936 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12940 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12946 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12951 * @cfg {Number} height (in pixels)
12955 * @cfg {Number} width (in pixels)
12960 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12963 stylesheets: false,
12968 // private properties
12969 validationEvent : false,
12971 initialized : false,
12973 sourceEditMode : false,
12974 onFocus : Roo.emptyFn,
12976 hideMode:'offsets',
12984 * Protected method that will not generally be called directly. It
12985 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12986 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12988 getDocMarkup : function(){
12991 Roo.log(this.stylesheets);
12993 // inherit styels from page...??
12994 if (this.stylesheets === false) {
12996 Roo.get(document.head).select('style').each(function(node) {
12997 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13000 Roo.get(document.head).select('link').each(function(node) {
13001 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13004 } else if (!this.stylesheets.length) {
13006 st = '<style type="text/css">' +
13007 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13010 Roo.each(this.stylesheets, function(s) {
13011 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13016 st += '<style type="text/css">' +
13017 'IMG { cursor: pointer } ' +
13021 return '<html><head>' + st +
13022 //<style type="text/css">' +
13023 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13025 ' </head><body class="roo-htmleditor-body"></body></html>';
13029 onRender : function(ct, position)
13032 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13033 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13036 this.el.dom.style.border = '0 none';
13037 this.el.dom.setAttribute('tabIndex', -1);
13038 this.el.addClass('x-hidden hide');
13042 if(Roo.isIE){ // fix IE 1px bogus margin
13043 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13047 this.frameId = Roo.id();
13051 var iframe = this.owner.wrap.createChild({
13053 cls: 'form-control', // bootstrap..
13055 name: this.frameId,
13056 frameBorder : 'no',
13057 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13062 this.iframe = iframe.dom;
13064 this.assignDocWin();
13066 this.doc.designMode = 'on';
13069 this.doc.write(this.getDocMarkup());
13073 var task = { // must defer to wait for browser to be ready
13075 //console.log("run task?" + this.doc.readyState);
13076 this.assignDocWin();
13077 if(this.doc.body || this.doc.readyState == 'complete'){
13079 this.doc.designMode="on";
13083 Roo.TaskMgr.stop(task);
13084 this.initEditor.defer(10, this);
13091 Roo.TaskMgr.start(task);
13098 onResize : function(w, h)
13100 Roo.log('resize: ' +w + ',' + h );
13101 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13105 if(typeof w == 'number'){
13107 this.iframe.style.width = w + 'px';
13109 if(typeof h == 'number'){
13111 this.iframe.style.height = h + 'px';
13113 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13120 * Toggles the editor between standard and source edit mode.
13121 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13123 toggleSourceEdit : function(sourceEditMode){
13125 this.sourceEditMode = sourceEditMode === true;
13127 if(this.sourceEditMode){
13129 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13132 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13133 //this.iframe.className = '';
13136 //this.setSize(this.owner.wrap.getSize());
13137 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13144 * Protected method that will not generally be called directly. If you need/want
13145 * custom HTML cleanup, this is the method you should override.
13146 * @param {String} html The HTML to be cleaned
13147 * return {String} The cleaned HTML
13149 cleanHtml : function(html){
13150 html = String(html);
13151 if(html.length > 5){
13152 if(Roo.isSafari){ // strip safari nonsense
13153 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13156 if(html == ' '){
13163 * HTML Editor -> Textarea
13164 * Protected method that will not generally be called directly. Syncs the contents
13165 * of the editor iframe with the textarea.
13167 syncValue : function(){
13168 if(this.initialized){
13169 var bd = (this.doc.body || this.doc.documentElement);
13170 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13171 var html = bd.innerHTML;
13173 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13174 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13176 html = '<div style="'+m[0]+'">' + html + '</div>';
13179 html = this.cleanHtml(html);
13180 // fix up the special chars.. normaly like back quotes in word...
13181 // however we do not want to do this with chinese..
13182 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13183 var cc = b.charCodeAt();
13185 (cc >= 0x4E00 && cc < 0xA000 ) ||
13186 (cc >= 0x3400 && cc < 0x4E00 ) ||
13187 (cc >= 0xf900 && cc < 0xfb00 )
13193 if(this.owner.fireEvent('beforesync', this, html) !== false){
13194 this.el.dom.value = html;
13195 this.owner.fireEvent('sync', this, html);
13201 * Protected method that will not generally be called directly. Pushes the value of the textarea
13202 * into the iframe editor.
13204 pushValue : function(){
13205 if(this.initialized){
13206 var v = this.el.dom.value.trim();
13208 // if(v.length < 1){
13212 if(this.owner.fireEvent('beforepush', this, v) !== false){
13213 var d = (this.doc.body || this.doc.documentElement);
13215 this.cleanUpPaste();
13216 this.el.dom.value = d.innerHTML;
13217 this.owner.fireEvent('push', this, v);
13223 deferFocus : function(){
13224 this.focus.defer(10, this);
13228 focus : function(){
13229 if(this.win && !this.sourceEditMode){
13236 assignDocWin: function()
13238 var iframe = this.iframe;
13241 this.doc = iframe.contentWindow.document;
13242 this.win = iframe.contentWindow;
13244 if (!Roo.get(this.frameId)) {
13247 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13248 this.win = Roo.get(this.frameId).dom.contentWindow;
13253 initEditor : function(){
13254 //console.log("INIT EDITOR");
13255 this.assignDocWin();
13259 this.doc.designMode="on";
13261 this.doc.write(this.getDocMarkup());
13264 var dbody = (this.doc.body || this.doc.documentElement);
13265 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13266 // this copies styles from the containing element into thsi one..
13267 // not sure why we need all of this..
13268 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13269 ss['background-attachment'] = 'fixed'; // w3c
13270 dbody.bgProperties = 'fixed'; // ie
13271 Roo.DomHelper.applyStyles(dbody, ss);
13272 Roo.EventManager.on(this.doc, {
13273 //'mousedown': this.onEditorEvent,
13274 'mouseup': this.onEditorEvent,
13275 'dblclick': this.onEditorEvent,
13276 'click': this.onEditorEvent,
13277 'keyup': this.onEditorEvent,
13282 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13284 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13285 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13287 this.initialized = true;
13289 this.owner.fireEvent('initialize', this);
13294 onDestroy : function(){
13300 //for (var i =0; i < this.toolbars.length;i++) {
13301 // // fixme - ask toolbars for heights?
13302 // this.toolbars[i].onDestroy();
13305 //this.wrap.dom.innerHTML = '';
13306 //this.wrap.remove();
13311 onFirstFocus : function(){
13313 this.assignDocWin();
13316 this.activated = true;
13319 if(Roo.isGecko){ // prevent silly gecko errors
13321 var s = this.win.getSelection();
13322 if(!s.focusNode || s.focusNode.nodeType != 3){
13323 var r = s.getRangeAt(0);
13324 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13329 this.execCmd('useCSS', true);
13330 this.execCmd('styleWithCSS', false);
13333 this.owner.fireEvent('activate', this);
13337 adjustFont: function(btn){
13338 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13339 //if(Roo.isSafari){ // safari
13342 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13343 if(Roo.isSafari){ // safari
13344 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13345 v = (v < 10) ? 10 : v;
13346 v = (v > 48) ? 48 : v;
13347 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13352 v = Math.max(1, v+adjust);
13354 this.execCmd('FontSize', v );
13357 onEditorEvent : function(e){
13358 this.owner.fireEvent('editorevent', this, e);
13359 // this.updateToolbar();
13360 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13363 insertTag : function(tg)
13365 // could be a bit smarter... -> wrap the current selected tRoo..
13366 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13368 range = this.createRange(this.getSelection());
13369 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13370 wrappingNode.appendChild(range.extractContents());
13371 range.insertNode(wrappingNode);
13378 this.execCmd("formatblock", tg);
13382 insertText : function(txt)
13386 var range = this.createRange();
13387 range.deleteContents();
13388 //alert(Sender.getAttribute('label'));
13390 range.insertNode(this.doc.createTextNode(txt));
13396 * Executes a Midas editor command on the editor document and performs necessary focus and
13397 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13398 * @param {String} cmd The Midas command
13399 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13401 relayCmd : function(cmd, value){
13403 this.execCmd(cmd, value);
13404 this.owner.fireEvent('editorevent', this);
13405 //this.updateToolbar();
13406 this.owner.deferFocus();
13410 * Executes a Midas editor command directly on the editor document.
13411 * For visual commands, you should use {@link #relayCmd} instead.
13412 * <b>This should only be called after the editor is initialized.</b>
13413 * @param {String} cmd The Midas command
13414 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13416 execCmd : function(cmd, value){
13417 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13424 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13426 * @param {String} text | dom node..
13428 insertAtCursor : function(text)
13433 if(!this.activated){
13439 var r = this.doc.selection.createRange();
13450 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13454 // from jquery ui (MIT licenced)
13456 var win = this.win;
13458 if (win.getSelection && win.getSelection().getRangeAt) {
13459 range = win.getSelection().getRangeAt(0);
13460 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13461 range.insertNode(node);
13462 } else if (win.document.selection && win.document.selection.createRange) {
13463 // no firefox support
13464 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13465 win.document.selection.createRange().pasteHTML(txt);
13467 // no firefox support
13468 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13469 this.execCmd('InsertHTML', txt);
13478 mozKeyPress : function(e){
13480 var c = e.getCharCode(), cmd;
13483 c = String.fromCharCode(c).toLowerCase();
13497 this.cleanUpPaste.defer(100, this);
13505 e.preventDefault();
13513 fixKeys : function(){ // load time branching for fastest keydown performance
13515 return function(e){
13516 var k = e.getKey(), r;
13519 r = this.doc.selection.createRange();
13522 r.pasteHTML('    ');
13529 r = this.doc.selection.createRange();
13531 var target = r.parentElement();
13532 if(!target || target.tagName.toLowerCase() != 'li'){
13534 r.pasteHTML('<br />');
13540 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13541 this.cleanUpPaste.defer(100, this);
13547 }else if(Roo.isOpera){
13548 return function(e){
13549 var k = e.getKey();
13553 this.execCmd('InsertHTML','    ');
13556 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13557 this.cleanUpPaste.defer(100, this);
13562 }else if(Roo.isSafari){
13563 return function(e){
13564 var k = e.getKey();
13568 this.execCmd('InsertText','\t');
13572 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13573 this.cleanUpPaste.defer(100, this);
13581 getAllAncestors: function()
13583 var p = this.getSelectedNode();
13586 a.push(p); // push blank onto stack..
13587 p = this.getParentElement();
13591 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13595 a.push(this.doc.body);
13599 lastSelNode : false,
13602 getSelection : function()
13604 this.assignDocWin();
13605 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13608 getSelectedNode: function()
13610 // this may only work on Gecko!!!
13612 // should we cache this!!!!
13617 var range = this.createRange(this.getSelection()).cloneRange();
13620 var parent = range.parentElement();
13622 var testRange = range.duplicate();
13623 testRange.moveToElementText(parent);
13624 if (testRange.inRange(range)) {
13627 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13630 parent = parent.parentElement;
13635 // is ancestor a text element.
13636 var ac = range.commonAncestorContainer;
13637 if (ac.nodeType == 3) {
13638 ac = ac.parentNode;
13641 var ar = ac.childNodes;
13644 var other_nodes = [];
13645 var has_other_nodes = false;
13646 for (var i=0;i<ar.length;i++) {
13647 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13650 // fullly contained node.
13652 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13657 // probably selected..
13658 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13659 other_nodes.push(ar[i]);
13663 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13668 has_other_nodes = true;
13670 if (!nodes.length && other_nodes.length) {
13671 nodes= other_nodes;
13673 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13679 createRange: function(sel)
13681 // this has strange effects when using with
13682 // top toolbar - not sure if it's a great idea.
13683 //this.editor.contentWindow.focus();
13684 if (typeof sel != "undefined") {
13686 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13688 return this.doc.createRange();
13691 return this.doc.createRange();
13694 getParentElement: function()
13697 this.assignDocWin();
13698 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13700 var range = this.createRange(sel);
13703 var p = range.commonAncestorContainer;
13704 while (p.nodeType == 3) { // text node
13715 * Range intersection.. the hard stuff...
13719 * [ -- selected range --- ]
13723 * if end is before start or hits it. fail.
13724 * if start is after end or hits it fail.
13726 * if either hits (but other is outside. - then it's not
13732 // @see http://www.thismuchiknow.co.uk/?p=64.
13733 rangeIntersectsNode : function(range, node)
13735 var nodeRange = node.ownerDocument.createRange();
13737 nodeRange.selectNode(node);
13739 nodeRange.selectNodeContents(node);
13742 var rangeStartRange = range.cloneRange();
13743 rangeStartRange.collapse(true);
13745 var rangeEndRange = range.cloneRange();
13746 rangeEndRange.collapse(false);
13748 var nodeStartRange = nodeRange.cloneRange();
13749 nodeStartRange.collapse(true);
13751 var nodeEndRange = nodeRange.cloneRange();
13752 nodeEndRange.collapse(false);
13754 return rangeStartRange.compareBoundaryPoints(
13755 Range.START_TO_START, nodeEndRange) == -1 &&
13756 rangeEndRange.compareBoundaryPoints(
13757 Range.START_TO_START, nodeStartRange) == 1;
13761 rangeCompareNode : function(range, node)
13763 var nodeRange = node.ownerDocument.createRange();
13765 nodeRange.selectNode(node);
13767 nodeRange.selectNodeContents(node);
13771 range.collapse(true);
13773 nodeRange.collapse(true);
13775 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13776 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13778 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13780 var nodeIsBefore = ss == 1;
13781 var nodeIsAfter = ee == -1;
13783 if (nodeIsBefore && nodeIsAfter)
13785 if (!nodeIsBefore && nodeIsAfter)
13786 return 1; //right trailed.
13788 if (nodeIsBefore && !nodeIsAfter)
13789 return 2; // left trailed.
13794 // private? - in a new class?
13795 cleanUpPaste : function()
13797 // cleans up the whole document..
13798 Roo.log('cleanuppaste');
13800 this.cleanUpChildren(this.doc.body);
13801 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13802 if (clean != this.doc.body.innerHTML) {
13803 this.doc.body.innerHTML = clean;
13808 cleanWordChars : function(input) {// change the chars to hex code
13809 var he = Roo.HtmlEditorCore;
13811 var output = input;
13812 Roo.each(he.swapCodes, function(sw) {
13813 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13815 output = output.replace(swapper, sw[1]);
13822 cleanUpChildren : function (n)
13824 if (!n.childNodes.length) {
13827 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13828 this.cleanUpChild(n.childNodes[i]);
13835 cleanUpChild : function (node)
13838 //console.log(node);
13839 if (node.nodeName == "#text") {
13840 // clean up silly Windows -- stuff?
13843 if (node.nodeName == "#comment") {
13844 node.parentNode.removeChild(node);
13845 // clean up silly Windows -- stuff?
13849 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13851 node.parentNode.removeChild(node);
13856 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13858 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13859 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13861 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13862 // remove_keep_children = true;
13865 if (remove_keep_children) {
13866 this.cleanUpChildren(node);
13867 // inserts everything just before this node...
13868 while (node.childNodes.length) {
13869 var cn = node.childNodes[0];
13870 node.removeChild(cn);
13871 node.parentNode.insertBefore(cn, node);
13873 node.parentNode.removeChild(node);
13877 if (!node.attributes || !node.attributes.length) {
13878 this.cleanUpChildren(node);
13882 function cleanAttr(n,v)
13885 if (v.match(/^\./) || v.match(/^\//)) {
13888 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13891 if (v.match(/^#/)) {
13894 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13895 node.removeAttribute(n);
13899 function cleanStyle(n,v)
13901 if (v.match(/expression/)) { //XSS?? should we even bother..
13902 node.removeAttribute(n);
13905 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13906 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13909 var parts = v.split(/;/);
13912 Roo.each(parts, function(p) {
13913 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13917 var l = p.split(':').shift().replace(/\s+/g,'');
13918 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13920 if ( cblack.indexOf(l) > -1) {
13921 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13922 //node.removeAttribute(n);
13926 // only allow 'c whitelisted system attributes'
13927 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13928 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13929 //node.removeAttribute(n);
13939 if (clean.length) {
13940 node.setAttribute(n, clean.join(';'));
13942 node.removeAttribute(n);
13948 for (var i = node.attributes.length-1; i > -1 ; i--) {
13949 var a = node.attributes[i];
13952 if (a.name.toLowerCase().substr(0,2)=='on') {
13953 node.removeAttribute(a.name);
13956 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13957 node.removeAttribute(a.name);
13960 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13961 cleanAttr(a.name,a.value); // fixme..
13964 if (a.name == 'style') {
13965 cleanStyle(a.name,a.value);
13968 /// clean up MS crap..
13969 // tecnically this should be a list of valid class'es..
13972 if (a.name == 'class') {
13973 if (a.value.match(/^Mso/)) {
13974 node.className = '';
13977 if (a.value.match(/body/)) {
13978 node.className = '';
13989 this.cleanUpChildren(node);
13995 // hide stuff that is not compatible
14009 * @event specialkey
14013 * @cfg {String} fieldClass @hide
14016 * @cfg {String} focusClass @hide
14019 * @cfg {String} autoCreate @hide
14022 * @cfg {String} inputType @hide
14025 * @cfg {String} invalidClass @hide
14028 * @cfg {String} invalidText @hide
14031 * @cfg {String} msgFx @hide
14034 * @cfg {String} validateOnBlur @hide
14038 Roo.HtmlEditorCore.white = [
14039 'area', 'br', 'img', 'input', 'hr', 'wbr',
14041 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14042 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14043 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14044 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14045 'table', 'ul', 'xmp',
14047 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14050 'dir', 'menu', 'ol', 'ul', 'dl',
14056 Roo.HtmlEditorCore.black = [
14057 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14059 'base', 'basefont', 'bgsound', 'blink', 'body',
14060 'frame', 'frameset', 'head', 'html', 'ilayer',
14061 'iframe', 'layer', 'link', 'meta', 'object',
14062 'script', 'style' ,'title', 'xml' // clean later..
14064 Roo.HtmlEditorCore.clean = [
14065 'script', 'style', 'title', 'xml'
14067 Roo.HtmlEditorCore.remove = [
14072 Roo.HtmlEditorCore.ablack = [
14076 Roo.HtmlEditorCore.aclean = [
14077 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14081 Roo.HtmlEditorCore.pwhite= [
14082 'http', 'https', 'mailto'
14085 // white listed style attributes.
14086 Roo.HtmlEditorCore.cwhite= [
14087 // 'text-align', /// default is to allow most things..
14093 // black listed style attributes.
14094 Roo.HtmlEditorCore.cblack= [
14095 // 'font-size' -- this can be set by the project
14099 Roo.HtmlEditorCore.swapCodes =[
14118 * @class Roo.bootstrap.HtmlEditor
14119 * @extends Roo.bootstrap.TextArea
14120 * Bootstrap HtmlEditor class
14123 * Create a new HtmlEditor
14124 * @param {Object} config The config object
14127 Roo.bootstrap.HtmlEditor = function(config){
14128 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14129 if (!this.toolbars) {
14130 this.toolbars = [];
14132 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14135 * @event initialize
14136 * Fires when the editor is fully initialized (including the iframe)
14137 * @param {HtmlEditor} this
14142 * Fires when the editor is first receives the focus. Any insertion must wait
14143 * until after this event.
14144 * @param {HtmlEditor} this
14148 * @event beforesync
14149 * Fires before the textarea is updated with content from the editor iframe. Return false
14150 * to cancel the sync.
14151 * @param {HtmlEditor} this
14152 * @param {String} html
14156 * @event beforepush
14157 * Fires before the iframe editor is updated with content from the textarea. Return false
14158 * to cancel the push.
14159 * @param {HtmlEditor} this
14160 * @param {String} html
14165 * Fires when the textarea is updated with content from the editor iframe.
14166 * @param {HtmlEditor} this
14167 * @param {String} html
14172 * Fires when the iframe editor is updated with content from the textarea.
14173 * @param {HtmlEditor} this
14174 * @param {String} html
14178 * @event editmodechange
14179 * Fires when the editor switches edit modes
14180 * @param {HtmlEditor} this
14181 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14183 editmodechange: true,
14185 * @event editorevent
14186 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14187 * @param {HtmlEditor} this
14191 * @event firstfocus
14192 * Fires when on first focus - needed by toolbars..
14193 * @param {HtmlEditor} this
14198 * Auto save the htmlEditor value as a file into Events
14199 * @param {HtmlEditor} this
14203 * @event savedpreview
14204 * preview the saved version of htmlEditor
14205 * @param {HtmlEditor} this
14212 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14216 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14221 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14226 * @cfg {Number} height (in pixels)
14230 * @cfg {Number} width (in pixels)
14235 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14238 stylesheets: false,
14243 // private properties
14244 validationEvent : false,
14246 initialized : false,
14249 onFocus : Roo.emptyFn,
14251 hideMode:'offsets',
14254 tbContainer : false,
14256 toolbarContainer :function() {
14257 return this.wrap.select('.x-html-editor-tb',true).first();
14261 * Protected method that will not generally be called directly. It
14262 * is called when the editor creates its toolbar. Override this method if you need to
14263 * add custom toolbar buttons.
14264 * @param {HtmlEditor} editor
14266 createToolbar : function(){
14268 Roo.log("create toolbars");
14270 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14271 this.toolbars[0].render(this.toolbarContainer());
14275 // if (!editor.toolbars || !editor.toolbars.length) {
14276 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14279 // for (var i =0 ; i < editor.toolbars.length;i++) {
14280 // editor.toolbars[i] = Roo.factory(
14281 // typeof(editor.toolbars[i]) == 'string' ?
14282 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14283 // Roo.bootstrap.HtmlEditor);
14284 // editor.toolbars[i].init(editor);
14290 onRender : function(ct, position)
14292 // Roo.log("Call onRender: " + this.xtype);
14294 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14296 this.wrap = this.inputEl().wrap({
14297 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14300 this.editorcore.onRender(ct, position);
14302 if (this.resizable) {
14303 this.resizeEl = new Roo.Resizable(this.wrap, {
14307 minHeight : this.height,
14308 height: this.height,
14309 handles : this.resizable,
14312 resize : function(r, w, h) {
14313 _t.onResize(w,h); // -something
14319 this.createToolbar(this);
14322 if(!this.width && this.resizable){
14323 this.setSize(this.wrap.getSize());
14325 if (this.resizeEl) {
14326 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14327 // should trigger onReize..
14333 onResize : function(w, h)
14335 Roo.log('resize: ' +w + ',' + h );
14336 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14340 if(this.inputEl() ){
14341 if(typeof w == 'number'){
14342 var aw = w - this.wrap.getFrameWidth('lr');
14343 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14346 if(typeof h == 'number'){
14347 var tbh = -11; // fixme it needs to tool bar size!
14348 for (var i =0; i < this.toolbars.length;i++) {
14349 // fixme - ask toolbars for heights?
14350 tbh += this.toolbars[i].el.getHeight();
14351 //if (this.toolbars[i].footer) {
14352 // tbh += this.toolbars[i].footer.el.getHeight();
14360 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14361 ah -= 5; // knock a few pixes off for look..
14362 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14366 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14367 this.editorcore.onResize(ew,eh);
14372 * Toggles the editor between standard and source edit mode.
14373 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14375 toggleSourceEdit : function(sourceEditMode)
14377 this.editorcore.toggleSourceEdit(sourceEditMode);
14379 if(this.editorcore.sourceEditMode){
14380 Roo.log('editor - showing textarea');
14383 // Roo.log(this.syncValue());
14385 this.inputEl().removeClass('hide');
14386 this.inputEl().dom.removeAttribute('tabIndex');
14387 this.inputEl().focus();
14389 Roo.log('editor - hiding textarea');
14391 // Roo.log(this.pushValue());
14394 this.inputEl().addClass('hide');
14395 this.inputEl().dom.setAttribute('tabIndex', -1);
14396 //this.deferFocus();
14399 if(this.resizable){
14400 this.setSize(this.wrap.getSize());
14403 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14406 // private (for BoxComponent)
14407 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14409 // private (for BoxComponent)
14410 getResizeEl : function(){
14414 // private (for BoxComponent)
14415 getPositionEl : function(){
14420 initEvents : function(){
14421 this.originalValue = this.getValue();
14425 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14428 // markInvalid : Roo.emptyFn,
14430 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14433 // clearInvalid : Roo.emptyFn,
14435 setValue : function(v){
14436 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14437 this.editorcore.pushValue();
14442 deferFocus : function(){
14443 this.focus.defer(10, this);
14447 focus : function(){
14448 this.editorcore.focus();
14454 onDestroy : function(){
14460 for (var i =0; i < this.toolbars.length;i++) {
14461 // fixme - ask toolbars for heights?
14462 this.toolbars[i].onDestroy();
14465 this.wrap.dom.innerHTML = '';
14466 this.wrap.remove();
14471 onFirstFocus : function(){
14472 //Roo.log("onFirstFocus");
14473 this.editorcore.onFirstFocus();
14474 for (var i =0; i < this.toolbars.length;i++) {
14475 this.toolbars[i].onFirstFocus();
14481 syncValue : function()
14483 this.editorcore.syncValue();
14486 pushValue : function()
14488 this.editorcore.pushValue();
14492 // hide stuff that is not compatible
14506 * @event specialkey
14510 * @cfg {String} fieldClass @hide
14513 * @cfg {String} focusClass @hide
14516 * @cfg {String} autoCreate @hide
14519 * @cfg {String} inputType @hide
14522 * @cfg {String} invalidClass @hide
14525 * @cfg {String} invalidText @hide
14528 * @cfg {String} msgFx @hide
14531 * @cfg {String} validateOnBlur @hide
14542 * @class Roo.bootstrap.HtmlEditorToolbar1
14547 new Roo.bootstrap.HtmlEditor({
14550 new Roo.bootstrap.HtmlEditorToolbar1({
14551 disable : { fonts: 1 , format: 1, ..., ... , ...],
14557 * @cfg {Object} disable List of elements to disable..
14558 * @cfg {Array} btns List of additional buttons.
14562 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14565 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14568 Roo.apply(this, config);
14570 // default disabled, based on 'good practice'..
14571 this.disable = this.disable || {};
14572 Roo.applyIf(this.disable, {
14575 specialElements : true
14577 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14579 this.editor = config.editor;
14580 this.editorcore = config.editor.editorcore;
14582 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14584 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14585 // dont call parent... till later.
14587 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14593 editorcore : false,
14598 "h1","h2","h3","h4","h5","h6",
14600 "abbr", "acronym", "address", "cite", "samp", "var",
14604 onRender : function(ct, position)
14606 // Roo.log("Call onRender: " + this.xtype);
14608 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14610 this.el.dom.style.marginBottom = '0';
14612 var editorcore = this.editorcore;
14613 var editor= this.editor;
14616 var btn = function(id,cmd , toggle, handler){
14618 var event = toggle ? 'toggle' : 'click';
14623 xns: Roo.bootstrap,
14626 enableToggle:toggle !== false,
14628 pressed : toggle ? false : null,
14631 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14632 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14641 xns: Roo.bootstrap,
14642 glyphicon : 'font',
14646 xns: Roo.bootstrap,
14650 Roo.each(this.formats, function(f) {
14651 style.menu.items.push({
14653 xns: Roo.bootstrap,
14654 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14659 editorcore.insertTag(this.tagname);
14666 children.push(style);
14669 btn('bold',false,true);
14670 btn('italic',false,true);
14671 btn('align-left', 'justifyleft',true);
14672 btn('align-center', 'justifycenter',true);
14673 btn('align-right' , 'justifyright',true);
14674 btn('link', false, false, function(btn) {
14675 //Roo.log("create link?");
14676 var url = prompt(this.createLinkText, this.defaultLinkValue);
14677 if(url && url != 'http:/'+'/'){
14678 this.editorcore.relayCmd('createlink', url);
14681 btn('list','insertunorderedlist',true);
14682 btn('pencil', false,true, function(btn){
14685 this.toggleSourceEdit(btn.pressed);
14691 xns: Roo.bootstrap,
14696 xns: Roo.bootstrap,
14701 cog.menu.items.push({
14703 xns: Roo.bootstrap,
14704 html : Clean styles,
14709 editorcore.insertTag(this.tagname);
14718 this.xtype = 'Navbar';
14720 for(var i=0;i< children.length;i++) {
14722 this.buttons.add(this.addxtypeChild(children[i]));
14726 editor.on('editorevent', this.updateToolbar, this);
14728 onBtnClick : function(id)
14730 this.editorcore.relayCmd(id);
14731 this.editorcore.focus();
14735 * Protected method that will not generally be called directly. It triggers
14736 * a toolbar update by reading the markup state of the current selection in the editor.
14738 updateToolbar: function(){
14740 if(!this.editorcore.activated){
14741 this.editor.onFirstFocus(); // is this neeed?
14745 var btns = this.buttons;
14746 var doc = this.editorcore.doc;
14747 btns.get('bold').setActive(doc.queryCommandState('bold'));
14748 btns.get('italic').setActive(doc.queryCommandState('italic'));
14749 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14751 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14752 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14753 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14755 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14756 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14759 var ans = this.editorcore.getAllAncestors();
14760 if (this.formatCombo) {
14763 var store = this.formatCombo.store;
14764 this.formatCombo.setValue("");
14765 for (var i =0; i < ans.length;i++) {
14766 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14768 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14776 // hides menus... - so this cant be on a menu...
14777 Roo.bootstrap.MenuMgr.hideAll();
14779 Roo.bootstrap.MenuMgr.hideAll();
14780 //this.editorsyncValue();
14782 onFirstFocus: function() {
14783 this.buttons.each(function(item){
14787 toggleSourceEdit : function(sourceEditMode){
14790 if(sourceEditMode){
14791 Roo.log("disabling buttons");
14792 this.buttons.each( function(item){
14793 if(item.cmd != 'pencil'){
14799 Roo.log("enabling buttons");
14800 if(this.editorcore.initialized){
14801 this.buttons.each( function(item){
14807 Roo.log("calling toggole on editor");
14808 // tell the editor that it's been pressed..
14809 this.editor.toggleSourceEdit(sourceEditMode);
14819 * @class Roo.bootstrap.Table.AbstractSelectionModel
14820 * @extends Roo.util.Observable
14821 * Abstract base class for grid SelectionModels. It provides the interface that should be
14822 * implemented by descendant classes. This class should not be directly instantiated.
14825 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14826 this.locked = false;
14827 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14831 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14832 /** @ignore Called by the grid automatically. Do not call directly. */
14833 init : function(grid){
14839 * Locks the selections.
14842 this.locked = true;
14846 * Unlocks the selections.
14848 unlock : function(){
14849 this.locked = false;
14853 * Returns true if the selections are locked.
14854 * @return {Boolean}
14856 isLocked : function(){
14857 return this.locked;
14861 * @class Roo.bootstrap.Table.ColumnModel
14862 * @extends Roo.util.Observable
14863 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14864 * the columns in the table.
14867 * @param {Object} config An Array of column config objects. See this class's
14868 * config objects for details.
14870 Roo.bootstrap.Table.ColumnModel = function(config){
14872 * The config passed into the constructor
14874 this.config = config;
14877 // if no id, create one
14878 // if the column does not have a dataIndex mapping,
14879 // map it to the order it is in the config
14880 for(var i = 0, len = config.length; i < len; i++){
14882 if(typeof c.dataIndex == "undefined"){
14885 if(typeof c.renderer == "string"){
14886 c.renderer = Roo.util.Format[c.renderer];
14888 if(typeof c.id == "undefined"){
14891 // if(c.editor && c.editor.xtype){
14892 // c.editor = Roo.factory(c.editor, Roo.grid);
14894 // if(c.editor && c.editor.isFormField){
14895 // c.editor = new Roo.grid.GridEditor(c.editor);
14898 this.lookup[c.id] = c;
14902 * The width of columns which have no width specified (defaults to 100)
14905 this.defaultWidth = 100;
14908 * Default sortable of columns which have no sortable specified (defaults to false)
14911 this.defaultSortable = false;
14915 * @event widthchange
14916 * Fires when the width of a column changes.
14917 * @param {ColumnModel} this
14918 * @param {Number} columnIndex The column index
14919 * @param {Number} newWidth The new width
14921 "widthchange": true,
14923 * @event headerchange
14924 * Fires when the text of a header changes.
14925 * @param {ColumnModel} this
14926 * @param {Number} columnIndex The column index
14927 * @param {Number} newText The new header text
14929 "headerchange": true,
14931 * @event hiddenchange
14932 * Fires when a column is hidden or "unhidden".
14933 * @param {ColumnModel} this
14934 * @param {Number} columnIndex The column index
14935 * @param {Boolean} hidden true if hidden, false otherwise
14937 "hiddenchange": true,
14939 * @event columnmoved
14940 * Fires when a column is moved.
14941 * @param {ColumnModel} this
14942 * @param {Number} oldIndex
14943 * @param {Number} newIndex
14945 "columnmoved" : true,
14947 * @event columlockchange
14948 * Fires when a column's locked state is changed
14949 * @param {ColumnModel} this
14950 * @param {Number} colIndex
14951 * @param {Boolean} locked true if locked
14953 "columnlockchange" : true
14955 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14957 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14959 * @cfg {String} header The header text to display in the Grid view.
14962 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14963 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14964 * specified, the column's index is used as an index into the Record's data Array.
14967 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14968 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14971 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14972 * Defaults to the value of the {@link #defaultSortable} property.
14973 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14976 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14979 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14982 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14985 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14988 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14989 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14990 * default renderer uses the raw data value.
14993 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14997 * Returns the id of the column at the specified index.
14998 * @param {Number} index The column index
14999 * @return {String} the id
15001 getColumnId : function(index){
15002 return this.config[index].id;
15006 * Returns the column for a specified id.
15007 * @param {String} id The column id
15008 * @return {Object} the column
15010 getColumnById : function(id){
15011 return this.lookup[id];
15016 * Returns the column for a specified dataIndex.
15017 * @param {String} dataIndex The column dataIndex
15018 * @return {Object|Boolean} the column or false if not found
15020 getColumnByDataIndex: function(dataIndex){
15021 var index = this.findColumnIndex(dataIndex);
15022 return index > -1 ? this.config[index] : false;
15026 * Returns the index for a specified column id.
15027 * @param {String} id The column id
15028 * @return {Number} the index, or -1 if not found
15030 getIndexById : function(id){
15031 for(var i = 0, len = this.config.length; i < len; i++){
15032 if(this.config[i].id == id){
15040 * Returns the index for a specified column dataIndex.
15041 * @param {String} dataIndex The column dataIndex
15042 * @return {Number} the index, or -1 if not found
15045 findColumnIndex : function(dataIndex){
15046 for(var i = 0, len = this.config.length; i < len; i++){
15047 if(this.config[i].dataIndex == dataIndex){
15055 moveColumn : function(oldIndex, newIndex){
15056 var c = this.config[oldIndex];
15057 this.config.splice(oldIndex, 1);
15058 this.config.splice(newIndex, 0, c);
15059 this.dataMap = null;
15060 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15063 isLocked : function(colIndex){
15064 return this.config[colIndex].locked === true;
15067 setLocked : function(colIndex, value, suppressEvent){
15068 if(this.isLocked(colIndex) == value){
15071 this.config[colIndex].locked = value;
15072 if(!suppressEvent){
15073 this.fireEvent("columnlockchange", this, colIndex, value);
15077 getTotalLockedWidth : function(){
15078 var totalWidth = 0;
15079 for(var i = 0; i < this.config.length; i++){
15080 if(this.isLocked(i) && !this.isHidden(i)){
15081 this.totalWidth += this.getColumnWidth(i);
15087 getLockedCount : function(){
15088 for(var i = 0, len = this.config.length; i < len; i++){
15089 if(!this.isLocked(i)){
15096 * Returns the number of columns.
15099 getColumnCount : function(visibleOnly){
15100 if(visibleOnly === true){
15102 for(var i = 0, len = this.config.length; i < len; i++){
15103 if(!this.isHidden(i)){
15109 return this.config.length;
15113 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15114 * @param {Function} fn
15115 * @param {Object} scope (optional)
15116 * @return {Array} result
15118 getColumnsBy : function(fn, scope){
15120 for(var i = 0, len = this.config.length; i < len; i++){
15121 var c = this.config[i];
15122 if(fn.call(scope||this, c, i) === true){
15130 * Returns true if the specified column is sortable.
15131 * @param {Number} col The column index
15132 * @return {Boolean}
15134 isSortable : function(col){
15135 if(typeof this.config[col].sortable == "undefined"){
15136 return this.defaultSortable;
15138 return this.config[col].sortable;
15142 * Returns the rendering (formatting) function defined for the column.
15143 * @param {Number} col The column index.
15144 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15146 getRenderer : function(col){
15147 if(!this.config[col].renderer){
15148 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15150 return this.config[col].renderer;
15154 * Sets the rendering (formatting) function for a column.
15155 * @param {Number} col The column index
15156 * @param {Function} fn The function to use to process the cell's raw data
15157 * to return HTML markup for the grid view. The render function is called with
15158 * the following parameters:<ul>
15159 * <li>Data value.</li>
15160 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15161 * <li>css A CSS style string to apply to the table cell.</li>
15162 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15163 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15164 * <li>Row index</li>
15165 * <li>Column index</li>
15166 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15168 setRenderer : function(col, fn){
15169 this.config[col].renderer = fn;
15173 * Returns the width for the specified column.
15174 * @param {Number} col The column index
15177 getColumnWidth : function(col){
15178 return this.config[col].width * 1 || this.defaultWidth;
15182 * Sets the width for a column.
15183 * @param {Number} col The column index
15184 * @param {Number} width The new width
15186 setColumnWidth : function(col, width, suppressEvent){
15187 this.config[col].width = width;
15188 this.totalWidth = null;
15189 if(!suppressEvent){
15190 this.fireEvent("widthchange", this, col, width);
15195 * Returns the total width of all columns.
15196 * @param {Boolean} includeHidden True to include hidden column widths
15199 getTotalWidth : function(includeHidden){
15200 if(!this.totalWidth){
15201 this.totalWidth = 0;
15202 for(var i = 0, len = this.config.length; i < len; i++){
15203 if(includeHidden || !this.isHidden(i)){
15204 this.totalWidth += this.getColumnWidth(i);
15208 return this.totalWidth;
15212 * Returns the header for the specified column.
15213 * @param {Number} col The column index
15216 getColumnHeader : function(col){
15217 return this.config[col].header;
15221 * Sets the header for a column.
15222 * @param {Number} col The column index
15223 * @param {String} header The new header
15225 setColumnHeader : function(col, header){
15226 this.config[col].header = header;
15227 this.fireEvent("headerchange", this, col, header);
15231 * Returns the tooltip for the specified column.
15232 * @param {Number} col The column index
15235 getColumnTooltip : function(col){
15236 return this.config[col].tooltip;
15239 * Sets the tooltip for a column.
15240 * @param {Number} col The column index
15241 * @param {String} tooltip The new tooltip
15243 setColumnTooltip : function(col, tooltip){
15244 this.config[col].tooltip = tooltip;
15248 * Returns the dataIndex for the specified column.
15249 * @param {Number} col The column index
15252 getDataIndex : function(col){
15253 return this.config[col].dataIndex;
15257 * Sets the dataIndex for a column.
15258 * @param {Number} col The column index
15259 * @param {Number} dataIndex The new dataIndex
15261 setDataIndex : function(col, dataIndex){
15262 this.config[col].dataIndex = dataIndex;
15268 * Returns true if the cell is editable.
15269 * @param {Number} colIndex The column index
15270 * @param {Number} rowIndex The row index
15271 * @return {Boolean}
15273 isCellEditable : function(colIndex, rowIndex){
15274 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15278 * Returns the editor defined for the cell/column.
15279 * return false or null to disable editing.
15280 * @param {Number} colIndex The column index
15281 * @param {Number} rowIndex The row index
15284 getCellEditor : function(colIndex, rowIndex){
15285 return this.config[colIndex].editor;
15289 * Sets if a column is editable.
15290 * @param {Number} col The column index
15291 * @param {Boolean} editable True if the column is editable
15293 setEditable : function(col, editable){
15294 this.config[col].editable = editable;
15299 * Returns true if the column is hidden.
15300 * @param {Number} colIndex The column index
15301 * @return {Boolean}
15303 isHidden : function(colIndex){
15304 return this.config[colIndex].hidden;
15309 * Returns true if the column width cannot be changed
15311 isFixed : function(colIndex){
15312 return this.config[colIndex].fixed;
15316 * Returns true if the column can be resized
15317 * @return {Boolean}
15319 isResizable : function(colIndex){
15320 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15323 * Sets if a column is hidden.
15324 * @param {Number} colIndex The column index
15325 * @param {Boolean} hidden True if the column is hidden
15327 setHidden : function(colIndex, hidden){
15328 this.config[colIndex].hidden = hidden;
15329 this.totalWidth = null;
15330 this.fireEvent("hiddenchange", this, colIndex, hidden);
15334 * Sets the editor for a column.
15335 * @param {Number} col The column index
15336 * @param {Object} editor The editor object
15338 setEditor : function(col, editor){
15339 this.config[col].editor = editor;
15343 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15344 if(typeof value == "string" && value.length < 1){
15350 // Alias for backwards compatibility
15351 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15354 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15355 * @class Roo.bootstrap.Table.RowSelectionModel
15356 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15357 * It supports multiple selections and keyboard selection/navigation.
15359 * @param {Object} config
15362 Roo.bootstrap.Table.RowSelectionModel = function(config){
15363 Roo.apply(this, config);
15364 this.selections = new Roo.util.MixedCollection(false, function(o){
15369 this.lastActive = false;
15373 * @event selectionchange
15374 * Fires when the selection changes
15375 * @param {SelectionModel} this
15377 "selectionchange" : true,
15379 * @event afterselectionchange
15380 * Fires after the selection changes (eg. by key press or clicking)
15381 * @param {SelectionModel} this
15383 "afterselectionchange" : true,
15385 * @event beforerowselect
15386 * Fires when a row is selected being selected, return false to cancel.
15387 * @param {SelectionModel} this
15388 * @param {Number} rowIndex The selected index
15389 * @param {Boolean} keepExisting False if other selections will be cleared
15391 "beforerowselect" : true,
15394 * Fires when a row is selected.
15395 * @param {SelectionModel} this
15396 * @param {Number} rowIndex The selected index
15397 * @param {Roo.data.Record} r The record
15399 "rowselect" : true,
15401 * @event rowdeselect
15402 * Fires when a row is deselected.
15403 * @param {SelectionModel} this
15404 * @param {Number} rowIndex The selected index
15406 "rowdeselect" : true
15408 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15409 this.locked = false;
15412 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15414 * @cfg {Boolean} singleSelect
15415 * True to allow selection of only one row at a time (defaults to false)
15417 singleSelect : false,
15420 initEvents : function(){
15422 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15423 this.grid.on("mousedown", this.handleMouseDown, this);
15424 }else{ // allow click to work like normal
15425 this.grid.on("rowclick", this.handleDragableRowClick, this);
15428 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15429 "up" : function(e){
15431 this.selectPrevious(e.shiftKey);
15432 }else if(this.last !== false && this.lastActive !== false){
15433 var last = this.last;
15434 this.selectRange(this.last, this.lastActive-1);
15435 this.grid.getView().focusRow(this.lastActive);
15436 if(last !== false){
15440 this.selectFirstRow();
15442 this.fireEvent("afterselectionchange", this);
15444 "down" : function(e){
15446 this.selectNext(e.shiftKey);
15447 }else if(this.last !== false && this.lastActive !== false){
15448 var last = this.last;
15449 this.selectRange(this.last, this.lastActive+1);
15450 this.grid.getView().focusRow(this.lastActive);
15451 if(last !== false){
15455 this.selectFirstRow();
15457 this.fireEvent("afterselectionchange", this);
15462 var view = this.grid.view;
15463 view.on("refresh", this.onRefresh, this);
15464 view.on("rowupdated", this.onRowUpdated, this);
15465 view.on("rowremoved", this.onRemove, this);
15469 onRefresh : function(){
15470 var ds = this.grid.dataSource, i, v = this.grid.view;
15471 var s = this.selections;
15472 s.each(function(r){
15473 if((i = ds.indexOfId(r.id)) != -1){
15482 onRemove : function(v, index, r){
15483 this.selections.remove(r);
15487 onRowUpdated : function(v, index, r){
15488 if(this.isSelected(r)){
15489 v.onRowSelect(index);
15495 * @param {Array} records The records to select
15496 * @param {Boolean} keepExisting (optional) True to keep existing selections
15498 selectRecords : function(records, keepExisting){
15500 this.clearSelections();
15502 var ds = this.grid.dataSource;
15503 for(var i = 0, len = records.length; i < len; i++){
15504 this.selectRow(ds.indexOf(records[i]), true);
15509 * Gets the number of selected rows.
15512 getCount : function(){
15513 return this.selections.length;
15517 * Selects the first row in the grid.
15519 selectFirstRow : function(){
15524 * Select the last row.
15525 * @param {Boolean} keepExisting (optional) True to keep existing selections
15527 selectLastRow : function(keepExisting){
15528 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15532 * Selects the row immediately following the last selected row.
15533 * @param {Boolean} keepExisting (optional) True to keep existing selections
15535 selectNext : function(keepExisting){
15536 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15537 this.selectRow(this.last+1, keepExisting);
15538 this.grid.getView().focusRow(this.last);
15543 * Selects the row that precedes the last selected row.
15544 * @param {Boolean} keepExisting (optional) True to keep existing selections
15546 selectPrevious : function(keepExisting){
15548 this.selectRow(this.last-1, keepExisting);
15549 this.grid.getView().focusRow(this.last);
15554 * Returns the selected records
15555 * @return {Array} Array of selected records
15557 getSelections : function(){
15558 return [].concat(this.selections.items);
15562 * Returns the first selected record.
15565 getSelected : function(){
15566 return this.selections.itemAt(0);
15571 * Clears all selections.
15573 clearSelections : function(fast){
15574 if(this.locked) return;
15576 var ds = this.grid.dataSource;
15577 var s = this.selections;
15578 s.each(function(r){
15579 this.deselectRow(ds.indexOfId(r.id));
15583 this.selections.clear();
15590 * Selects all rows.
15592 selectAll : function(){
15593 if(this.locked) return;
15594 this.selections.clear();
15595 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15596 this.selectRow(i, true);
15601 * Returns True if there is a selection.
15602 * @return {Boolean}
15604 hasSelection : function(){
15605 return this.selections.length > 0;
15609 * Returns True if the specified row is selected.
15610 * @param {Number/Record} record The record or index of the record to check
15611 * @return {Boolean}
15613 isSelected : function(index){
15614 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15615 return (r && this.selections.key(r.id) ? true : false);
15619 * Returns True if the specified record id is selected.
15620 * @param {String} id The id of record to check
15621 * @return {Boolean}
15623 isIdSelected : function(id){
15624 return (this.selections.key(id) ? true : false);
15628 handleMouseDown : function(e, t){
15629 var view = this.grid.getView(), rowIndex;
15630 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15633 if(e.shiftKey && this.last !== false){
15634 var last = this.last;
15635 this.selectRange(last, rowIndex, e.ctrlKey);
15636 this.last = last; // reset the last
15637 view.focusRow(rowIndex);
15639 var isSelected = this.isSelected(rowIndex);
15640 if(e.button !== 0 && isSelected){
15641 view.focusRow(rowIndex);
15642 }else if(e.ctrlKey && isSelected){
15643 this.deselectRow(rowIndex);
15644 }else if(!isSelected){
15645 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15646 view.focusRow(rowIndex);
15649 this.fireEvent("afterselectionchange", this);
15652 handleDragableRowClick : function(grid, rowIndex, e)
15654 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15655 this.selectRow(rowIndex, false);
15656 grid.view.focusRow(rowIndex);
15657 this.fireEvent("afterselectionchange", this);
15662 * Selects multiple rows.
15663 * @param {Array} rows Array of the indexes of the row to select
15664 * @param {Boolean} keepExisting (optional) True to keep existing selections
15666 selectRows : function(rows, keepExisting){
15668 this.clearSelections();
15670 for(var i = 0, len = rows.length; i < len; i++){
15671 this.selectRow(rows[i], true);
15676 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15677 * @param {Number} startRow The index of the first row in the range
15678 * @param {Number} endRow The index of the last row in the range
15679 * @param {Boolean} keepExisting (optional) True to retain existing selections
15681 selectRange : function(startRow, endRow, keepExisting){
15682 if(this.locked) return;
15684 this.clearSelections();
15686 if(startRow <= endRow){
15687 for(var i = startRow; i <= endRow; i++){
15688 this.selectRow(i, true);
15691 for(var i = startRow; i >= endRow; i--){
15692 this.selectRow(i, true);
15698 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15699 * @param {Number} startRow The index of the first row in the range
15700 * @param {Number} endRow The index of the last row in the range
15702 deselectRange : function(startRow, endRow, preventViewNotify){
15703 if(this.locked) return;
15704 for(var i = startRow; i <= endRow; i++){
15705 this.deselectRow(i, preventViewNotify);
15711 * @param {Number} row The index of the row to select
15712 * @param {Boolean} keepExisting (optional) True to keep existing selections
15714 selectRow : function(index, keepExisting, preventViewNotify){
15715 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15716 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15717 if(!keepExisting || this.singleSelect){
15718 this.clearSelections();
15720 var r = this.grid.dataSource.getAt(index);
15721 this.selections.add(r);
15722 this.last = this.lastActive = index;
15723 if(!preventViewNotify){
15724 this.grid.getView().onRowSelect(index);
15726 this.fireEvent("rowselect", this, index, r);
15727 this.fireEvent("selectionchange", this);
15733 * @param {Number} row The index of the row to deselect
15735 deselectRow : function(index, preventViewNotify){
15736 if(this.locked) return;
15737 if(this.last == index){
15740 if(this.lastActive == index){
15741 this.lastActive = false;
15743 var r = this.grid.dataSource.getAt(index);
15744 this.selections.remove(r);
15745 if(!preventViewNotify){
15746 this.grid.getView().onRowDeselect(index);
15748 this.fireEvent("rowdeselect", this, index);
15749 this.fireEvent("selectionchange", this);
15753 restoreLast : function(){
15755 this.last = this._last;
15760 acceptsNav : function(row, col, cm){
15761 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15765 onEditorKey : function(field, e){
15766 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15771 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15773 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15775 }else if(k == e.ENTER && !e.ctrlKey){
15779 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15781 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15783 }else if(k == e.ESC){
15787 g.startEditing(newCell[0], newCell[1]);
15798 * @class Roo.bootstrap.MessageBar
15799 * @extends Roo.bootstrap.Component
15800 * Bootstrap MessageBar class
15801 * @cfg {String} html contents of the MessageBar
15802 * @cfg {String} weight (info | success | warning | danger) default info
15803 * @cfg {String} beforeClass insert the bar before the given class
15804 * @cfg {Boolean} closable (true | false) default false
15805 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15808 * Create a new Element
15809 * @param {Object} config The config object
15812 Roo.bootstrap.MessageBar = function(config){
15813 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15816 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15822 beforeClass: 'bootstrap-sticky-wrap',
15824 getAutoCreate : function(){
15828 cls: 'alert alert-dismissable alert-' + this.weight,
15833 html: this.html || ''
15839 cfg.cls += ' alert-messages-fixed';
15853 onRender : function(ct, position)
15855 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15858 var cfg = Roo.apply({}, this.getAutoCreate());
15862 cfg.cls += ' ' + this.cls;
15865 cfg.style = this.style;
15867 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15869 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15872 this.el.select('>button.close').on('click', this.hide, this);
15878 if (!this.rendered) {
15884 this.fireEvent('show', this);
15890 if (!this.rendered) {
15896 this.fireEvent('hide', this);
15899 update : function()
15901 // var e = this.el.dom.firstChild;
15903 // if(this.closable){
15904 // e = e.nextSibling;
15907 // e.data = this.html || '';
15909 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';