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)
307 Roo.log("Roo.bootstrap.Body - onRender");
308 if (this.cls && this.cls.length) {
309 Roo.get(document.body).addClass(this.cls);
328 * @class Roo.bootstrap.ButtonGroup
329 * @extends Roo.bootstrap.Component
330 * Bootstrap ButtonGroup class
331 * @cfg {String} size lg | sm | xs (default empty normal)
332 * @cfg {String} align vertical | justified (default none)
333 * @cfg {String} direction up | down (default down)
334 * @cfg {Boolean} toolbar false | true
335 * @cfg {Boolean} btn true | false
340 * @param {Object} config The config object
343 Roo.bootstrap.ButtonGroup = function(config){
344 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
347 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
355 getAutoCreate : function(){
361 cfg.html = this.html || cfg.html;
372 if (['vertical','justified'].indexOf(this.align)!==-1) {
373 cfg.cls = 'btn-group-' + this.align;
375 if (this.align == 'justified') {
376 console.log(this.items);
380 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
381 cfg.cls += ' btn-group-' + this.size;
384 if (this.direction == 'up') {
385 cfg.cls += ' dropup' ;
401 * @class Roo.bootstrap.Button
402 * @extends Roo.bootstrap.Component
403 * Bootstrap Button class
404 * @cfg {String} html The button content
405 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
406 * @cfg {String} size empty | lg | sm | xs
407 * @cfg {String} tag empty | a | input | submit
408 * @cfg {String} href empty or href
409 * @cfg {Boolean} disabled false | true
410 * @cfg {Boolean} isClose false | true
411 * @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
412 * @cfg {String} badge text for badge
413 * @cfg {String} theme default (or empty) | glow
414 * @cfg {Boolean} inverse false | true
415 * @cfg {Boolean} toggle false | true
416 * @cfg {String} ontext text for on toggle state
417 * @cfg {String} offtext text for off toggle state
418 * @cfg {Boolean} defaulton true | false
419 * @cfg {Boolean} preventDefault (true | false) default true
420 * @cfg {Boolean} removeClass true | false remove the standard class..
421 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
424 * Create a new button
425 * @param {Object} config The config object
429 Roo.bootstrap.Button = function(config){
430 Roo.bootstrap.Button.superclass.constructor.call(this, config);
435 * When a butotn is pressed
436 * @param {Roo.EventObject} e
441 * After the button has been toggles
442 * @param {Roo.EventObject} e
443 * @param {boolean} pressed (also available as button.pressed)
449 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
467 preventDefault: true,
476 getAutoCreate : function(){
484 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
485 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
490 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
492 if (this.toggle == true) {
495 cls: 'slider-frame roo-button',
500 'data-off-text':'OFF',
501 cls: 'slider-button',
507 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
508 cfg.cls += ' '+this.weight;
517 cfg["aria-hidden"] = true;
519 cfg.html = "×";
525 if (this.theme==='default') {
526 cfg.cls = 'btn roo-button';
528 //if (this.parentType != 'Navbar') {
529 this.weight = this.weight.length ? this.weight : 'default';
531 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
533 cfg.cls += ' btn-' + this.weight;
535 } else if (this.theme==='glow') {
538 cfg.cls = 'btn-glow roo-button';
540 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
542 cfg.cls += ' ' + this.weight;
548 this.cls += ' inverse';
553 cfg.cls += ' active';
557 cfg.disabled = 'disabled';
561 Roo.log('changing to ul' );
563 this.glyphicon = 'caret';
566 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
568 //gsRoo.log(this.parentType);
569 if (this.parentType === 'Navbar' && !this.parent().bar) {
570 Roo.log('changing to li?');
579 href : this.href || '#'
582 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
583 cfg.cls += ' dropdown';
590 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
592 if (this.glyphicon) {
593 cfg.html = ' ' + cfg.html;
598 cls: 'glyphicon glyphicon-' + this.glyphicon
608 // cfg.cls='btn roo-button';
612 var value = cfg.html;
617 cls: 'glyphicon glyphicon-' + this.glyphicon,
636 cfg.cls += ' dropdown';
637 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
640 if (cfg.tag !== 'a' && this.href !== '') {
641 throw "Tag must be a to set href.";
642 } else if (this.href.length > 0) {
643 cfg.href = this.href;
646 if(this.removeClass){
651 cfg.target = this.target;
656 initEvents: function() {
657 // Roo.log('init events?');
658 // Roo.log(this.el.dom);
659 if (this.el.hasClass('roo-button')) {
660 this.el.on('click', this.onClick, this);
662 this.el.select('.roo-button').on('click', this.onClick, this);
665 if(this.removeClass){
666 this.el.on('click', this.onClick, this);
669 this.el.enableDisplayMode();
672 onClick : function(e)
678 Roo.log('button on click ');
679 if(this.preventDefault){
682 if (this.pressed === true || this.pressed === false) {
683 this.pressed = !this.pressed;
684 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
685 this.fireEvent('toggle', this, e, this.pressed);
689 this.fireEvent('click', this, e);
693 * Enables this button
697 this.disabled = false;
698 this.el.removeClass('disabled');
702 * Disable this button
706 this.disabled = true;
707 this.el.addClass('disabled');
710 * sets the active state on/off,
711 * @param {Boolean} state (optional) Force a particular state
713 setActive : function(v) {
715 this.el[v ? 'addClass' : 'removeClass']('active');
718 * toggles the current active state
720 toggleActive : function()
722 var active = this.el.hasClass('active');
723 this.setActive(!active);
727 setText : function(str)
729 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
752 * @class Roo.bootstrap.Column
753 * @extends Roo.bootstrap.Component
754 * Bootstrap Column class
755 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
756 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
757 * @cfg {Number} md colspan out of 12 for computer-sized screens
758 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
759 * @cfg {String} html content of column.
762 * Create a new Column
763 * @param {Object} config The config object
766 Roo.bootstrap.Column = function(config){
767 Roo.bootstrap.Column.superclass.constructor.call(this, config);
770 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
779 getAutoCreate : function(){
780 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
788 ['xs','sm','md','lg'].map(function(size){
789 if (settings[size]) {
790 cfg.cls += ' col-' + size + '-' + settings[size];
793 if (this.html.length) {
794 cfg.html = this.html;
813 * @class Roo.bootstrap.Container
814 * @extends Roo.bootstrap.Component
815 * Bootstrap Container class
816 * @cfg {Boolean} jumbotron is it a jumbotron element
817 * @cfg {String} html content of element
818 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
819 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
820 * @cfg {String} header content of header (for panel)
821 * @cfg {String} footer content of footer (for panel)
822 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
825 * Create a new Container
826 * @param {Object} config The config object
829 Roo.bootstrap.Container = function(config){
830 Roo.bootstrap.Container.superclass.constructor.call(this, config);
833 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
843 getChildContainer : function() {
849 if (this.panel.length) {
850 return this.el.select('.panel-body',true).first();
857 getAutoCreate : function(){
863 if (this.jumbotron) {
864 cfg.cls = 'jumbotron';
867 cfg.cls = this.cls + '';
870 if (this.sticky.length) {
872 var bd = Roo.get(document.body);
873 if (!bd.hasClass('bootstrap-sticky')) {
874 bd.addClass('bootstrap-sticky');
875 Roo.select('html',true).setStyle('height', '100%');
878 cfg.cls += 'bootstrap-sticky-' + this.sticky;
882 if (this.well.length) {
886 cfg.cls +=' well well-' +this.well;
896 if (this.panel.length) {
897 cfg.cls += ' panel panel-' + this.panel;
899 if (this.header.length) {
902 cls : 'panel-heading',
918 if (this.footer.length) {
920 cls : 'panel-footer',
928 body.html = this.html || cfg.html;
930 if (!cfg.cls.length) {
931 cfg.cls = 'container';
948 * @class Roo.bootstrap.Img
949 * @extends Roo.bootstrap.Component
950 * Bootstrap Img class
951 * @cfg {Boolean} imgResponsive false | true
952 * @cfg {String} border rounded | circle | thumbnail
953 * @cfg {String} src image source
954 * @cfg {String} alt image alternative text
955 * @cfg {String} href a tag href
956 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
960 * @param {Object} config The config object
963 Roo.bootstrap.Img = function(config){
964 Roo.bootstrap.Img.superclass.constructor.call(this, config);
970 * The img click event for the img.
971 * @param {Roo.EventObject} e
977 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
985 getAutoCreate : function(){
989 cls: (this.imgResponsive) ? 'img-responsive' : '',
993 cfg.html = this.html || cfg.html;
995 cfg.src = this.src || cfg.src;
997 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
998 cfg.cls += ' img-' + this.border;
1015 a.target = this.target;
1021 return (this.href) ? a : cfg;
1024 initEvents: function() {
1027 this.el.on('click', this.onClick, this);
1031 onClick : function(e)
1033 Roo.log('img onclick');
1034 this.fireEvent('click', this, e);
1047 * @class Roo.bootstrap.Header
1048 * @extends Roo.bootstrap.Component
1049 * Bootstrap Header class
1050 * @cfg {String} html content of header
1051 * @cfg {Number} level (1|2|3|4|5|6) default 1
1054 * Create a new Header
1055 * @param {Object} config The config object
1059 Roo.bootstrap.Header = function(config){
1060 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1063 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1071 getAutoCreate : function(){
1074 tag: 'h' + (1 *this.level),
1075 html: this.html || 'fill in html'
1087 * Ext JS Library 1.1.1
1088 * Copyright(c) 2006-2007, Ext JS, LLC.
1090 * Originally Released Under LGPL - original licence link has changed is not relivant.
1093 * <script type="text/javascript">
1097 * @class Roo.bootstrap.MenuMgr
1098 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1101 Roo.bootstrap.MenuMgr = function(){
1102 var menus, active, groups = {}, attached = false, lastShow = new Date();
1104 // private - called when first menu is created
1107 active = new Roo.util.MixedCollection();
1108 Roo.get(document).addKeyListener(27, function(){
1109 if(active.length > 0){
1117 if(active && active.length > 0){
1118 var c = active.clone();
1128 if(active.length < 1){
1129 Roo.get(document).un("mouseup", onMouseDown);
1137 var last = active.last();
1138 lastShow = new Date();
1141 Roo.get(document).on("mouseup", onMouseDown);
1146 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1147 m.parentMenu.activeChild = m;
1148 }else if(last && last.isVisible()){
1149 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1154 function onBeforeHide(m){
1156 m.activeChild.hide();
1158 if(m.autoHideTimer){
1159 clearTimeout(m.autoHideTimer);
1160 delete m.autoHideTimer;
1165 function onBeforeShow(m){
1166 var pm = m.parentMenu;
1167 if(!pm && !m.allowOtherMenus){
1169 }else if(pm && pm.activeChild && active != m){
1170 pm.activeChild.hide();
1175 function onMouseDown(e){
1176 Roo.log("on MouseDown");
1177 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1185 function onBeforeCheck(mi, state){
1187 var g = groups[mi.group];
1188 for(var i = 0, l = g.length; i < l; i++){
1190 g[i].setChecked(false);
1199 * Hides all menus that are currently visible
1201 hideAll : function(){
1206 register : function(menu){
1210 menus[menu.id] = menu;
1211 menu.on("beforehide", onBeforeHide);
1212 menu.on("hide", onHide);
1213 menu.on("beforeshow", onBeforeShow);
1214 menu.on("show", onShow);
1216 if(g && menu.events["checkchange"]){
1220 groups[g].push(menu);
1221 menu.on("checkchange", onCheck);
1226 * Returns a {@link Roo.menu.Menu} object
1227 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1228 * be used to generate and return a new Menu instance.
1230 get : function(menu){
1231 if(typeof menu == "string"){ // menu id
1233 }else if(menu.events){ // menu instance
1236 /*else if(typeof menu.length == 'number'){ // array of menu items?
1237 return new Roo.bootstrap.Menu({items:menu});
1238 }else{ // otherwise, must be a config
1239 return new Roo.bootstrap.Menu(menu);
1246 unregister : function(menu){
1247 delete menus[menu.id];
1248 menu.un("beforehide", onBeforeHide);
1249 menu.un("hide", onHide);
1250 menu.un("beforeshow", onBeforeShow);
1251 menu.un("show", onShow);
1253 if(g && menu.events["checkchange"]){
1254 groups[g].remove(menu);
1255 menu.un("checkchange", onCheck);
1260 registerCheckable : function(menuItem){
1261 var g = menuItem.group;
1266 groups[g].push(menuItem);
1267 menuItem.on("beforecheckchange", onBeforeCheck);
1272 unregisterCheckable : function(menuItem){
1273 var g = menuItem.group;
1275 groups[g].remove(menuItem);
1276 menuItem.un("beforecheckchange", onBeforeCheck);
1288 * @class Roo.bootstrap.Menu
1289 * @extends Roo.bootstrap.Component
1290 * Bootstrap Menu class - container for MenuItems
1291 * @cfg {String} type type of menu
1295 * @param {Object} config The config object
1299 Roo.bootstrap.Menu = function(config){
1300 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1301 if (this.registerMenu) {
1302 Roo.bootstrap.MenuMgr.register(this);
1307 * Fires before this menu is displayed
1308 * @param {Roo.menu.Menu} this
1313 * Fires before this menu is hidden
1314 * @param {Roo.menu.Menu} this
1319 * Fires after this menu is displayed
1320 * @param {Roo.menu.Menu} this
1325 * Fires after this menu is hidden
1326 * @param {Roo.menu.Menu} this
1331 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1332 * @param {Roo.menu.Menu} this
1333 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1334 * @param {Roo.EventObject} e
1339 * Fires when the mouse is hovering over this menu
1340 * @param {Roo.menu.Menu} this
1341 * @param {Roo.EventObject} e
1342 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1347 * Fires when the mouse exits this menu
1348 * @param {Roo.menu.Menu} this
1349 * @param {Roo.EventObject} e
1350 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1355 * Fires when a menu item contained in this menu is clicked
1356 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1357 * @param {Roo.EventObject} e
1361 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1364 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1368 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1371 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1373 registerMenu : true,
1375 menuItems :false, // stores the menu items..
1381 getChildContainer : function() {
1385 getAutoCreate : function(){
1387 //if (['right'].indexOf(this.align)!==-1) {
1388 // cfg.cn[1].cls += ' pull-right'
1392 cls : 'dropdown-menu' ,
1393 style : 'z-index:1000'
1397 if (this.type === 'submenu') {
1398 cfg.cls = 'submenu active'
1403 initEvents : function() {
1405 // Roo.log("ADD event");
1406 // Roo.log(this.triggerEl.dom);
1407 this.triggerEl.on('click', this.onTriggerPress, this);
1408 this.triggerEl.addClass('dropdown-toggle');
1409 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1411 this.el.on("mouseover", this.onMouseOver, this);
1412 this.el.on("mouseout", this.onMouseOut, this);
1416 findTargetItem : function(e){
1417 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1421 //Roo.log(t); Roo.log(t.id);
1423 //Roo.log(this.menuitems);
1424 return this.menuitems.get(t.id);
1426 //return this.items.get(t.menuItemId);
1431 onClick : function(e){
1432 Roo.log("menu.onClick");
1433 var t = this.findTargetItem(e);
1439 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1440 if(t == this.activeItem && t.shouldDeactivate(e)){
1441 this.activeItem.deactivate();
1442 delete this.activeItem;
1446 this.setActiveItem(t, true);
1453 Roo.log('pass click event');
1457 this.fireEvent("click", this, t, e);
1461 onMouseOver : function(e){
1462 var t = this.findTargetItem(e);
1465 // if(t.canActivate && !t.disabled){
1466 // this.setActiveItem(t, true);
1470 this.fireEvent("mouseover", this, e, t);
1472 isVisible : function(){
1473 return !this.hidden;
1475 onMouseOut : function(e){
1476 var t = this.findTargetItem(e);
1479 // if(t == this.activeItem && t.shouldDeactivate(e)){
1480 // this.activeItem.deactivate();
1481 // delete this.activeItem;
1484 this.fireEvent("mouseout", this, e, t);
1489 * Displays this menu relative to another element
1490 * @param {String/HTMLElement/Roo.Element} element The element to align to
1491 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1492 * the element (defaults to this.defaultAlign)
1493 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1495 show : function(el, pos, parentMenu){
1496 this.parentMenu = parentMenu;
1500 this.fireEvent("beforeshow", this);
1501 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1504 * Displays this menu at a specific xy position
1505 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1506 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1508 showAt : function(xy, parentMenu, /* private: */_e){
1509 this.parentMenu = parentMenu;
1514 this.fireEvent("beforeshow", this);
1516 //xy = this.el.adjustForConstraints(xy);
1518 //this.el.setXY(xy);
1520 this.hideMenuItems();
1521 this.hidden = false;
1522 this.triggerEl.addClass('open');
1524 this.fireEvent("show", this);
1530 this.doFocus.defer(50, this);
1534 doFocus : function(){
1536 this.focusEl.focus();
1541 * Hides this menu and optionally all parent menus
1542 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1544 hide : function(deep){
1546 this.hideMenuItems();
1547 if(this.el && this.isVisible()){
1548 this.fireEvent("beforehide", this);
1549 if(this.activeItem){
1550 this.activeItem.deactivate();
1551 this.activeItem = null;
1553 this.triggerEl.removeClass('open');;
1555 this.fireEvent("hide", this);
1557 if(deep === true && this.parentMenu){
1558 this.parentMenu.hide(true);
1562 onTriggerPress : function(e)
1565 Roo.log('trigger press');
1566 //Roo.log(e.getTarget());
1567 // Roo.log(this.triggerEl.dom);
1568 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1571 if (this.isVisible()) {
1575 this.show(this.triggerEl, false, false);
1584 hideMenuItems : function()
1586 //$(backdrop).remove()
1587 Roo.select('.open',true).each(function(aa) {
1589 aa.removeClass('open');
1590 //var parent = getParent($(this))
1591 //var relatedTarget = { relatedTarget: this }
1593 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1594 //if (e.isDefaultPrevented()) return
1595 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1598 addxtypeChild : function (tree, cntr) {
1599 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1601 this.menuitems.add(comp);
1622 * @class Roo.bootstrap.MenuItem
1623 * @extends Roo.bootstrap.Component
1624 * Bootstrap MenuItem class
1625 * @cfg {String} html the menu label
1626 * @cfg {String} href the link
1627 * @cfg {Boolean} preventDefault (true | false) default true
1631 * Create a new MenuItem
1632 * @param {Object} config The config object
1636 Roo.bootstrap.MenuItem = function(config){
1637 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1642 * The raw click event for the entire grid.
1643 * @param {Roo.EventObject} e
1649 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1653 preventDefault: true,
1655 getAutoCreate : function(){
1658 cls: 'dropdown-menu-item',
1668 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1669 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1673 initEvents: function() {
1675 //this.el.select('a').on('click', this.onClick, this);
1678 onClick : function(e)
1680 Roo.log('item on click ');
1681 //if(this.preventDefault){
1682 // e.preventDefault();
1684 //this.parent().hideMenuItems();
1686 this.fireEvent('click', this, e);
1705 * @class Roo.bootstrap.MenuSeparator
1706 * @extends Roo.bootstrap.Component
1707 * Bootstrap MenuSeparator class
1710 * Create a new MenuItem
1711 * @param {Object} config The config object
1715 Roo.bootstrap.MenuSeparator = function(config){
1716 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1719 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1721 getAutoCreate : function(){
1736 <div class="modal fade">
1737 <div class="modal-dialog">
1738 <div class="modal-content">
1739 <div class="modal-header">
1740 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1741 <h4 class="modal-title">Modal title</h4>
1743 <div class="modal-body">
1744 <p>One fine body…</p>
1746 <div class="modal-footer">
1747 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1748 <button type="button" class="btn btn-primary">Save changes</button>
1750 </div><!-- /.modal-content -->
1751 </div><!-- /.modal-dialog -->
1752 </div><!-- /.modal -->
1762 * @class Roo.bootstrap.Modal
1763 * @extends Roo.bootstrap.Component
1764 * Bootstrap Modal class
1765 * @cfg {String} title Title of dialog
1766 * @cfg {Boolean} specificTitle (true|false) default false
1767 * @cfg {Array} buttons Array of buttons or standard button set..
1768 * @cfg {String} buttonPosition (left|right|center) default right
1771 * Create a new Modal Dialog
1772 * @param {Object} config The config object
1775 Roo.bootstrap.Modal = function(config){
1776 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1781 * The raw btnclick event for the button
1782 * @param {Roo.EventObject} e
1786 this.buttons = this.buttons || [];
1789 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1791 title : 'test dialog',
1798 specificTitle: false,
1800 buttonPosition: 'right',
1802 onRender : function(ct, position)
1804 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1807 var cfg = Roo.apply({}, this.getAutoCreate());
1810 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1812 //if (!cfg.name.length) {
1816 cfg.cls += ' ' + this.cls;
1819 cfg.style = this.style;
1821 this.el = Roo.get(document.body).createChild(cfg, position);
1823 //var type = this.el.dom.type;
1825 if(this.tabIndex !== undefined){
1826 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1831 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1832 this.maskEl.enableDisplayMode("block");
1834 //this.el.addClass("x-dlg-modal");
1836 if (this.buttons.length) {
1837 Roo.each(this.buttons, function(bb) {
1838 b = Roo.apply({}, bb);
1839 b.xns = b.xns || Roo.bootstrap;
1840 b.xtype = b.xtype || 'Button';
1841 if (typeof(b.listeners) == 'undefined') {
1842 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1845 var btn = Roo.factory(b);
1847 btn.onRender(this.el.select('.modal-footer div').first());
1851 // render the children.
1854 if(typeof(this.items) != 'undefined'){
1855 var items = this.items;
1858 for(var i =0;i < items.length;i++) {
1859 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1863 this.items = nitems;
1865 this.body = this.el.select('.modal-body',true).first();
1866 this.close = this.el.select('.modal-header .close', true).first();
1867 this.footer = this.el.select('.modal-footer',true).first();
1869 //this.el.addClass([this.fieldClass, this.cls]);
1872 getAutoCreate : function(){
1877 html : this.html || ''
1882 cls : 'modal-title',
1886 if(this.specificTitle){
1892 style : 'display: none',
1895 cls: "modal-dialog",
1898 cls : "modal-content",
1901 cls : 'modal-header',
1913 cls : 'modal-footer',
1917 cls: 'btn-' + this.buttonPosition
1936 getChildContainer : function() {
1938 return this.el.select('.modal-body',true).first();
1941 getButtonContainer : function() {
1942 return this.el.select('.modal-footer div',true).first();
1945 initEvents : function()
1947 this.el.select('.modal-header .close').on('click', this.hide, this);
1949 // this.addxtype(this);
1953 if (!this.rendered) {
1957 this.el.addClass('on');
1958 this.el.removeClass('fade');
1959 this.el.setStyle('display', 'block');
1960 Roo.get(document.body).addClass("x-body-masked");
1961 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1963 this.el.setStyle('zIndex', '10001');
1964 this.fireEvent('show', this);
1970 Roo.log('Modal hide?!');
1972 Roo.get(document.body).removeClass("x-body-masked");
1973 this.el.removeClass('on');
1974 this.el.addClass('fade');
1975 this.el.setStyle('display', 'none');
1976 this.fireEvent('hide', this);
1979 addButton : function(str, cb)
1983 var b = Roo.apply({}, { html : str } );
1984 b.xns = b.xns || Roo.bootstrap;
1985 b.xtype = b.xtype || 'Button';
1986 if (typeof(b.listeners) == 'undefined') {
1987 b.listeners = { click : cb.createDelegate(this) };
1990 var btn = Roo.factory(b);
1992 btn.onRender(this.el.select('.modal-footer div').first());
1998 setDefaultButton : function(btn)
2000 //this.el.select('.modal-footer').()
2002 resizeTo: function(w,h)
2006 setContentSize : function(w, h)
2010 onButtonClick: function(btn,e)
2013 this.fireEvent('btnclick', btn.name, e);
2015 setTitle: function(str) {
2016 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2022 Roo.apply(Roo.bootstrap.Modal, {
2024 * Button config that displays a single OK button
2033 * Button config that displays Yes and No buttons
2049 * Button config that displays OK and Cancel buttons
2064 * Button config that displays Yes, No and Cancel buttons
2086 * messagebox - can be used as a replace
2090 * @class Roo.MessageBox
2091 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2095 Roo.Msg.alert('Status', 'Changes saved successfully.');
2097 // Prompt for user data:
2098 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2100 // process text value...
2104 // Show a dialog using config options:
2106 title:'Save Changes?',
2107 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2108 buttons: Roo.Msg.YESNOCANCEL,
2115 Roo.bootstrap.MessageBox = function(){
2116 var dlg, opt, mask, waitTimer;
2117 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2118 var buttons, activeTextEl, bwidth;
2122 var handleButton = function(button){
2124 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2128 var handleHide = function(){
2130 dlg.el.removeClass(opt.cls);
2133 // Roo.TaskMgr.stop(waitTimer);
2134 // waitTimer = null;
2139 var updateButtons = function(b){
2142 buttons["ok"].hide();
2143 buttons["cancel"].hide();
2144 buttons["yes"].hide();
2145 buttons["no"].hide();
2146 //dlg.footer.dom.style.display = 'none';
2149 dlg.footer.dom.style.display = '';
2150 for(var k in buttons){
2151 if(typeof buttons[k] != "function"){
2154 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2155 width += buttons[k].el.getWidth()+15;
2165 var handleEsc = function(d, k, e){
2166 if(opt && opt.closable !== false){
2176 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2177 * @return {Roo.BasicDialog} The BasicDialog element
2179 getDialog : function(){
2181 dlg = new Roo.bootstrap.Modal( {
2184 //constraintoviewport:false,
2186 //collapsible : false,
2191 //buttonAlign:"center",
2192 closeClick : function(){
2193 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2196 handleButton("cancel");
2201 dlg.on("hide", handleHide);
2203 //dlg.addKeyListener(27, handleEsc);
2205 this.buttons = buttons;
2206 var bt = this.buttonText;
2207 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2208 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2209 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2210 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2212 bodyEl = dlg.body.createChild({
2214 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2215 '<textarea class="roo-mb-textarea"></textarea>' +
2216 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2218 msgEl = bodyEl.dom.firstChild;
2219 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2220 textboxEl.enableDisplayMode();
2221 textboxEl.addKeyListener([10,13], function(){
2222 if(dlg.isVisible() && opt && opt.buttons){
2225 }else if(opt.buttons.yes){
2226 handleButton("yes");
2230 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2231 textareaEl.enableDisplayMode();
2232 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2233 progressEl.enableDisplayMode();
2234 var pf = progressEl.dom.firstChild;
2236 pp = Roo.get(pf.firstChild);
2237 pp.setHeight(pf.offsetHeight);
2245 * Updates the message box body text
2246 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2247 * the XHTML-compliant non-breaking space character '&#160;')
2248 * @return {Roo.MessageBox} This message box
2250 updateText : function(text){
2251 if(!dlg.isVisible() && !opt.width){
2252 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2254 msgEl.innerHTML = text || ' ';
2256 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2257 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2259 Math.min(opt.width || cw , this.maxWidth),
2260 Math.max(opt.minWidth || this.minWidth, bwidth)
2263 activeTextEl.setWidth(w);
2265 if(dlg.isVisible()){
2266 dlg.fixedcenter = false;
2268 // to big, make it scroll. = But as usual stupid IE does not support
2271 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2272 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2273 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2275 bodyEl.dom.style.height = '';
2276 bodyEl.dom.style.overflowY = '';
2279 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2281 bodyEl.dom.style.overflowX = '';
2284 dlg.setContentSize(w, bodyEl.getHeight());
2285 if(dlg.isVisible()){
2286 dlg.fixedcenter = true;
2292 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2293 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2294 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2295 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2296 * @return {Roo.MessageBox} This message box
2298 updateProgress : function(value, text){
2300 this.updateText(text);
2302 if (pp) { // weird bug on my firefox - for some reason this is not defined
2303 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2309 * Returns true if the message box is currently displayed
2310 * @return {Boolean} True if the message box is visible, else false
2312 isVisible : function(){
2313 return dlg && dlg.isVisible();
2317 * Hides the message box if it is displayed
2320 if(this.isVisible()){
2326 * Displays a new message box, or reinitializes an existing message box, based on the config options
2327 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2328 * The following config object properties are supported:
2330 Property Type Description
2331 ---------- --------------- ------------------------------------------------------------------------------------
2332 animEl String/Element An id or Element from which the message box should animate as it opens and
2333 closes (defaults to undefined)
2334 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2335 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2336 closable Boolean False to hide the top-right close button (defaults to true). Note that
2337 progress and wait dialogs will ignore this property and always hide the
2338 close button as they can only be closed programmatically.
2339 cls String A custom CSS class to apply to the message box element
2340 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2341 displayed (defaults to 75)
2342 fn Function A callback function to execute after closing the dialog. The arguments to the
2343 function will be btn (the name of the button that was clicked, if applicable,
2344 e.g. "ok"), and text (the value of the active text field, if applicable).
2345 Progress and wait dialogs will ignore this option since they do not respond to
2346 user actions and can only be closed programmatically, so any required function
2347 should be called by the same code after it closes the dialog.
2348 icon String A CSS class that provides a background image to be used as an icon for
2349 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2350 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2351 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2352 modal Boolean False to allow user interaction with the page while the message box is
2353 displayed (defaults to true)
2354 msg String A string that will replace the existing message box body text (defaults
2355 to the XHTML-compliant non-breaking space character ' ')
2356 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2357 progress Boolean True to display a progress bar (defaults to false)
2358 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2359 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2360 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2361 title String The title text
2362 value String The string value to set into the active textbox element if displayed
2363 wait Boolean True to display a progress bar (defaults to false)
2364 width Number The width of the dialog in pixels
2371 msg: 'Please enter your address:',
2373 buttons: Roo.MessageBox.OKCANCEL,
2376 animEl: 'addAddressBtn'
2379 * @param {Object} config Configuration options
2380 * @return {Roo.MessageBox} This message box
2382 show : function(options)
2385 // this causes nightmares if you show one dialog after another
2386 // especially on callbacks..
2388 if(this.isVisible()){
2391 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2392 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2393 Roo.log("New Dialog Message:" + options.msg )
2394 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2395 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2398 var d = this.getDialog();
2400 d.setTitle(opt.title || " ");
2401 d.close.setDisplayed(opt.closable !== false);
2402 activeTextEl = textboxEl;
2403 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2408 textareaEl.setHeight(typeof opt.multiline == "number" ?
2409 opt.multiline : this.defaultTextHeight);
2410 activeTextEl = textareaEl;
2419 progressEl.setDisplayed(opt.progress === true);
2420 this.updateProgress(0);
2421 activeTextEl.dom.value = opt.value || "";
2423 dlg.setDefaultButton(activeTextEl);
2425 var bs = opt.buttons;
2429 }else if(bs && bs.yes){
2430 db = buttons["yes"];
2432 dlg.setDefaultButton(db);
2434 bwidth = updateButtons(opt.buttons);
2435 this.updateText(opt.msg);
2437 d.el.addClass(opt.cls);
2439 d.proxyDrag = opt.proxyDrag === true;
2440 d.modal = opt.modal !== false;
2441 d.mask = opt.modal !== false ? mask : false;
2443 // force it to the end of the z-index stack so it gets a cursor in FF
2444 document.body.appendChild(dlg.el.dom);
2445 d.animateTarget = null;
2446 d.show(options.animEl);
2452 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2453 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2454 * and closing the message box when the process is complete.
2455 * @param {String} title The title bar text
2456 * @param {String} msg The message box body text
2457 * @return {Roo.MessageBox} This message box
2459 progress : function(title, msg){
2466 minWidth: this.minProgressWidth,
2473 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2474 * If a callback function is passed it will be called after the user clicks the button, and the
2475 * id of the button that was clicked will be passed as the only parameter to the callback
2476 * (could also be the top-right close button).
2477 * @param {String} title The title bar text
2478 * @param {String} msg The message box body text
2479 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2480 * @param {Object} scope (optional) The scope of the callback function
2481 * @return {Roo.MessageBox} This message box
2483 alert : function(title, msg, fn, scope){
2496 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2497 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2498 * You are responsible for closing the message box when the process is complete.
2499 * @param {String} msg The message box body text
2500 * @param {String} title (optional) The title bar text
2501 * @return {Roo.MessageBox} This message box
2503 wait : function(msg, title){
2514 waitTimer = Roo.TaskMgr.start({
2516 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2524 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2525 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2526 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2527 * @param {String} title The title bar text
2528 * @param {String} msg The message box body text
2529 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2530 * @param {Object} scope (optional) The scope of the callback function
2531 * @return {Roo.MessageBox} This message box
2533 confirm : function(title, msg, fn, scope){
2537 buttons: this.YESNO,
2546 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2547 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2548 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2549 * (could also be the top-right close button) and the text that was entered will be passed as the two
2550 * parameters to the callback.
2551 * @param {String} title The title bar text
2552 * @param {String} msg The message box body text
2553 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2554 * @param {Object} scope (optional) The scope of the callback function
2555 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2556 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2557 * @return {Roo.MessageBox} This message box
2559 prompt : function(title, msg, fn, scope, multiline){
2563 buttons: this.OKCANCEL,
2568 multiline: multiline,
2575 * Button config that displays a single OK button
2580 * Button config that displays Yes and No buttons
2583 YESNO : {yes:true, no:true},
2585 * Button config that displays OK and Cancel buttons
2588 OKCANCEL : {ok:true, cancel:true},
2590 * Button config that displays Yes, No and Cancel buttons
2593 YESNOCANCEL : {yes:true, no:true, cancel:true},
2596 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2599 defaultTextHeight : 75,
2601 * The maximum width in pixels of the message box (defaults to 600)
2606 * The minimum width in pixels of the message box (defaults to 100)
2611 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2612 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2615 minProgressWidth : 250,
2617 * An object containing the default button text strings that can be overriden for localized language support.
2618 * Supported properties are: ok, cancel, yes and no.
2619 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2632 * Shorthand for {@link Roo.MessageBox}
2634 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2635 Roo.Msg = Roo.Msg || Roo.MessageBox;
2644 * @class Roo.bootstrap.Navbar
2645 * @extends Roo.bootstrap.Component
2646 * Bootstrap Navbar class
2647 * @cfg {Boolean} sidebar has side bar
2648 * @cfg {Boolean} bar is a bar?
2649 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2650 * @cfg {String} brand what is brand
2651 * @cfg {Boolean} inverse is inverted color
2652 * @cfg {String} type (nav | pills | tabs)
2653 * @cfg {Boolean} arrangement stacked | justified
2654 * @cfg {String} align (left | right) alignment
2655 * @cfg {String} brand_href href of the brand
2656 * @cfg {Boolean} main (true|false) main nav bar? default false
2657 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2661 * Create a new Navbar
2662 * @param {Object} config The config object
2666 Roo.bootstrap.Navbar = function(config){
2667 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2672 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2691 getAutoCreate : function(){
2696 if (this.sidebar === true) {
2704 if (this.bar === true) {
2712 cls: 'navbar-header',
2717 cls: 'navbar-toggle',
2718 'data-toggle': 'collapse',
2723 html: 'Toggle navigation'
2743 cls: 'collapse navbar-collapse'
2748 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2750 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2751 cfg.cls += ' navbar-' + this.position;
2752 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2755 if (this.brand !== '') {
2758 href: this.brand_href ? this.brand_href : '#',
2759 cls: 'navbar-brand',
2767 cfg.cls += ' main-nav';
2773 } else if (this.bar === false) {
2776 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2786 if (['tabs','pills'].indexOf(this.type)!==-1) {
2787 cfg.cn[0].cls += ' nav-' + this.type
2789 if (this.type!=='nav') {
2790 Roo.log('nav type must be nav/tabs/pills')
2792 cfg.cn[0].cls += ' navbar-nav'
2795 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2796 cfg.cn[0].cls += ' nav-' + this.arrangement;
2799 if (this.align === 'right') {
2800 cfg.cn[0].cls += ' navbar-right';
2803 cfg.cls += ' navbar-inverse';
2811 initEvents :function ()
2813 //Roo.log(this.el.select('.navbar-toggle',true));
2814 this.el.select('.navbar-toggle',true).on('click', function() {
2815 // Roo.log('click');
2816 this.el.select('.navbar-collapse',true).toggleClass('in');
2824 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2826 var size = this.el.getSize();
2827 this.maskEl.setSize(size.width, size.height);
2828 this.maskEl.enableDisplayMode("block");
2837 getChildContainer : function()
2839 if (this.bar === true) {
2840 return this.el.select('.collapse',true).first();
2872 * @class Roo.bootstrap.NavGroup
2873 * @extends Roo.bootstrap.Component
2874 * Bootstrap NavGroup class
2875 * @cfg {String} align left | right
2876 * @cfg {Boolean} inverse false | true
2877 * @cfg {String} type (nav|pills|tab) default nav
2878 * @cfg {String} navId - reference Id for navbar.
2882 * Create a new nav group
2883 * @param {Object} config The config object
2886 Roo.bootstrap.NavGroup = function(config){
2887 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2889 Roo.bootstrap.NavGroup.register(this);
2893 * Fires when the active item changes
2894 * @param {Roo.bootstrap.NavGroup} this
2895 * @param {Roo.bootstrap.Navbar.Item} item The item selected
2896 * @param {Roo.bootstrap.Navbar.Item} item The previously selected item
2903 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2914 getAutoCreate : function()
2916 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2923 if (['tabs','pills'].indexOf(this.type)!==-1) {
2924 cfg.cls += ' nav-' + this.type
2926 if (this.type!=='nav') {
2927 Roo.log('nav type must be nav/tabs/pills')
2929 cfg.cls += ' navbar-nav'
2932 if (this.parent().sidebar === true) {
2935 cls: 'dashboard-menu'
2941 if (this.form === true) {
2947 if (this.align === 'right') {
2948 cfg.cls += ' navbar-right';
2950 cfg.cls += ' navbar-left';
2954 if (this.align === 'right') {
2955 cfg.cls += ' navbar-right';
2959 cfg.cls += ' navbar-inverse';
2967 setActiveItem : function(item)
2970 Roo.each(this.navItems, function(v){
2975 v.setActive(false, true);
2982 item.setActive(true, true);
2983 this.fireEvent('changed', this, item, prev);
2989 register : function(item)
2991 this.navItems.push( item);
2992 item.navId = this.navId;
2995 getNavItem: function(tabId)
2998 Roo.each(this.navItems, function(e) {
2999 if (e.tabId == tabId) {
3011 Roo.apply(Roo.bootstrap.NavGroup, {
3015 register : function(navgrp)
3017 this.groups[navgrp.navId] = navgrp;
3020 get: function(navId) {
3021 return this.groups[navId];
3036 * @class Roo.bootstrap.Navbar.Item
3037 * @extends Roo.bootstrap.Component
3038 * Bootstrap Navbar.Button class
3039 * @cfg {String} href link to
3040 * @cfg {String} html content of button
3041 * @cfg {String} badge text inside badge
3042 * @cfg {String} glyphicon name of glyphicon
3043 * @cfg {String} icon name of font awesome icon
3044 * @cfg {Boolean} active Is item active
3045 * @cfg {Boolean} preventDefault (true | false) default false
3046 * @cfg {String} tabId the tab that this item activates.
3049 * Create a new Navbar Button
3050 * @param {Object} config The config object
3052 Roo.bootstrap.Navbar.Item = function(config){
3053 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3058 * The raw click event for the entire grid.
3059 * @param {Roo.EventObject} e
3064 * Fires when the active item active state changes
3065 * @param {Roo.bootstrap.Navbar.Item} this
3066 * @param {boolean} state the new state
3074 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
3082 preventDefault : false,
3085 getAutoCreate : function(){
3087 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3089 if (this.parent().parent().sidebar === true) {
3102 cfg.cn[0].html = this.html;
3106 this.cls += ' active';
3110 cfg.cn[0].cls += ' dropdown-toggle';
3111 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3115 cfg.cn[0].tag = 'a',
3116 cfg.cn[0].href = this.href;
3119 if (this.glyphicon) {
3120 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3124 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3136 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3146 if (this.glyphicon) {
3147 if(cfg.html){cfg.html = ' ' + this.html};
3151 cls: 'glyphicon glyphicon-' + this.glyphicon
3156 cfg.cn[0].html = this.html || cfg.cn[0].html ;
3161 cfg.cn[0].html += " <span class='caret'></span>";
3162 //}else if (!this.href) {
3163 // cfg.cn[0].tag='p';
3164 // cfg.cn[0].cls='navbar-text';
3167 cfg.cn[0].href=this.href||'#';
3168 cfg.cn[0].html=this.html;
3171 if (this.badge !== '') {
3174 cfg.cn[0].html + ' ',
3185 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3190 initEvents: function() {
3191 // Roo.log('init events?');
3192 // Roo.log(this.el.dom);
3193 this.el.select('a',true).on('click', this.onClick, this);
3194 // at this point parent should be available..
3195 this.parent().register(this);
3198 onClick : function(e)
3200 if(this.preventDefault){
3204 if(this.fireEvent('click', this, e) === false){
3208 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3209 if (typeof(this.parent().setActiveItem) !== 'undefined') {
3210 this.parent().setActiveItem(this);
3218 isActive: function () {
3221 setActive : function(state, fire)
3223 this.active = state;
3225 this.el.removeClass('active');
3226 } else if (!this.el.hasClass('active')) {
3227 this.el.addClass('active');
3230 this.fireEvent('changed', this, state);
3235 // this should not be here...
3248 * @class Roo.bootstrap.Row
3249 * @extends Roo.bootstrap.Component
3250 * Bootstrap Row class (contains columns...)
3254 * @param {Object} config The config object
3257 Roo.bootstrap.Row = function(config){
3258 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3261 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3263 getAutoCreate : function(){
3282 * @class Roo.bootstrap.Element
3283 * @extends Roo.bootstrap.Component
3284 * Bootstrap Element class
3285 * @cfg {String} html contents of the element
3286 * @cfg {String} tag tag of the element
3287 * @cfg {String} cls class of the element
3290 * Create a new Element
3291 * @param {Object} config The config object
3294 Roo.bootstrap.Element = function(config){
3295 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3298 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
3305 getAutoCreate : function(){
3330 * @class Roo.bootstrap.Pagination
3331 * @extends Roo.bootstrap.Component
3332 * Bootstrap Pagination class
3333 * @cfg {String} size xs | sm | md | lg
3334 * @cfg {Boolean} inverse false | true
3337 * Create a new Pagination
3338 * @param {Object} config The config object
3341 Roo.bootstrap.Pagination = function(config){
3342 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
3351 getAutoCreate : function(){
3357 cfg.cls += ' inverse';
3363 cfg.cls += " " + this.cls;
3381 * @class Roo.bootstrap.PaginationItem
3382 * @extends Roo.bootstrap.Component
3383 * Bootstrap PaginationItem class
3384 * @cfg {String} html text
3385 * @cfg {String} href the link
3386 * @cfg {Boolean} preventDefault (true | false) default true
3387 * @cfg {Boolean} active (true | false) default false
3391 * Create a new PaginationItem
3392 * @param {Object} config The config object
3396 Roo.bootstrap.PaginationItem = function(config){
3397 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3402 * The raw click event for the entire grid.
3403 * @param {Roo.EventObject} e
3409 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
3413 preventDefault: true,
3417 getAutoCreate : function(){
3423 href : this.href ? this.href : '#',
3424 html : this.html ? this.html : ''
3434 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3440 initEvents: function() {
3442 this.el.on('click', this.onClick, this);
3445 onClick : function(e)
3447 Roo.log('PaginationItem on click ');
3448 if(this.preventDefault){
3452 this.fireEvent('click', this, e);
3468 * @class Roo.bootstrap.Slider
3469 * @extends Roo.bootstrap.Component
3470 * Bootstrap Slider class
3473 * Create a new Slider
3474 * @param {Object} config The config object
3477 Roo.bootstrap.Slider = function(config){
3478 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3481 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
3483 getAutoCreate : function(){
3487 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3491 cls: 'ui-slider-handle ui-state-default ui-corner-all'
3509 * @class Roo.bootstrap.Table
3510 * @extends Roo.bootstrap.Component
3511 * Bootstrap Table class
3512 * @cfg {String} cls table class
3513 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3514 * @cfg {String} bgcolor Specifies the background color for a table
3515 * @cfg {Number} border Specifies whether the table cells should have borders or not
3516 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3517 * @cfg {Number} cellspacing Specifies the space between cells
3518 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3519 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3520 * @cfg {String} sortable Specifies that the table should be sortable
3521 * @cfg {String} summary Specifies a summary of the content of a table
3522 * @cfg {Number} width Specifies the width of a table
3524 * @cfg {boolean} striped Should the rows be alternative striped
3525 * @cfg {boolean} bordered Add borders to the table
3526 * @cfg {boolean} hover Add hover highlighting
3527 * @cfg {boolean} condensed Format condensed
3528 * @cfg {boolean} responsive Format condensed
3534 * Create a new Table
3535 * @param {Object} config The config object
3538 Roo.bootstrap.Table = function(config){
3539 Roo.bootstrap.Table.superclass.constructor.call(this, config);
3542 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3543 this.sm = this.selModel;
3544 this.sm.xmodule = this.xmodule || false;
3546 if (this.cm && typeof(this.cm.config) == 'undefined') {
3547 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3548 this.cm = this.colModel;
3549 this.cm.xmodule = this.xmodule || false;
3552 this.store= Roo.factory(this.store, Roo.data);
3553 this.ds = this.store;
3554 this.ds.xmodule = this.xmodule || false;
3559 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
3581 getAutoCreate : function(){
3582 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3591 cfg.cls += ' table-striped';
3594 cfg.cls += ' table-hover';
3596 if (this.bordered) {
3597 cfg.cls += ' table-bordered';
3599 if (this.condensed) {
3600 cfg.cls += ' table-condensed';
3602 if (this.responsive) {
3603 cfg.cls += ' table-responsive';
3610 cfg.cls+= ' ' +this.cls;
3613 // this lot should be simplifed...
3616 cfg.align=this.align;
3619 cfg.bgcolor=this.bgcolor;
3622 cfg.border=this.border;
3624 if (this.cellpadding) {
3625 cfg.cellpadding=this.cellpadding;
3627 if (this.cellspacing) {
3628 cfg.cellspacing=this.cellspacing;
3631 cfg.frame=this.frame;
3634 cfg.rules=this.rules;
3636 if (this.sortable) {
3637 cfg.sortable=this.sortable;
3640 cfg.summary=this.summary;
3643 cfg.width=this.width;
3646 if(this.store || this.cm){
3647 cfg.cn.push(this.renderHeader());
3648 cfg.cn.push(this.renderBody());
3649 cfg.cn.push(this.renderFooter());
3651 cfg.cls+= ' TableGrid';
3657 // initTableGrid : function()
3666 // var cm = this.cm;
3668 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3671 // html: cm.getColumnHeader(i)
3675 // cfg.push(header);
3682 initEvents : function()
3684 if(!this.store || !this.cm){
3688 Roo.log('initEvents with ds!!!!');
3692 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3693 e.on('click', _this.sort, _this);
3695 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3696 // this.maskEl.enableDisplayMode("block");
3697 // this.maskEl.show();
3699 this.store.on('load', this.onLoad, this);
3700 this.store.on('beforeload', this.onBeforeLoad, this);
3708 sort : function(e,el)
3710 var col = Roo.get(el)
3712 if(!col.hasClass('sortable')){
3716 var sort = col.attr('sort');
3719 if(col.hasClass('glyphicon-arrow-up')){
3723 this.store.sortInfo = {field : sort, direction : dir};
3728 renderHeader : function()
3737 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3739 var config = cm.config[i];
3743 html: cm.getColumnHeader(i)
3746 if(typeof(config.dataIndex) != 'undefined'){
3747 c.sort = config.dataIndex;
3750 if(typeof(config.sortable) != 'undefined' && config.sortable){
3754 if(typeof(config.width) != 'undefined'){
3755 c.style = 'width:' + config.width + 'px';
3764 renderBody : function()
3774 renderFooter : function()
3786 Roo.log('ds onload');
3791 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3792 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3794 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3795 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3798 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3799 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3803 var tbody = this.el.select('tbody', true).first();
3807 if(this.store.getCount() > 0){
3808 this.store.data.each(function(d){
3814 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3815 var renderer = cm.getRenderer(i);
3816 var config = cm.config[i];
3820 if(typeof(renderer) !== 'undefined'){
3821 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3824 if(typeof(value) === 'object'){
3834 html: (typeof(value) === 'object') ? '' : value
3837 if(typeof(config.width) != 'undefined'){
3838 td.style = 'width:' + config.width + 'px';
3845 tbody.createChild(row);
3853 Roo.each(renders, function(r){
3854 _this.renderColumn(r);
3858 // if(this.loadMask){
3859 // this.maskEl.hide();
3863 onBeforeLoad : function()
3865 Roo.log('ds onBeforeLoad');
3869 // if(this.loadMask){
3870 // this.maskEl.show();
3876 this.el.select('tbody', true).first().dom.innerHTML = '';
3879 getSelectionModel : function(){
3881 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3883 return this.selModel;
3886 renderColumn : function(r)
3889 r.cfg.render(Roo.get(r.id));
3892 Roo.each(r.cfg.cn, function(c){
3897 _this.renderColumn(child);
3914 * @class Roo.bootstrap.TableCell
3915 * @extends Roo.bootstrap.Component
3916 * Bootstrap TableCell class
3917 * @cfg {String} html cell contain text
3918 * @cfg {String} cls cell class
3919 * @cfg {String} tag cell tag (td|th) default td
3920 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3921 * @cfg {String} align Aligns the content in a cell
3922 * @cfg {String} axis Categorizes cells
3923 * @cfg {String} bgcolor Specifies the background color of a cell
3924 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3925 * @cfg {Number} colspan Specifies the number of columns a cell should span
3926 * @cfg {String} headers Specifies one or more header cells a cell is related to
3927 * @cfg {Number} height Sets the height of a cell
3928 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3929 * @cfg {Number} rowspan Sets the number of rows a cell should span
3930 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3931 * @cfg {String} valign Vertical aligns the content in a cell
3932 * @cfg {Number} width Specifies the width of a cell
3935 * Create a new TableCell
3936 * @param {Object} config The config object
3939 Roo.bootstrap.TableCell = function(config){
3940 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3943 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3963 getAutoCreate : function(){
3964 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3984 cfg.align=this.align
3990 cfg.bgcolor=this.bgcolor
3993 cfg.charoff=this.charoff
3996 cfg.colspan=this.colspan
3999 cfg.headers=this.headers
4002 cfg.height=this.height
4005 cfg.nowrap=this.nowrap
4008 cfg.rowspan=this.rowspan
4011 cfg.scope=this.scope
4014 cfg.valign=this.valign
4017 cfg.width=this.width
4036 * @class Roo.bootstrap.TableRow
4037 * @extends Roo.bootstrap.Component
4038 * Bootstrap TableRow class
4039 * @cfg {String} cls row class
4040 * @cfg {String} align Aligns the content in a table row
4041 * @cfg {String} bgcolor Specifies a background color for a table row
4042 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4043 * @cfg {String} valign Vertical aligns the content in a table row
4046 * Create a new TableRow
4047 * @param {Object} config The config object
4050 Roo.bootstrap.TableRow = function(config){
4051 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4054 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
4062 getAutoCreate : function(){
4063 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4073 cfg.align = this.align;
4076 cfg.bgcolor = this.bgcolor;
4079 cfg.charoff = this.charoff;
4082 cfg.valign = this.valign;
4100 * @class Roo.bootstrap.TableBody
4101 * @extends Roo.bootstrap.Component
4102 * Bootstrap TableBody class
4103 * @cfg {String} cls element class
4104 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4105 * @cfg {String} align Aligns the content inside the element
4106 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4107 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4110 * Create a new TableBody
4111 * @param {Object} config The config object
4114 Roo.bootstrap.TableBody = function(config){
4115 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4118 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
4126 getAutoCreate : function(){
4127 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4141 cfg.align = this.align;
4144 cfg.charoff = this.charoff;
4147 cfg.valign = this.valign;
4154 // initEvents : function()
4161 // this.store = Roo.factory(this.store, Roo.data);
4162 // this.store.on('load', this.onLoad, this);
4164 // this.store.load();
4168 // onLoad: function ()
4170 // this.fireEvent('load', this);
4180 * Ext JS Library 1.1.1
4181 * Copyright(c) 2006-2007, Ext JS, LLC.
4183 * Originally Released Under LGPL - original licence link has changed is not relivant.
4186 * <script type="text/javascript">
4189 // as we use this in bootstrap.
4190 Roo.namespace('Roo.form');
4192 * @class Roo.form.Action
4193 * Internal Class used to handle form actions
4195 * @param {Roo.form.BasicForm} el The form element or its id
4196 * @param {Object} config Configuration options
4201 // define the action interface
4202 Roo.form.Action = function(form, options){
4204 this.options = options || {};
4207 * Client Validation Failed
4210 Roo.form.Action.CLIENT_INVALID = 'client';
4212 * Server Validation Failed
4215 Roo.form.Action.SERVER_INVALID = 'server';
4217 * Connect to Server Failed
4220 Roo.form.Action.CONNECT_FAILURE = 'connect';
4222 * Reading Data from Server Failed
4225 Roo.form.Action.LOAD_FAILURE = 'load';
4227 Roo.form.Action.prototype = {
4229 failureType : undefined,
4230 response : undefined,
4234 run : function(options){
4239 success : function(response){
4244 handleResponse : function(response){
4248 // default connection failure
4249 failure : function(response){
4251 this.response = response;
4252 this.failureType = Roo.form.Action.CONNECT_FAILURE;
4253 this.form.afterAction(this, false);
4256 processResponse : function(response){
4257 this.response = response;
4258 if(!response.responseText){
4261 this.result = this.handleResponse(response);
4265 // utility functions used internally
4266 getUrl : function(appendParams){
4267 var url = this.options.url || this.form.url || this.form.el.dom.action;
4269 var p = this.getParams();
4271 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4277 getMethod : function(){
4278 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4281 getParams : function(){
4282 var bp = this.form.baseParams;
4283 var p = this.options.params;
4285 if(typeof p == "object"){
4286 p = Roo.urlEncode(Roo.applyIf(p, bp));
4287 }else if(typeof p == 'string' && bp){
4288 p += '&' + Roo.urlEncode(bp);
4291 p = Roo.urlEncode(bp);
4296 createCallback : function(){
4298 success: this.success,
4299 failure: this.failure,
4301 timeout: (this.form.timeout*1000),
4302 upload: this.form.fileUpload ? this.success : undefined
4307 Roo.form.Action.Submit = function(form, options){
4308 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4311 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4314 haveProgress : false,
4315 uploadComplete : false,
4317 // uploadProgress indicator.
4318 uploadProgress : function()
4320 if (!this.form.progressUrl) {
4324 if (!this.haveProgress) {
4325 Roo.MessageBox.progress("Uploading", "Uploading");
4327 if (this.uploadComplete) {
4328 Roo.MessageBox.hide();
4332 this.haveProgress = true;
4334 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4336 var c = new Roo.data.Connection();
4338 url : this.form.progressUrl,
4343 success : function(req){
4344 //console.log(data);
4348 rdata = Roo.decode(req.responseText)
4350 Roo.log("Invalid data from server..");
4354 if (!rdata || !rdata.success) {
4356 Roo.MessageBox.alert(Roo.encode(rdata));
4359 var data = rdata.data;
4361 if (this.uploadComplete) {
4362 Roo.MessageBox.hide();
4367 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4368 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4371 this.uploadProgress.defer(2000,this);
4374 failure: function(data) {
4375 Roo.log('progress url failed ');
4386 // run get Values on the form, so it syncs any secondary forms.
4387 this.form.getValues();
4389 var o = this.options;
4390 var method = this.getMethod();
4391 var isPost = method == 'POST';
4392 if(o.clientValidation === false || this.form.isValid()){
4394 if (this.form.progressUrl) {
4395 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4396 (new Date() * 1) + '' + Math.random());
4401 Roo.Ajax.request(Roo.apply(this.createCallback(), {
4402 form:this.form.el.dom,
4403 url:this.getUrl(!isPost),
4405 params:isPost ? this.getParams() : null,
4406 isUpload: this.form.fileUpload
4409 this.uploadProgress();
4411 }else if (o.clientValidation !== false){ // client validation failed
4412 this.failureType = Roo.form.Action.CLIENT_INVALID;
4413 this.form.afterAction(this, false);
4417 success : function(response)
4419 this.uploadComplete= true;
4420 if (this.haveProgress) {
4421 Roo.MessageBox.hide();
4425 var result = this.processResponse(response);
4426 if(result === true || result.success){
4427 this.form.afterAction(this, true);
4431 this.form.markInvalid(result.errors);
4432 this.failureType = Roo.form.Action.SERVER_INVALID;
4434 this.form.afterAction(this, false);
4436 failure : function(response)
4438 this.uploadComplete= true;
4439 if (this.haveProgress) {
4440 Roo.MessageBox.hide();
4443 this.response = response;
4444 this.failureType = Roo.form.Action.CONNECT_FAILURE;
4445 this.form.afterAction(this, false);
4448 handleResponse : function(response){
4449 if(this.form.errorReader){
4450 var rs = this.form.errorReader.read(response);
4453 for(var i = 0, len = rs.records.length; i < len; i++) {
4454 var r = rs.records[i];
4458 if(errors.length < 1){
4462 success : rs.success,
4468 ret = Roo.decode(response.responseText);
4472 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4482 Roo.form.Action.Load = function(form, options){
4483 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4484 this.reader = this.form.reader;
4487 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4492 Roo.Ajax.request(Roo.apply(
4493 this.createCallback(), {
4494 method:this.getMethod(),
4495 url:this.getUrl(false),
4496 params:this.getParams()
4500 success : function(response){
4502 var result = this.processResponse(response);
4503 if(result === true || !result.success || !result.data){
4504 this.failureType = Roo.form.Action.LOAD_FAILURE;
4505 this.form.afterAction(this, false);
4508 this.form.clearInvalid();
4509 this.form.setValues(result.data);
4510 this.form.afterAction(this, true);
4513 handleResponse : function(response){
4514 if(this.form.reader){
4515 var rs = this.form.reader.read(response);
4516 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4518 success : rs.success,
4522 return Roo.decode(response.responseText);
4526 Roo.form.Action.ACTION_TYPES = {
4527 'load' : Roo.form.Action.Load,
4528 'submit' : Roo.form.Action.Submit
4537 * @class Roo.bootstrap.Form
4538 * @extends Roo.bootstrap.Component
4539 * Bootstrap Form class
4540 * @cfg {String} method GET | POST (default POST)
4541 * @cfg {String} labelAlign top | left (default top)
4542 * @cfg {String} align left | right - for navbars
4547 * @param {Object} config The config object
4551 Roo.bootstrap.Form = function(config){
4552 Roo.bootstrap.Form.superclass.constructor.call(this, config);
4555 * @event clientvalidation
4556 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4557 * @param {Form} this
4558 * @param {Boolean} valid true if the form has passed client-side validation
4560 clientvalidation: true,
4562 * @event beforeaction
4563 * Fires before any action is performed. Return false to cancel the action.
4564 * @param {Form} this
4565 * @param {Action} action The action to be performed
4569 * @event actionfailed
4570 * Fires when an action fails.
4571 * @param {Form} this
4572 * @param {Action} action The action that failed
4574 actionfailed : true,
4576 * @event actioncomplete
4577 * Fires when an action is completed.
4578 * @param {Form} this
4579 * @param {Action} action The action that completed
4581 actioncomplete : true
4586 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
4589 * @cfg {String} method
4590 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4595 * The URL to use for form actions if one isn't supplied in the action options.
4598 * @cfg {Boolean} fileUpload
4599 * Set to true if this form is a file upload.
4603 * @cfg {Object} baseParams
4604 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4608 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4612 * @cfg {Sting} align (left|right) for navbar forms
4617 activeAction : null,
4620 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4621 * element by passing it or its id or mask the form itself by passing in true.
4624 waitMsgTarget : false,
4629 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4630 * element by passing it or its id or mask the form itself by passing in true.
4634 getAutoCreate : function(){
4638 method : this.method || 'POST',
4639 id : this.id || Roo.id(),
4642 if (this.parent().xtype.match(/^Nav/)) {
4643 cfg.cls = 'navbar-form navbar-' + this.align;
4647 if (this.labelAlign == 'left' ) {
4648 cfg.cls += ' form-horizontal';
4654 initEvents : function()
4656 this.el.on('submit', this.onSubmit, this);
4661 onSubmit : function(e){
4666 * Returns true if client-side validation on the form is successful.
4669 isValid : function(){
4670 var items = this.getItems();
4672 items.each(function(f){
4681 * Returns true if any fields in this form have changed since their original load.
4684 isDirty : function(){
4686 var items = this.getItems();
4687 items.each(function(f){
4697 * Performs a predefined action (submit or load) or custom actions you define on this form.
4698 * @param {String} actionName The name of the action type
4699 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
4700 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4701 * accept other config options):
4703 Property Type Description
4704 ---------------- --------------- ----------------------------------------------------------------------------------
4705 url String The url for the action (defaults to the form's url)
4706 method String The form method to use (defaults to the form's method, or POST if not defined)
4707 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
4708 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
4709 validate the form on the client (defaults to false)
4711 * @return {BasicForm} this
4713 doAction : function(action, options){
4714 if(typeof action == 'string'){
4715 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4717 if(this.fireEvent('beforeaction', this, action) !== false){
4718 this.beforeAction(action);
4719 action.run.defer(100, action);
4725 beforeAction : function(action){
4726 var o = action.options;
4728 // not really supported yet.. ??
4730 //if(this.waitMsgTarget === true){
4731 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4732 //}else if(this.waitMsgTarget){
4733 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4734 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4736 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4742 afterAction : function(action, success){
4743 this.activeAction = null;
4744 var o = action.options;
4746 //if(this.waitMsgTarget === true){
4748 //}else if(this.waitMsgTarget){
4749 // this.waitMsgTarget.unmask();
4751 // Roo.MessageBox.updateProgress(1);
4752 // Roo.MessageBox.hide();
4759 Roo.callback(o.success, o.scope, [this, action]);
4760 this.fireEvent('actioncomplete', this, action);
4764 // failure condition..
4765 // we have a scenario where updates need confirming.
4766 // eg. if a locking scenario exists..
4767 // we look for { errors : { needs_confirm : true }} in the response.
4769 (typeof(action.result) != 'undefined') &&
4770 (typeof(action.result.errors) != 'undefined') &&
4771 (typeof(action.result.errors.needs_confirm) != 'undefined')
4774 Roo.log("not supported yet");
4777 Roo.MessageBox.confirm(
4778 "Change requires confirmation",
4779 action.result.errorMsg,
4784 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4794 Roo.callback(o.failure, o.scope, [this, action]);
4795 // show an error message if no failed handler is set..
4796 if (!this.hasListener('actionfailed')) {
4797 Roo.log("need to add dialog support");
4799 Roo.MessageBox.alert("Error",
4800 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4801 action.result.errorMsg :
4802 "Saving Failed, please check your entries or try again"
4807 this.fireEvent('actionfailed', this, action);
4812 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4813 * @param {String} id The value to search for
4816 findField : function(id){
4817 var items = this.getItems();
4818 var field = items.get(id);
4820 items.each(function(f){
4821 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4828 return field || null;
4831 * Mark fields in this form invalid in bulk.
4832 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4833 * @return {BasicForm} this
4835 markInvalid : function(errors){
4836 if(errors instanceof Array){
4837 for(var i = 0, len = errors.length; i < len; i++){
4838 var fieldError = errors[i];
4839 var f = this.findField(fieldError.id);
4841 f.markInvalid(fieldError.msg);
4847 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4848 field.markInvalid(errors[id]);
4852 //Roo.each(this.childForms || [], function (f) {
4853 // f.markInvalid(errors);
4860 * Set values for fields in this form in bulk.
4861 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4862 * @return {BasicForm} this
4864 setValues : function(values){
4865 if(values instanceof Array){ // array of objects
4866 for(var i = 0, len = values.length; i < len; i++){
4868 var f = this.findField(v.id);
4870 f.setValue(v.value);
4871 if(this.trackResetOnLoad){
4872 f.originalValue = f.getValue();
4876 }else{ // object hash
4879 if(typeof values[id] != 'function' && (field = this.findField(id))){
4881 if (field.setFromData &&
4883 field.displayField &&
4884 // combos' with local stores can
4885 // be queried via setValue()
4886 // to set their value..
4887 (field.store && !field.store.isLocal)
4891 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4892 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4893 field.setFromData(sd);
4896 field.setValue(values[id]);
4900 if(this.trackResetOnLoad){
4901 field.originalValue = field.getValue();
4907 //Roo.each(this.childForms || [], function (f) {
4908 // f.setValues(values);
4915 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4916 * they are returned as an array.
4917 * @param {Boolean} asString
4920 getValues : function(asString){
4921 //if (this.childForms) {
4922 // copy values from the child forms
4923 // Roo.each(this.childForms, function (f) {
4924 // this.setValues(f.getValues());
4930 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4931 if(asString === true){
4934 return Roo.urlDecode(fs);
4938 * Returns the fields in this form as an object with key/value pairs.
4939 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4942 getFieldValues : function(with_hidden)
4944 var items = this.getItems();
4946 items.each(function(f){
4950 var v = f.getValue();
4951 if (f.inputType =='radio') {
4952 if (typeof(ret[f.getName()]) == 'undefined') {
4953 ret[f.getName()] = ''; // empty..
4956 if (!f.el.dom.checked) {
4964 // not sure if this supported any more..
4965 if ((typeof(v) == 'object') && f.getRawValue) {
4966 v = f.getRawValue() ; // dates..
4968 // combo boxes where name != hiddenName...
4969 if (f.name != f.getName()) {
4970 ret[f.name] = f.getRawValue();
4972 ret[f.getName()] = v;
4979 * Clears all invalid messages in this form.
4980 * @return {BasicForm} this
4982 clearInvalid : function(){
4983 var items = this.getItems();
4985 items.each(function(f){
4996 * @return {BasicForm} this
4999 var items = this.getItems();
5000 items.each(function(f){
5004 Roo.each(this.childForms || [], function (f) {
5011 getItems : function()
5013 var r=new Roo.util.MixedCollection(false, function(o){
5014 return o.id || (o.id = Roo.id());
5016 var iter = function(el) {
5023 Roo.each(el.items,function(e) {
5042 * Ext JS Library 1.1.1
5043 * Copyright(c) 2006-2007, Ext JS, LLC.
5045 * Originally Released Under LGPL - original licence link has changed is not relivant.
5048 * <script type="text/javascript">
5051 * @class Roo.form.VTypes
5052 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5055 Roo.form.VTypes = function(){
5056 // closure these in so they are only created once.
5057 var alpha = /^[a-zA-Z_]+$/;
5058 var alphanum = /^[a-zA-Z0-9_]+$/;
5059 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5060 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5062 // All these messages and functions are configurable
5065 * The function used to validate email addresses
5066 * @param {String} value The email address
5068 'email' : function(v){
5069 return email.test(v);
5072 * The error text to display when the email validation function returns false
5075 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5077 * The keystroke filter mask to be applied on email input
5080 'emailMask' : /[a-z0-9_\.\-@]/i,
5083 * The function used to validate URLs
5084 * @param {String} value The URL
5086 'url' : function(v){
5090 * The error text to display when the url validation function returns false
5093 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5096 * The function used to validate alpha values
5097 * @param {String} value The value
5099 'alpha' : function(v){
5100 return alpha.test(v);
5103 * The error text to display when the alpha validation function returns false
5106 'alphaText' : 'This field should only contain letters and _',
5108 * The keystroke filter mask to be applied on alpha input
5111 'alphaMask' : /[a-z_]/i,
5114 * The function used to validate alphanumeric values
5115 * @param {String} value The value
5117 'alphanum' : function(v){
5118 return alphanum.test(v);
5121 * The error text to display when the alphanumeric validation function returns false
5124 'alphanumText' : 'This field should only contain letters, numbers and _',
5126 * The keystroke filter mask to be applied on alphanumeric input
5129 'alphanumMask' : /[a-z0-9_]/i
5139 * @class Roo.bootstrap.Input
5140 * @extends Roo.bootstrap.Component
5141 * Bootstrap Input class
5142 * @cfg {Boolean} disabled is it disabled
5143 * @cfg {String} fieldLabel - the label associated
5144 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5145 * @cfg {String} name name of the input
5146 * @cfg {string} fieldLabel - the label associated
5147 * @cfg {string} inputType - input / file submit ...
5148 * @cfg {string} placeholder - placeholder to put in text.
5149 * @cfg {string} before - input group add on before
5150 * @cfg {string} after - input group add on after
5151 * @cfg {string} size - (lg|sm) or leave empty..
5152 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5153 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5154 * @cfg {Number} md colspan out of 12 for computer-sized screens
5155 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5156 * @cfg {string} value default value of the input
5157 * @cfg {Number} labelWidth set the width of label (0-12)
5158 * @cfg {String} labelAlign (top|left)
5159 * @cfg {Boolean} readOnly Specifies that the field should be read-only
5163 * Create a new Input
5164 * @param {Object} config The config object
5167 Roo.bootstrap.Input = function(config){
5168 Roo.bootstrap.Input.superclass.constructor.call(this, config);
5173 * Fires when this field receives input focus.
5174 * @param {Roo.form.Field} this
5179 * Fires when this field loses input focus.
5180 * @param {Roo.form.Field} this
5185 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
5186 * {@link Roo.EventObject#getKey} to determine which key was pressed.
5187 * @param {Roo.form.Field} this
5188 * @param {Roo.EventObject} e The event object
5193 * Fires just before the field blurs if the field value has changed.
5194 * @param {Roo.form.Field} this
5195 * @param {Mixed} newValue The new value
5196 * @param {Mixed} oldValue The original value
5201 * Fires after the field has been marked as invalid.
5202 * @param {Roo.form.Field} this
5203 * @param {String} msg The validation message
5208 * Fires after the field has been validated with no errors.
5209 * @param {Roo.form.Field} this
5214 * Fires after the key up
5215 * @param {Roo.form.Field} this
5216 * @param {Roo.EventObject} e The event Object
5222 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
5224 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5225 automatic validation (defaults to "keyup").
5227 validationEvent : "keyup",
5229 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5231 validateOnBlur : true,
5233 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5235 validationDelay : 250,
5237 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5239 focusClass : "x-form-focus", // not needed???
5243 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5245 invalidClass : "has-error",
5248 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5250 selectOnFocus : false,
5253 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5257 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5262 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5264 disableKeyFilter : false,
5267 * @cfg {Boolean} disabled True to disable the field (defaults to false).
5271 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5275 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5277 blankText : "This field is required",
5280 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5284 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5286 maxLength : Number.MAX_VALUE,
5288 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5290 minLengthText : "The minimum length for this field is {0}",
5292 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5294 maxLengthText : "The maximum length for this field is {0}",
5298 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5299 * If available, this function will be called only after the basic validators all return true, and will be passed the
5300 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5304 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5305 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5306 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
5310 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5333 parentLabelAlign : function()
5336 while (parent.parent()) {
5337 parent = parent.parent();
5338 if (typeof(parent.labelAlign) !='undefined') {
5339 return parent.labelAlign;
5346 getAutoCreate : function(){
5348 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5354 if(this.inputType != 'hidden'){
5355 cfg.cls = 'form-group' //input-group
5361 type : this.inputType,
5363 cls : 'form-control',
5364 placeholder : this.placeholder || ''
5368 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5369 input.maxLength = this.maxLength;
5372 if (this.disabled) {
5373 input.disabled=true;
5376 if (this.readOnly) {
5377 input.readonly=true;
5381 input.name = this.name;
5384 input.cls += ' input-' + this.size;
5387 ['xs','sm','md','lg'].map(function(size){
5388 if (settings[size]) {
5389 cfg.cls += ' col-' + size + '-' + settings[size];
5393 var inputblock = input;
5395 if (this.before || this.after) {
5398 cls : 'input-group',
5402 inputblock.cn.push({
5404 cls : 'input-group-addon',
5408 inputblock.cn.push(input);
5410 inputblock.cn.push({
5412 cls : 'input-group-addon',
5419 if (align ==='left' && this.fieldLabel.length) {
5420 Roo.log("left and has label");
5426 cls : 'control-label col-sm-' + this.labelWidth,
5427 html : this.fieldLabel
5431 cls : "col-sm-" + (12 - this.labelWidth),
5438 } else if ( this.fieldLabel.length) {
5444 //cls : 'input-group-addon',
5445 html : this.fieldLabel
5455 Roo.log(" no label && no align");
5464 Roo.log('input-parentType: ' + this.parentType);
5466 if (this.parentType === 'Navbar' && this.parent().bar) {
5467 cfg.cls += ' navbar-form';
5475 * return the real input element.
5477 inputEl: function ()
5479 return this.el.select('input.form-control',true).first();
5481 setDisabled : function(v)
5483 var i = this.inputEl().dom;
5485 i.removeAttribute('disabled');
5489 i.setAttribute('disabled','true');
5491 initEvents : function()
5494 this.inputEl().on("keydown" , this.fireKey, this);
5495 this.inputEl().on("focus", this.onFocus, this);
5496 this.inputEl().on("blur", this.onBlur, this);
5498 this.inputEl().relayEvent('keyup', this);
5500 // reference to original value for reset
5501 this.originalValue = this.getValue();
5502 //Roo.form.TextField.superclass.initEvents.call(this);
5503 if(this.validationEvent == 'keyup'){
5504 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5505 this.inputEl().on('keyup', this.filterValidation, this);
5507 else if(this.validationEvent !== false){
5508 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5511 if(this.selectOnFocus){
5512 this.on("focus", this.preFocus, this);
5515 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5516 this.inputEl().on("keypress", this.filterKeys, this);
5519 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
5520 this.el.on("click", this.autoSize, this);
5523 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5524 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5528 filterValidation : function(e){
5529 if(!e.isNavKeyPress()){
5530 this.validationTask.delay(this.validationDelay);
5534 * Validates the field value
5535 * @return {Boolean} True if the value is valid, else false
5537 validate : function(){
5538 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5539 if(this.disabled || this.validateValue(this.getRawValue())){
5540 this.clearInvalid();
5548 * Validates a value according to the field's validation rules and marks the field as invalid
5549 * if the validation fails
5550 * @param {Mixed} value The value to validate
5551 * @return {Boolean} True if the value is valid, else false
5553 validateValue : function(value){
5554 if(value.length < 1) { // if it's blank
5555 if(this.allowBlank){
5556 this.clearInvalid();
5559 this.markInvalid(this.blankText);
5563 if(value.length < this.minLength){
5564 this.markInvalid(String.format(this.minLengthText, this.minLength));
5567 if(value.length > this.maxLength){
5568 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5572 var vt = Roo.form.VTypes;
5573 if(!vt[this.vtype](value, this)){
5574 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5578 if(typeof this.validator == "function"){
5579 var msg = this.validator(value);
5581 this.markInvalid(msg);
5585 if(this.regex && !this.regex.test(value)){
5586 this.markInvalid(this.regexText);
5595 fireKey : function(e){
5596 //Roo.log('field ' + e.getKey());
5597 if(e.isNavKeyPress()){
5598 this.fireEvent("specialkey", this, e);
5601 focus : function (selectText){
5603 this.inputEl().focus();
5604 if(selectText === true){
5605 this.inputEl().dom.select();
5611 onFocus : function(){
5612 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5613 // this.el.addClass(this.focusClass);
5616 this.hasFocus = true;
5617 this.startValue = this.getValue();
5618 this.fireEvent("focus", this);
5622 beforeBlur : Roo.emptyFn,
5626 onBlur : function(){
5628 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5629 //this.el.removeClass(this.focusClass);
5631 this.hasFocus = false;
5632 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5635 var v = this.getValue();
5636 if(String(v) !== String(this.startValue)){
5637 this.fireEvent('change', this, v, this.startValue);
5639 this.fireEvent("blur", this);
5643 * Resets the current field value to the originally loaded value and clears any validation messages
5646 this.setValue(this.originalValue);
5647 this.clearInvalid();
5650 * Returns the name of the field
5651 * @return {Mixed} name The name field
5653 getName: function(){
5657 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
5658 * @return {Mixed} value The field value
5660 getValue : function(){
5661 return this.inputEl().getValue();
5664 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
5665 * @return {Mixed} value The field value
5667 getRawValue : function(){
5668 var v = this.inputEl().getValue();
5674 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
5675 * @param {Mixed} value The value to set
5677 setRawValue : function(v){
5678 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5681 selectText : function(start, end){
5682 var v = this.getRawValue();
5684 start = start === undefined ? 0 : start;
5685 end = end === undefined ? v.length : end;
5686 var d = this.inputEl().dom;
5687 if(d.setSelectionRange){
5688 d.setSelectionRange(start, end);
5689 }else if(d.createTextRange){
5690 var range = d.createTextRange();
5691 range.moveStart("character", start);
5692 range.moveEnd("character", v.length-end);
5699 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
5700 * @param {Mixed} value The value to set
5702 setValue : function(v){
5705 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5711 processValue : function(value){
5712 if(this.stripCharsRe){
5713 var newValue = value.replace(this.stripCharsRe, '');
5714 if(newValue !== value){
5715 this.setRawValue(newValue);
5722 preFocus : function(){
5724 if(this.selectOnFocus){
5725 this.inputEl().dom.select();
5728 filterKeys : function(e){
5730 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5733 var c = e.getCharCode(), cc = String.fromCharCode(c);
5734 if(Roo.isIE && (e.isSpecialKey() || !cc)){
5737 if(!this.maskRe.test(cc)){
5742 * Clear any invalid styles/messages for this field
5744 clearInvalid : function(){
5746 if(!this.el || this.preventMark){ // not rendered
5749 this.el.removeClass(this.invalidClass);
5751 switch(this.msgTarget){
5753 this.el.dom.qtip = '';
5756 this.el.dom.title = '';
5760 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5765 this.errorIcon.dom.qtip = '';
5766 this.errorIcon.hide();
5767 this.un('resize', this.alignErrorIcon, this);
5771 var t = Roo.getDom(this.msgTarget);
5773 t.style.display = 'none';
5777 this.fireEvent('valid', this);
5780 * Mark this field as invalid
5781 * @param {String} msg The validation message
5783 markInvalid : function(msg){
5784 if(!this.el || this.preventMark){ // not rendered
5787 this.el.addClass(this.invalidClass);
5789 msg = msg || this.invalidText;
5790 switch(this.msgTarget){
5792 this.el.dom.qtip = msg;
5793 this.el.dom.qclass = 'x-form-invalid-tip';
5794 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5795 Roo.QuickTips.enable();
5799 this.el.dom.title = msg;
5803 var elp = this.el.findParent('.x-form-element', 5, true);
5804 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5805 this.errorEl.setWidth(elp.getWidth(true)-20);
5807 this.errorEl.update(msg);
5808 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5811 if(!this.errorIcon){
5812 var elp = this.el.findParent('.x-form-element', 5, true);
5813 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5815 this.alignErrorIcon();
5816 this.errorIcon.dom.qtip = msg;
5817 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5818 this.errorIcon.show();
5819 this.on('resize', this.alignErrorIcon, this);
5822 var t = Roo.getDom(this.msgTarget);
5824 t.style.display = this.msgDisplay;
5828 this.fireEvent('invalid', this, msg);
5831 SafariOnKeyDown : function(event)
5833 // this is a workaround for a password hang bug on chrome/ webkit.
5835 var isSelectAll = false;
5837 if(this.inputEl().dom.selectionEnd > 0){
5838 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5840 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5841 event.preventDefault();
5846 if(isSelectAll){ // backspace and delete key
5848 event.preventDefault();
5849 // this is very hacky as keydown always get's upper case.
5851 var cc = String.fromCharCode(event.getCharCode());
5852 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5856 adjustWidth : function(tag, w){
5857 tag = tag.toLowerCase();
5858 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5859 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5863 if(tag == 'textarea'){
5866 }else if(Roo.isOpera){
5870 if(tag == 'textarea'){
5889 * @class Roo.bootstrap.TextArea
5890 * @extends Roo.bootstrap.Input
5891 * Bootstrap TextArea class
5892 * @cfg {Number} cols Specifies the visible width of a text area
5893 * @cfg {Number} rows Specifies the visible number of lines in a text area
5894 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5895 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5896 * @cfg {string} html text
5899 * Create a new TextArea
5900 * @param {Object} config The config object
5903 Roo.bootstrap.TextArea = function(config){
5904 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5908 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5918 getAutoCreate : function(){
5920 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5931 value : this.value || '',
5932 html: this.html || '',
5933 cls : 'form-control',
5934 placeholder : this.placeholder || ''
5938 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5939 input.maxLength = this.maxLength;
5943 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5947 input.cols = this.cols;
5950 if (this.readOnly) {
5951 input.readonly = true;
5955 input.name = this.name;
5959 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5963 ['xs','sm','md','lg'].map(function(size){
5964 if (settings[size]) {
5965 cfg.cls += ' col-' + size + '-' + settings[size];
5969 var inputblock = input;
5971 if (this.before || this.after) {
5974 cls : 'input-group',
5978 inputblock.cn.push({
5980 cls : 'input-group-addon',
5984 inputblock.cn.push(input);
5986 inputblock.cn.push({
5988 cls : 'input-group-addon',
5995 if (align ==='left' && this.fieldLabel.length) {
5996 Roo.log("left and has label");
6002 cls : 'control-label col-sm-' + this.labelWidth,
6003 html : this.fieldLabel
6007 cls : "col-sm-" + (12 - this.labelWidth),
6014 } else if ( this.fieldLabel.length) {
6020 //cls : 'input-group-addon',
6021 html : this.fieldLabel
6031 Roo.log(" no label && no align");
6041 if (this.disabled) {
6042 input.disabled=true;
6049 * return the real textarea element.
6051 inputEl: function ()
6053 return this.el.select('textarea.form-control',true).first();
6061 * trigger field - base class for combo..
6066 * @class Roo.bootstrap.TriggerField
6067 * @extends Roo.bootstrap.Input
6068 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6069 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6070 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6071 * for which you can provide a custom implementation. For example:
6073 var trigger = new Roo.bootstrap.TriggerField();
6074 trigger.onTriggerClick = myTriggerFn;
6075 trigger.applyTo('my-field');
6078 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6079 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6080 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
6081 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6083 * Create a new TriggerField.
6084 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6085 * to the base TextField)
6087 Roo.bootstrap.TriggerField = function(config){
6088 this.mimicing = false;
6089 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6092 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
6094 * @cfg {String} triggerClass A CSS class to apply to the trigger
6097 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6101 /** @cfg {Boolean} grow @hide */
6102 /** @cfg {Number} growMin @hide */
6103 /** @cfg {Number} growMax @hide */
6109 autoSize: Roo.emptyFn,
6116 actionMode : 'wrap',
6120 getAutoCreate : function(){
6122 var parent = this.parent();
6124 var align = this.parentLabelAlign();
6129 cls: 'form-group' //input-group
6136 type : this.inputType,
6137 cls : 'form-control',
6138 autocomplete: 'off',
6139 placeholder : this.placeholder || ''
6143 input.name = this.name;
6146 input.cls += ' input-' + this.size;
6149 if (this.disabled) {
6150 input.disabled=true;
6153 var inputblock = input;
6155 if (this.before || this.after) {
6158 cls : 'input-group',
6162 inputblock.cn.push({
6164 cls : 'input-group-addon',
6168 inputblock.cn.push(input);
6170 inputblock.cn.push({
6172 cls : 'input-group-addon',
6185 cls: 'form-hidden-field'
6193 Roo.log('multiple');
6201 cls: 'form-hidden-field'
6205 cls: 'select2-choices',
6209 cls: 'select2-search-field',
6222 cls: 'select2-container input-group',
6227 cls: 'typeahead typeahead-long dropdown-menu',
6228 style: 'display:none'
6236 cls : 'input-group-addon btn dropdown-toggle',
6244 cls: 'combobox-clear',
6258 combobox.cls += ' select2-container-multi';
6261 if (align ==='left' && this.fieldLabel.length) {
6263 Roo.log("left and has label");
6269 cls : 'control-label col-sm-' + this.labelWidth,
6270 html : this.fieldLabel
6274 cls : "col-sm-" + (12 - this.labelWidth),
6281 } else if ( this.fieldLabel.length) {
6287 //cls : 'input-group-addon',
6288 html : this.fieldLabel
6298 Roo.log(" no label && no align");
6305 ['xs','sm','md','lg'].map(function(size){
6306 if (settings[size]) {
6307 cfg.cls += ' col-' + size + '-' + settings[size];
6318 onResize : function(w, h){
6319 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6320 // if(typeof w == 'number'){
6321 // var x = w - this.trigger.getWidth();
6322 // this.inputEl().setWidth(this.adjustWidth('input', x));
6323 // this.trigger.setStyle('left', x+'px');
6328 adjustSize : Roo.BoxComponent.prototype.adjustSize,
6331 getResizeEl : function(){
6332 return this.inputEl();
6336 getPositionEl : function(){
6337 return this.inputEl();
6341 alignErrorIcon : function(){
6342 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6346 initEvents : function(){
6348 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6349 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6351 this.trigger = this.el.select('span.dropdown-toggle',true).first();
6352 if(this.hideTrigger){
6353 this.trigger.setDisplayed(false);
6355 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6359 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6362 //this.trigger.addClassOnOver('x-form-trigger-over');
6363 //this.trigger.addClassOnClick('x-form-trigger-click');
6366 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6371 initTrigger : function(){
6376 onDestroy : function(){
6378 this.trigger.removeAllListeners();
6379 // this.trigger.remove();
6382 // this.wrap.remove();
6384 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6388 onFocus : function(){
6389 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6392 this.wrap.addClass('x-trigger-wrap-focus');
6393 this.mimicing = true;
6394 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6395 if(this.monitorTab){
6396 this.el.on("keydown", this.checkTab, this);
6403 checkTab : function(e){
6404 if(e.getKey() == e.TAB){
6410 onBlur : function(){
6415 mimicBlur : function(e, t){
6417 if(!this.wrap.contains(t) && this.validateBlur()){
6424 triggerBlur : function(){
6425 this.mimicing = false;
6426 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6427 if(this.monitorTab){
6428 this.el.un("keydown", this.checkTab, this);
6430 //this.wrap.removeClass('x-trigger-wrap-focus');
6431 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6435 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6436 validateBlur : function(e, t){
6441 onDisable : function(){
6442 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6444 // this.wrap.addClass('x-item-disabled');
6449 onEnable : function(){
6450 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6452 // this.el.removeClass('x-item-disabled');
6457 onShow : function(){
6458 var ae = this.getActionEl();
6461 ae.dom.style.display = '';
6462 ae.dom.style.visibility = 'visible';
6468 onHide : function(){
6469 var ae = this.getActionEl();
6470 ae.dom.style.display = 'none';
6474 * The function that should handle the trigger's click event. This method does nothing by default until overridden
6475 * by an implementing function.
6477 * @param {EventObject} e
6479 onTriggerClick : Roo.emptyFn
6483 * Ext JS Library 1.1.1
6484 * Copyright(c) 2006-2007, Ext JS, LLC.
6486 * Originally Released Under LGPL - original licence link has changed is not relivant.
6489 * <script type="text/javascript">
6494 * @class Roo.data.SortTypes
6496 * Defines the default sorting (casting?) comparison functions used when sorting data.
6498 Roo.data.SortTypes = {
6500 * Default sort that does nothing
6501 * @param {Mixed} s The value being converted
6502 * @return {Mixed} The comparison value
6509 * The regular expression used to strip tags
6513 stripTagsRE : /<\/?[^>]+>/gi,
6516 * Strips all HTML tags to sort on text only
6517 * @param {Mixed} s The value being converted
6518 * @return {String} The comparison value
6520 asText : function(s){
6521 return String(s).replace(this.stripTagsRE, "");
6525 * Strips all HTML tags to sort on text only - Case insensitive
6526 * @param {Mixed} s The value being converted
6527 * @return {String} The comparison value
6529 asUCText : function(s){
6530 return String(s).toUpperCase().replace(this.stripTagsRE, "");
6534 * Case insensitive string
6535 * @param {Mixed} s The value being converted
6536 * @return {String} The comparison value
6538 asUCString : function(s) {
6539 return String(s).toUpperCase();
6544 * @param {Mixed} s The value being converted
6545 * @return {Number} The comparison value
6547 asDate : function(s) {
6551 if(s instanceof Date){
6554 return Date.parse(String(s));
6559 * @param {Mixed} s The value being converted
6560 * @return {Float} The comparison value
6562 asFloat : function(s) {
6563 var val = parseFloat(String(s).replace(/,/g, ""));
6564 if(isNaN(val)) val = 0;
6570 * @param {Mixed} s The value being converted
6571 * @return {Number} The comparison value
6573 asInt : function(s) {
6574 var val = parseInt(String(s).replace(/,/g, ""));
6575 if(isNaN(val)) val = 0;
6580 * Ext JS Library 1.1.1
6581 * Copyright(c) 2006-2007, Ext JS, LLC.
6583 * Originally Released Under LGPL - original licence link has changed is not relivant.
6586 * <script type="text/javascript">
6590 * @class Roo.data.Record
6591 * Instances of this class encapsulate both record <em>definition</em> information, and record
6592 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6593 * to access Records cached in an {@link Roo.data.Store} object.<br>
6595 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6596 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6599 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6601 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6602 * {@link #create}. The parameters are the same.
6603 * @param {Array} data An associative Array of data values keyed by the field name.
6604 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6605 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6606 * not specified an integer id is generated.
6608 Roo.data.Record = function(data, id){
6609 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6614 * Generate a constructor for a specific record layout.
6615 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6616 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6617 * Each field definition object may contain the following properties: <ul>
6618 * <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,
6619 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6620 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6621 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6622 * is being used, then this is a string containing the javascript expression to reference the data relative to
6623 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6624 * to the data item relative to the record element. If the mapping expression is the same as the field name,
6625 * this may be omitted.</p></li>
6626 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6627 * <ul><li>auto (Default, implies no conversion)</li>
6632 * <li>date</li></ul></p></li>
6633 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6634 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6635 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6636 * by the Reader into an object that will be stored in the Record. It is passed the
6637 * following parameters:<ul>
6638 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6640 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6642 * <br>usage:<br><pre><code>
6643 var TopicRecord = Roo.data.Record.create(
6644 {name: 'title', mapping: 'topic_title'},
6645 {name: 'author', mapping: 'username'},
6646 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6647 {name: 'lastPost', mapping: 'post_time', type: 'date'},
6648 {name: 'lastPoster', mapping: 'user2'},
6649 {name: 'excerpt', mapping: 'post_text'}
6652 var myNewRecord = new TopicRecord({
6653 title: 'Do my job please',
6656 lastPost: new Date(),
6657 lastPoster: 'Animal',
6658 excerpt: 'No way dude!'
6660 myStore.add(myNewRecord);
6665 Roo.data.Record.create = function(o){
6667 f.superclass.constructor.apply(this, arguments);
6669 Roo.extend(f, Roo.data.Record);
6670 var p = f.prototype;
6671 p.fields = new Roo.util.MixedCollection(false, function(field){
6674 for(var i = 0, len = o.length; i < len; i++){
6675 p.fields.add(new Roo.data.Field(o[i]));
6677 f.getField = function(name){
6678 return p.fields.get(name);
6683 Roo.data.Record.AUTO_ID = 1000;
6684 Roo.data.Record.EDIT = 'edit';
6685 Roo.data.Record.REJECT = 'reject';
6686 Roo.data.Record.COMMIT = 'commit';
6688 Roo.data.Record.prototype = {
6690 * Readonly flag - true if this record has been modified.
6699 join : function(store){
6704 * Set the named field to the specified value.
6705 * @param {String} name The name of the field to set.
6706 * @param {Object} value The value to set the field to.
6708 set : function(name, value){
6709 if(this.data[name] == value){
6716 if(typeof this.modified[name] == 'undefined'){
6717 this.modified[name] = this.data[name];
6719 this.data[name] = value;
6720 if(!this.editing && this.store){
6721 this.store.afterEdit(this);
6726 * Get the value of the named field.
6727 * @param {String} name The name of the field to get the value of.
6728 * @return {Object} The value of the field.
6730 get : function(name){
6731 return this.data[name];
6735 beginEdit : function(){
6736 this.editing = true;
6741 cancelEdit : function(){
6742 this.editing = false;
6743 delete this.modified;
6747 endEdit : function(){
6748 this.editing = false;
6749 if(this.dirty && this.store){
6750 this.store.afterEdit(this);
6755 * Usually called by the {@link Roo.data.Store} which owns the Record.
6756 * Rejects all changes made to the Record since either creation, or the last commit operation.
6757 * Modified fields are reverted to their original values.
6759 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6760 * of reject operations.
6762 reject : function(){
6763 var m = this.modified;
6765 if(typeof m[n] != "function"){
6766 this.data[n] = m[n];
6770 delete this.modified;
6771 this.editing = false;
6773 this.store.afterReject(this);
6778 * Usually called by the {@link Roo.data.Store} which owns the Record.
6779 * Commits all changes made to the Record since either creation, or the last commit operation.
6781 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6782 * of commit operations.
6784 commit : function(){
6786 delete this.modified;
6787 this.editing = false;
6789 this.store.afterCommit(this);
6794 hasError : function(){
6795 return this.error != null;
6799 clearError : function(){
6804 * Creates a copy of this record.
6805 * @param {String} id (optional) A new record id if you don't want to use this record's id
6808 copy : function(newId) {
6809 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6813 * Ext JS Library 1.1.1
6814 * Copyright(c) 2006-2007, Ext JS, LLC.
6816 * Originally Released Under LGPL - original licence link has changed is not relivant.
6819 * <script type="text/javascript">
6825 * @class Roo.data.Store
6826 * @extends Roo.util.Observable
6827 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6828 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6830 * 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
6831 * has no knowledge of the format of the data returned by the Proxy.<br>
6833 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6834 * instances from the data object. These records are cached and made available through accessor functions.
6836 * Creates a new Store.
6837 * @param {Object} config A config object containing the objects needed for the Store to access data,
6838 * and read the data into Records.
6840 Roo.data.Store = function(config){
6841 this.data = new Roo.util.MixedCollection(false);
6842 this.data.getKey = function(o){
6845 this.baseParams = {};
6852 "multisort" : "_multisort"
6855 if(config && config.data){
6856 this.inlineData = config.data;
6860 Roo.apply(this, config);
6862 if(this.reader){ // reader passed
6863 this.reader = Roo.factory(this.reader, Roo.data);
6864 this.reader.xmodule = this.xmodule || false;
6865 if(!this.recordType){
6866 this.recordType = this.reader.recordType;
6868 if(this.reader.onMetaChange){
6869 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6873 if(this.recordType){
6874 this.fields = this.recordType.prototype.fields;
6880 * @event datachanged
6881 * Fires when the data cache has changed, and a widget which is using this Store
6882 * as a Record cache should refresh its view.
6883 * @param {Store} this
6888 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6889 * @param {Store} this
6890 * @param {Object} meta The JSON metadata
6895 * Fires when Records have been added to the Store
6896 * @param {Store} this
6897 * @param {Roo.data.Record[]} records The array of Records added
6898 * @param {Number} index The index at which the record(s) were added
6903 * Fires when a Record has been removed from the Store
6904 * @param {Store} this
6905 * @param {Roo.data.Record} record The Record that was removed
6906 * @param {Number} index The index at which the record was removed
6911 * Fires when a Record has been updated
6912 * @param {Store} this
6913 * @param {Roo.data.Record} record The Record that was updated
6914 * @param {String} operation The update operation being performed. Value may be one of:
6916 Roo.data.Record.EDIT
6917 Roo.data.Record.REJECT
6918 Roo.data.Record.COMMIT
6924 * Fires when the data cache has been cleared.
6925 * @param {Store} this
6930 * Fires before a request is made for a new data object. If the beforeload handler returns false
6931 * the load action will be canceled.
6932 * @param {Store} this
6933 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6937 * @event beforeloadadd
6938 * Fires after a new set of Records has been loaded.
6939 * @param {Store} this
6940 * @param {Roo.data.Record[]} records The Records that were loaded
6941 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6943 beforeloadadd : true,
6946 * Fires after a new set of Records has been loaded, before they are added to the store.
6947 * @param {Store} this
6948 * @param {Roo.data.Record[]} records The Records that were loaded
6949 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6950 * @params {Object} return from reader
6954 * @event loadexception
6955 * Fires if an exception occurs in the Proxy during loading.
6956 * Called with the signature of the Proxy's "loadexception" event.
6957 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6960 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6961 * @param {Object} load options
6962 * @param {Object} jsonData from your request (normally this contains the Exception)
6964 loadexception : true
6968 this.proxy = Roo.factory(this.proxy, Roo.data);
6969 this.proxy.xmodule = this.xmodule || false;
6970 this.relayEvents(this.proxy, ["loadexception"]);
6972 this.sortToggle = {};
6973 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6975 Roo.data.Store.superclass.constructor.call(this);
6977 if(this.inlineData){
6978 this.loadData(this.inlineData);
6979 delete this.inlineData;
6983 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6985 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6986 * without a remote query - used by combo/forms at present.
6990 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6993 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6996 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6997 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7000 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7001 * on any HTTP request
7004 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7007 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7011 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7012 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7017 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7018 * loaded or when a record is removed. (defaults to false).
7020 pruneModifiedRecords : false,
7026 * Add Records to the Store and fires the add event.
7027 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7029 add : function(records){
7030 records = [].concat(records);
7031 for(var i = 0, len = records.length; i < len; i++){
7032 records[i].join(this);
7034 var index = this.data.length;
7035 this.data.addAll(records);
7036 this.fireEvent("add", this, records, index);
7040 * Remove a Record from the Store and fires the remove event.
7041 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7043 remove : function(record){
7044 var index = this.data.indexOf(record);
7045 this.data.removeAt(index);
7046 if(this.pruneModifiedRecords){
7047 this.modified.remove(record);
7049 this.fireEvent("remove", this, record, index);
7053 * Remove all Records from the Store and fires the clear event.
7055 removeAll : function(){
7057 if(this.pruneModifiedRecords){
7060 this.fireEvent("clear", this);
7064 * Inserts Records to the Store at the given index and fires the add event.
7065 * @param {Number} index The start index at which to insert the passed Records.
7066 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7068 insert : function(index, records){
7069 records = [].concat(records);
7070 for(var i = 0, len = records.length; i < len; i++){
7071 this.data.insert(index, records[i]);
7072 records[i].join(this);
7074 this.fireEvent("add", this, records, index);
7078 * Get the index within the cache of the passed Record.
7079 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7080 * @return {Number} The index of the passed Record. Returns -1 if not found.
7082 indexOf : function(record){
7083 return this.data.indexOf(record);
7087 * Get the index within the cache of the Record with the passed id.
7088 * @param {String} id The id of the Record to find.
7089 * @return {Number} The index of the Record. Returns -1 if not found.
7091 indexOfId : function(id){
7092 return this.data.indexOfKey(id);
7096 * Get the Record with the specified id.
7097 * @param {String} id The id of the Record to find.
7098 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7100 getById : function(id){
7101 return this.data.key(id);
7105 * Get the Record at the specified index.
7106 * @param {Number} index The index of the Record to find.
7107 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7109 getAt : function(index){
7110 return this.data.itemAt(index);
7114 * Returns a range of Records between specified indices.
7115 * @param {Number} startIndex (optional) The starting index (defaults to 0)
7116 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7117 * @return {Roo.data.Record[]} An array of Records
7119 getRange : function(start, end){
7120 return this.data.getRange(start, end);
7124 storeOptions : function(o){
7125 o = Roo.apply({}, o);
7128 this.lastOptions = o;
7132 * Loads the Record cache from the configured Proxy using the configured Reader.
7134 * If using remote paging, then the first load call must specify the <em>start</em>
7135 * and <em>limit</em> properties in the options.params property to establish the initial
7136 * position within the dataset, and the number of Records to cache on each read from the Proxy.
7138 * <strong>It is important to note that for remote data sources, loading is asynchronous,
7139 * and this call will return before the new data has been loaded. Perform any post-processing
7140 * in a callback function, or in a "load" event handler.</strong>
7142 * @param {Object} options An object containing properties which control loading options:<ul>
7143 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7144 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7145 * passed the following arguments:<ul>
7146 * <li>r : Roo.data.Record[]</li>
7147 * <li>options: Options object from the load call</li>
7148 * <li>success: Boolean success indicator</li></ul></li>
7149 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7150 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7153 load : function(options){
7154 options = options || {};
7155 if(this.fireEvent("beforeload", this, options) !== false){
7156 this.storeOptions(options);
7157 var p = Roo.apply(options.params || {}, this.baseParams);
7158 // if meta was not loaded from remote source.. try requesting it.
7159 if (!this.reader.metaFromRemote) {
7162 if(this.sortInfo && this.remoteSort){
7163 var pn = this.paramNames;
7164 p[pn["sort"]] = this.sortInfo.field;
7165 p[pn["dir"]] = this.sortInfo.direction;
7167 if (this.multiSort) {
7168 var pn = this.paramNames;
7169 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7172 this.proxy.load(p, this.reader, this.loadRecords, this, options);
7177 * Reloads the Record cache from the configured Proxy using the configured Reader and
7178 * the options from the last load operation performed.
7179 * @param {Object} options (optional) An object containing properties which may override the options
7180 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7181 * the most recently used options are reused).
7183 reload : function(options){
7184 this.load(Roo.applyIf(options||{}, this.lastOptions));
7188 // Called as a callback by the Reader during a load operation.
7189 loadRecords : function(o, options, success){
7190 if(!o || success === false){
7191 if(success !== false){
7192 this.fireEvent("load", this, [], options, o);
7194 if(options.callback){
7195 options.callback.call(options.scope || this, [], options, false);
7199 // if data returned failure - throw an exception.
7200 if (o.success === false) {
7201 // show a message if no listener is registered.
7202 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7203 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7205 // loadmask wil be hooked into this..
7206 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7209 var r = o.records, t = o.totalRecords || r.length;
7211 this.fireEvent("beforeloadadd", this, r, options, o);
7213 if(!options || options.add !== true){
7214 if(this.pruneModifiedRecords){
7217 for(var i = 0, len = r.length; i < len; i++){
7221 this.data = this.snapshot;
7222 delete this.snapshot;
7225 this.data.addAll(r);
7226 this.totalLength = t;
7228 this.fireEvent("datachanged", this);
7230 this.totalLength = Math.max(t, this.data.length+r.length);
7233 this.fireEvent("load", this, r, options, o);
7234 if(options.callback){
7235 options.callback.call(options.scope || this, r, options, true);
7241 * Loads data from a passed data block. A Reader which understands the format of the data
7242 * must have been configured in the constructor.
7243 * @param {Object} data The data block from which to read the Records. The format of the data expected
7244 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7245 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7247 loadData : function(o, append){
7248 var r = this.reader.readRecords(o);
7249 this.loadRecords(r, {add: append}, true);
7253 * Gets the number of cached records.
7255 * <em>If using paging, this may not be the total size of the dataset. If the data object
7256 * used by the Reader contains the dataset size, then the getTotalCount() function returns
7257 * the data set size</em>
7259 getCount : function(){
7260 return this.data.length || 0;
7264 * Gets the total number of records in the dataset as returned by the server.
7266 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7267 * the dataset size</em>
7269 getTotalCount : function(){
7270 return this.totalLength || 0;
7274 * Returns the sort state of the Store as an object with two properties:
7276 field {String} The name of the field by which the Records are sorted
7277 direction {String} The sort order, "ASC" or "DESC"
7280 getSortState : function(){
7281 return this.sortInfo;
7285 applySort : function(){
7286 if(this.sortInfo && !this.remoteSort){
7287 var s = this.sortInfo, f = s.field;
7288 var st = this.fields.get(f).sortType;
7289 var fn = function(r1, r2){
7290 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7291 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7293 this.data.sort(s.direction, fn);
7294 if(this.snapshot && this.snapshot != this.data){
7295 this.snapshot.sort(s.direction, fn);
7301 * Sets the default sort column and order to be used by the next load operation.
7302 * @param {String} fieldName The name of the field to sort by.
7303 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7305 setDefaultSort : function(field, dir){
7306 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7311 * If remote sorting is used, the sort is performed on the server, and the cache is
7312 * reloaded. If local sorting is used, the cache is sorted internally.
7313 * @param {String} fieldName The name of the field to sort by.
7314 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7316 sort : function(fieldName, dir){
7317 var f = this.fields.get(fieldName);
7319 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7321 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7322 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7327 this.sortToggle[f.name] = dir;
7328 this.sortInfo = {field: f.name, direction: dir};
7329 if(!this.remoteSort){
7331 this.fireEvent("datachanged", this);
7333 this.load(this.lastOptions);
7338 * Calls the specified function for each of the Records in the cache.
7339 * @param {Function} fn The function to call. The Record is passed as the first parameter.
7340 * Returning <em>false</em> aborts and exits the iteration.
7341 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7343 each : function(fn, scope){
7344 this.data.each(fn, scope);
7348 * Gets all records modified since the last commit. Modified records are persisted across load operations
7349 * (e.g., during paging).
7350 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7352 getModifiedRecords : function(){
7353 return this.modified;
7357 createFilterFn : function(property, value, anyMatch){
7358 if(!value.exec){ // not a regex
7359 value = String(value);
7360 if(value.length == 0){
7363 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7366 return value.test(r.data[property]);
7371 * Sums the value of <i>property</i> for each record between start and end and returns the result.
7372 * @param {String} property A field on your records
7373 * @param {Number} start The record index to start at (defaults to 0)
7374 * @param {Number} end The last record index to include (defaults to length - 1)
7375 * @return {Number} The sum
7377 sum : function(property, start, end){
7378 var rs = this.data.items, v = 0;
7380 end = (end || end === 0) ? end : rs.length-1;
7382 for(var i = start; i <= end; i++){
7383 v += (rs[i].data[property] || 0);
7389 * Filter the records by a specified property.
7390 * @param {String} field A field on your records
7391 * @param {String/RegExp} value Either a string that the field
7392 * should start with or a RegExp to test against the field
7393 * @param {Boolean} anyMatch True to match any part not just the beginning
7395 filter : function(property, value, anyMatch){
7396 var fn = this.createFilterFn(property, value, anyMatch);
7397 return fn ? this.filterBy(fn) : this.clearFilter();
7401 * Filter by a function. The specified function will be called with each
7402 * record in this data source. If the function returns true the record is included,
7403 * otherwise it is filtered.
7404 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7405 * @param {Object} scope (optional) The scope of the function (defaults to this)
7407 filterBy : function(fn, scope){
7408 this.snapshot = this.snapshot || this.data;
7409 this.data = this.queryBy(fn, scope||this);
7410 this.fireEvent("datachanged", this);
7414 * Query the records by a specified property.
7415 * @param {String} field A field on your records
7416 * @param {String/RegExp} value Either a string that the field
7417 * should start with or a RegExp to test against the field
7418 * @param {Boolean} anyMatch True to match any part not just the beginning
7419 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7421 query : function(property, value, anyMatch){
7422 var fn = this.createFilterFn(property, value, anyMatch);
7423 return fn ? this.queryBy(fn) : this.data.clone();
7427 * Query by a function. The specified function will be called with each
7428 * record in this data source. If the function returns true the record is included
7430 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7431 * @param {Object} scope (optional) The scope of the function (defaults to this)
7432 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7434 queryBy : function(fn, scope){
7435 var data = this.snapshot || this.data;
7436 return data.filterBy(fn, scope||this);
7440 * Collects unique values for a particular dataIndex from this store.
7441 * @param {String} dataIndex The property to collect
7442 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7443 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7444 * @return {Array} An array of the unique values
7446 collect : function(dataIndex, allowNull, bypassFilter){
7447 var d = (bypassFilter === true && this.snapshot) ?
7448 this.snapshot.items : this.data.items;
7449 var v, sv, r = [], l = {};
7450 for(var i = 0, len = d.length; i < len; i++){
7451 v = d[i].data[dataIndex];
7453 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7462 * Revert to a view of the Record cache with no filtering applied.
7463 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7465 clearFilter : function(suppressEvent){
7466 if(this.snapshot && this.snapshot != this.data){
7467 this.data = this.snapshot;
7468 delete this.snapshot;
7469 if(suppressEvent !== true){
7470 this.fireEvent("datachanged", this);
7476 afterEdit : function(record){
7477 if(this.modified.indexOf(record) == -1){
7478 this.modified.push(record);
7480 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7484 afterReject : function(record){
7485 this.modified.remove(record);
7486 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7490 afterCommit : function(record){
7491 this.modified.remove(record);
7492 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7496 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7497 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7499 commitChanges : function(){
7500 var m = this.modified.slice(0);
7502 for(var i = 0, len = m.length; i < len; i++){
7508 * Cancel outstanding changes on all changed records.
7510 rejectChanges : function(){
7511 var m = this.modified.slice(0);
7513 for(var i = 0, len = m.length; i < len; i++){
7518 onMetaChange : function(meta, rtype, o){
7519 this.recordType = rtype;
7520 this.fields = rtype.prototype.fields;
7521 delete this.snapshot;
7522 this.sortInfo = meta.sortInfo || this.sortInfo;
7524 this.fireEvent('metachange', this, this.reader.meta);
7527 moveIndex : function(data, type)
7529 var index = this.indexOf(data);
7531 var newIndex = index + type;
7535 this.insert(newIndex, data);
7540 * Ext JS Library 1.1.1
7541 * Copyright(c) 2006-2007, Ext JS, LLC.
7543 * Originally Released Under LGPL - original licence link has changed is not relivant.
7546 * <script type="text/javascript">
7550 * @class Roo.data.SimpleStore
7551 * @extends Roo.data.Store
7552 * Small helper class to make creating Stores from Array data easier.
7553 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7554 * @cfg {Array} fields An array of field definition objects, or field name strings.
7555 * @cfg {Array} data The multi-dimensional array of data
7557 * @param {Object} config
7559 Roo.data.SimpleStore = function(config){
7560 Roo.data.SimpleStore.superclass.constructor.call(this, {
7562 reader: new Roo.data.ArrayReader({
7565 Roo.data.Record.create(config.fields)
7567 proxy : new Roo.data.MemoryProxy(config.data)
7571 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7573 * Ext JS Library 1.1.1
7574 * Copyright(c) 2006-2007, Ext JS, LLC.
7576 * Originally Released Under LGPL - original licence link has changed is not relivant.
7579 * <script type="text/javascript">
7584 * @extends Roo.data.Store
7585 * @class Roo.data.JsonStore
7586 * Small helper class to make creating Stores for JSON data easier. <br/>
7588 var store = new Roo.data.JsonStore({
7589 url: 'get-images.php',
7591 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7594 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7595 * JsonReader and HttpProxy (unless inline data is provided).</b>
7596 * @cfg {Array} fields An array of field definition objects, or field name strings.
7598 * @param {Object} config
7600 Roo.data.JsonStore = function(c){
7601 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7602 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7603 reader: new Roo.data.JsonReader(c, c.fields)
7606 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7608 * Ext JS Library 1.1.1
7609 * Copyright(c) 2006-2007, Ext JS, LLC.
7611 * Originally Released Under LGPL - original licence link has changed is not relivant.
7614 * <script type="text/javascript">
7618 Roo.data.Field = function(config){
7619 if(typeof config == "string"){
7620 config = {name: config};
7622 Roo.apply(this, config);
7628 var st = Roo.data.SortTypes;
7629 // named sortTypes are supported, here we look them up
7630 if(typeof this.sortType == "string"){
7631 this.sortType = st[this.sortType];
7634 // set default sortType for strings and dates
7638 this.sortType = st.asUCString;
7641 this.sortType = st.asDate;
7644 this.sortType = st.none;
7649 var stripRe = /[\$,%]/g;
7651 // prebuilt conversion function for this field, instead of
7652 // switching every time we're reading a value
7654 var cv, dateFormat = this.dateFormat;
7659 cv = function(v){ return v; };
7662 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7666 return v !== undefined && v !== null && v !== '' ?
7667 parseInt(String(v).replace(stripRe, ""), 10) : '';
7672 return v !== undefined && v !== null && v !== '' ?
7673 parseFloat(String(v).replace(stripRe, ""), 10) : '';
7678 cv = function(v){ return v === true || v === "true" || v == 1; };
7685 if(v instanceof Date){
7689 if(dateFormat == "timestamp"){
7690 return new Date(v*1000);
7692 return Date.parseDate(v, dateFormat);
7694 var parsed = Date.parse(v);
7695 return parsed ? new Date(parsed) : null;
7704 Roo.data.Field.prototype = {
7712 * Ext JS Library 1.1.1
7713 * Copyright(c) 2006-2007, Ext JS, LLC.
7715 * Originally Released Under LGPL - original licence link has changed is not relivant.
7718 * <script type="text/javascript">
7721 // Base class for reading structured data from a data source. This class is intended to be
7722 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7725 * @class Roo.data.DataReader
7726 * Base class for reading structured data from a data source. This class is intended to be
7727 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7730 Roo.data.DataReader = function(meta, recordType){
7734 this.recordType = recordType instanceof Array ?
7735 Roo.data.Record.create(recordType) : recordType;
7738 Roo.data.DataReader.prototype = {
7740 * Create an empty record
7741 * @param {Object} data (optional) - overlay some values
7742 * @return {Roo.data.Record} record created.
7744 newRow : function(d) {
7746 this.recordType.prototype.fields.each(function(c) {
7748 case 'int' : da[c.name] = 0; break;
7749 case 'date' : da[c.name] = new Date(); break;
7750 case 'float' : da[c.name] = 0.0; break;
7751 case 'boolean' : da[c.name] = false; break;
7752 default : da[c.name] = ""; break;
7756 return new this.recordType(Roo.apply(da, d));
7761 * Ext JS Library 1.1.1
7762 * Copyright(c) 2006-2007, Ext JS, LLC.
7764 * Originally Released Under LGPL - original licence link has changed is not relivant.
7767 * <script type="text/javascript">
7771 * @class Roo.data.DataProxy
7772 * @extends Roo.data.Observable
7773 * This class is an abstract base class for implementations which provide retrieval of
7774 * unformatted data objects.<br>
7776 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7777 * (of the appropriate type which knows how to parse the data object) to provide a block of
7778 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7780 * Custom implementations must implement the load method as described in
7781 * {@link Roo.data.HttpProxy#load}.
7783 Roo.data.DataProxy = function(){
7787 * Fires before a network request is made to retrieve a data object.
7788 * @param {Object} This DataProxy object.
7789 * @param {Object} params The params parameter to the load function.
7794 * Fires before the load method's callback is called.
7795 * @param {Object} This DataProxy object.
7796 * @param {Object} o The data object.
7797 * @param {Object} arg The callback argument object passed to the load function.
7801 * @event loadexception
7802 * Fires if an Exception occurs during data retrieval.
7803 * @param {Object} This DataProxy object.
7804 * @param {Object} o The data object.
7805 * @param {Object} arg The callback argument object passed to the load function.
7806 * @param {Object} e The Exception.
7808 loadexception : true
7810 Roo.data.DataProxy.superclass.constructor.call(this);
7813 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7816 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7820 * Ext JS Library 1.1.1
7821 * Copyright(c) 2006-2007, Ext JS, LLC.
7823 * Originally Released Under LGPL - original licence link has changed is not relivant.
7826 * <script type="text/javascript">
7829 * @class Roo.data.MemoryProxy
7830 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7831 * to the Reader when its load method is called.
7833 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7835 Roo.data.MemoryProxy = function(data){
7839 Roo.data.MemoryProxy.superclass.constructor.call(this);
7843 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7845 * Load data from the requested source (in this case an in-memory
7846 * data object passed to the constructor), read the data object into
7847 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7848 * process that block using the passed callback.
7849 * @param {Object} params This parameter is not used by the MemoryProxy class.
7850 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7851 * object into a block of Roo.data.Records.
7852 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7853 * The function must be passed <ul>
7854 * <li>The Record block object</li>
7855 * <li>The "arg" argument from the load function</li>
7856 * <li>A boolean success indicator</li>
7858 * @param {Object} scope The scope in which to call the callback
7859 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7861 load : function(params, reader, callback, scope, arg){
7862 params = params || {};
7865 result = reader.readRecords(this.data);
7867 this.fireEvent("loadexception", this, arg, null, e);
7868 callback.call(scope, null, arg, false);
7871 callback.call(scope, result, arg, true);
7875 update : function(params, records){
7880 * Ext JS Library 1.1.1
7881 * Copyright(c) 2006-2007, Ext JS, LLC.
7883 * Originally Released Under LGPL - original licence link has changed is not relivant.
7886 * <script type="text/javascript">
7889 * @class Roo.data.HttpProxy
7890 * @extends Roo.data.DataProxy
7891 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7892 * configured to reference a certain URL.<br><br>
7894 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7895 * from which the running page was served.<br><br>
7897 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7899 * Be aware that to enable the browser to parse an XML document, the server must set
7900 * the Content-Type header in the HTTP response to "text/xml".
7902 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7903 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7904 * will be used to make the request.
7906 Roo.data.HttpProxy = function(conn){
7907 Roo.data.HttpProxy.superclass.constructor.call(this);
7908 // is conn a conn config or a real conn?
7910 this.useAjax = !conn || !conn.events;
7914 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7915 // thse are take from connection...
7918 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7921 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7922 * extra parameters to each request made by this object. (defaults to undefined)
7925 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7926 * to each request made by this object. (defaults to undefined)
7929 * @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)
7932 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7935 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7941 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7945 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7946 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7947 * a finer-grained basis than the DataProxy events.
7949 getConnection : function(){
7950 return this.useAjax ? Roo.Ajax : this.conn;
7954 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7955 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7956 * process that block using the passed callback.
7957 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7958 * for the request to the remote server.
7959 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7960 * object into a block of Roo.data.Records.
7961 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7962 * The function must be passed <ul>
7963 * <li>The Record block object</li>
7964 * <li>The "arg" argument from the load function</li>
7965 * <li>A boolean success indicator</li>
7967 * @param {Object} scope The scope in which to call the callback
7968 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7970 load : function(params, reader, callback, scope, arg){
7971 if(this.fireEvent("beforeload", this, params) !== false){
7973 params : params || {},
7975 callback : callback,
7980 callback : this.loadResponse,
7984 Roo.applyIf(o, this.conn);
7985 if(this.activeRequest){
7986 Roo.Ajax.abort(this.activeRequest);
7988 this.activeRequest = Roo.Ajax.request(o);
7990 this.conn.request(o);
7993 callback.call(scope||this, null, arg, false);
7998 loadResponse : function(o, success, response){
7999 delete this.activeRequest;
8001 this.fireEvent("loadexception", this, o, response);
8002 o.request.callback.call(o.request.scope, null, o.request.arg, false);
8007 result = o.reader.read(response);
8009 this.fireEvent("loadexception", this, o, response, e);
8010 o.request.callback.call(o.request.scope, null, o.request.arg, false);
8014 this.fireEvent("load", this, o, o.request.arg);
8015 o.request.callback.call(o.request.scope, result, o.request.arg, true);
8019 update : function(dataSet){
8024 updateResponse : function(dataSet){
8029 * Ext JS Library 1.1.1
8030 * Copyright(c) 2006-2007, Ext JS, LLC.
8032 * Originally Released Under LGPL - original licence link has changed is not relivant.
8035 * <script type="text/javascript">
8039 * @class Roo.data.ScriptTagProxy
8040 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8041 * other than the originating domain of the running page.<br><br>
8043 * <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
8044 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8046 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8047 * source code that is used as the source inside a <script> tag.<br><br>
8049 * In order for the browser to process the returned data, the server must wrap the data object
8050 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8051 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8052 * depending on whether the callback name was passed:
8055 boolean scriptTag = false;
8056 String cb = request.getParameter("callback");
8059 response.setContentType("text/javascript");
8061 response.setContentType("application/x-json");
8063 Writer out = response.getWriter();
8065 out.write(cb + "(");
8067 out.print(dataBlock.toJsonString());
8074 * @param {Object} config A configuration object.
8076 Roo.data.ScriptTagProxy = function(config){
8077 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8078 Roo.apply(this, config);
8079 this.head = document.getElementsByTagName("head")[0];
8082 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8084 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8086 * @cfg {String} url The URL from which to request the data object.
8089 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8093 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8094 * the server the name of the callback function set up by the load call to process the returned data object.
8095 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8096 * javascript output which calls this named function passing the data object as its only parameter.
8098 callbackParam : "callback",
8100 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8101 * name to the request.
8106 * Load data from the configured URL, read the data object into
8107 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8108 * process that block using the passed callback.
8109 * @param {Object} params An object containing properties which are to be used as HTTP parameters
8110 * for the request to the remote server.
8111 * @param {Roo.data.DataReader} reader The Reader object which converts the data
8112 * object into a block of Roo.data.Records.
8113 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8114 * The function must be passed <ul>
8115 * <li>The Record block object</li>
8116 * <li>The "arg" argument from the load function</li>
8117 * <li>A boolean success indicator</li>
8119 * @param {Object} scope The scope in which to call the callback
8120 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8122 load : function(params, reader, callback, scope, arg){
8123 if(this.fireEvent("beforeload", this, params) !== false){
8125 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8128 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8130 url += "&_dc=" + (new Date().getTime());
8132 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8135 cb : "stcCallback"+transId,
8136 scriptId : "stcScript"+transId,
8140 callback : callback,
8146 window[trans.cb] = function(o){
8147 conn.handleResponse(o, trans);
8150 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8152 if(this.autoAbort !== false){
8156 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8158 var script = document.createElement("script");
8159 script.setAttribute("src", url);
8160 script.setAttribute("type", "text/javascript");
8161 script.setAttribute("id", trans.scriptId);
8162 this.head.appendChild(script);
8166 callback.call(scope||this, null, arg, false);
8171 isLoading : function(){
8172 return this.trans ? true : false;
8176 * Abort the current server request.
8179 if(this.isLoading()){
8180 this.destroyTrans(this.trans);
8185 destroyTrans : function(trans, isLoaded){
8186 this.head.removeChild(document.getElementById(trans.scriptId));
8187 clearTimeout(trans.timeoutId);
8189 window[trans.cb] = undefined;
8191 delete window[trans.cb];
8194 // if hasn't been loaded, wait for load to remove it to prevent script error
8195 window[trans.cb] = function(){
8196 window[trans.cb] = undefined;
8198 delete window[trans.cb];
8205 handleResponse : function(o, trans){
8207 this.destroyTrans(trans, true);
8210 result = trans.reader.readRecords(o);
8212 this.fireEvent("loadexception", this, o, trans.arg, e);
8213 trans.callback.call(trans.scope||window, null, trans.arg, false);
8216 this.fireEvent("load", this, o, trans.arg);
8217 trans.callback.call(trans.scope||window, result, trans.arg, true);
8221 handleFailure : function(trans){
8223 this.destroyTrans(trans, false);
8224 this.fireEvent("loadexception", this, null, trans.arg);
8225 trans.callback.call(trans.scope||window, null, trans.arg, false);
8229 * Ext JS Library 1.1.1
8230 * Copyright(c) 2006-2007, Ext JS, LLC.
8232 * Originally Released Under LGPL - original licence link has changed is not relivant.
8235 * <script type="text/javascript">
8239 * @class Roo.data.JsonReader
8240 * @extends Roo.data.DataReader
8241 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8242 * based on mappings in a provided Roo.data.Record constructor.
8244 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8245 * in the reply previously.
8250 var RecordDef = Roo.data.Record.create([
8251 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
8252 {name: 'occupation'} // This field will use "occupation" as the mapping.
8254 var myReader = new Roo.data.JsonReader({
8255 totalProperty: "results", // The property which contains the total dataset size (optional)
8256 root: "rows", // The property which contains an Array of row objects
8257 id: "id" // The property within each row object that provides an ID for the record (optional)
8261 * This would consume a JSON file like this:
8263 { 'results': 2, 'rows': [
8264 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8265 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8268 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8269 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8270 * paged from the remote server.
8271 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8272 * @cfg {String} root name of the property which contains the Array of row objects.
8273 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8275 * Create a new JsonReader
8276 * @param {Object} meta Metadata configuration options
8277 * @param {Object} recordType Either an Array of field definition objects,
8278 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8280 Roo.data.JsonReader = function(meta, recordType){
8283 // set some defaults:
8285 totalProperty: 'total',
8286 successProperty : 'success',
8291 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8293 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8296 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
8297 * Used by Store query builder to append _requestMeta to params.
8300 metaFromRemote : false,
8302 * This method is only used by a DataProxy which has retrieved data from a remote server.
8303 * @param {Object} response The XHR object which contains the JSON data in its responseText.
8304 * @return {Object} data A data block which is used by an Roo.data.Store object as
8305 * a cache of Roo.data.Records.
8307 read : function(response){
8308 var json = response.responseText;
8310 var o = /* eval:var:o */ eval("("+json+")");
8312 throw {message: "JsonReader.read: Json object not found"};
8318 this.metaFromRemote = true;
8319 this.meta = o.metaData;
8320 this.recordType = Roo.data.Record.create(o.metaData.fields);
8321 this.onMetaChange(this.meta, this.recordType, o);
8323 return this.readRecords(o);
8326 // private function a store will implement
8327 onMetaChange : function(meta, recordType, o){
8334 simpleAccess: function(obj, subsc) {
8341 getJsonAccessor: function(){
8343 return function(expr) {
8345 return(re.test(expr))
8346 ? new Function("obj", "return obj." + expr)
8356 * Create a data block containing Roo.data.Records from an XML document.
8357 * @param {Object} o An object which contains an Array of row objects in the property specified
8358 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8359 * which contains the total size of the dataset.
8360 * @return {Object} data A data block which is used by an Roo.data.Store object as
8361 * a cache of Roo.data.Records.
8363 readRecords : function(o){
8365 * After any data loads, the raw JSON data is available for further custom processing.
8369 var s = this.meta, Record = this.recordType,
8370 f = Record.prototype.fields, fi = f.items, fl = f.length;
8372 // Generate extraction functions for the totalProperty, the root, the id, and for each field
8374 if(s.totalProperty) {
8375 this.getTotal = this.getJsonAccessor(s.totalProperty);
8377 if(s.successProperty) {
8378 this.getSuccess = this.getJsonAccessor(s.successProperty);
8380 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8382 var g = this.getJsonAccessor(s.id);
8383 this.getId = function(rec) {
8385 return (r === undefined || r === "") ? null : r;
8388 this.getId = function(){return null;};
8391 for(var jj = 0; jj < fl; jj++){
8393 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8394 this.ef[jj] = this.getJsonAccessor(map);
8398 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8399 if(s.totalProperty){
8400 var vt = parseInt(this.getTotal(o), 10);
8405 if(s.successProperty){
8406 var vs = this.getSuccess(o);
8407 if(vs === false || vs === 'false'){
8412 for(var i = 0; i < c; i++){
8415 var id = this.getId(n);
8416 for(var j = 0; j < fl; j++){
8418 var v = this.ef[j](n);
8420 Roo.log('missing convert for ' + f.name);
8424 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8426 var record = new Record(values, id);
8428 records[i] = record;
8434 totalRecords : totalRecords
8439 * Ext JS Library 1.1.1
8440 * Copyright(c) 2006-2007, Ext JS, LLC.
8442 * Originally Released Under LGPL - original licence link has changed is not relivant.
8445 * <script type="text/javascript">
8449 * @class Roo.data.ArrayReader
8450 * @extends Roo.data.DataReader
8451 * Data reader class to create an Array of Roo.data.Record objects from an Array.
8452 * Each element of that Array represents a row of data fields. The
8453 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8454 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8458 var RecordDef = Roo.data.Record.create([
8459 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
8460 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
8462 var myReader = new Roo.data.ArrayReader({
8463 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
8467 * This would consume an Array like this:
8469 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8471 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8473 * Create a new JsonReader
8474 * @param {Object} meta Metadata configuration options.
8475 * @param {Object} recordType Either an Array of field definition objects
8476 * as specified to {@link Roo.data.Record#create},
8477 * or an {@link Roo.data.Record} object
8478 * created using {@link Roo.data.Record#create}.
8480 Roo.data.ArrayReader = function(meta, recordType){
8481 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8484 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8486 * Create a data block containing Roo.data.Records from an XML document.
8487 * @param {Object} o An Array of row objects which represents the dataset.
8488 * @return {Object} data A data block which is used by an Roo.data.Store object as
8489 * a cache of Roo.data.Records.
8491 readRecords : function(o){
8492 var sid = this.meta ? this.meta.id : null;
8493 var recordType = this.recordType, fields = recordType.prototype.fields;
8496 for(var i = 0; i < root.length; i++){
8499 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8500 for(var j = 0, jlen = fields.length; j < jlen; j++){
8501 var f = fields.items[j];
8502 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8503 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8507 var record = new recordType(values, id);
8509 records[records.length] = record;
8513 totalRecords : records.length
8522 * @class Roo.bootstrap.ComboBox
8523 * @extends Roo.bootstrap.TriggerField
8524 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8525 * @cfg {Boolean} append (true|false) default false
8527 * Create a new ComboBox.
8528 * @param {Object} config Configuration options
8530 Roo.bootstrap.ComboBox = function(config){
8531 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8535 * Fires when the dropdown list is expanded
8536 * @param {Roo.bootstrap.ComboBox} combo This combo box
8541 * Fires when the dropdown list is collapsed
8542 * @param {Roo.bootstrap.ComboBox} combo This combo box
8546 * @event beforeselect
8547 * Fires before a list item is selected. Return false to cancel the selection.
8548 * @param {Roo.bootstrap.ComboBox} combo This combo box
8549 * @param {Roo.data.Record} record The data record returned from the underlying store
8550 * @param {Number} index The index of the selected item in the dropdown list
8552 'beforeselect' : true,
8555 * Fires when a list item is selected
8556 * @param {Roo.bootstrap.ComboBox} combo This combo box
8557 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8558 * @param {Number} index The index of the selected item in the dropdown list
8562 * @event beforequery
8563 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8564 * The event object passed has these properties:
8565 * @param {Roo.bootstrap.ComboBox} combo This combo box
8566 * @param {String} query The query
8567 * @param {Boolean} forceAll true to force "all" query
8568 * @param {Boolean} cancel true to cancel the query
8569 * @param {Object} e The query event object
8571 'beforequery': true,
8574 * Fires when the 'add' icon is pressed (add a listener to enable add button)
8575 * @param {Roo.bootstrap.ComboBox} combo This combo box
8580 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8581 * @param {Roo.bootstrap.ComboBox} combo This combo box
8582 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8587 * Fires when the remove value from the combobox array
8588 * @param {Roo.bootstrap.ComboBox} combo This combo box
8595 this.selectedIndex = -1;
8596 if(this.mode == 'local'){
8597 if(config.queryDelay === undefined){
8598 this.queryDelay = 10;
8600 if(config.minChars === undefined){
8606 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8609 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8610 * rendering into an Roo.Editor, defaults to false)
8613 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8614 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8617 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8620 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8621 * the dropdown list (defaults to undefined, with no header element)
8625 * @cfg {String/Roo.Template} tpl The template to use to render the output
8629 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8631 listWidth: undefined,
8633 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8634 * mode = 'remote' or 'text' if mode = 'local')
8636 displayField: undefined,
8638 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8639 * mode = 'remote' or 'value' if mode = 'local').
8640 * Note: use of a valueField requires the user make a selection
8641 * in order for a value to be mapped.
8643 valueField: undefined,
8647 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8648 * field's data value (defaults to the underlying DOM element's name)
8650 hiddenName: undefined,
8652 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8656 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8658 selectedClass: 'active',
8661 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8665 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8666 * anchor positions (defaults to 'tl-bl')
8668 listAlign: 'tl-bl?',
8670 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8674 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
8675 * query specified by the allQuery config option (defaults to 'query')
8677 triggerAction: 'query',
8679 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8680 * (defaults to 4, does not apply if editable = false)
8684 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8685 * delay (typeAheadDelay) if it matches a known value (defaults to false)
8689 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8690 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8694 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8695 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
8699 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
8700 * when editable = true (defaults to false)
8702 selectOnFocus:false,
8704 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8706 queryParam: 'query',
8708 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
8709 * when mode = 'remote' (defaults to 'Loading...')
8711 loadingText: 'Loading...',
8713 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8717 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8721 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8722 * traditional select (defaults to true)
8726 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8730 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8734 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8735 * listWidth has a higher value)
8739 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8740 * allow the user to set arbitrary text into the field (defaults to false)
8742 forceSelection:false,
8744 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8745 * if typeAhead = true (defaults to 250)
8747 typeAheadDelay : 250,
8749 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8750 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8752 valueNotFoundText : undefined,
8754 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8759 * @cfg {Boolean} disableClear Disable showing of clear button.
8761 disableClear : false,
8763 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8765 alwaysQuery : false,
8768 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8782 // element that contains real text value.. (when hidden is used..)
8785 initEvents: function(){
8788 throw "can not find store for combo";
8790 this.store = Roo.factory(this.store, Roo.data);
8794 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8797 if(this.hiddenName){
8799 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8801 this.hiddenField.dom.value =
8802 this.hiddenValue !== undefined ? this.hiddenValue :
8803 this.value !== undefined ? this.value : '';
8805 // prevent input submission
8806 this.el.dom.removeAttribute('name');
8807 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8812 // this.el.dom.setAttribute('autocomplete', 'off');
8815 var cls = 'x-combo-list';
8816 this.list = this.el.select('ul.dropdown-menu',true).first();
8818 //this.list = new Roo.Layer({
8819 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8822 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8823 this.list.setWidth(lw);
8825 this.list.on('mouseover', this.onViewOver, this);
8826 this.list.on('mousemove', this.onViewMove, this);
8828 this.list.on('scroll', this.onViewScroll, this);
8831 this.list.swallowEvent('mousewheel');
8832 this.assetHeight = 0;
8835 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8836 this.assetHeight += this.header.getHeight();
8839 this.innerList = this.list.createChild({cls:cls+'-inner'});
8840 this.innerList.on('mouseover', this.onViewOver, this);
8841 this.innerList.on('mousemove', this.onViewMove, this);
8842 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8844 if(this.allowBlank && !this.pageSize && !this.disableClear){
8845 this.footer = this.list.createChild({cls:cls+'-ft'});
8846 this.pageTb = new Roo.Toolbar(this.footer);
8850 this.footer = this.list.createChild({cls:cls+'-ft'});
8851 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8852 {pageSize: this.pageSize});
8856 if (this.pageTb && this.allowBlank && !this.disableClear) {
8858 this.pageTb.add(new Roo.Toolbar.Fill(), {
8859 cls: 'x-btn-icon x-btn-clear',
8865 _this.onSelect(false, -1);
8870 this.assetHeight += this.footer.getHeight();
8875 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8878 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8879 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8881 //this.view.wrapEl.setDisplayed(false);
8882 this.view.on('click', this.onViewClick, this);
8886 this.store.on('beforeload', this.onBeforeLoad, this);
8887 this.store.on('load', this.onLoad, this);
8888 this.store.on('loadexception', this.onLoadException, this);
8891 this.resizer = new Roo.Resizable(this.list, {
8892 pinned:true, handles:'se'
8894 this.resizer.on('resize', function(r, w, h){
8895 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8897 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8898 this.restrictHeight();
8900 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8904 this.editable = true;
8905 this.setEditable(false);
8910 if (typeof(this.events.add.listeners) != 'undefined') {
8912 this.addicon = this.wrap.createChild(
8913 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8915 this.addicon.on('click', function(e) {
8916 this.fireEvent('add', this);
8919 if (typeof(this.events.edit.listeners) != 'undefined') {
8921 this.editicon = this.wrap.createChild(
8922 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8924 this.editicon.setStyle('margin-left', '40px');
8926 this.editicon.on('click', function(e) {
8928 // we fire even if inothing is selected..
8929 this.fireEvent('edit', this, this.lastData );
8935 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8937 this.inKeyMode = true;
8941 "down" : function(e){
8942 if(!this.isExpanded()){
8943 this.onTriggerClick();
8945 this.inKeyMode = true;
8950 "enter" : function(e){
8955 "esc" : function(e){
8959 "tab" : function(e){
8962 if(this.fireEvent("specialkey", this, e)){
8963 this.onViewClick(false);
8971 doRelay : function(foo, bar, hname){
8972 if(hname == 'down' || this.scope.isExpanded()){
8973 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8982 this.queryDelay = Math.max(this.queryDelay || 10,
8983 this.mode == 'local' ? 10 : 250);
8986 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8989 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8991 if(this.editable !== false){
8992 this.inputEl().on("keyup", this.onKeyUp, this);
8994 if(this.forceSelection){
8995 this.on('blur', this.doForce, this);
8999 this.choices = this.el.select('ul.select2-choices', true).first();
9000 this.searchField = this.el.select('ul li.select2-search-field', true).first();
9004 onDestroy : function(){
9006 this.view.setStore(null);
9007 this.view.el.removeAllListeners();
9008 this.view.el.remove();
9009 this.view.purgeListeners();
9012 this.list.dom.innerHTML = '';
9015 this.store.un('beforeload', this.onBeforeLoad, this);
9016 this.store.un('load', this.onLoad, this);
9017 this.store.un('loadexception', this.onLoadException, this);
9019 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9023 fireKey : function(e){
9024 if(e.isNavKeyPress() && !this.list.isVisible()){
9025 this.fireEvent("specialkey", this, e);
9030 onResize: function(w, h){
9031 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9033 // if(typeof w != 'number'){
9034 // // we do not handle it!?!?
9037 // var tw = this.trigger.getWidth();
9038 // // tw += this.addicon ? this.addicon.getWidth() : 0;
9039 // // tw += this.editicon ? this.editicon.getWidth() : 0;
9041 // this.inputEl().setWidth( this.adjustWidth('input', x));
9043 // //this.trigger.setStyle('left', x+'px');
9045 // if(this.list && this.listWidth === undefined){
9046 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9047 // this.list.setWidth(lw);
9048 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9056 * Allow or prevent the user from directly editing the field text. If false is passed,
9057 * the user will only be able to select from the items defined in the dropdown list. This method
9058 * is the runtime equivalent of setting the 'editable' config option at config time.
9059 * @param {Boolean} value True to allow the user to directly edit the field text
9061 setEditable : function(value){
9062 if(value == this.editable){
9065 this.editable = value;
9067 this.inputEl().dom.setAttribute('readOnly', true);
9068 this.inputEl().on('mousedown', this.onTriggerClick, this);
9069 this.inputEl().addClass('x-combo-noedit');
9071 this.inputEl().dom.setAttribute('readOnly', false);
9072 this.inputEl().un('mousedown', this.onTriggerClick, this);
9073 this.inputEl().removeClass('x-combo-noedit');
9079 onBeforeLoad : function(combo,opts){
9084 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9086 this.restrictHeight();
9087 this.selectedIndex = -1;
9091 onLoad : function(){
9093 this.hasQuery = false;
9099 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9100 this.loading.hide();
9103 if(this.store.getCount() > 0){
9105 this.restrictHeight();
9106 if(this.lastQuery == this.allQuery){
9108 this.inputEl().dom.select();
9110 if(!this.selectByValue(this.value, true)){
9111 this.select(0, true);
9115 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9116 this.taTask.delay(this.typeAheadDelay);
9120 this.onEmptyResults();
9126 onLoadException : function()
9128 this.hasQuery = false;
9130 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9131 this.loading.hide();
9135 Roo.log(this.store.reader.jsonData);
9136 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9138 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9144 onTypeAhead : function(){
9145 if(this.store.getCount() > 0){
9146 var r = this.store.getAt(0);
9147 var newValue = r.data[this.displayField];
9148 var len = newValue.length;
9149 var selStart = this.getRawValue().length;
9151 if(selStart != len){
9152 this.setRawValue(newValue);
9153 this.selectText(selStart, newValue.length);
9159 onSelect : function(record, index){
9161 if(this.fireEvent('beforeselect', this, record, index) !== false){
9163 this.setFromData(index > -1 ? record.data : false);
9166 this.fireEvent('select', this, record, index);
9171 * Returns the currently selected field value or empty string if no value is set.
9172 * @return {String} value The selected value
9174 getValue : function(){
9177 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9180 if(this.valueField){
9181 return typeof this.value != 'undefined' ? this.value : '';
9183 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9188 * Clears any text/value currently set in the field
9190 clearValue : function(){
9191 if(this.hiddenField){
9192 this.hiddenField.dom.value = '';
9195 this.setRawValue('');
9196 this.lastSelectionText = '';
9201 * Sets the specified value into the field. If the value finds a match, the corresponding record text
9202 * will be displayed in the field. If the value does not match the data value of an existing item,
9203 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9204 * Otherwise the field will be blank (although the value will still be set).
9205 * @param {String} value The value to match
9207 setValue : function(v){
9214 if(this.valueField){
9215 var r = this.findRecord(this.valueField, v);
9217 text = r.data[this.displayField];
9218 }else if(this.valueNotFoundText !== undefined){
9219 text = this.valueNotFoundText;
9222 this.lastSelectionText = text;
9223 if(this.hiddenField){
9224 this.hiddenField.dom.value = v;
9226 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9230 * @property {Object} the last set data for the element
9235 * Sets the value of the field based on a object which is related to the record format for the store.
9236 * @param {Object} value the value to set as. or false on reset?
9238 setFromData : function(o){
9245 var dv = ''; // display value
9246 var vv = ''; // value value..
9248 if (this.displayField) {
9249 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9251 // this is an error condition!!!
9252 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
9255 if(this.valueField){
9256 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9259 if(this.hiddenField){
9260 this.hiddenField.dom.value = vv;
9262 this.lastSelectionText = dv;
9263 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9267 // no hidden field.. - we store the value in 'value', but still display
9268 // display field!!!!
9269 this.lastSelectionText = dv;
9270 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9277 // overridden so that last data is reset..
9278 this.setValue(this.originalValue);
9279 this.clearInvalid();
9280 this.lastData = false;
9282 this.view.clearSelections();
9286 findRecord : function(prop, value){
9288 if(this.store.getCount() > 0){
9289 this.store.each(function(r){
9290 if(r.data[prop] == value){
9302 // returns hidden if it's set..
9303 if (!this.rendered) {return ''};
9304 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
9308 onViewMove : function(e, t){
9309 this.inKeyMode = false;
9313 onViewOver : function(e, t){
9314 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9317 var item = this.view.findItemFromChild(t);
9319 var index = this.view.indexOf(item);
9320 this.select(index, false);
9325 onViewClick : function(doFocus)
9327 var index = this.view.getSelectedIndexes()[0];
9328 var r = this.store.getAt(index);
9330 this.onSelect(r, index);
9332 if(doFocus !== false && !this.blockFocus){
9333 this.inputEl().focus();
9338 restrictHeight : function(){
9339 //this.innerList.dom.style.height = '';
9340 //var inner = this.innerList.dom;
9341 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9342 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9343 //this.list.beginUpdate();
9344 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9345 this.list.alignTo(this.inputEl(), this.listAlign);
9346 //this.list.endUpdate();
9350 onEmptyResults : function(){
9355 * Returns true if the dropdown list is expanded, else false.
9357 isExpanded : function(){
9358 return this.list.isVisible();
9362 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9363 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9364 * @param {String} value The data value of the item to select
9365 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9366 * selected item if it is not currently in view (defaults to true)
9367 * @return {Boolean} True if the value matched an item in the list, else false
9369 selectByValue : function(v, scrollIntoView){
9370 if(v !== undefined && v !== null){
9371 var r = this.findRecord(this.valueField || this.displayField, v);
9373 this.select(this.store.indexOf(r), scrollIntoView);
9381 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9382 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9383 * @param {Number} index The zero-based index of the list item to select
9384 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9385 * selected item if it is not currently in view (defaults to true)
9387 select : function(index, scrollIntoView){
9388 this.selectedIndex = index;
9389 this.view.select(index);
9390 if(scrollIntoView !== false){
9391 var el = this.view.getNode(index);
9393 //this.innerList.scrollChildIntoView(el, false);
9400 selectNext : function(){
9401 var ct = this.store.getCount();
9403 if(this.selectedIndex == -1){
9405 }else if(this.selectedIndex < ct-1){
9406 this.select(this.selectedIndex+1);
9412 selectPrev : function(){
9413 var ct = this.store.getCount();
9415 if(this.selectedIndex == -1){
9417 }else if(this.selectedIndex != 0){
9418 this.select(this.selectedIndex-1);
9424 onKeyUp : function(e){
9425 if(this.editable !== false && !e.isSpecialKey()){
9426 this.lastKey = e.getKey();
9427 this.dqTask.delay(this.queryDelay);
9432 validateBlur : function(){
9433 return !this.list || !this.list.isVisible();
9437 initQuery : function(){
9438 this.doQuery(this.getRawValue());
9442 doForce : function(){
9443 if(this.el.dom.value.length > 0){
9445 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9451 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
9452 * query allowing the query action to be canceled if needed.
9453 * @param {String} query The SQL query to execute
9454 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9455 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
9456 * saved in the current store (defaults to false)
9458 doQuery : function(q, forceAll){
9460 if(q === undefined || q === null){
9469 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9474 forceAll = qe.forceAll;
9475 if(forceAll === true || (q.length >= this.minChars)){
9477 this.hasQuery = true;
9479 if(this.lastQuery != q || this.alwaysQuery){
9481 if(this.mode == 'local'){
9482 this.selectedIndex = -1;
9484 this.store.clearFilter();
9486 this.store.filter(this.displayField, q);
9490 this.store.baseParams[this.queryParam] = q;
9492 var options = {params : this.getParams(q)};
9496 options.params.start = this.page * this.pageSize;
9499 this.store.load(options);
9503 this.selectedIndex = -1;
9508 this.loadNext = false;
9512 getParams : function(q){
9514 //p[this.queryParam] = q;
9518 p.limit = this.pageSize;
9524 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9526 collapse : function(){
9527 if(!this.isExpanded()){
9532 Roo.get(document).un('mousedown', this.collapseIf, this);
9533 Roo.get(document).un('mousewheel', this.collapseIf, this);
9534 if (!this.editable) {
9535 Roo.get(document).un('keydown', this.listKeyPress, this);
9537 this.fireEvent('collapse', this);
9541 collapseIf : function(e){
9542 var in_combo = e.within(this.el);
9543 var in_list = e.within(this.list);
9545 if (in_combo || in_list) {
9546 //e.stopPropagation();
9555 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9557 expand : function(){
9559 if(this.isExpanded() || !this.hasFocus){
9563 this.list.alignTo(this.inputEl(), this.listAlign);
9565 Roo.get(document).on('mousedown', this.collapseIf, this);
9566 Roo.get(document).on('mousewheel', this.collapseIf, this);
9567 if (!this.editable) {
9568 Roo.get(document).on('keydown', this.listKeyPress, this);
9571 this.fireEvent('expand', this);
9575 // Implements the default empty TriggerField.onTriggerClick function
9576 onTriggerClick : function()
9578 Roo.log('trigger click');
9585 this.loadNext = false;
9587 if(this.isExpanded()){
9589 if (!this.blockFocus) {
9590 this.inputEl().focus();
9594 this.hasFocus = true;
9595 if(this.triggerAction == 'all') {
9596 this.doQuery(this.allQuery, true);
9598 this.doQuery(this.getRawValue());
9600 if (!this.blockFocus) {
9601 this.inputEl().focus();
9605 listKeyPress : function(e)
9607 //Roo.log('listkeypress');
9608 // scroll to first matching element based on key pres..
9609 if (e.isSpecialKey()) {
9612 var k = String.fromCharCode(e.getKey()).toUpperCase();
9615 var csel = this.view.getSelectedNodes();
9616 var cselitem = false;
9618 var ix = this.view.indexOf(csel[0]);
9619 cselitem = this.store.getAt(ix);
9620 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9626 this.store.each(function(v) {
9628 // start at existing selection.
9629 if (cselitem.id == v.id) {
9635 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9636 match = this.store.indexOf(v);
9642 if (match === false) {
9643 return true; // no more action?
9646 this.view.select(match);
9647 var sn = Roo.get(this.view.getSelectedNodes()[0])
9648 //sn.scrollIntoView(sn.dom.parentNode, false);
9651 onViewScroll : function(e, t){
9653 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9657 this.hasQuery = true;
9659 this.loading = this.list.select('.loading', true).first();
9661 if(this.loading === null){
9662 this.list.createChild({
9664 cls: 'loading select2-more-results select2-active',
9665 html: 'Loading more results...'
9668 this.loading = this.list.select('.loading', true).first();
9670 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9672 this.loading.hide();
9675 this.loading.show();
9680 this.loadNext = true;
9682 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9687 addItem : function(o)
9689 var dv = ''; // display value
9691 if (this.displayField) {
9692 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9694 // this is an error condition!!!
9695 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
9702 var choice = this.choices.createChild({
9704 cls: 'select2-search-choice',
9713 cls: 'select2-search-choice-close',
9718 }, this.searchField);
9720 var close = choice.select('a.select2-search-choice-close', true).first()
9722 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9729 this.inputEl().dom.value = '';
9733 onRemoveItem : function(e, _self, o)
9735 Roo.log('remove item');
9736 var index = this.item.indexOf(o.data) * 1;
9739 Roo.log('not this item?!');
9743 this.item.splice(index, 1);
9748 this.fireEvent('remove', this);
9752 syncValue : function()
9754 if(!this.item.length){
9761 Roo.each(this.item, function(i){
9762 if(_this.valueField){
9763 value.push(i[_this.valueField]);
9770 this.value = value.join(',');
9772 if(this.hiddenField){
9773 this.hiddenField.dom.value = this.value;
9777 clearItem : function()
9785 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9795 * @cfg {Boolean} grow
9799 * @cfg {Number} growMin
9803 * @cfg {Number} growMax
9813 * Ext JS Library 1.1.1
9814 * Copyright(c) 2006-2007, Ext JS, LLC.
9816 * Originally Released Under LGPL - original licence link has changed is not relivant.
9819 * <script type="text/javascript">
9824 * @extends Roo.util.Observable
9825 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9826 * This class also supports single and multi selection modes. <br>
9827 * Create a data model bound view:
9829 var store = new Roo.data.Store(...);
9831 var view = new Roo.View({
9833 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9836 selectedClass: "ydataview-selected",
9840 // listen for node click?
9841 view.on("click", function(vw, index, node, e){
9842 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9846 dataModel.load("foobar.xml");
9848 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9850 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9851 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9853 * Note: old style constructor is still suported (container, template, config)
9857 * @param {Object} config The config object
9860 Roo.View = function(config, depreciated_tpl, depreciated_config){
9862 if (typeof(depreciated_tpl) == 'undefined') {
9863 // new way.. - universal constructor.
9864 Roo.apply(this, config);
9865 this.el = Roo.get(this.el);
9868 this.el = Roo.get(config);
9869 this.tpl = depreciated_tpl;
9870 Roo.apply(this, depreciated_config);
9872 this.wrapEl = this.el.wrap().wrap();
9873 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9876 if(typeof(this.tpl) == "string"){
9877 this.tpl = new Roo.Template(this.tpl);
9879 // support xtype ctors..
9880 this.tpl = new Roo.factory(this.tpl, Roo);
9892 * @event beforeclick
9893 * Fires before a click is processed. Returns false to cancel the default action.
9894 * @param {Roo.View} this
9895 * @param {Number} index The index of the target node
9896 * @param {HTMLElement} node The target node
9897 * @param {Roo.EventObject} e The raw event object
9899 "beforeclick" : true,
9902 * Fires when a template node is clicked.
9903 * @param {Roo.View} this
9904 * @param {Number} index The index of the target node
9905 * @param {HTMLElement} node The target node
9906 * @param {Roo.EventObject} e The raw event object
9911 * Fires when a template node is double clicked.
9912 * @param {Roo.View} this
9913 * @param {Number} index The index of the target node
9914 * @param {HTMLElement} node The target node
9915 * @param {Roo.EventObject} e The raw event object
9919 * @event contextmenu
9920 * Fires when a template node is right clicked.
9921 * @param {Roo.View} this
9922 * @param {Number} index The index of the target node
9923 * @param {HTMLElement} node The target node
9924 * @param {Roo.EventObject} e The raw event object
9926 "contextmenu" : true,
9928 * @event selectionchange
9929 * Fires when the selected nodes change.
9930 * @param {Roo.View} this
9931 * @param {Array} selections Array of the selected nodes
9933 "selectionchange" : true,
9936 * @event beforeselect
9937 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9938 * @param {Roo.View} this
9939 * @param {HTMLElement} node The node to be selected
9940 * @param {Array} selections Array of currently selected nodes
9942 "beforeselect" : true,
9944 * @event preparedata
9945 * Fires on every row to render, to allow you to change the data.
9946 * @param {Roo.View} this
9947 * @param {Object} data to be rendered (change this)
9949 "preparedata" : true
9957 "click": this.onClick,
9958 "dblclick": this.onDblClick,
9959 "contextmenu": this.onContextMenu,
9963 this.selections = [];
9965 this.cmp = new Roo.CompositeElementLite([]);
9967 this.store = Roo.factory(this.store, Roo.data);
9968 this.setStore(this.store, true);
9971 if ( this.footer && this.footer.xtype) {
9973 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9975 this.footer.dataSource = this.store
9976 this.footer.container = fctr;
9977 this.footer = Roo.factory(this.footer, Roo);
9978 fctr.insertFirst(this.el);
9980 // this is a bit insane - as the paging toolbar seems to detach the el..
9981 // dom.parentNode.parentNode.parentNode
9982 // they get detached?
9986 Roo.View.superclass.constructor.call(this);
9991 Roo.extend(Roo.View, Roo.util.Observable, {
9994 * @cfg {Roo.data.Store} store Data store to load data from.
9999 * @cfg {String|Roo.Element} el The container element.
10004 * @cfg {String|Roo.Template} tpl The template used by this View
10008 * @cfg {String} dataName the named area of the template to use as the data area
10009 * Works with domtemplates roo-name="name"
10013 * @cfg {String} selectedClass The css class to add to selected nodes
10015 selectedClass : "x-view-selected",
10017 * @cfg {String} emptyText The empty text to show when nothing is loaded.
10022 * @cfg {String} text to display on mask (default Loading)
10026 * @cfg {Boolean} multiSelect Allow multiple selection
10028 multiSelect : false,
10030 * @cfg {Boolean} singleSelect Allow single selection
10032 singleSelect: false,
10035 * @cfg {Boolean} toggleSelect - selecting
10037 toggleSelect : false,
10040 * Returns the element this view is bound to.
10041 * @return {Roo.Element}
10043 getEl : function(){
10044 return this.wrapEl;
10050 * Refreshes the view. - called by datachanged on the store. - do not call directly.
10052 refresh : function(){
10053 Roo.log('refresh');
10056 // if we are using something like 'domtemplate', then
10057 // the what gets used is:
10058 // t.applySubtemplate(NAME, data, wrapping data..)
10059 // the outer template then get' applied with
10060 // the store 'extra data'
10061 // and the body get's added to the
10062 // roo-name="data" node?
10063 // <span class='roo-tpl-{name}'></span> ?????
10067 this.clearSelections();
10068 this.el.update("");
10070 var records = this.store.getRange();
10071 if(records.length < 1) {
10073 // is this valid?? = should it render a template??
10075 this.el.update(this.emptyText);
10079 if (this.dataName) {
10080 this.el.update(t.apply(this.store.meta)); //????
10081 el = this.el.child('.roo-tpl-' + this.dataName);
10084 for(var i = 0, len = records.length; i < len; i++){
10085 var data = this.prepareData(records[i].data, i, records[i]);
10086 this.fireEvent("preparedata", this, data, i, records[i]);
10087 html[html.length] = Roo.util.Format.trim(
10089 t.applySubtemplate(this.dataName, data, this.store.meta) :
10096 el.update(html.join(""));
10097 this.nodes = el.dom.childNodes;
10098 this.updateIndexes(0);
10103 * Function to override to reformat the data that is sent to
10104 * the template for each node.
10105 * DEPRICATED - use the preparedata event handler.
10106 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10107 * a JSON object for an UpdateManager bound view).
10109 prepareData : function(data, index, record)
10111 this.fireEvent("preparedata", this, data, index, record);
10115 onUpdate : function(ds, record){
10116 Roo.log('on update');
10117 this.clearSelections();
10118 var index = this.store.indexOf(record);
10119 var n = this.nodes[index];
10120 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10121 n.parentNode.removeChild(n);
10122 this.updateIndexes(index, index);
10128 onAdd : function(ds, records, index)
10130 Roo.log(['on Add', ds, records, index] );
10131 this.clearSelections();
10132 if(this.nodes.length == 0){
10136 var n = this.nodes[index];
10137 for(var i = 0, len = records.length; i < len; i++){
10138 var d = this.prepareData(records[i].data, i, records[i]);
10140 this.tpl.insertBefore(n, d);
10143 this.tpl.append(this.el, d);
10146 this.updateIndexes(index);
10149 onRemove : function(ds, record, index){
10150 Roo.log('onRemove');
10151 this.clearSelections();
10152 var el = this.dataName ?
10153 this.el.child('.roo-tpl-' + this.dataName) :
10156 el.dom.removeChild(this.nodes[index]);
10157 this.updateIndexes(index);
10161 * Refresh an individual node.
10162 * @param {Number} index
10164 refreshNode : function(index){
10165 this.onUpdate(this.store, this.store.getAt(index));
10168 updateIndexes : function(startIndex, endIndex){
10169 var ns = this.nodes;
10170 startIndex = startIndex || 0;
10171 endIndex = endIndex || ns.length - 1;
10172 for(var i = startIndex; i <= endIndex; i++){
10173 ns[i].nodeIndex = i;
10178 * Changes the data store this view uses and refresh the view.
10179 * @param {Store} store
10181 setStore : function(store, initial){
10182 if(!initial && this.store){
10183 this.store.un("datachanged", this.refresh);
10184 this.store.un("add", this.onAdd);
10185 this.store.un("remove", this.onRemove);
10186 this.store.un("update", this.onUpdate);
10187 this.store.un("clear", this.refresh);
10188 this.store.un("beforeload", this.onBeforeLoad);
10189 this.store.un("load", this.onLoad);
10190 this.store.un("loadexception", this.onLoad);
10194 store.on("datachanged", this.refresh, this);
10195 store.on("add", this.onAdd, this);
10196 store.on("remove", this.onRemove, this);
10197 store.on("update", this.onUpdate, this);
10198 store.on("clear", this.refresh, this);
10199 store.on("beforeload", this.onBeforeLoad, this);
10200 store.on("load", this.onLoad, this);
10201 store.on("loadexception", this.onLoad, this);
10209 * onbeforeLoad - masks the loading area.
10212 onBeforeLoad : function(store,opts)
10214 Roo.log('onBeforeLoad');
10216 this.el.update("");
10218 this.el.mask(this.mask ? this.mask : "Loading" );
10220 onLoad : function ()
10227 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10228 * @param {HTMLElement} node
10229 * @return {HTMLElement} The template node
10231 findItemFromChild : function(node){
10232 var el = this.dataName ?
10233 this.el.child('.roo-tpl-' + this.dataName,true) :
10236 if(!node || node.parentNode == el){
10239 var p = node.parentNode;
10240 while(p && p != el){
10241 if(p.parentNode == el){
10250 onClick : function(e){
10251 var item = this.findItemFromChild(e.getTarget());
10253 var index = this.indexOf(item);
10254 if(this.onItemClick(item, index, e) !== false){
10255 this.fireEvent("click", this, index, item, e);
10258 this.clearSelections();
10263 onContextMenu : function(e){
10264 var item = this.findItemFromChild(e.getTarget());
10266 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10271 onDblClick : function(e){
10272 var item = this.findItemFromChild(e.getTarget());
10274 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10278 onItemClick : function(item, index, e)
10280 if(this.fireEvent("beforeclick", this, index, item, e) === false){
10283 if (this.toggleSelect) {
10284 var m = this.isSelected(item) ? 'unselect' : 'select';
10287 _t[m](item, true, false);
10290 if(this.multiSelect || this.singleSelect){
10291 if(this.multiSelect && e.shiftKey && this.lastSelection){
10292 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10294 this.select(item, this.multiSelect && e.ctrlKey);
10295 this.lastSelection = item;
10297 e.preventDefault();
10303 * Get the number of selected nodes.
10306 getSelectionCount : function(){
10307 return this.selections.length;
10311 * Get the currently selected nodes.
10312 * @return {Array} An array of HTMLElements
10314 getSelectedNodes : function(){
10315 return this.selections;
10319 * Get the indexes of the selected nodes.
10322 getSelectedIndexes : function(){
10323 var indexes = [], s = this.selections;
10324 for(var i = 0, len = s.length; i < len; i++){
10325 indexes.push(s[i].nodeIndex);
10331 * Clear all selections
10332 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10334 clearSelections : function(suppressEvent){
10335 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10336 this.cmp.elements = this.selections;
10337 this.cmp.removeClass(this.selectedClass);
10338 this.selections = [];
10339 if(!suppressEvent){
10340 this.fireEvent("selectionchange", this, this.selections);
10346 * Returns true if the passed node is selected
10347 * @param {HTMLElement/Number} node The node or node index
10348 * @return {Boolean}
10350 isSelected : function(node){
10351 var s = this.selections;
10355 node = this.getNode(node);
10356 return s.indexOf(node) !== -1;
10361 * @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
10362 * @param {Boolean} keepExisting (optional) true to keep existing selections
10363 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10365 select : function(nodeInfo, keepExisting, suppressEvent){
10366 if(nodeInfo instanceof Array){
10368 this.clearSelections(true);
10370 for(var i = 0, len = nodeInfo.length; i < len; i++){
10371 this.select(nodeInfo[i], true, true);
10375 var node = this.getNode(nodeInfo);
10376 if(!node || this.isSelected(node)){
10377 return; // already selected.
10380 this.clearSelections(true);
10382 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10383 Roo.fly(node).addClass(this.selectedClass);
10384 this.selections.push(node);
10385 if(!suppressEvent){
10386 this.fireEvent("selectionchange", this, this.selections);
10394 * @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
10395 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10396 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10398 unselect : function(nodeInfo, keepExisting, suppressEvent)
10400 if(nodeInfo instanceof Array){
10401 Roo.each(this.selections, function(s) {
10402 this.unselect(s, nodeInfo);
10406 var node = this.getNode(nodeInfo);
10407 if(!node || !this.isSelected(node)){
10408 Roo.log("not selected");
10409 return; // not selected.
10413 Roo.each(this.selections, function(s) {
10415 Roo.fly(node).removeClass(this.selectedClass);
10422 this.selections= ns;
10423 this.fireEvent("selectionchange", this, this.selections);
10427 * Gets a template node.
10428 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10429 * @return {HTMLElement} The node or null if it wasn't found
10431 getNode : function(nodeInfo){
10432 if(typeof nodeInfo == "string"){
10433 return document.getElementById(nodeInfo);
10434 }else if(typeof nodeInfo == "number"){
10435 return this.nodes[nodeInfo];
10441 * Gets a range template nodes.
10442 * @param {Number} startIndex
10443 * @param {Number} endIndex
10444 * @return {Array} An array of nodes
10446 getNodes : function(start, end){
10447 var ns = this.nodes;
10448 start = start || 0;
10449 end = typeof end == "undefined" ? ns.length - 1 : end;
10452 for(var i = start; i <= end; i++){
10456 for(var i = start; i >= end; i--){
10464 * Finds the index of the passed node
10465 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10466 * @return {Number} The index of the node or -1
10468 indexOf : function(node){
10469 node = this.getNode(node);
10470 if(typeof node.nodeIndex == "number"){
10471 return node.nodeIndex;
10473 var ns = this.nodes;
10474 for(var i = 0, len = ns.length; i < len; i++){
10485 * based on jquery fullcalendar
10489 Roo.bootstrap = Roo.bootstrap || {};
10491 * @class Roo.bootstrap.Calendar
10492 * @extends Roo.bootstrap.Component
10493 * Bootstrap Calendar class
10494 * @cfg {Boolean} loadMask (true|false) default false
10495 * @cfg {Object} header generate the user specific header of the calendar, default false
10498 * Create a new Container
10499 * @param {Object} config The config object
10504 Roo.bootstrap.Calendar = function(config){
10505 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10509 * Fires when a date is selected
10510 * @param {DatePicker} this
10511 * @param {Date} date The selected date
10515 * @event monthchange
10516 * Fires when the displayed month changes
10517 * @param {DatePicker} this
10518 * @param {Date} date The selected month
10520 'monthchange': true,
10522 * @event evententer
10523 * Fires when mouse over an event
10524 * @param {Calendar} this
10525 * @param {event} Event
10527 'evententer': true,
10529 * @event eventleave
10530 * Fires when the mouse leaves an
10531 * @param {Calendar} this
10534 'eventleave': true,
10536 * @event eventclick
10537 * Fires when the mouse click an
10538 * @param {Calendar} this
10547 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
10550 * @cfg {Number} startDay
10551 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10559 getAutoCreate : function(){
10562 var fc_button = function(name, corner, style, content ) {
10563 return Roo.apply({},{
10565 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
10567 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10570 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10581 style : 'width:100%',
10588 cls : 'fc-header-left',
10590 fc_button('prev', 'left', 'arrow', '‹' ),
10591 fc_button('next', 'right', 'arrow', '›' ),
10592 { tag: 'span', cls: 'fc-header-space' },
10593 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
10601 cls : 'fc-header-center',
10605 cls: 'fc-header-title',
10608 html : 'month / year'
10616 cls : 'fc-header-right',
10618 /* fc_button('month', 'left', '', 'month' ),
10619 fc_button('week', '', '', 'week' ),
10620 fc_button('day', 'right', '', 'day' )
10632 header = this.header;
10635 var cal_heads = function() {
10637 // fixme - handle this.
10639 for (var i =0; i < Date.dayNames.length; i++) {
10640 var d = Date.dayNames[i];
10643 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10644 html : d.substring(0,3)
10648 ret[0].cls += ' fc-first';
10649 ret[6].cls += ' fc-last';
10652 var cal_cell = function(n) {
10655 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10660 cls: 'fc-day-number',
10664 cls: 'fc-day-content',
10668 style: 'position: relative;' // height: 17px;
10680 var cal_rows = function() {
10683 for (var r = 0; r < 6; r++) {
10690 for (var i =0; i < Date.dayNames.length; i++) {
10691 var d = Date.dayNames[i];
10692 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10695 row.cn[0].cls+=' fc-first';
10696 row.cn[0].cn[0].style = 'min-height:90px';
10697 row.cn[6].cls+=' fc-last';
10701 ret[0].cls += ' fc-first';
10702 ret[4].cls += ' fc-prev-last';
10703 ret[5].cls += ' fc-last';
10710 cls: 'fc-border-separate',
10711 style : 'width:100%',
10719 cls : 'fc-first fc-last',
10737 cls : 'fc-content',
10738 style : "position: relative;",
10741 cls : 'fc-view fc-view-month fc-grid',
10742 style : 'position: relative',
10743 unselectable : 'on',
10746 cls : 'fc-event-container',
10747 style : 'position:absolute;z-index:8;top:0;left:0;'
10765 initEvents : function()
10768 throw "can not find store for calendar";
10774 style: "text-align:center",
10778 style: "background-color:white;width:50%;margin:250 auto",
10782 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10793 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10795 var size = this.el.select('.fc-content', true).first().getSize();
10796 this.maskEl.setSize(size.width, size.height);
10797 this.maskEl.enableDisplayMode("block");
10798 if(!this.loadMask){
10799 this.maskEl.hide();
10802 this.store = Roo.factory(this.store, Roo.data);
10803 this.store.on('load', this.onLoad, this);
10804 this.store.on('beforeload', this.onBeforeLoad, this);
10808 this.cells = this.el.select('.fc-day',true);
10809 //Roo.log(this.cells);
10810 this.textNodes = this.el.query('.fc-day-number');
10811 this.cells.addClassOnOver('fc-state-hover');
10813 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10814 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10815 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10816 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10818 this.on('monthchange', this.onMonthChange, this);
10820 this.update(new Date().clearTime());
10823 resize : function() {
10824 var sz = this.el.getSize();
10826 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10827 this.el.select('.fc-day-content div',true).setHeight(34);
10832 showPrevMonth : function(e){
10833 this.update(this.activeDate.add("mo", -1));
10835 showToday : function(e){
10836 this.update(new Date().clearTime());
10839 showNextMonth : function(e){
10840 this.update(this.activeDate.add("mo", 1));
10844 showPrevYear : function(){
10845 this.update(this.activeDate.add("y", -1));
10849 showNextYear : function(){
10850 this.update(this.activeDate.add("y", 1));
10855 update : function(date)
10857 var vd = this.activeDate;
10858 this.activeDate = date;
10859 // if(vd && this.el){
10860 // var t = date.getTime();
10861 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10862 // Roo.log('using add remove');
10864 // this.fireEvent('monthchange', this, date);
10866 // this.cells.removeClass("fc-state-highlight");
10867 // this.cells.each(function(c){
10868 // if(c.dateValue == t){
10869 // c.addClass("fc-state-highlight");
10870 // setTimeout(function(){
10871 // try{c.dom.firstChild.focus();}catch(e){}
10881 var days = date.getDaysInMonth();
10883 var firstOfMonth = date.getFirstDateOfMonth();
10884 var startingPos = firstOfMonth.getDay()-this.startDay;
10886 if(startingPos < this.startDay){
10890 var pm = date.add(Date.MONTH, -1);
10891 var prevStart = pm.getDaysInMonth()-startingPos;
10893 this.cells = this.el.select('.fc-day',true);
10894 this.textNodes = this.el.query('.fc-day-number');
10895 this.cells.addClassOnOver('fc-state-hover');
10897 var cells = this.cells.elements;
10898 var textEls = this.textNodes;
10900 Roo.each(cells, function(cell){
10901 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10904 days += startingPos;
10906 // convert everything to numbers so it's fast
10907 var day = 86400000;
10908 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10911 //Roo.log(prevStart);
10913 var today = new Date().clearTime().getTime();
10914 var sel = date.clearTime().getTime();
10915 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10916 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10917 var ddMatch = this.disabledDatesRE;
10918 var ddText = this.disabledDatesText;
10919 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10920 var ddaysText = this.disabledDaysText;
10921 var format = this.format;
10923 var setCellClass = function(cal, cell){
10925 //Roo.log('set Cell Class');
10927 var t = d.getTime();
10931 cell.dateValue = t;
10933 cell.className += " fc-today";
10934 cell.className += " fc-state-highlight";
10935 cell.title = cal.todayText;
10938 // disable highlight in other month..
10939 //cell.className += " fc-state-highlight";
10944 cell.className = " fc-state-disabled";
10945 cell.title = cal.minText;
10949 cell.className = " fc-state-disabled";
10950 cell.title = cal.maxText;
10954 if(ddays.indexOf(d.getDay()) != -1){
10955 cell.title = ddaysText;
10956 cell.className = " fc-state-disabled";
10959 if(ddMatch && format){
10960 var fvalue = d.dateFormat(format);
10961 if(ddMatch.test(fvalue)){
10962 cell.title = ddText.replace("%0", fvalue);
10963 cell.className = " fc-state-disabled";
10967 if (!cell.initialClassName) {
10968 cell.initialClassName = cell.dom.className;
10971 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10976 for(; i < startingPos; i++) {
10977 textEls[i].innerHTML = (++prevStart);
10978 d.setDate(d.getDate()+1);
10980 cells[i].className = "fc-past fc-other-month";
10981 setCellClass(this, cells[i]);
10986 for(; i < days; i++){
10987 intDay = i - startingPos + 1;
10988 textEls[i].innerHTML = (intDay);
10989 d.setDate(d.getDate()+1);
10991 cells[i].className = ''; // "x-date-active";
10992 setCellClass(this, cells[i]);
10996 for(; i < 42; i++) {
10997 textEls[i].innerHTML = (++extraDays);
10998 d.setDate(d.getDate()+1);
11000 cells[i].className = "fc-future fc-other-month";
11001 setCellClass(this, cells[i]);
11004 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11006 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11008 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11009 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11011 if(totalRows != 6){
11012 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11013 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11016 this.fireEvent('monthchange', this, date);
11020 if(!this.internalRender){
11021 var main = this.el.dom.firstChild;
11022 var w = main.offsetWidth;
11023 this.el.setWidth(w + this.el.getBorderWidth("lr"));
11024 Roo.fly(main).setWidth(w);
11025 this.internalRender = true;
11026 // opera does not respect the auto grow header center column
11027 // then, after it gets a width opera refuses to recalculate
11028 // without a second pass
11029 if(Roo.isOpera && !this.secondPass){
11030 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11031 this.secondPass = true;
11032 this.update.defer(10, this, [date]);
11039 findCell : function(dt) {
11040 dt = dt.clearTime().getTime();
11042 this.cells.each(function(c){
11043 //Roo.log("check " +c.dateValue + '?=' + dt);
11044 if(c.dateValue == dt){
11054 findCells : function(ev) {
11055 var s = ev.start.clone().clearTime().getTime();
11057 var e= ev.end.clone().clearTime().getTime();
11060 this.cells.each(function(c){
11061 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11063 if(c.dateValue > e){
11066 if(c.dateValue < s){
11075 findBestRow: function(cells)
11079 for (var i =0 ; i < cells.length;i++) {
11080 ret = Math.max(cells[i].rows || 0,ret);
11087 addItem : function(ev)
11089 // look for vertical location slot in
11090 var cells = this.findCells(ev);
11092 ev.row = this.findBestRow(cells);
11094 // work out the location.
11098 for(var i =0; i < cells.length; i++) {
11106 if (crow.start.getY() == cells[i].getY()) {
11108 crow.end = cells[i];
11124 for (var i = 0; i < cells.length;i++) {
11125 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11129 this.calevents.push(ev);
11132 clearEvents: function() {
11134 if(!this.calevents){
11138 Roo.each(this.cells.elements, function(c){
11142 Roo.each(this.calevents, function(e) {
11143 Roo.each(e.els, function(el) {
11144 el.un('mouseenter' ,this.onEventEnter, this);
11145 el.un('mouseleave' ,this.onEventLeave, this);
11152 renderEvents: function()
11154 // first make sure there is enough space..
11156 this.cells.each(function(c) {
11158 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11161 for (var e = 0; e < this.calevents.length; e++) {
11162 var ev = this.calevents[e];
11163 var cells = ev.cells;
11164 var rows = ev.rows;
11166 for(var i =0; i < rows.length; i++) {
11169 // how many rows should it span..
11172 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11173 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11175 unselectable : "on",
11178 cls: 'fc-event-inner',
11182 // cls: 'fc-event-time',
11183 // html : cells.length > 1 ? '' : ev.time
11187 cls: 'fc-event-title',
11188 html : String.format('{0}', ev.title)
11195 cls: 'ui-resizable-handle ui-resizable-e',
11196 html : '  '
11202 cfg.cls += ' fc-event-start';
11204 if ((i+1) == rows.length) {
11205 cfg.cls += ' fc-event-end';
11208 var ctr = this.el.select('.fc-event-container',true).first();
11209 var cg = ctr.createChild(cfg);
11211 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11212 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11213 cg.on('click', this.onEventClick, this, ev);
11217 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11218 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11220 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
11221 cg.setWidth(ebox.right - sbox.x -2);
11229 onEventEnter: function (e, el,event,d) {
11230 this.fireEvent('evententer', this, el, event);
11233 onEventLeave: function (e, el,event,d) {
11234 this.fireEvent('eventleave', this, el, event);
11237 onEventClick: function (e, el,event,d) {
11238 this.fireEvent('eventclick', this, el, event);
11241 onMonthChange: function () {
11245 onLoad: function ()
11247 this.calevents = [];
11250 if(this.store.getCount() > 0){
11251 this.store.data.each(function(d){
11254 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11255 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11256 time : d.data.start_time,
11257 title : d.data.title,
11258 description : d.data.description,
11259 venue : d.data.venue
11264 this.renderEvents();
11267 this.maskEl.hide();
11271 onBeforeLoad: function()
11273 this.clearEvents();
11276 this.maskEl.show();
11290 * @class Roo.bootstrap.Popover
11291 * @extends Roo.bootstrap.Component
11292 * Bootstrap Popover class
11293 * @cfg {String} html contents of the popover (or false to use children..)
11294 * @cfg {String} title of popover (or false to hide)
11295 * @cfg {String} placement how it is placed
11296 * @cfg {String} trigger click || hover (or false to trigger manually)
11297 * @cfg {String} over what (parent or false to trigger manually.)
11300 * Create a new Popover
11301 * @param {Object} config The config object
11304 Roo.bootstrap.Popover = function(config){
11305 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11308 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
11310 title: 'Fill in a title',
11313 placement : 'right',
11314 trigger : 'hover', // hover
11318 can_build_overlaid : false,
11320 getChildContainer : function()
11322 return this.el.select('.popover-content',true).first();
11325 getAutoCreate : function(){
11326 Roo.log('make popover?');
11328 cls : 'popover roo-dynamic',
11329 style: 'display:block',
11335 cls : 'popover-inner',
11339 cls: 'popover-title',
11343 cls : 'popover-content',
11354 setTitle: function(str)
11356 this.el.select('.popover-title',true).first().dom.innerHTML = str;
11358 setContent: function(str)
11360 this.el.select('.popover-content',true).first().dom.innerHTML = str;
11362 // as it get's added to the bottom of the page.
11363 onRender : function(ct, position)
11365 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11367 var cfg = Roo.apply({}, this.getAutoCreate());
11371 cfg.cls += ' ' + this.cls;
11374 cfg.style = this.style;
11376 Roo.log("adding to ")
11377 this.el = Roo.get(document.body).createChild(cfg, position);
11383 initEvents : function()
11385 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11386 this.el.enableDisplayMode('block');
11388 if (this.over === false) {
11391 if (this.triggers === false) {
11394 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11395 var triggers = this.trigger ? this.trigger.split(' ') : [];
11396 Roo.each(triggers, function(trigger) {
11398 if (trigger == 'click') {
11399 on_el.on('click', this.toggle, this);
11400 } else if (trigger != 'manual') {
11401 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
11402 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11404 on_el.on(eventIn ,this.enter, this);
11405 on_el.on(eventOut, this.leave, this);
11416 toggle : function () {
11417 this.hoverState == 'in' ? this.leave() : this.enter();
11420 enter : function () {
11423 clearTimeout(this.timeout);
11425 this.hoverState = 'in'
11427 if (!this.delay || !this.delay.show) {
11432 this.timeout = setTimeout(function () {
11433 if (_t.hoverState == 'in') {
11436 }, this.delay.show)
11438 leave : function() {
11439 clearTimeout(this.timeout);
11441 this.hoverState = 'out'
11443 if (!this.delay || !this.delay.hide) {
11448 this.timeout = setTimeout(function () {
11449 if (_t.hoverState == 'out') {
11452 }, this.delay.hide)
11455 show : function (on_el)
11458 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11461 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11462 if (this.html !== false) {
11463 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11465 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11466 if (!this.title.length) {
11467 this.el.select('.popover-title',true).hide();
11470 var placement = typeof this.placement == 'function' ?
11471 this.placement.call(this, this.el, on_el) :
11474 var autoToken = /\s?auto?\s?/i;
11475 var autoPlace = autoToken.test(placement);
11477 placement = placement.replace(autoToken, '') || 'top';
11481 //this.el.setXY([0,0]);
11483 this.el.dom.style.display='block';
11484 this.el.addClass(placement);
11486 //this.el.appendTo(on_el);
11488 var p = this.getPosition();
11489 var box = this.el.getBox();
11494 var align = Roo.bootstrap.Popover.alignment[placement]
11495 this.el.alignTo(on_el, align[0],align[1]);
11496 //var arrow = this.el.select('.arrow',true).first();
11497 //arrow.set(align[2],
11499 this.el.addClass('in');
11500 this.hoverState = null;
11502 if (this.el.hasClass('fade')) {
11509 this.el.setXY([0,0]);
11510 this.el.removeClass('in');
11517 Roo.bootstrap.Popover.alignment = {
11518 'left' : ['r-l', [-10,0], 'right'],
11519 'right' : ['l-r', [10,0], 'left'],
11520 'bottom' : ['t-b', [0,10], 'top'],
11521 'top' : [ 'b-t', [0,-10], 'bottom']
11532 * @class Roo.bootstrap.Progress
11533 * @extends Roo.bootstrap.Component
11534 * Bootstrap Progress class
11535 * @cfg {Boolean} striped striped of the progress bar
11536 * @cfg {Boolean} active animated of the progress bar
11540 * Create a new Progress
11541 * @param {Object} config The config object
11544 Roo.bootstrap.Progress = function(config){
11545 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11548 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
11553 getAutoCreate : function(){
11561 cfg.cls += ' progress-striped';
11565 cfg.cls += ' active';
11584 * @class Roo.bootstrap.ProgressBar
11585 * @extends Roo.bootstrap.Component
11586 * Bootstrap ProgressBar class
11587 * @cfg {Number} aria_valuenow aria-value now
11588 * @cfg {Number} aria_valuemin aria-value min
11589 * @cfg {Number} aria_valuemax aria-value max
11590 * @cfg {String} label label for the progress bar
11591 * @cfg {String} panel (success | info | warning | danger )
11592 * @cfg {String} role role of the progress bar
11593 * @cfg {String} sr_only text
11597 * Create a new ProgressBar
11598 * @param {Object} config The config object
11601 Roo.bootstrap.ProgressBar = function(config){
11602 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11605 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
11609 aria_valuemax : 100,
11615 getAutoCreate : function()
11620 cls: 'progress-bar',
11621 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11633 cfg.role = this.role;
11636 if(this.aria_valuenow){
11637 cfg['aria-valuenow'] = this.aria_valuenow;
11640 if(this.aria_valuemin){
11641 cfg['aria-valuemin'] = this.aria_valuemin;
11644 if(this.aria_valuemax){
11645 cfg['aria-valuemax'] = this.aria_valuemax;
11648 if(this.label && !this.sr_only){
11649 cfg.html = this.label;
11653 cfg.cls += ' progress-bar-' + this.panel;
11659 update : function(aria_valuenow)
11661 this.aria_valuenow = aria_valuenow;
11663 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11678 * @class Roo.bootstrap.TabPanel
11679 * @extends Roo.bootstrap.Component
11680 * Bootstrap TabPanel class
11681 * @cfg {Boolean} active panel active
11682 * @cfg {String} html panel content
11683 * @cfg {String} tabId tab relate id
11684 * @cfg {String} navId The navbar which triggers show hide
11688 * Create a new TabPanel
11689 * @param {Object} config The config object
11692 Roo.bootstrap.TabPanel = function(config){
11693 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11697 * Fires when the active status changes
11698 * @param {Roo.bootstrap.TabPanel} this
11699 * @param {Boolean} state the new state
11706 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
11713 getAutoCreate : function(){
11717 html: this.html || ''
11721 cfg.cls += ' active';
11725 cfg.tabId = this.tabId;
11730 onRender : function(ct, position)
11732 // Roo.log("Call onRender: " + this.xtype);
11734 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11736 if (this.navId && this.tabId) {
11737 var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11739 Roo.log("could not find navID:" + this.navId + ", tabId: " + this.tabId);
11741 item.on('changed', function(item, state) {
11742 this.setActive(state);
11748 setActive: function(state)
11750 Roo.log("panel - set active " + this.tabId + "=" + state);
11752 this.active = state;
11754 this.el.removeClass('active');
11756 } else if (!this.el.hasClass('active')) {
11757 this.el.addClass('active');
11759 this.fireEvent('changed', this, state);
11776 * @class Roo.bootstrap.DateField
11777 * @extends Roo.bootstrap.Input
11778 * Bootstrap DateField class
11779 * @cfg {Number} weekStart default 0
11780 * @cfg {Number} weekStart default 0
11781 * @cfg {Number} viewMode default empty, (months|years)
11782 * @cfg {Number} minViewMode default empty, (months|years)
11783 * @cfg {Number} startDate default -Infinity
11784 * @cfg {Number} endDate default Infinity
11785 * @cfg {Boolean} todayHighlight default false
11786 * @cfg {Boolean} todayBtn default false
11787 * @cfg {Boolean} calendarWeeks default false
11788 * @cfg {Object} daysOfWeekDisabled default empty
11790 * @cfg {Boolean} keyboardNavigation default true
11791 * @cfg {String} language default en
11794 * Create a new DateField
11795 * @param {Object} config The config object
11798 Roo.bootstrap.DateField = function(config){
11799 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11803 * Fires when this field show.
11804 * @param {Roo.bootstrap.DateField} this
11805 * @param {Mixed} date The date value
11810 * Fires when this field hide.
11811 * @param {Roo.bootstrap.DateField} this
11812 * @param {Mixed} date The date value
11817 * Fires when select a date.
11818 * @param {Roo.bootstrap.DateField} this
11819 * @param {Mixed} date The date value
11825 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11828 * @cfg {String} format
11829 * The default date format string which can be overriden for localization support. The format must be
11830 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11834 * @cfg {String} altFormats
11835 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11836 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11838 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11846 todayHighlight : false,
11852 keyboardNavigation: true,
11854 calendarWeeks: false,
11856 startDate: -Infinity,
11860 daysOfWeekDisabled: [],
11864 UTCDate: function()
11866 return new Date(Date.UTC.apply(Date, arguments));
11869 UTCToday: function()
11871 var today = new Date();
11872 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11875 getDate: function() {
11876 var d = this.getUTCDate();
11877 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11880 getUTCDate: function() {
11884 setDate: function(d) {
11885 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11888 setUTCDate: function(d) {
11890 this.setValue(this.formatDate(this.date));
11893 onRender: function(ct, position)
11896 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11898 this.language = this.language || 'en';
11899 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11900 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11902 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11903 this.format = this.format || 'm/d/y';
11904 this.isInline = false;
11905 this.isInput = true;
11906 this.component = this.el.select('.add-on', true).first() || false;
11907 this.component = (this.component && this.component.length === 0) ? false : this.component;
11908 this.hasInput = this.component && this.inputEL().length;
11910 if (typeof(this.minViewMode === 'string')) {
11911 switch (this.minViewMode) {
11913 this.minViewMode = 1;
11916 this.minViewMode = 2;
11919 this.minViewMode = 0;
11924 if (typeof(this.viewMode === 'string')) {
11925 switch (this.viewMode) {
11938 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11940 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11942 this.picker().on('mousedown', this.onMousedown, this);
11943 this.picker().on('click', this.onClick, this);
11945 this.picker().addClass('datepicker-dropdown');
11947 this.startViewMode = this.viewMode;
11950 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11951 if(!this.calendarWeeks){
11956 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11957 v.attr('colspan', function(i, val){
11958 return parseInt(val) + 1;
11963 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11965 this.setStartDate(this.startDate);
11966 this.setEndDate(this.endDate);
11968 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11975 if(this.isInline) {
11980 picker : function()
11982 return this.el.select('.datepicker', true).first();
11985 fillDow: function()
11987 var dowCnt = this.weekStart;
11996 if(this.calendarWeeks){
12004 while (dowCnt < this.weekStart + 7) {
12008 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12012 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12015 fillMonths: function()
12018 var months = this.picker().select('>.datepicker-months td', true).first();
12020 months.dom.innerHTML = '';
12026 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12029 months.createChild(month);
12034 update: function(){
12036 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12038 if (this.date < this.startDate) {
12039 this.viewDate = new Date(this.startDate);
12040 } else if (this.date > this.endDate) {
12041 this.viewDate = new Date(this.endDate);
12043 this.viewDate = new Date(this.date);
12050 var d = new Date(this.viewDate),
12051 year = d.getUTCFullYear(),
12052 month = d.getUTCMonth(),
12053 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12054 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12055 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12056 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12057 currentDate = this.date && this.date.valueOf(),
12058 today = this.UTCToday();
12060 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12062 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12064 // this.picker.select('>tfoot th.today').
12065 // .text(dates[this.language].today)
12066 // .toggle(this.todayBtn !== false);
12068 this.updateNavArrows();
12071 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12073 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12075 prevMonth.setUTCDate(day);
12077 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12079 var nextMonth = new Date(prevMonth);
12081 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12083 nextMonth = nextMonth.valueOf();
12085 var fillMonths = false;
12087 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12089 while(prevMonth.valueOf() < nextMonth) {
12092 if (prevMonth.getUTCDay() === this.weekStart) {
12094 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12102 if(this.calendarWeeks){
12103 // ISO 8601: First week contains first thursday.
12104 // ISO also states week starts on Monday, but we can be more abstract here.
12106 // Start of current week: based on weekstart/current date
12107 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12108 // Thursday of this week
12109 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12110 // First Thursday of year, year from thursday
12111 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12112 // Calendar week: ms between thursdays, div ms per day, div 7 days
12113 calWeek = (th - yth) / 864e5 / 7 + 1;
12115 fillMonths.cn.push({
12123 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12125 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12128 if (this.todayHighlight &&
12129 prevMonth.getUTCFullYear() == today.getFullYear() &&
12130 prevMonth.getUTCMonth() == today.getMonth() &&
12131 prevMonth.getUTCDate() == today.getDate()) {
12132 clsName += ' today';
12135 if (currentDate && prevMonth.valueOf() === currentDate) {
12136 clsName += ' active';
12139 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12140 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12141 clsName += ' disabled';
12144 fillMonths.cn.push({
12146 cls: 'day ' + clsName,
12147 html: prevMonth.getDate()
12150 prevMonth.setDate(prevMonth.getDate()+1);
12153 var currentYear = this.date && this.date.getUTCFullYear();
12154 var currentMonth = this.date && this.date.getUTCMonth();
12156 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12158 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12159 v.removeClass('active');
12161 if(currentYear === year && k === currentMonth){
12162 v.addClass('active');
12165 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12166 v.addClass('disabled');
12172 year = parseInt(year/10, 10) * 10;
12174 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12176 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12179 for (var i = -1; i < 11; i++) {
12180 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12182 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12190 showMode: function(dir) {
12192 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12194 Roo.each(this.picker().select('>div',true).elements, function(v){
12195 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12198 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12203 if(this.isInline) return;
12205 this.picker().removeClass(['bottom', 'top']);
12207 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12209 * place to the top of element!
12213 this.picker().addClass('top');
12214 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12219 this.picker().addClass('bottom');
12221 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12224 parseDate : function(value){
12225 if(!value || value instanceof Date){
12228 var v = Date.parseDate(value, this.format);
12229 if (!v && this.useIso) {
12230 v = Date.parseDate(value, 'Y-m-d');
12232 if(!v && this.altFormats){
12233 if(!this.altFormatsArray){
12234 this.altFormatsArray = this.altFormats.split("|");
12236 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12237 v = Date.parseDate(value, this.altFormatsArray[i]);
12243 formatDate : function(date, fmt){
12244 return (!date || !(date instanceof Date)) ?
12245 date : date.dateFormat(fmt || this.format);
12248 onFocus : function()
12250 Roo.bootstrap.DateField.superclass.onFocus.call(this);
12254 onBlur : function()
12256 Roo.bootstrap.DateField.superclass.onBlur.call(this);
12262 this.picker().show();
12266 this.fireEvent('show', this, this.date);
12271 if(this.isInline) return;
12272 this.picker().hide();
12273 this.viewMode = this.startViewMode;
12276 this.fireEvent('hide', this, this.date);
12280 onMousedown: function(e){
12281 e.stopPropagation();
12282 e.preventDefault();
12285 keyup: function(e){
12286 Roo.bootstrap.DateField.superclass.keyup.call(this);
12291 setValue: function(v){
12292 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12294 this.fireEvent('select', this, this.date);
12298 fireKey: function(e){
12299 if (!this.picker().isVisible()){
12300 if (e.keyCode == 27) // allow escape to hide and re-show picker
12304 var dateChanged = false,
12306 newDate, newViewDate;
12310 e.preventDefault();
12314 if (!this.keyboardNavigation) break;
12315 dir = e.keyCode == 37 ? -1 : 1;
12318 newDate = this.moveYear(this.date, dir);
12319 newViewDate = this.moveYear(this.viewDate, dir);
12320 } else if (e.shiftKey){
12321 newDate = this.moveMonth(this.date, dir);
12322 newViewDate = this.moveMonth(this.viewDate, dir);
12324 newDate = new Date(this.date);
12325 newDate.setUTCDate(this.date.getUTCDate() + dir);
12326 newViewDate = new Date(this.viewDate);
12327 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12329 if (this.dateWithinRange(newDate)){
12330 this.date = newDate;
12331 this.viewDate = newViewDate;
12332 this.setValue(this.formatDate(this.date));
12334 e.preventDefault();
12335 dateChanged = true;
12340 if (!this.keyboardNavigation) break;
12341 dir = e.keyCode == 38 ? -1 : 1;
12343 newDate = this.moveYear(this.date, dir);
12344 newViewDate = this.moveYear(this.viewDate, dir);
12345 } else if (e.shiftKey){
12346 newDate = this.moveMonth(this.date, dir);
12347 newViewDate = this.moveMonth(this.viewDate, dir);
12349 newDate = new Date(this.date);
12350 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12351 newViewDate = new Date(this.viewDate);
12352 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12354 if (this.dateWithinRange(newDate)){
12355 this.date = newDate;
12356 this.viewDate = newViewDate;
12357 this.setValue(this.formatDate(this.date));
12359 e.preventDefault();
12360 dateChanged = true;
12364 this.setValue(this.formatDate(this.date));
12366 e.preventDefault();
12369 this.setValue(this.formatDate(this.date));
12376 onClick: function(e) {
12377 e.stopPropagation();
12378 e.preventDefault();
12380 var target = e.getTarget();
12382 if(target.nodeName.toLowerCase() === 'i'){
12383 target = Roo.get(target).dom.parentNode;
12386 var nodeName = target.nodeName;
12387 var className = target.className;
12388 var html = target.innerHTML;
12390 switch(nodeName.toLowerCase()) {
12392 switch(className) {
12398 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12399 switch(this.viewMode){
12401 this.viewDate = this.moveMonth(this.viewDate, dir);
12405 this.viewDate = this.moveYear(this.viewDate, dir);
12411 var date = new Date();
12412 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12414 this.setValue(this.formatDate(this.date));
12420 if (className.indexOf('disabled') === -1) {
12421 this.viewDate.setUTCDate(1);
12422 if (className.indexOf('month') !== -1) {
12423 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12425 var year = parseInt(html, 10) || 0;
12426 this.viewDate.setUTCFullYear(year);
12435 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12436 var day = parseInt(html, 10) || 1;
12437 var year = this.viewDate.getUTCFullYear(),
12438 month = this.viewDate.getUTCMonth();
12440 if (className.indexOf('old') !== -1) {
12447 } else if (className.indexOf('new') !== -1) {
12455 this.date = this.UTCDate(year, month, day,0,0,0,0);
12456 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12458 this.setValue(this.formatDate(this.date));
12465 setStartDate: function(startDate){
12466 this.startDate = startDate || -Infinity;
12467 if (this.startDate !== -Infinity) {
12468 this.startDate = this.parseDate(this.startDate);
12471 this.updateNavArrows();
12474 setEndDate: function(endDate){
12475 this.endDate = endDate || Infinity;
12476 if (this.endDate !== Infinity) {
12477 this.endDate = this.parseDate(this.endDate);
12480 this.updateNavArrows();
12483 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12484 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12485 if (typeof(this.daysOfWeekDisabled) !== 'object') {
12486 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12488 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12489 return parseInt(d, 10);
12492 this.updateNavArrows();
12495 updateNavArrows: function() {
12496 var d = new Date(this.viewDate),
12497 year = d.getUTCFullYear(),
12498 month = d.getUTCMonth();
12500 Roo.each(this.picker().select('.prev', true).elements, function(v){
12502 switch (this.viewMode) {
12505 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12511 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12518 Roo.each(this.picker().select('.next', true).elements, function(v){
12520 switch (this.viewMode) {
12523 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12529 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12537 moveMonth: function(date, dir){
12538 if (!dir) return date;
12539 var new_date = new Date(date.valueOf()),
12540 day = new_date.getUTCDate(),
12541 month = new_date.getUTCMonth(),
12542 mag = Math.abs(dir),
12544 dir = dir > 0 ? 1 : -1;
12547 // If going back one month, make sure month is not current month
12548 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12550 return new_date.getUTCMonth() == month;
12552 // If going forward one month, make sure month is as expected
12553 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12555 return new_date.getUTCMonth() != new_month;
12557 new_month = month + dir;
12558 new_date.setUTCMonth(new_month);
12559 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12560 if (new_month < 0 || new_month > 11)
12561 new_month = (new_month + 12) % 12;
12563 // For magnitudes >1, move one month at a time...
12564 for (var i=0; i<mag; i++)
12565 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12566 new_date = this.moveMonth(new_date, dir);
12567 // ...then reset the day, keeping it in the new month
12568 new_month = new_date.getUTCMonth();
12569 new_date.setUTCDate(day);
12571 return new_month != new_date.getUTCMonth();
12574 // Common date-resetting loop -- if date is beyond end of month, make it
12577 new_date.setUTCDate(--day);
12578 new_date.setUTCMonth(new_month);
12583 moveYear: function(date, dir){
12584 return this.moveMonth(date, dir*12);
12587 dateWithinRange: function(date){
12588 return date >= this.startDate && date <= this.endDate;
12592 remove: function() {
12593 this.picker().remove();
12598 Roo.apply(Roo.bootstrap.DateField, {
12609 html: '<i class="icon-arrow-left"/>'
12619 html: '<i class="icon-arrow-right"/>'
12661 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12662 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12663 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12664 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12665 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12678 navFnc: 'FullYear',
12683 navFnc: 'FullYear',
12688 Roo.apply(Roo.bootstrap.DateField, {
12692 cls: 'datepicker dropdown-menu',
12696 cls: 'datepicker-days',
12700 cls: 'table-condensed',
12702 Roo.bootstrap.DateField.head,
12706 Roo.bootstrap.DateField.footer
12713 cls: 'datepicker-months',
12717 cls: 'table-condensed',
12719 Roo.bootstrap.DateField.head,
12720 Roo.bootstrap.DateField.content,
12721 Roo.bootstrap.DateField.footer
12728 cls: 'datepicker-years',
12732 cls: 'table-condensed',
12734 Roo.bootstrap.DateField.head,
12735 Roo.bootstrap.DateField.content,
12736 Roo.bootstrap.DateField.footer
12755 * @class Roo.bootstrap.TimeField
12756 * @extends Roo.bootstrap.Input
12757 * Bootstrap DateField class
12761 * Create a new TimeField
12762 * @param {Object} config The config object
12765 Roo.bootstrap.TimeField = function(config){
12766 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12770 * Fires when this field show.
12771 * @param {Roo.bootstrap.DateField} this
12772 * @param {Mixed} date The date value
12777 * Fires when this field hide.
12778 * @param {Roo.bootstrap.DateField} this
12779 * @param {Mixed} date The date value
12784 * Fires when select a date.
12785 * @param {Roo.bootstrap.DateField} this
12786 * @param {Mixed} date The date value
12792 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
12795 * @cfg {String} format
12796 * The default time format string which can be overriden for localization support. The format must be
12797 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12801 onRender: function(ct, position)
12804 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12806 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12808 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12810 this.pop = this.picker().select('>.datepicker-time',true).first();
12811 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12813 this.picker().on('mousedown', this.onMousedown, this);
12814 this.picker().on('click', this.onClick, this);
12816 this.picker().addClass('datepicker-dropdown');
12821 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12822 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12823 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12824 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12825 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12826 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12830 fireKey: function(e){
12831 if (!this.picker().isVisible()){
12832 if (e.keyCode == 27) // allow escape to hide and re-show picker
12837 e.preventDefault();
12845 this.onTogglePeriod();
12848 this.onIncrementMinutes();
12851 this.onDecrementMinutes();
12860 onClick: function(e) {
12861 e.stopPropagation();
12862 e.preventDefault();
12865 picker : function()
12867 return this.el.select('.datepicker', true).first();
12870 fillTime: function()
12872 var time = this.pop.select('tbody', true).first();
12874 time.dom.innerHTML = '';
12889 cls: 'hours-up glyphicon glyphicon-chevron-up'
12909 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12930 cls: 'timepicker-hour',
12945 cls: 'timepicker-minute',
12960 cls: 'btn btn-primary period',
12982 cls: 'hours-down glyphicon glyphicon-chevron-down'
13002 cls: 'minutes-down glyphicon glyphicon-chevron-down'
13020 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13027 var hours = this.time.getHours();
13028 var minutes = this.time.getMinutes();
13041 hours = hours - 12;
13045 hours = '0' + hours;
13049 minutes = '0' + minutes;
13052 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13053 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13054 this.pop.select('button', true).first().dom.innerHTML = period;
13060 this.picker().removeClass(['bottom', 'top']);
13062 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13064 * place to the top of element!
13068 this.picker().addClass('top');
13069 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13074 this.picker().addClass('bottom');
13076 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13079 onFocus : function()
13081 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13085 onBlur : function()
13087 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13093 this.picker().show();
13098 this.fireEvent('show', this, this.date);
13103 this.picker().hide();
13106 this.fireEvent('hide', this, this.date);
13109 setTime : function()
13112 this.setValue(this.time.format(this.format));
13114 this.fireEvent('select', this, this.date);
13119 onMousedown: function(e){
13120 e.stopPropagation();
13121 e.preventDefault();
13124 onIncrementHours: function()
13126 Roo.log('onIncrementHours');
13127 this.time = this.time.add(Date.HOUR, 1);
13132 onDecrementHours: function()
13134 Roo.log('onDecrementHours');
13135 this.time = this.time.add(Date.HOUR, -1);
13139 onIncrementMinutes: function()
13141 Roo.log('onIncrementMinutes');
13142 this.time = this.time.add(Date.MINUTE, 1);
13146 onDecrementMinutes: function()
13148 Roo.log('onDecrementMinutes');
13149 this.time = this.time.add(Date.MINUTE, -1);
13153 onTogglePeriod: function()
13155 Roo.log('onTogglePeriod');
13156 this.time = this.time.add(Date.HOUR, 12);
13163 Roo.apply(Roo.bootstrap.TimeField, {
13193 cls: 'btn btn-info ok',
13205 Roo.apply(Roo.bootstrap.TimeField, {
13209 cls: 'datepicker dropdown-menu',
13213 cls: 'datepicker-time',
13217 cls: 'table-condensed',
13219 Roo.bootstrap.TimeField.content,
13220 Roo.bootstrap.TimeField.footer
13239 * @class Roo.bootstrap.CheckBox
13240 * @extends Roo.bootstrap.Input
13241 * Bootstrap CheckBox class
13243 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13244 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13245 * @cfg {String} boxLabel The text that appears beside the checkbox
13246 * @cfg {Boolean} checked initnal the element
13249 * Create a new CheckBox
13250 * @param {Object} config The config object
13253 Roo.bootstrap.CheckBox = function(config){
13254 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13259 * Fires when the element is checked or unchecked.
13260 * @param {Roo.bootstrap.CheckBox} this This input
13261 * @param {Boolean} checked The new checked value
13267 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
13269 inputType: 'checkbox',
13275 getAutoCreate : function()
13277 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13283 cfg.cls = 'form-group' //input-group
13288 type : this.inputType,
13289 value : (!this.checked) ? this.valueOff : this.inputValue,
13291 placeholder : this.placeholder || ''
13295 if (this.disabled) {
13296 input.disabled=true;
13300 input.checked = this.checked;
13304 input.name = this.name;
13308 input.cls += ' input-' + this.size;
13312 ['xs','sm','md','lg'].map(function(size){
13313 if (settings[size]) {
13314 cfg.cls += ' col-' + size + '-' + settings[size];
13318 var inputblock = input;
13320 if (this.before || this.after) {
13323 cls : 'input-group',
13327 inputblock.cn.push({
13329 cls : 'input-group-addon',
13333 inputblock.cn.push(input);
13335 inputblock.cn.push({
13337 cls : 'input-group-addon',
13344 if (align ==='left' && this.fieldLabel.length) {
13345 Roo.log("left and has label");
13351 cls : 'control-label col-md-' + this.labelWidth,
13352 html : this.fieldLabel
13356 cls : "col-md-" + (12 - this.labelWidth),
13363 } else if ( this.fieldLabel.length) {
13368 tag: this.boxLabel ? 'span' : 'label',
13370 cls: 'control-label box-input-label',
13371 //cls : 'input-group-addon',
13372 html : this.fieldLabel
13382 Roo.log(" no label && no align");
13397 html: this.boxLabel
13406 * return the real input element.
13408 inputEl: function ()
13410 return this.el.select('input.form-box',true).first();
13415 return this.el.select('label.control-label',true).first();
13418 initEvents : function()
13420 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13422 this.inputEl().on('click', this.onClick, this);
13426 onClick : function()
13428 this.setChecked(!this.checked);
13431 setChecked : function(state,suppressEvent)
13433 this.checked = state;
13435 this.inputEl().dom.checked = state;
13437 if(suppressEvent !== true){
13438 this.fireEvent('check', this, state);
13441 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13445 setValue : function(v,suppressEvent)
13447 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13461 * @class Roo.bootstrap.Radio
13462 * @extends Roo.bootstrap.CheckBox
13463 * Bootstrap Radio class
13466 * Create a new Radio
13467 * @param {Object} config The config object
13470 Roo.bootstrap.Radio = function(config){
13471 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13475 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
13477 inputType: 'radio',
13481 getAutoCreate : function()
13483 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13489 cfg.cls = 'form-group' //input-group
13494 type : this.inputType,
13495 value : (!this.checked) ? this.valueOff : this.inputValue,
13497 placeholder : this.placeholder || ''
13501 if (this.disabled) {
13502 input.disabled=true;
13506 input.checked = this.checked;
13510 input.name = this.name;
13514 input.cls += ' input-' + this.size;
13518 ['xs','sm','md','lg'].map(function(size){
13519 if (settings[size]) {
13520 cfg.cls += ' col-' + size + '-' + settings[size];
13524 var inputblock = input;
13526 if (this.before || this.after) {
13529 cls : 'input-group',
13533 inputblock.cn.push({
13535 cls : 'input-group-addon',
13539 inputblock.cn.push(input);
13541 inputblock.cn.push({
13543 cls : 'input-group-addon',
13550 if (align ==='left' && this.fieldLabel.length) {
13551 Roo.log("left and has label");
13557 cls : 'control-label col-md-' + this.labelWidth,
13558 html : this.fieldLabel
13562 cls : "col-md-" + (12 - this.labelWidth),
13569 } else if ( this.fieldLabel.length) {
13576 cls: 'control-label box-input-label',
13577 //cls : 'input-group-addon',
13578 html : this.fieldLabel
13588 Roo.log(" no label && no align");
13603 html: this.boxLabel
13611 onClick : function()
13613 this.setChecked(true);
13616 setChecked : function(state,suppressEvent)
13619 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13620 v.dom.checked = false;
13624 this.checked = state;
13625 this.inputEl().dom.checked = state;
13627 if(suppressEvent !== true){
13628 this.fireEvent('check', this, state);
13631 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13635 getGroupValue : function()
13638 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13639 if(v.dom.checked == true){
13640 value = v.dom.value;
13648 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13649 * @return {Mixed} value The field value
13651 getValue : function(){
13652 return this.getGroupValue();
13658 //<script type="text/javascript">
13661 * Based Ext JS Library 1.1.1
13662 * Copyright(c) 2006-2007, Ext JS, LLC.
13668 * @class Roo.HtmlEditorCore
13669 * @extends Roo.Component
13670 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13672 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13675 Roo.HtmlEditorCore = function(config){
13678 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13681 * @event initialize
13682 * Fires when the editor is fully initialized (including the iframe)
13683 * @param {Roo.HtmlEditorCore} this
13688 * Fires when the editor is first receives the focus. Any insertion must wait
13689 * until after this event.
13690 * @param {Roo.HtmlEditorCore} this
13694 * @event beforesync
13695 * Fires before the textarea is updated with content from the editor iframe. Return false
13696 * to cancel the sync.
13697 * @param {Roo.HtmlEditorCore} this
13698 * @param {String} html
13702 * @event beforepush
13703 * Fires before the iframe editor is updated with content from the textarea. Return false
13704 * to cancel the push.
13705 * @param {Roo.HtmlEditorCore} this
13706 * @param {String} html
13711 * Fires when the textarea is updated with content from the editor iframe.
13712 * @param {Roo.HtmlEditorCore} this
13713 * @param {String} html
13718 * Fires when the iframe editor is updated with content from the textarea.
13719 * @param {Roo.HtmlEditorCore} this
13720 * @param {String} html
13725 * @event editorevent
13726 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13727 * @param {Roo.HtmlEditorCore} this
13735 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
13739 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
13745 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
13750 * @cfg {Number} height (in pixels)
13754 * @cfg {Number} width (in pixels)
13759 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13762 stylesheets: false,
13767 // private properties
13768 validationEvent : false,
13770 initialized : false,
13772 sourceEditMode : false,
13773 onFocus : Roo.emptyFn,
13775 hideMode:'offsets',
13783 * Protected method that will not generally be called directly. It
13784 * is called when the editor initializes the iframe with HTML contents. Override this method if you
13785 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13787 getDocMarkup : function(){
13790 Roo.log(this.stylesheets);
13792 // inherit styels from page...??
13793 if (this.stylesheets === false) {
13795 Roo.get(document.head).select('style').each(function(node) {
13796 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13799 Roo.get(document.head).select('link').each(function(node) {
13800 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13803 } else if (!this.stylesheets.length) {
13805 st = '<style type="text/css">' +
13806 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13809 Roo.each(this.stylesheets, function(s) {
13810 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13815 st += '<style type="text/css">' +
13816 'IMG { cursor: pointer } ' +
13820 return '<html><head>' + st +
13821 //<style type="text/css">' +
13822 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13824 ' </head><body class="roo-htmleditor-body"></body></html>';
13828 onRender : function(ct, position)
13831 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13832 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13835 this.el.dom.style.border = '0 none';
13836 this.el.dom.setAttribute('tabIndex', -1);
13837 this.el.addClass('x-hidden hide');
13841 if(Roo.isIE){ // fix IE 1px bogus margin
13842 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13846 this.frameId = Roo.id();
13850 var iframe = this.owner.wrap.createChild({
13852 cls: 'form-control', // bootstrap..
13854 name: this.frameId,
13855 frameBorder : 'no',
13856 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13861 this.iframe = iframe.dom;
13863 this.assignDocWin();
13865 this.doc.designMode = 'on';
13868 this.doc.write(this.getDocMarkup());
13872 var task = { // must defer to wait for browser to be ready
13874 //console.log("run task?" + this.doc.readyState);
13875 this.assignDocWin();
13876 if(this.doc.body || this.doc.readyState == 'complete'){
13878 this.doc.designMode="on";
13882 Roo.TaskMgr.stop(task);
13883 this.initEditor.defer(10, this);
13890 Roo.TaskMgr.start(task);
13897 onResize : function(w, h)
13899 Roo.log('resize: ' +w + ',' + h );
13900 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13904 if(typeof w == 'number'){
13906 this.iframe.style.width = w + 'px';
13908 if(typeof h == 'number'){
13910 this.iframe.style.height = h + 'px';
13912 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13919 * Toggles the editor between standard and source edit mode.
13920 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13922 toggleSourceEdit : function(sourceEditMode){
13924 this.sourceEditMode = sourceEditMode === true;
13926 if(this.sourceEditMode){
13928 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13931 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13932 //this.iframe.className = '';
13935 //this.setSize(this.owner.wrap.getSize());
13936 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13943 * Protected method that will not generally be called directly. If you need/want
13944 * custom HTML cleanup, this is the method you should override.
13945 * @param {String} html The HTML to be cleaned
13946 * return {String} The cleaned HTML
13948 cleanHtml : function(html){
13949 html = String(html);
13950 if(html.length > 5){
13951 if(Roo.isSafari){ // strip safari nonsense
13952 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13955 if(html == ' '){
13962 * HTML Editor -> Textarea
13963 * Protected method that will not generally be called directly. Syncs the contents
13964 * of the editor iframe with the textarea.
13966 syncValue : function(){
13967 if(this.initialized){
13968 var bd = (this.doc.body || this.doc.documentElement);
13969 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13970 var html = bd.innerHTML;
13972 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13973 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13975 html = '<div style="'+m[0]+'">' + html + '</div>';
13978 html = this.cleanHtml(html);
13979 // fix up the special chars.. normaly like back quotes in word...
13980 // however we do not want to do this with chinese..
13981 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13982 var cc = b.charCodeAt();
13984 (cc >= 0x4E00 && cc < 0xA000 ) ||
13985 (cc >= 0x3400 && cc < 0x4E00 ) ||
13986 (cc >= 0xf900 && cc < 0xfb00 )
13992 if(this.owner.fireEvent('beforesync', this, html) !== false){
13993 this.el.dom.value = html;
13994 this.owner.fireEvent('sync', this, html);
14000 * Protected method that will not generally be called directly. Pushes the value of the textarea
14001 * into the iframe editor.
14003 pushValue : function(){
14004 if(this.initialized){
14005 var v = this.el.dom.value.trim();
14007 // if(v.length < 1){
14011 if(this.owner.fireEvent('beforepush', this, v) !== false){
14012 var d = (this.doc.body || this.doc.documentElement);
14014 this.cleanUpPaste();
14015 this.el.dom.value = d.innerHTML;
14016 this.owner.fireEvent('push', this, v);
14022 deferFocus : function(){
14023 this.focus.defer(10, this);
14027 focus : function(){
14028 if(this.win && !this.sourceEditMode){
14035 assignDocWin: function()
14037 var iframe = this.iframe;
14040 this.doc = iframe.contentWindow.document;
14041 this.win = iframe.contentWindow;
14043 if (!Roo.get(this.frameId)) {
14046 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14047 this.win = Roo.get(this.frameId).dom.contentWindow;
14052 initEditor : function(){
14053 //console.log("INIT EDITOR");
14054 this.assignDocWin();
14058 this.doc.designMode="on";
14060 this.doc.write(this.getDocMarkup());
14063 var dbody = (this.doc.body || this.doc.documentElement);
14064 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14065 // this copies styles from the containing element into thsi one..
14066 // not sure why we need all of this..
14067 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14068 ss['background-attachment'] = 'fixed'; // w3c
14069 dbody.bgProperties = 'fixed'; // ie
14070 Roo.DomHelper.applyStyles(dbody, ss);
14071 Roo.EventManager.on(this.doc, {
14072 //'mousedown': this.onEditorEvent,
14073 'mouseup': this.onEditorEvent,
14074 'dblclick': this.onEditorEvent,
14075 'click': this.onEditorEvent,
14076 'keyup': this.onEditorEvent,
14081 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14083 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14084 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14086 this.initialized = true;
14088 this.owner.fireEvent('initialize', this);
14093 onDestroy : function(){
14099 //for (var i =0; i < this.toolbars.length;i++) {
14100 // // fixme - ask toolbars for heights?
14101 // this.toolbars[i].onDestroy();
14104 //this.wrap.dom.innerHTML = '';
14105 //this.wrap.remove();
14110 onFirstFocus : function(){
14112 this.assignDocWin();
14115 this.activated = true;
14118 if(Roo.isGecko){ // prevent silly gecko errors
14120 var s = this.win.getSelection();
14121 if(!s.focusNode || s.focusNode.nodeType != 3){
14122 var r = s.getRangeAt(0);
14123 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14128 this.execCmd('useCSS', true);
14129 this.execCmd('styleWithCSS', false);
14132 this.owner.fireEvent('activate', this);
14136 adjustFont: function(btn){
14137 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14138 //if(Roo.isSafari){ // safari
14141 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14142 if(Roo.isSafari){ // safari
14143 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14144 v = (v < 10) ? 10 : v;
14145 v = (v > 48) ? 48 : v;
14146 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14151 v = Math.max(1, v+adjust);
14153 this.execCmd('FontSize', v );
14156 onEditorEvent : function(e){
14157 this.owner.fireEvent('editorevent', this, e);
14158 // this.updateToolbar();
14159 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14162 insertTag : function(tg)
14164 // could be a bit smarter... -> wrap the current selected tRoo..
14165 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14167 range = this.createRange(this.getSelection());
14168 var wrappingNode = this.doc.createElement(tg.toLowerCase());
14169 wrappingNode.appendChild(range.extractContents());
14170 range.insertNode(wrappingNode);
14177 this.execCmd("formatblock", tg);
14181 insertText : function(txt)
14185 var range = this.createRange();
14186 range.deleteContents();
14187 //alert(Sender.getAttribute('label'));
14189 range.insertNode(this.doc.createTextNode(txt));
14195 * Executes a Midas editor command on the editor document and performs necessary focus and
14196 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14197 * @param {String} cmd The Midas command
14198 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14200 relayCmd : function(cmd, value){
14202 this.execCmd(cmd, value);
14203 this.owner.fireEvent('editorevent', this);
14204 //this.updateToolbar();
14205 this.owner.deferFocus();
14209 * Executes a Midas editor command directly on the editor document.
14210 * For visual commands, you should use {@link #relayCmd} instead.
14211 * <b>This should only be called after the editor is initialized.</b>
14212 * @param {String} cmd The Midas command
14213 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14215 execCmd : function(cmd, value){
14216 this.doc.execCommand(cmd, false, value === undefined ? null : value);
14223 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14225 * @param {String} text | dom node..
14227 insertAtCursor : function(text)
14232 if(!this.activated){
14238 var r = this.doc.selection.createRange();
14249 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14253 // from jquery ui (MIT licenced)
14255 var win = this.win;
14257 if (win.getSelection && win.getSelection().getRangeAt) {
14258 range = win.getSelection().getRangeAt(0);
14259 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14260 range.insertNode(node);
14261 } else if (win.document.selection && win.document.selection.createRange) {
14262 // no firefox support
14263 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14264 win.document.selection.createRange().pasteHTML(txt);
14266 // no firefox support
14267 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14268 this.execCmd('InsertHTML', txt);
14277 mozKeyPress : function(e){
14279 var c = e.getCharCode(), cmd;
14282 c = String.fromCharCode(c).toLowerCase();
14296 this.cleanUpPaste.defer(100, this);
14304 e.preventDefault();
14312 fixKeys : function(){ // load time branching for fastest keydown performance
14314 return function(e){
14315 var k = e.getKey(), r;
14318 r = this.doc.selection.createRange();
14321 r.pasteHTML('    ');
14328 r = this.doc.selection.createRange();
14330 var target = r.parentElement();
14331 if(!target || target.tagName.toLowerCase() != 'li'){
14333 r.pasteHTML('<br />');
14339 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14340 this.cleanUpPaste.defer(100, this);
14346 }else if(Roo.isOpera){
14347 return function(e){
14348 var k = e.getKey();
14352 this.execCmd('InsertHTML','    ');
14355 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14356 this.cleanUpPaste.defer(100, this);
14361 }else if(Roo.isSafari){
14362 return function(e){
14363 var k = e.getKey();
14367 this.execCmd('InsertText','\t');
14371 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14372 this.cleanUpPaste.defer(100, this);
14380 getAllAncestors: function()
14382 var p = this.getSelectedNode();
14385 a.push(p); // push blank onto stack..
14386 p = this.getParentElement();
14390 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14394 a.push(this.doc.body);
14398 lastSelNode : false,
14401 getSelection : function()
14403 this.assignDocWin();
14404 return Roo.isIE ? this.doc.selection : this.win.getSelection();
14407 getSelectedNode: function()
14409 // this may only work on Gecko!!!
14411 // should we cache this!!!!
14416 var range = this.createRange(this.getSelection()).cloneRange();
14419 var parent = range.parentElement();
14421 var testRange = range.duplicate();
14422 testRange.moveToElementText(parent);
14423 if (testRange.inRange(range)) {
14426 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14429 parent = parent.parentElement;
14434 // is ancestor a text element.
14435 var ac = range.commonAncestorContainer;
14436 if (ac.nodeType == 3) {
14437 ac = ac.parentNode;
14440 var ar = ac.childNodes;
14443 var other_nodes = [];
14444 var has_other_nodes = false;
14445 for (var i=0;i<ar.length;i++) {
14446 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
14449 // fullly contained node.
14451 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14456 // probably selected..
14457 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14458 other_nodes.push(ar[i]);
14462 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
14467 has_other_nodes = true;
14469 if (!nodes.length && other_nodes.length) {
14470 nodes= other_nodes;
14472 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14478 createRange: function(sel)
14480 // this has strange effects when using with
14481 // top toolbar - not sure if it's a great idea.
14482 //this.editor.contentWindow.focus();
14483 if (typeof sel != "undefined") {
14485 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14487 return this.doc.createRange();
14490 return this.doc.createRange();
14493 getParentElement: function()
14496 this.assignDocWin();
14497 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14499 var range = this.createRange(sel);
14502 var p = range.commonAncestorContainer;
14503 while (p.nodeType == 3) { // text node
14514 * Range intersection.. the hard stuff...
14518 * [ -- selected range --- ]
14522 * if end is before start or hits it. fail.
14523 * if start is after end or hits it fail.
14525 * if either hits (but other is outside. - then it's not
14531 // @see http://www.thismuchiknow.co.uk/?p=64.
14532 rangeIntersectsNode : function(range, node)
14534 var nodeRange = node.ownerDocument.createRange();
14536 nodeRange.selectNode(node);
14538 nodeRange.selectNodeContents(node);
14541 var rangeStartRange = range.cloneRange();
14542 rangeStartRange.collapse(true);
14544 var rangeEndRange = range.cloneRange();
14545 rangeEndRange.collapse(false);
14547 var nodeStartRange = nodeRange.cloneRange();
14548 nodeStartRange.collapse(true);
14550 var nodeEndRange = nodeRange.cloneRange();
14551 nodeEndRange.collapse(false);
14553 return rangeStartRange.compareBoundaryPoints(
14554 Range.START_TO_START, nodeEndRange) == -1 &&
14555 rangeEndRange.compareBoundaryPoints(
14556 Range.START_TO_START, nodeStartRange) == 1;
14560 rangeCompareNode : function(range, node)
14562 var nodeRange = node.ownerDocument.createRange();
14564 nodeRange.selectNode(node);
14566 nodeRange.selectNodeContents(node);
14570 range.collapse(true);
14572 nodeRange.collapse(true);
14574 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14575 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
14577 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14579 var nodeIsBefore = ss == 1;
14580 var nodeIsAfter = ee == -1;
14582 if (nodeIsBefore && nodeIsAfter)
14584 if (!nodeIsBefore && nodeIsAfter)
14585 return 1; //right trailed.
14587 if (nodeIsBefore && !nodeIsAfter)
14588 return 2; // left trailed.
14593 // private? - in a new class?
14594 cleanUpPaste : function()
14596 // cleans up the whole document..
14597 Roo.log('cleanuppaste');
14599 this.cleanUpChildren(this.doc.body);
14600 var clean = this.cleanWordChars(this.doc.body.innerHTML);
14601 if (clean != this.doc.body.innerHTML) {
14602 this.doc.body.innerHTML = clean;
14607 cleanWordChars : function(input) {// change the chars to hex code
14608 var he = Roo.HtmlEditorCore;
14610 var output = input;
14611 Roo.each(he.swapCodes, function(sw) {
14612 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14614 output = output.replace(swapper, sw[1]);
14621 cleanUpChildren : function (n)
14623 if (!n.childNodes.length) {
14626 for (var i = n.childNodes.length-1; i > -1 ; i--) {
14627 this.cleanUpChild(n.childNodes[i]);
14634 cleanUpChild : function (node)
14637 //console.log(node);
14638 if (node.nodeName == "#text") {
14639 // clean up silly Windows -- stuff?
14642 if (node.nodeName == "#comment") {
14643 node.parentNode.removeChild(node);
14644 // clean up silly Windows -- stuff?
14648 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14650 node.parentNode.removeChild(node);
14655 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14657 // remove <a name=....> as rendering on yahoo mailer is borked with this.
14658 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14660 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14661 // remove_keep_children = true;
14664 if (remove_keep_children) {
14665 this.cleanUpChildren(node);
14666 // inserts everything just before this node...
14667 while (node.childNodes.length) {
14668 var cn = node.childNodes[0];
14669 node.removeChild(cn);
14670 node.parentNode.insertBefore(cn, node);
14672 node.parentNode.removeChild(node);
14676 if (!node.attributes || !node.attributes.length) {
14677 this.cleanUpChildren(node);
14681 function cleanAttr(n,v)
14684 if (v.match(/^\./) || v.match(/^\//)) {
14687 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14690 if (v.match(/^#/)) {
14693 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14694 node.removeAttribute(n);
14698 function cleanStyle(n,v)
14700 if (v.match(/expression/)) { //XSS?? should we even bother..
14701 node.removeAttribute(n);
14704 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14705 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14708 var parts = v.split(/;/);
14711 Roo.each(parts, function(p) {
14712 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14716 var l = p.split(':').shift().replace(/\s+/g,'');
14717 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14719 if ( cblack.indexOf(l) > -1) {
14720 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14721 //node.removeAttribute(n);
14725 // only allow 'c whitelisted system attributes'
14726 if ( cwhite.length && cwhite.indexOf(l) < 0) {
14727 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14728 //node.removeAttribute(n);
14738 if (clean.length) {
14739 node.setAttribute(n, clean.join(';'));
14741 node.removeAttribute(n);
14747 for (var i = node.attributes.length-1; i > -1 ; i--) {
14748 var a = node.attributes[i];
14751 if (a.name.toLowerCase().substr(0,2)=='on') {
14752 node.removeAttribute(a.name);
14755 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14756 node.removeAttribute(a.name);
14759 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14760 cleanAttr(a.name,a.value); // fixme..
14763 if (a.name == 'style') {
14764 cleanStyle(a.name,a.value);
14767 /// clean up MS crap..
14768 // tecnically this should be a list of valid class'es..
14771 if (a.name == 'class') {
14772 if (a.value.match(/^Mso/)) {
14773 node.className = '';
14776 if (a.value.match(/body/)) {
14777 node.className = '';
14788 this.cleanUpChildren(node);
14794 // hide stuff that is not compatible
14808 * @event specialkey
14812 * @cfg {String} fieldClass @hide
14815 * @cfg {String} focusClass @hide
14818 * @cfg {String} autoCreate @hide
14821 * @cfg {String} inputType @hide
14824 * @cfg {String} invalidClass @hide
14827 * @cfg {String} invalidText @hide
14830 * @cfg {String} msgFx @hide
14833 * @cfg {String} validateOnBlur @hide
14837 Roo.HtmlEditorCore.white = [
14838 'area', 'br', 'img', 'input', 'hr', 'wbr',
14840 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14841 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14842 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14843 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14844 'table', 'ul', 'xmp',
14846 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14849 'dir', 'menu', 'ol', 'ul', 'dl',
14855 Roo.HtmlEditorCore.black = [
14856 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14858 'base', 'basefont', 'bgsound', 'blink', 'body',
14859 'frame', 'frameset', 'head', 'html', 'ilayer',
14860 'iframe', 'layer', 'link', 'meta', 'object',
14861 'script', 'style' ,'title', 'xml' // clean later..
14863 Roo.HtmlEditorCore.clean = [
14864 'script', 'style', 'title', 'xml'
14866 Roo.HtmlEditorCore.remove = [
14871 Roo.HtmlEditorCore.ablack = [
14875 Roo.HtmlEditorCore.aclean = [
14876 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14880 Roo.HtmlEditorCore.pwhite= [
14881 'http', 'https', 'mailto'
14884 // white listed style attributes.
14885 Roo.HtmlEditorCore.cwhite= [
14886 // 'text-align', /// default is to allow most things..
14892 // black listed style attributes.
14893 Roo.HtmlEditorCore.cblack= [
14894 // 'font-size' -- this can be set by the project
14898 Roo.HtmlEditorCore.swapCodes =[
14917 * @class Roo.bootstrap.HtmlEditor
14918 * @extends Roo.bootstrap.TextArea
14919 * Bootstrap HtmlEditor class
14922 * Create a new HtmlEditor
14923 * @param {Object} config The config object
14926 Roo.bootstrap.HtmlEditor = function(config){
14927 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14928 if (!this.toolbars) {
14929 this.toolbars = [];
14931 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14934 * @event initialize
14935 * Fires when the editor is fully initialized (including the iframe)
14936 * @param {HtmlEditor} this
14941 * Fires when the editor is first receives the focus. Any insertion must wait
14942 * until after this event.
14943 * @param {HtmlEditor} this
14947 * @event beforesync
14948 * Fires before the textarea is updated with content from the editor iframe. Return false
14949 * to cancel the sync.
14950 * @param {HtmlEditor} this
14951 * @param {String} html
14955 * @event beforepush
14956 * Fires before the iframe editor is updated with content from the textarea. Return false
14957 * to cancel the push.
14958 * @param {HtmlEditor} this
14959 * @param {String} html
14964 * Fires when the textarea is updated with content from the editor iframe.
14965 * @param {HtmlEditor} this
14966 * @param {String} html
14971 * Fires when the iframe editor is updated with content from the textarea.
14972 * @param {HtmlEditor} this
14973 * @param {String} html
14977 * @event editmodechange
14978 * Fires when the editor switches edit modes
14979 * @param {HtmlEditor} this
14980 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14982 editmodechange: true,
14984 * @event editorevent
14985 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14986 * @param {HtmlEditor} this
14990 * @event firstfocus
14991 * Fires when on first focus - needed by toolbars..
14992 * @param {HtmlEditor} this
14997 * Auto save the htmlEditor value as a file into Events
14998 * @param {HtmlEditor} this
15002 * @event savedpreview
15003 * preview the saved version of htmlEditor
15004 * @param {HtmlEditor} this
15011 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
15015 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15020 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
15025 * @cfg {Number} height (in pixels)
15029 * @cfg {Number} width (in pixels)
15034 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15037 stylesheets: false,
15042 // private properties
15043 validationEvent : false,
15045 initialized : false,
15048 onFocus : Roo.emptyFn,
15050 hideMode:'offsets',
15053 tbContainer : false,
15055 toolbarContainer :function() {
15056 return this.wrap.select('.x-html-editor-tb',true).first();
15060 * Protected method that will not generally be called directly. It
15061 * is called when the editor creates its toolbar. Override this method if you need to
15062 * add custom toolbar buttons.
15063 * @param {HtmlEditor} editor
15065 createToolbar : function(){
15067 Roo.log("create toolbars");
15069 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15070 this.toolbars[0].render(this.toolbarContainer());
15074 // if (!editor.toolbars || !editor.toolbars.length) {
15075 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15078 // for (var i =0 ; i < editor.toolbars.length;i++) {
15079 // editor.toolbars[i] = Roo.factory(
15080 // typeof(editor.toolbars[i]) == 'string' ?
15081 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
15082 // Roo.bootstrap.HtmlEditor);
15083 // editor.toolbars[i].init(editor);
15089 onRender : function(ct, position)
15091 // Roo.log("Call onRender: " + this.xtype);
15093 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15095 this.wrap = this.inputEl().wrap({
15096 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15099 this.editorcore.onRender(ct, position);
15101 if (this.resizable) {
15102 this.resizeEl = new Roo.Resizable(this.wrap, {
15106 minHeight : this.height,
15107 height: this.height,
15108 handles : this.resizable,
15111 resize : function(r, w, h) {
15112 _t.onResize(w,h); // -something
15118 this.createToolbar(this);
15121 if(!this.width && this.resizable){
15122 this.setSize(this.wrap.getSize());
15124 if (this.resizeEl) {
15125 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15126 // should trigger onReize..
15132 onResize : function(w, h)
15134 Roo.log('resize: ' +w + ',' + h );
15135 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15139 if(this.inputEl() ){
15140 if(typeof w == 'number'){
15141 var aw = w - this.wrap.getFrameWidth('lr');
15142 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15145 if(typeof h == 'number'){
15146 var tbh = -11; // fixme it needs to tool bar size!
15147 for (var i =0; i < this.toolbars.length;i++) {
15148 // fixme - ask toolbars for heights?
15149 tbh += this.toolbars[i].el.getHeight();
15150 //if (this.toolbars[i].footer) {
15151 // tbh += this.toolbars[i].footer.el.getHeight();
15159 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15160 ah -= 5; // knock a few pixes off for look..
15161 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15165 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15166 this.editorcore.onResize(ew,eh);
15171 * Toggles the editor between standard and source edit mode.
15172 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15174 toggleSourceEdit : function(sourceEditMode)
15176 this.editorcore.toggleSourceEdit(sourceEditMode);
15178 if(this.editorcore.sourceEditMode){
15179 Roo.log('editor - showing textarea');
15182 // Roo.log(this.syncValue());
15184 this.inputEl().removeClass('hide');
15185 this.inputEl().dom.removeAttribute('tabIndex');
15186 this.inputEl().focus();
15188 Roo.log('editor - hiding textarea');
15190 // Roo.log(this.pushValue());
15193 this.inputEl().addClass('hide');
15194 this.inputEl().dom.setAttribute('tabIndex', -1);
15195 //this.deferFocus();
15198 if(this.resizable){
15199 this.setSize(this.wrap.getSize());
15202 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15205 // private (for BoxComponent)
15206 adjustSize : Roo.BoxComponent.prototype.adjustSize,
15208 // private (for BoxComponent)
15209 getResizeEl : function(){
15213 // private (for BoxComponent)
15214 getPositionEl : function(){
15219 initEvents : function(){
15220 this.originalValue = this.getValue();
15224 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15227 // markInvalid : Roo.emptyFn,
15229 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15232 // clearInvalid : Roo.emptyFn,
15234 setValue : function(v){
15235 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15236 this.editorcore.pushValue();
15241 deferFocus : function(){
15242 this.focus.defer(10, this);
15246 focus : function(){
15247 this.editorcore.focus();
15253 onDestroy : function(){
15259 for (var i =0; i < this.toolbars.length;i++) {
15260 // fixme - ask toolbars for heights?
15261 this.toolbars[i].onDestroy();
15264 this.wrap.dom.innerHTML = '';
15265 this.wrap.remove();
15270 onFirstFocus : function(){
15271 //Roo.log("onFirstFocus");
15272 this.editorcore.onFirstFocus();
15273 for (var i =0; i < this.toolbars.length;i++) {
15274 this.toolbars[i].onFirstFocus();
15280 syncValue : function()
15282 this.editorcore.syncValue();
15285 pushValue : function()
15287 this.editorcore.pushValue();
15291 // hide stuff that is not compatible
15305 * @event specialkey
15309 * @cfg {String} fieldClass @hide
15312 * @cfg {String} focusClass @hide
15315 * @cfg {String} autoCreate @hide
15318 * @cfg {String} inputType @hide
15321 * @cfg {String} invalidClass @hide
15324 * @cfg {String} invalidText @hide
15327 * @cfg {String} msgFx @hide
15330 * @cfg {String} validateOnBlur @hide
15341 * @class Roo.bootstrap.HtmlEditorToolbar1
15346 new Roo.bootstrap.HtmlEditor({
15349 new Roo.bootstrap.HtmlEditorToolbar1({
15350 disable : { fonts: 1 , format: 1, ..., ... , ...],
15356 * @cfg {Object} disable List of elements to disable..
15357 * @cfg {Array} btns List of additional buttons.
15361 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15364 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15367 Roo.apply(this, config);
15369 // default disabled, based on 'good practice'..
15370 this.disable = this.disable || {};
15371 Roo.applyIf(this.disable, {
15374 specialElements : true
15376 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15378 this.editor = config.editor;
15379 this.editorcore = config.editor.editorcore;
15381 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15383 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15384 // dont call parent... till later.
15386 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
15392 editorcore : false,
15397 "h1","h2","h3","h4","h5","h6",
15399 "abbr", "acronym", "address", "cite", "samp", "var",
15403 onRender : function(ct, position)
15405 // Roo.log("Call onRender: " + this.xtype);
15407 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15409 this.el.dom.style.marginBottom = '0';
15411 var editorcore = this.editorcore;
15412 var editor= this.editor;
15415 var btn = function(id,cmd , toggle, handler){
15417 var event = toggle ? 'toggle' : 'click';
15422 xns: Roo.bootstrap,
15425 enableToggle:toggle !== false,
15427 pressed : toggle ? false : null,
15430 a.listeners[toggle ? 'toggle' : 'click'] = function() {
15431 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
15440 xns: Roo.bootstrap,
15441 glyphicon : 'font',
15445 xns: Roo.bootstrap,
15449 Roo.each(this.formats, function(f) {
15450 style.menu.items.push({
15452 xns: Roo.bootstrap,
15453 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15458 editorcore.insertTag(this.tagname);
15465 children.push(style);
15468 btn('bold',false,true);
15469 btn('italic',false,true);
15470 btn('align-left', 'justifyleft',true);
15471 btn('align-center', 'justifycenter',true);
15472 btn('align-right' , 'justifyright',true);
15473 btn('link', false, false, function(btn) {
15474 //Roo.log("create link?");
15475 var url = prompt(this.createLinkText, this.defaultLinkValue);
15476 if(url && url != 'http:/'+'/'){
15477 this.editorcore.relayCmd('createlink', url);
15480 btn('list','insertunorderedlist',true);
15481 btn('pencil', false,true, function(btn){
15484 this.toggleSourceEdit(btn.pressed);
15490 xns: Roo.bootstrap,
15495 xns: Roo.bootstrap,
15500 cog.menu.items.push({
15502 xns: Roo.bootstrap,
15503 html : Clean styles,
15508 editorcore.insertTag(this.tagname);
15517 this.xtype = 'Navbar';
15519 for(var i=0;i< children.length;i++) {
15521 this.buttons.add(this.addxtypeChild(children[i]));
15525 editor.on('editorevent', this.updateToolbar, this);
15527 onBtnClick : function(id)
15529 this.editorcore.relayCmd(id);
15530 this.editorcore.focus();
15534 * Protected method that will not generally be called directly. It triggers
15535 * a toolbar update by reading the markup state of the current selection in the editor.
15537 updateToolbar: function(){
15539 if(!this.editorcore.activated){
15540 this.editor.onFirstFocus(); // is this neeed?
15544 var btns = this.buttons;
15545 var doc = this.editorcore.doc;
15546 btns.get('bold').setActive(doc.queryCommandState('bold'));
15547 btns.get('italic').setActive(doc.queryCommandState('italic'));
15548 //btns.get('underline').setActive(doc.queryCommandState('underline'));
15550 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15551 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15552 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15554 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15555 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15558 var ans = this.editorcore.getAllAncestors();
15559 if (this.formatCombo) {
15562 var store = this.formatCombo.store;
15563 this.formatCombo.setValue("");
15564 for (var i =0; i < ans.length;i++) {
15565 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15567 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15575 // hides menus... - so this cant be on a menu...
15576 Roo.bootstrap.MenuMgr.hideAll();
15578 Roo.bootstrap.MenuMgr.hideAll();
15579 //this.editorsyncValue();
15581 onFirstFocus: function() {
15582 this.buttons.each(function(item){
15586 toggleSourceEdit : function(sourceEditMode){
15589 if(sourceEditMode){
15590 Roo.log("disabling buttons");
15591 this.buttons.each( function(item){
15592 if(item.cmd != 'pencil'){
15598 Roo.log("enabling buttons");
15599 if(this.editorcore.initialized){
15600 this.buttons.each( function(item){
15606 Roo.log("calling toggole on editor");
15607 // tell the editor that it's been pressed..
15608 this.editor.toggleSourceEdit(sourceEditMode);
15618 * @class Roo.bootstrap.Table.AbstractSelectionModel
15619 * @extends Roo.util.Observable
15620 * Abstract base class for grid SelectionModels. It provides the interface that should be
15621 * implemented by descendant classes. This class should not be directly instantiated.
15624 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15625 this.locked = false;
15626 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15630 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
15631 /** @ignore Called by the grid automatically. Do not call directly. */
15632 init : function(grid){
15638 * Locks the selections.
15641 this.locked = true;
15645 * Unlocks the selections.
15647 unlock : function(){
15648 this.locked = false;
15652 * Returns true if the selections are locked.
15653 * @return {Boolean}
15655 isLocked : function(){
15656 return this.locked;
15660 * @class Roo.bootstrap.Table.ColumnModel
15661 * @extends Roo.util.Observable
15662 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15663 * the columns in the table.
15666 * @param {Object} config An Array of column config objects. See this class's
15667 * config objects for details.
15669 Roo.bootstrap.Table.ColumnModel = function(config){
15671 * The config passed into the constructor
15673 this.config = config;
15676 // if no id, create one
15677 // if the column does not have a dataIndex mapping,
15678 // map it to the order it is in the config
15679 for(var i = 0, len = config.length; i < len; i++){
15681 if(typeof c.dataIndex == "undefined"){
15684 if(typeof c.renderer == "string"){
15685 c.renderer = Roo.util.Format[c.renderer];
15687 if(typeof c.id == "undefined"){
15690 // if(c.editor && c.editor.xtype){
15691 // c.editor = Roo.factory(c.editor, Roo.grid);
15693 // if(c.editor && c.editor.isFormField){
15694 // c.editor = new Roo.grid.GridEditor(c.editor);
15697 this.lookup[c.id] = c;
15701 * The width of columns which have no width specified (defaults to 100)
15704 this.defaultWidth = 100;
15707 * Default sortable of columns which have no sortable specified (defaults to false)
15710 this.defaultSortable = false;
15714 * @event widthchange
15715 * Fires when the width of a column changes.
15716 * @param {ColumnModel} this
15717 * @param {Number} columnIndex The column index
15718 * @param {Number} newWidth The new width
15720 "widthchange": true,
15722 * @event headerchange
15723 * Fires when the text of a header changes.
15724 * @param {ColumnModel} this
15725 * @param {Number} columnIndex The column index
15726 * @param {Number} newText The new header text
15728 "headerchange": true,
15730 * @event hiddenchange
15731 * Fires when a column is hidden or "unhidden".
15732 * @param {ColumnModel} this
15733 * @param {Number} columnIndex The column index
15734 * @param {Boolean} hidden true if hidden, false otherwise
15736 "hiddenchange": true,
15738 * @event columnmoved
15739 * Fires when a column is moved.
15740 * @param {ColumnModel} this
15741 * @param {Number} oldIndex
15742 * @param {Number} newIndex
15744 "columnmoved" : true,
15746 * @event columlockchange
15747 * Fires when a column's locked state is changed
15748 * @param {ColumnModel} this
15749 * @param {Number} colIndex
15750 * @param {Boolean} locked true if locked
15752 "columnlockchange" : true
15754 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15756 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15758 * @cfg {String} header The header text to display in the Grid view.
15761 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15762 * {@link Roo.data.Record} definition from which to draw the column's value. If not
15763 * specified, the column's index is used as an index into the Record's data Array.
15766 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15767 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15770 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15771 * Defaults to the value of the {@link #defaultSortable} property.
15772 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15775 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
15778 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
15781 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15784 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15787 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15788 * given the cell's data value. See {@link #setRenderer}. If not specified, the
15789 * default renderer uses the raw data value.
15792 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
15796 * Returns the id of the column at the specified index.
15797 * @param {Number} index The column index
15798 * @return {String} the id
15800 getColumnId : function(index){
15801 return this.config[index].id;
15805 * Returns the column for a specified id.
15806 * @param {String} id The column id
15807 * @return {Object} the column
15809 getColumnById : function(id){
15810 return this.lookup[id];
15815 * Returns the column for a specified dataIndex.
15816 * @param {String} dataIndex The column dataIndex
15817 * @return {Object|Boolean} the column or false if not found
15819 getColumnByDataIndex: function(dataIndex){
15820 var index = this.findColumnIndex(dataIndex);
15821 return index > -1 ? this.config[index] : false;
15825 * Returns the index for a specified column id.
15826 * @param {String} id The column id
15827 * @return {Number} the index, or -1 if not found
15829 getIndexById : function(id){
15830 for(var i = 0, len = this.config.length; i < len; i++){
15831 if(this.config[i].id == id){
15839 * Returns the index for a specified column dataIndex.
15840 * @param {String} dataIndex The column dataIndex
15841 * @return {Number} the index, or -1 if not found
15844 findColumnIndex : function(dataIndex){
15845 for(var i = 0, len = this.config.length; i < len; i++){
15846 if(this.config[i].dataIndex == dataIndex){
15854 moveColumn : function(oldIndex, newIndex){
15855 var c = this.config[oldIndex];
15856 this.config.splice(oldIndex, 1);
15857 this.config.splice(newIndex, 0, c);
15858 this.dataMap = null;
15859 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15862 isLocked : function(colIndex){
15863 return this.config[colIndex].locked === true;
15866 setLocked : function(colIndex, value, suppressEvent){
15867 if(this.isLocked(colIndex) == value){
15870 this.config[colIndex].locked = value;
15871 if(!suppressEvent){
15872 this.fireEvent("columnlockchange", this, colIndex, value);
15876 getTotalLockedWidth : function(){
15877 var totalWidth = 0;
15878 for(var i = 0; i < this.config.length; i++){
15879 if(this.isLocked(i) && !this.isHidden(i)){
15880 this.totalWidth += this.getColumnWidth(i);
15886 getLockedCount : function(){
15887 for(var i = 0, len = this.config.length; i < len; i++){
15888 if(!this.isLocked(i)){
15895 * Returns the number of columns.
15898 getColumnCount : function(visibleOnly){
15899 if(visibleOnly === true){
15901 for(var i = 0, len = this.config.length; i < len; i++){
15902 if(!this.isHidden(i)){
15908 return this.config.length;
15912 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15913 * @param {Function} fn
15914 * @param {Object} scope (optional)
15915 * @return {Array} result
15917 getColumnsBy : function(fn, scope){
15919 for(var i = 0, len = this.config.length; i < len; i++){
15920 var c = this.config[i];
15921 if(fn.call(scope||this, c, i) === true){
15929 * Returns true if the specified column is sortable.
15930 * @param {Number} col The column index
15931 * @return {Boolean}
15933 isSortable : function(col){
15934 if(typeof this.config[col].sortable == "undefined"){
15935 return this.defaultSortable;
15937 return this.config[col].sortable;
15941 * Returns the rendering (formatting) function defined for the column.
15942 * @param {Number} col The column index.
15943 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15945 getRenderer : function(col){
15946 if(!this.config[col].renderer){
15947 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15949 return this.config[col].renderer;
15953 * Sets the rendering (formatting) function for a column.
15954 * @param {Number} col The column index
15955 * @param {Function} fn The function to use to process the cell's raw data
15956 * to return HTML markup for the grid view. The render function is called with
15957 * the following parameters:<ul>
15958 * <li>Data value.</li>
15959 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15960 * <li>css A CSS style string to apply to the table cell.</li>
15961 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15962 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15963 * <li>Row index</li>
15964 * <li>Column index</li>
15965 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15967 setRenderer : function(col, fn){
15968 this.config[col].renderer = fn;
15972 * Returns the width for the specified column.
15973 * @param {Number} col The column index
15976 getColumnWidth : function(col){
15977 return this.config[col].width * 1 || this.defaultWidth;
15981 * Sets the width for a column.
15982 * @param {Number} col The column index
15983 * @param {Number} width The new width
15985 setColumnWidth : function(col, width, suppressEvent){
15986 this.config[col].width = width;
15987 this.totalWidth = null;
15988 if(!suppressEvent){
15989 this.fireEvent("widthchange", this, col, width);
15994 * Returns the total width of all columns.
15995 * @param {Boolean} includeHidden True to include hidden column widths
15998 getTotalWidth : function(includeHidden){
15999 if(!this.totalWidth){
16000 this.totalWidth = 0;
16001 for(var i = 0, len = this.config.length; i < len; i++){
16002 if(includeHidden || !this.isHidden(i)){
16003 this.totalWidth += this.getColumnWidth(i);
16007 return this.totalWidth;
16011 * Returns the header for the specified column.
16012 * @param {Number} col The column index
16015 getColumnHeader : function(col){
16016 return this.config[col].header;
16020 * Sets the header for a column.
16021 * @param {Number} col The column index
16022 * @param {String} header The new header
16024 setColumnHeader : function(col, header){
16025 this.config[col].header = header;
16026 this.fireEvent("headerchange", this, col, header);
16030 * Returns the tooltip for the specified column.
16031 * @param {Number} col The column index
16034 getColumnTooltip : function(col){
16035 return this.config[col].tooltip;
16038 * Sets the tooltip for a column.
16039 * @param {Number} col The column index
16040 * @param {String} tooltip The new tooltip
16042 setColumnTooltip : function(col, tooltip){
16043 this.config[col].tooltip = tooltip;
16047 * Returns the dataIndex for the specified column.
16048 * @param {Number} col The column index
16051 getDataIndex : function(col){
16052 return this.config[col].dataIndex;
16056 * Sets the dataIndex for a column.
16057 * @param {Number} col The column index
16058 * @param {Number} dataIndex The new dataIndex
16060 setDataIndex : function(col, dataIndex){
16061 this.config[col].dataIndex = dataIndex;
16067 * Returns true if the cell is editable.
16068 * @param {Number} colIndex The column index
16069 * @param {Number} rowIndex The row index
16070 * @return {Boolean}
16072 isCellEditable : function(colIndex, rowIndex){
16073 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16077 * Returns the editor defined for the cell/column.
16078 * return false or null to disable editing.
16079 * @param {Number} colIndex The column index
16080 * @param {Number} rowIndex The row index
16083 getCellEditor : function(colIndex, rowIndex){
16084 return this.config[colIndex].editor;
16088 * Sets if a column is editable.
16089 * @param {Number} col The column index
16090 * @param {Boolean} editable True if the column is editable
16092 setEditable : function(col, editable){
16093 this.config[col].editable = editable;
16098 * Returns true if the column is hidden.
16099 * @param {Number} colIndex The column index
16100 * @return {Boolean}
16102 isHidden : function(colIndex){
16103 return this.config[colIndex].hidden;
16108 * Returns true if the column width cannot be changed
16110 isFixed : function(colIndex){
16111 return this.config[colIndex].fixed;
16115 * Returns true if the column can be resized
16116 * @return {Boolean}
16118 isResizable : function(colIndex){
16119 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16122 * Sets if a column is hidden.
16123 * @param {Number} colIndex The column index
16124 * @param {Boolean} hidden True if the column is hidden
16126 setHidden : function(colIndex, hidden){
16127 this.config[colIndex].hidden = hidden;
16128 this.totalWidth = null;
16129 this.fireEvent("hiddenchange", this, colIndex, hidden);
16133 * Sets the editor for a column.
16134 * @param {Number} col The column index
16135 * @param {Object} editor The editor object
16137 setEditor : function(col, editor){
16138 this.config[col].editor = editor;
16142 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16143 if(typeof value == "string" && value.length < 1){
16149 // Alias for backwards compatibility
16150 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16153 * @extends Roo.bootstrap.Table.AbstractSelectionModel
16154 * @class Roo.bootstrap.Table.RowSelectionModel
16155 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16156 * It supports multiple selections and keyboard selection/navigation.
16158 * @param {Object} config
16161 Roo.bootstrap.Table.RowSelectionModel = function(config){
16162 Roo.apply(this, config);
16163 this.selections = new Roo.util.MixedCollection(false, function(o){
16168 this.lastActive = false;
16172 * @event selectionchange
16173 * Fires when the selection changes
16174 * @param {SelectionModel} this
16176 "selectionchange" : true,
16178 * @event afterselectionchange
16179 * Fires after the selection changes (eg. by key press or clicking)
16180 * @param {SelectionModel} this
16182 "afterselectionchange" : true,
16184 * @event beforerowselect
16185 * Fires when a row is selected being selected, return false to cancel.
16186 * @param {SelectionModel} this
16187 * @param {Number} rowIndex The selected index
16188 * @param {Boolean} keepExisting False if other selections will be cleared
16190 "beforerowselect" : true,
16193 * Fires when a row is selected.
16194 * @param {SelectionModel} this
16195 * @param {Number} rowIndex The selected index
16196 * @param {Roo.data.Record} r The record
16198 "rowselect" : true,
16200 * @event rowdeselect
16201 * Fires when a row is deselected.
16202 * @param {SelectionModel} this
16203 * @param {Number} rowIndex The selected index
16205 "rowdeselect" : true
16207 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16208 this.locked = false;
16211 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
16213 * @cfg {Boolean} singleSelect
16214 * True to allow selection of only one row at a time (defaults to false)
16216 singleSelect : false,
16219 initEvents : function(){
16221 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16222 this.grid.on("mousedown", this.handleMouseDown, this);
16223 }else{ // allow click to work like normal
16224 this.grid.on("rowclick", this.handleDragableRowClick, this);
16227 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16228 "up" : function(e){
16230 this.selectPrevious(e.shiftKey);
16231 }else if(this.last !== false && this.lastActive !== false){
16232 var last = this.last;
16233 this.selectRange(this.last, this.lastActive-1);
16234 this.grid.getView().focusRow(this.lastActive);
16235 if(last !== false){
16239 this.selectFirstRow();
16241 this.fireEvent("afterselectionchange", this);
16243 "down" : function(e){
16245 this.selectNext(e.shiftKey);
16246 }else if(this.last !== false && this.lastActive !== false){
16247 var last = this.last;
16248 this.selectRange(this.last, this.lastActive+1);
16249 this.grid.getView().focusRow(this.lastActive);
16250 if(last !== false){
16254 this.selectFirstRow();
16256 this.fireEvent("afterselectionchange", this);
16261 var view = this.grid.view;
16262 view.on("refresh", this.onRefresh, this);
16263 view.on("rowupdated", this.onRowUpdated, this);
16264 view.on("rowremoved", this.onRemove, this);
16268 onRefresh : function(){
16269 var ds = this.grid.dataSource, i, v = this.grid.view;
16270 var s = this.selections;
16271 s.each(function(r){
16272 if((i = ds.indexOfId(r.id)) != -1){
16281 onRemove : function(v, index, r){
16282 this.selections.remove(r);
16286 onRowUpdated : function(v, index, r){
16287 if(this.isSelected(r)){
16288 v.onRowSelect(index);
16294 * @param {Array} records The records to select
16295 * @param {Boolean} keepExisting (optional) True to keep existing selections
16297 selectRecords : function(records, keepExisting){
16299 this.clearSelections();
16301 var ds = this.grid.dataSource;
16302 for(var i = 0, len = records.length; i < len; i++){
16303 this.selectRow(ds.indexOf(records[i]), true);
16308 * Gets the number of selected rows.
16311 getCount : function(){
16312 return this.selections.length;
16316 * Selects the first row in the grid.
16318 selectFirstRow : function(){
16323 * Select the last row.
16324 * @param {Boolean} keepExisting (optional) True to keep existing selections
16326 selectLastRow : function(keepExisting){
16327 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16331 * Selects the row immediately following the last selected row.
16332 * @param {Boolean} keepExisting (optional) True to keep existing selections
16334 selectNext : function(keepExisting){
16335 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16336 this.selectRow(this.last+1, keepExisting);
16337 this.grid.getView().focusRow(this.last);
16342 * Selects the row that precedes the last selected row.
16343 * @param {Boolean} keepExisting (optional) True to keep existing selections
16345 selectPrevious : function(keepExisting){
16347 this.selectRow(this.last-1, keepExisting);
16348 this.grid.getView().focusRow(this.last);
16353 * Returns the selected records
16354 * @return {Array} Array of selected records
16356 getSelections : function(){
16357 return [].concat(this.selections.items);
16361 * Returns the first selected record.
16364 getSelected : function(){
16365 return this.selections.itemAt(0);
16370 * Clears all selections.
16372 clearSelections : function(fast){
16373 if(this.locked) return;
16375 var ds = this.grid.dataSource;
16376 var s = this.selections;
16377 s.each(function(r){
16378 this.deselectRow(ds.indexOfId(r.id));
16382 this.selections.clear();
16389 * Selects all rows.
16391 selectAll : function(){
16392 if(this.locked) return;
16393 this.selections.clear();
16394 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16395 this.selectRow(i, true);
16400 * Returns True if there is a selection.
16401 * @return {Boolean}
16403 hasSelection : function(){
16404 return this.selections.length > 0;
16408 * Returns True if the specified row is selected.
16409 * @param {Number/Record} record The record or index of the record to check
16410 * @return {Boolean}
16412 isSelected : function(index){
16413 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16414 return (r && this.selections.key(r.id) ? true : false);
16418 * Returns True if the specified record id is selected.
16419 * @param {String} id The id of record to check
16420 * @return {Boolean}
16422 isIdSelected : function(id){
16423 return (this.selections.key(id) ? true : false);
16427 handleMouseDown : function(e, t){
16428 var view = this.grid.getView(), rowIndex;
16429 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16432 if(e.shiftKey && this.last !== false){
16433 var last = this.last;
16434 this.selectRange(last, rowIndex, e.ctrlKey);
16435 this.last = last; // reset the last
16436 view.focusRow(rowIndex);
16438 var isSelected = this.isSelected(rowIndex);
16439 if(e.button !== 0 && isSelected){
16440 view.focusRow(rowIndex);
16441 }else if(e.ctrlKey && isSelected){
16442 this.deselectRow(rowIndex);
16443 }else if(!isSelected){
16444 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16445 view.focusRow(rowIndex);
16448 this.fireEvent("afterselectionchange", this);
16451 handleDragableRowClick : function(grid, rowIndex, e)
16453 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16454 this.selectRow(rowIndex, false);
16455 grid.view.focusRow(rowIndex);
16456 this.fireEvent("afterselectionchange", this);
16461 * Selects multiple rows.
16462 * @param {Array} rows Array of the indexes of the row to select
16463 * @param {Boolean} keepExisting (optional) True to keep existing selections
16465 selectRows : function(rows, keepExisting){
16467 this.clearSelections();
16469 for(var i = 0, len = rows.length; i < len; i++){
16470 this.selectRow(rows[i], true);
16475 * Selects a range of rows. All rows in between startRow and endRow are also selected.
16476 * @param {Number} startRow The index of the first row in the range
16477 * @param {Number} endRow The index of the last row in the range
16478 * @param {Boolean} keepExisting (optional) True to retain existing selections
16480 selectRange : function(startRow, endRow, keepExisting){
16481 if(this.locked) return;
16483 this.clearSelections();
16485 if(startRow <= endRow){
16486 for(var i = startRow; i <= endRow; i++){
16487 this.selectRow(i, true);
16490 for(var i = startRow; i >= endRow; i--){
16491 this.selectRow(i, true);
16497 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16498 * @param {Number} startRow The index of the first row in the range
16499 * @param {Number} endRow The index of the last row in the range
16501 deselectRange : function(startRow, endRow, preventViewNotify){
16502 if(this.locked) return;
16503 for(var i = startRow; i <= endRow; i++){
16504 this.deselectRow(i, preventViewNotify);
16510 * @param {Number} row The index of the row to select
16511 * @param {Boolean} keepExisting (optional) True to keep existing selections
16513 selectRow : function(index, keepExisting, preventViewNotify){
16514 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16515 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16516 if(!keepExisting || this.singleSelect){
16517 this.clearSelections();
16519 var r = this.grid.dataSource.getAt(index);
16520 this.selections.add(r);
16521 this.last = this.lastActive = index;
16522 if(!preventViewNotify){
16523 this.grid.getView().onRowSelect(index);
16525 this.fireEvent("rowselect", this, index, r);
16526 this.fireEvent("selectionchange", this);
16532 * @param {Number} row The index of the row to deselect
16534 deselectRow : function(index, preventViewNotify){
16535 if(this.locked) return;
16536 if(this.last == index){
16539 if(this.lastActive == index){
16540 this.lastActive = false;
16542 var r = this.grid.dataSource.getAt(index);
16543 this.selections.remove(r);
16544 if(!preventViewNotify){
16545 this.grid.getView().onRowDeselect(index);
16547 this.fireEvent("rowdeselect", this, index);
16548 this.fireEvent("selectionchange", this);
16552 restoreLast : function(){
16554 this.last = this._last;
16559 acceptsNav : function(row, col, cm){
16560 return !cm.isHidden(col) && cm.isCellEditable(col, row);
16564 onEditorKey : function(field, e){
16565 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16570 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16572 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16574 }else if(k == e.ENTER && !e.ctrlKey){
16578 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16580 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16582 }else if(k == e.ESC){
16586 g.startEditing(newCell[0], newCell[1]);
16597 * @class Roo.bootstrap.MessageBar
16598 * @extends Roo.bootstrap.Component
16599 * Bootstrap MessageBar class
16600 * @cfg {String} html contents of the MessageBar
16601 * @cfg {String} weight (info | success | warning | danger) default info
16602 * @cfg {String} beforeClass insert the bar before the given class
16603 * @cfg {Boolean} closable (true | false) default false
16604 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16607 * Create a new Element
16608 * @param {Object} config The config object
16611 Roo.bootstrap.MessageBar = function(config){
16612 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16615 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
16621 beforeClass: 'bootstrap-sticky-wrap',
16623 getAutoCreate : function(){
16627 cls: 'alert alert-dismissable alert-' + this.weight,
16632 html: this.html || ''
16638 cfg.cls += ' alert-messages-fixed';
16652 onRender : function(ct, position)
16654 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16657 var cfg = Roo.apply({}, this.getAutoCreate());
16661 cfg.cls += ' ' + this.cls;
16664 cfg.style = this.style;
16666 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16668 this.el.setVisibilityMode(Roo.Element.DISPLAY);
16671 this.el.select('>button.close').on('click', this.hide, this);
16677 if (!this.rendered) {
16683 this.fireEvent('show', this);
16689 if (!this.rendered) {
16695 this.fireEvent('hide', this);
16698 update : function()
16700 // var e = this.el.dom.firstChild;
16702 // if(this.closable){
16703 // e = e.nextSibling;
16706 // e.data = this.html || '';
16708 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';