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);
298 if (this.cls && this.cls.length) {
299 Roo.get(document.body).addClass(this.cls);
303 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
308 onRender : function(ct, position)
310 /* Roo.log("Roo.bootstrap.Body - onRender");
311 if (this.cls && this.cls.length) {
312 Roo.get(document.body).addClass(this.cls);
332 * @class Roo.bootstrap.ButtonGroup
333 * @extends Roo.bootstrap.Component
334 * Bootstrap ButtonGroup class
335 * @cfg {String} size lg | sm | xs (default empty normal)
336 * @cfg {String} align vertical | justified (default none)
337 * @cfg {String} direction up | down (default down)
338 * @cfg {Boolean} toolbar false | true
339 * @cfg {Boolean} btn true | false
344 * @param {Object} config The config object
347 Roo.bootstrap.ButtonGroup = function(config){
348 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
351 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
359 getAutoCreate : function(){
365 cfg.html = this.html || cfg.html;
376 if (['vertical','justified'].indexOf(this.align)!==-1) {
377 cfg.cls = 'btn-group-' + this.align;
379 if (this.align == 'justified') {
380 console.log(this.items);
384 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
385 cfg.cls += ' btn-group-' + this.size;
388 if (this.direction == 'up') {
389 cfg.cls += ' dropup' ;
405 * @class Roo.bootstrap.Button
406 * @extends Roo.bootstrap.Component
407 * Bootstrap Button class
408 * @cfg {String} html The button content
409 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
410 * @cfg {String} size empty | lg | sm | xs
411 * @cfg {String} tag empty | a | input | submit
412 * @cfg {String} href empty or href
413 * @cfg {Boolean} disabled false | true
414 * @cfg {Boolean} isClose false | true
415 * @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
416 * @cfg {String} badge text for badge
417 * @cfg {String} theme default (or empty) | glow
418 * @cfg {Boolean} inverse false | true
419 * @cfg {Boolean} toggle false | true
420 * @cfg {String} ontext text for on toggle state
421 * @cfg {String} offtext text for off toggle state
422 * @cfg {Boolean} defaulton true | false
423 * @cfg {Boolean} preventDefault (true | false) default true
424 * @cfg {Boolean} removeClass true | false remove the standard class..
425 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
428 * Create a new button
429 * @param {Object} config The config object
433 Roo.bootstrap.Button = function(config){
434 Roo.bootstrap.Button.superclass.constructor.call(this, config);
439 * When a butotn is pressed
440 * @param {Roo.EventObject} e
445 * After the button has been toggles
446 * @param {Roo.EventObject} e
447 * @param {boolean} pressed (also available as button.pressed)
453 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
471 preventDefault: true,
480 getAutoCreate : function(){
488 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
489 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
494 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496 if (this.toggle == true) {
499 cls: 'slider-frame roo-button',
504 'data-off-text':'OFF',
505 cls: 'slider-button',
511 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
512 cfg.cls += ' '+this.weight;
521 cfg["aria-hidden"] = true;
523 cfg.html = "×";
529 if (this.theme==='default') {
530 cfg.cls = 'btn roo-button';
532 //if (this.parentType != 'Navbar') {
533 this.weight = this.weight.length ? this.weight : 'default';
535 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537 cfg.cls += ' btn-' + this.weight;
539 } else if (this.theme==='glow') {
542 cfg.cls = 'btn-glow roo-button';
544 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546 cfg.cls += ' ' + this.weight;
552 this.cls += ' inverse';
557 cfg.cls += ' active';
561 cfg.disabled = 'disabled';
565 Roo.log('changing to ul' );
567 this.glyphicon = 'caret';
570 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572 //gsRoo.log(this.parentType);
573 if (this.parentType === 'Navbar' && !this.parent().bar) {
574 Roo.log('changing to li?');
583 href : this.href || '#'
586 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
587 cfg.cls += ' dropdown';
594 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
596 if (this.glyphicon) {
597 cfg.html = ' ' + cfg.html;
602 cls: 'glyphicon glyphicon-' + this.glyphicon
612 // cfg.cls='btn roo-button';
616 var value = cfg.html;
621 cls: 'glyphicon glyphicon-' + this.glyphicon,
640 cfg.cls += ' dropdown';
641 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
644 if (cfg.tag !== 'a' && this.href !== '') {
645 throw "Tag must be a to set href.";
646 } else if (this.href.length > 0) {
647 cfg.href = this.href;
650 if(this.removeClass){
655 cfg.target = this.target;
660 initEvents: function() {
661 // Roo.log('init events?');
662 // Roo.log(this.el.dom);
663 if (this.el.hasClass('roo-button')) {
664 this.el.on('click', this.onClick, this);
666 this.el.select('.roo-button').on('click', this.onClick, this);
669 if(this.removeClass){
670 this.el.on('click', this.onClick, this);
673 this.el.enableDisplayMode();
676 onClick : function(e)
682 Roo.log('button on click ');
683 if(this.preventDefault){
686 if (this.pressed === true || this.pressed === false) {
687 this.pressed = !this.pressed;
688 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
689 this.fireEvent('toggle', this, e, this.pressed);
693 this.fireEvent('click', this, e);
697 * Enables this button
701 this.disabled = false;
702 this.el.removeClass('disabled');
706 * Disable this button
710 this.disabled = true;
711 this.el.addClass('disabled');
714 * sets the active state on/off,
715 * @param {Boolean} state (optional) Force a particular state
717 setActive : function(v) {
719 this.el[v ? 'addClass' : 'removeClass']('active');
722 * toggles the current active state
724 toggleActive : function()
726 var active = this.el.hasClass('active');
727 this.setActive(!active);
731 setText : function(str)
733 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
756 * @class Roo.bootstrap.Column
757 * @extends Roo.bootstrap.Component
758 * Bootstrap Column class
759 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
760 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
761 * @cfg {Number} md colspan out of 12 for computer-sized screens
762 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
763 * @cfg {String} html content of column.
766 * Create a new Column
767 * @param {Object} config The config object
770 Roo.bootstrap.Column = function(config){
771 Roo.bootstrap.Column.superclass.constructor.call(this, config);
774 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
783 getAutoCreate : function(){
784 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
792 ['xs','sm','md','lg'].map(function(size){
793 if (settings[size]) {
794 cfg.cls += ' col-' + size + '-' + settings[size];
797 if (this.html.length) {
798 cfg.html = this.html;
817 * @class Roo.bootstrap.Container
818 * @extends Roo.bootstrap.Component
819 * Bootstrap Container class
820 * @cfg {Boolean} jumbotron is it a jumbotron element
821 * @cfg {String} html content of element
822 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
823 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
824 * @cfg {String} header content of header (for panel)
825 * @cfg {String} footer content of footer (for panel)
826 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
827 * @cfg {String} tag (header|aside|section) type of HTML tag.
831 * Create a new Container
832 * @param {Object} config The config object
835 Roo.bootstrap.Container = function(config){
836 Roo.bootstrap.Container.superclass.constructor.call(this, config);
839 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
850 getChildContainer : function() {
856 if (this.panel.length) {
857 return this.el.select('.panel-body',true).first();
864 getAutoCreate : function(){
867 tag : this.tag || 'div',
871 if (this.jumbotron) {
872 cfg.cls = 'jumbotron';
874 // - this is applied by the parent..
876 // cfg.cls = this.cls + '';
879 if (this.sticky.length) {
881 var bd = Roo.get(document.body);
882 if (!bd.hasClass('bootstrap-sticky')) {
883 bd.addClass('bootstrap-sticky');
884 Roo.select('html',true).setStyle('height', '100%');
887 cfg.cls += 'bootstrap-sticky-' + this.sticky;
891 if (this.well.length) {
895 cfg.cls +=' well well-' +this.well;
905 if (this.panel.length) {
906 cfg.cls += ' panel panel-' + this.panel;
908 if (this.header.length) {
911 cls : 'panel-heading',
927 if (this.footer.length) {
929 cls : 'panel-footer',
937 body.html = this.html || cfg.html;
939 if (!cfg.cls.length) {
940 cfg.cls = 'container';
957 * @class Roo.bootstrap.Img
958 * @extends Roo.bootstrap.Component
959 * Bootstrap Img class
960 * @cfg {Boolean} imgResponsive false | true
961 * @cfg {String} border rounded | circle | thumbnail
962 * @cfg {String} src image source
963 * @cfg {String} alt image alternative text
964 * @cfg {String} href a tag href
965 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
969 * @param {Object} config The config object
972 Roo.bootstrap.Img = function(config){
973 Roo.bootstrap.Img.superclass.constructor.call(this, config);
979 * The img click event for the img.
980 * @param {Roo.EventObject} e
986 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
994 getAutoCreate : function(){
998 cls: (this.imgResponsive) ? 'img-responsive' : '',
1002 cfg.html = this.html || cfg.html;
1004 cfg.src = this.src || cfg.src;
1006 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1007 cfg.cls += ' img-' + this.border;
1024 a.target = this.target;
1030 return (this.href) ? a : cfg;
1033 initEvents: function() {
1036 this.el.on('click', this.onClick, this);
1040 onClick : function(e)
1042 Roo.log('img onclick');
1043 this.fireEvent('click', this, e);
1056 * @class Roo.bootstrap.Header
1057 * @extends Roo.bootstrap.Component
1058 * Bootstrap Header class
1059 * @cfg {String} html content of header
1060 * @cfg {Number} level (1|2|3|4|5|6) default 1
1063 * Create a new Header
1064 * @param {Object} config The config object
1068 Roo.bootstrap.Header = function(config){
1069 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1072 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1080 getAutoCreate : function(){
1083 tag: 'h' + (1 *this.level),
1084 html: this.html || 'fill in html'
1096 * Ext JS Library 1.1.1
1097 * Copyright(c) 2006-2007, Ext JS, LLC.
1099 * Originally Released Under LGPL - original licence link has changed is not relivant.
1102 * <script type="text/javascript">
1106 * @class Roo.bootstrap.MenuMgr
1107 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1110 Roo.bootstrap.MenuMgr = function(){
1111 var menus, active, groups = {}, attached = false, lastShow = new Date();
1113 // private - called when first menu is created
1116 active = new Roo.util.MixedCollection();
1117 Roo.get(document).addKeyListener(27, function(){
1118 if(active.length > 0){
1126 if(active && active.length > 0){
1127 var c = active.clone();
1137 if(active.length < 1){
1138 Roo.get(document).un("mouseup", onMouseDown);
1146 var last = active.last();
1147 lastShow = new Date();
1150 Roo.get(document).on("mouseup", onMouseDown);
1155 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1156 m.parentMenu.activeChild = m;
1157 }else if(last && last.isVisible()){
1158 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1163 function onBeforeHide(m){
1165 m.activeChild.hide();
1167 if(m.autoHideTimer){
1168 clearTimeout(m.autoHideTimer);
1169 delete m.autoHideTimer;
1174 function onBeforeShow(m){
1175 var pm = m.parentMenu;
1176 if(!pm && !m.allowOtherMenus){
1178 }else if(pm && pm.activeChild && active != m){
1179 pm.activeChild.hide();
1184 function onMouseDown(e){
1185 Roo.log("on MouseDown");
1186 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1194 function onBeforeCheck(mi, state){
1196 var g = groups[mi.group];
1197 for(var i = 0, l = g.length; i < l; i++){
1199 g[i].setChecked(false);
1208 * Hides all menus that are currently visible
1210 hideAll : function(){
1215 register : function(menu){
1219 menus[menu.id] = menu;
1220 menu.on("beforehide", onBeforeHide);
1221 menu.on("hide", onHide);
1222 menu.on("beforeshow", onBeforeShow);
1223 menu.on("show", onShow);
1225 if(g && menu.events["checkchange"]){
1229 groups[g].push(menu);
1230 menu.on("checkchange", onCheck);
1235 * Returns a {@link Roo.menu.Menu} object
1236 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1237 * be used to generate and return a new Menu instance.
1239 get : function(menu){
1240 if(typeof menu == "string"){ // menu id
1242 }else if(menu.events){ // menu instance
1245 /*else if(typeof menu.length == 'number'){ // array of menu items?
1246 return new Roo.bootstrap.Menu({items:menu});
1247 }else{ // otherwise, must be a config
1248 return new Roo.bootstrap.Menu(menu);
1255 unregister : function(menu){
1256 delete menus[menu.id];
1257 menu.un("beforehide", onBeforeHide);
1258 menu.un("hide", onHide);
1259 menu.un("beforeshow", onBeforeShow);
1260 menu.un("show", onShow);
1262 if(g && menu.events["checkchange"]){
1263 groups[g].remove(menu);
1264 menu.un("checkchange", onCheck);
1269 registerCheckable : function(menuItem){
1270 var g = menuItem.group;
1275 groups[g].push(menuItem);
1276 menuItem.on("beforecheckchange", onBeforeCheck);
1281 unregisterCheckable : function(menuItem){
1282 var g = menuItem.group;
1284 groups[g].remove(menuItem);
1285 menuItem.un("beforecheckchange", onBeforeCheck);
1297 * @class Roo.bootstrap.Menu
1298 * @extends Roo.bootstrap.Component
1299 * Bootstrap Menu class - container for MenuItems
1300 * @cfg {String} type type of menu
1304 * @param {Object} config The config object
1308 Roo.bootstrap.Menu = function(config){
1309 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1310 if (this.registerMenu) {
1311 Roo.bootstrap.MenuMgr.register(this);
1316 * Fires before this menu is displayed
1317 * @param {Roo.menu.Menu} this
1322 * Fires before this menu is hidden
1323 * @param {Roo.menu.Menu} this
1328 * Fires after this menu is displayed
1329 * @param {Roo.menu.Menu} this
1334 * Fires after this menu is hidden
1335 * @param {Roo.menu.Menu} this
1340 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1341 * @param {Roo.menu.Menu} this
1342 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1343 * @param {Roo.EventObject} e
1348 * Fires when the mouse is hovering over this menu
1349 * @param {Roo.menu.Menu} this
1350 * @param {Roo.EventObject} e
1351 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1356 * Fires when the mouse exits this menu
1357 * @param {Roo.menu.Menu} this
1358 * @param {Roo.EventObject} e
1359 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1364 * Fires when a menu item contained in this menu is clicked
1365 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1366 * @param {Roo.EventObject} e
1370 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1373 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1377 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1380 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1382 registerMenu : true,
1384 menuItems :false, // stores the menu items..
1390 getChildContainer : function() {
1394 getAutoCreate : function(){
1396 //if (['right'].indexOf(this.align)!==-1) {
1397 // cfg.cn[1].cls += ' pull-right'
1401 cls : 'dropdown-menu' ,
1402 style : 'z-index:1000'
1406 if (this.type === 'submenu') {
1407 cfg.cls = 'submenu active'
1412 initEvents : function() {
1414 // Roo.log("ADD event");
1415 // Roo.log(this.triggerEl.dom);
1416 this.triggerEl.on('click', this.onTriggerPress, this);
1417 this.triggerEl.addClass('dropdown-toggle');
1418 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1420 this.el.on("mouseover", this.onMouseOver, this);
1421 this.el.on("mouseout", this.onMouseOut, this);
1425 findTargetItem : function(e){
1426 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1430 //Roo.log(t); Roo.log(t.id);
1432 //Roo.log(this.menuitems);
1433 return this.menuitems.get(t.id);
1435 //return this.items.get(t.menuItemId);
1440 onClick : function(e){
1441 Roo.log("menu.onClick");
1442 var t = this.findTargetItem(e);
1448 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1449 if(t == this.activeItem && t.shouldDeactivate(e)){
1450 this.activeItem.deactivate();
1451 delete this.activeItem;
1455 this.setActiveItem(t, true);
1462 Roo.log('pass click event');
1466 this.fireEvent("click", this, t, e);
1470 onMouseOver : function(e){
1471 var t = this.findTargetItem(e);
1474 // if(t.canActivate && !t.disabled){
1475 // this.setActiveItem(t, true);
1479 this.fireEvent("mouseover", this, e, t);
1481 isVisible : function(){
1482 return !this.hidden;
1484 onMouseOut : function(e){
1485 var t = this.findTargetItem(e);
1488 // if(t == this.activeItem && t.shouldDeactivate(e)){
1489 // this.activeItem.deactivate();
1490 // delete this.activeItem;
1493 this.fireEvent("mouseout", this, e, t);
1498 * Displays this menu relative to another element
1499 * @param {String/HTMLElement/Roo.Element} element The element to align to
1500 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1501 * the element (defaults to this.defaultAlign)
1502 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1504 show : function(el, pos, parentMenu){
1505 this.parentMenu = parentMenu;
1509 this.fireEvent("beforeshow", this);
1510 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1513 * Displays this menu at a specific xy position
1514 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1515 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1517 showAt : function(xy, parentMenu, /* private: */_e){
1518 this.parentMenu = parentMenu;
1523 this.fireEvent("beforeshow", this);
1525 //xy = this.el.adjustForConstraints(xy);
1527 //this.el.setXY(xy);
1529 this.hideMenuItems();
1530 this.hidden = false;
1531 this.triggerEl.addClass('open');
1533 this.fireEvent("show", this);
1539 this.doFocus.defer(50, this);
1543 doFocus : function(){
1545 this.focusEl.focus();
1550 * Hides this menu and optionally all parent menus
1551 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1553 hide : function(deep){
1555 this.hideMenuItems();
1556 if(this.el && this.isVisible()){
1557 this.fireEvent("beforehide", this);
1558 if(this.activeItem){
1559 this.activeItem.deactivate();
1560 this.activeItem = null;
1562 this.triggerEl.removeClass('open');;
1564 this.fireEvent("hide", this);
1566 if(deep === true && this.parentMenu){
1567 this.parentMenu.hide(true);
1571 onTriggerPress : function(e)
1574 Roo.log('trigger press');
1575 //Roo.log(e.getTarget());
1576 // Roo.log(this.triggerEl.dom);
1577 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1580 if (this.isVisible()) {
1584 this.show(this.triggerEl, false, false);
1593 hideMenuItems : function()
1595 //$(backdrop).remove()
1596 Roo.select('.open',true).each(function(aa) {
1598 aa.removeClass('open');
1599 //var parent = getParent($(this))
1600 //var relatedTarget = { relatedTarget: this }
1602 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1603 //if (e.isDefaultPrevented()) return
1604 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1607 addxtypeChild : function (tree, cntr) {
1608 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1610 this.menuitems.add(comp);
1631 * @class Roo.bootstrap.MenuItem
1632 * @extends Roo.bootstrap.Component
1633 * Bootstrap MenuItem class
1634 * @cfg {String} html the menu label
1635 * @cfg {String} href the link
1636 * @cfg {Boolean} preventDefault (true | false) default true
1640 * Create a new MenuItem
1641 * @param {Object} config The config object
1645 Roo.bootstrap.MenuItem = function(config){
1646 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1651 * The raw click event for the entire grid.
1652 * @param {Roo.EventObject} e
1658 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1662 preventDefault: true,
1664 getAutoCreate : function(){
1667 cls: 'dropdown-menu-item',
1677 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1678 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1682 initEvents: function() {
1684 //this.el.select('a').on('click', this.onClick, this);
1687 onClick : function(e)
1689 Roo.log('item on click ');
1690 //if(this.preventDefault){
1691 // e.preventDefault();
1693 //this.parent().hideMenuItems();
1695 this.fireEvent('click', this, e);
1714 * @class Roo.bootstrap.MenuSeparator
1715 * @extends Roo.bootstrap.Component
1716 * Bootstrap MenuSeparator class
1719 * Create a new MenuItem
1720 * @param {Object} config The config object
1724 Roo.bootstrap.MenuSeparator = function(config){
1725 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1728 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1730 getAutoCreate : function(){
1745 <div class="modal fade">
1746 <div class="modal-dialog">
1747 <div class="modal-content">
1748 <div class="modal-header">
1749 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1750 <h4 class="modal-title">Modal title</h4>
1752 <div class="modal-body">
1753 <p>One fine body…</p>
1755 <div class="modal-footer">
1756 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1757 <button type="button" class="btn btn-primary">Save changes</button>
1759 </div><!-- /.modal-content -->
1760 </div><!-- /.modal-dialog -->
1761 </div><!-- /.modal -->
1771 * @class Roo.bootstrap.Modal
1772 * @extends Roo.bootstrap.Component
1773 * Bootstrap Modal class
1774 * @cfg {String} title Title of dialog
1775 * @cfg {Boolean} specificTitle (true|false) default false
1776 * @cfg {Array} buttons Array of buttons or standard button set..
1777 * @cfg {String} buttonPosition (left|right|center) default right
1780 * Create a new Modal Dialog
1781 * @param {Object} config The config object
1784 Roo.bootstrap.Modal = function(config){
1785 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1790 * The raw btnclick event for the button
1791 * @param {Roo.EventObject} e
1795 this.buttons = this.buttons || [];
1798 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1800 title : 'test dialog',
1807 specificTitle: false,
1809 buttonPosition: 'right',
1811 onRender : function(ct, position)
1813 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1816 var cfg = Roo.apply({}, this.getAutoCreate());
1819 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1821 //if (!cfg.name.length) {
1825 cfg.cls += ' ' + this.cls;
1828 cfg.style = this.style;
1830 this.el = Roo.get(document.body).createChild(cfg, position);
1832 //var type = this.el.dom.type;
1834 if(this.tabIndex !== undefined){
1835 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1840 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1841 this.maskEl.enableDisplayMode("block");
1843 //this.el.addClass("x-dlg-modal");
1845 if (this.buttons.length) {
1846 Roo.each(this.buttons, function(bb) {
1847 b = Roo.apply({}, bb);
1848 b.xns = b.xns || Roo.bootstrap;
1849 b.xtype = b.xtype || 'Button';
1850 if (typeof(b.listeners) == 'undefined') {
1851 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1854 var btn = Roo.factory(b);
1856 btn.onRender(this.el.select('.modal-footer div').first());
1860 // render the children.
1863 if(typeof(this.items) != 'undefined'){
1864 var items = this.items;
1867 for(var i =0;i < items.length;i++) {
1868 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1872 this.items = nitems;
1874 this.body = this.el.select('.modal-body',true).first();
1875 this.close = this.el.select('.modal-header .close', true).first();
1876 this.footer = this.el.select('.modal-footer',true).first();
1878 //this.el.addClass([this.fieldClass, this.cls]);
1881 getAutoCreate : function(){
1886 html : this.html || ''
1891 cls : 'modal-title',
1895 if(this.specificTitle){
1901 style : 'display: none',
1904 cls: "modal-dialog",
1907 cls : "modal-content",
1910 cls : 'modal-header',
1922 cls : 'modal-footer',
1926 cls: 'btn-' + this.buttonPosition
1945 getChildContainer : function() {
1947 return this.el.select('.modal-body',true).first();
1950 getButtonContainer : function() {
1951 return this.el.select('.modal-footer div',true).first();
1954 initEvents : function()
1956 this.el.select('.modal-header .close').on('click', this.hide, this);
1958 // this.addxtype(this);
1962 if (!this.rendered) {
1966 this.el.addClass('on');
1967 this.el.removeClass('fade');
1968 this.el.setStyle('display', 'block');
1969 Roo.get(document.body).addClass("x-body-masked");
1970 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1972 this.el.setStyle('zIndex', '10001');
1973 this.fireEvent('show', this);
1979 Roo.log('Modal hide?!');
1981 Roo.get(document.body).removeClass("x-body-masked");
1982 this.el.removeClass('on');
1983 this.el.addClass('fade');
1984 this.el.setStyle('display', 'none');
1985 this.fireEvent('hide', this);
1988 addButton : function(str, cb)
1992 var b = Roo.apply({}, { html : str } );
1993 b.xns = b.xns || Roo.bootstrap;
1994 b.xtype = b.xtype || 'Button';
1995 if (typeof(b.listeners) == 'undefined') {
1996 b.listeners = { click : cb.createDelegate(this) };
1999 var btn = Roo.factory(b);
2001 btn.onRender(this.el.select('.modal-footer div').first());
2007 setDefaultButton : function(btn)
2009 //this.el.select('.modal-footer').()
2011 resizeTo: function(w,h)
2015 setContentSize : function(w, h)
2019 onButtonClick: function(btn,e)
2022 this.fireEvent('btnclick', btn.name, e);
2024 setTitle: function(str) {
2025 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2031 Roo.apply(Roo.bootstrap.Modal, {
2033 * Button config that displays a single OK button
2042 * Button config that displays Yes and No buttons
2058 * Button config that displays OK and Cancel buttons
2073 * Button config that displays Yes, No and Cancel buttons
2095 * messagebox - can be used as a replace
2099 * @class Roo.MessageBox
2100 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2104 Roo.Msg.alert('Status', 'Changes saved successfully.');
2106 // Prompt for user data:
2107 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2109 // process text value...
2113 // Show a dialog using config options:
2115 title:'Save Changes?',
2116 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2117 buttons: Roo.Msg.YESNOCANCEL,
2124 Roo.bootstrap.MessageBox = function(){
2125 var dlg, opt, mask, waitTimer;
2126 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2127 var buttons, activeTextEl, bwidth;
2131 var handleButton = function(button){
2133 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2137 var handleHide = function(){
2139 dlg.el.removeClass(opt.cls);
2142 // Roo.TaskMgr.stop(waitTimer);
2143 // waitTimer = null;
2148 var updateButtons = function(b){
2151 buttons["ok"].hide();
2152 buttons["cancel"].hide();
2153 buttons["yes"].hide();
2154 buttons["no"].hide();
2155 //dlg.footer.dom.style.display = 'none';
2158 dlg.footer.dom.style.display = '';
2159 for(var k in buttons){
2160 if(typeof buttons[k] != "function"){
2163 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2164 width += buttons[k].el.getWidth()+15;
2174 var handleEsc = function(d, k, e){
2175 if(opt && opt.closable !== false){
2185 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2186 * @return {Roo.BasicDialog} The BasicDialog element
2188 getDialog : function(){
2190 dlg = new Roo.bootstrap.Modal( {
2193 //constraintoviewport:false,
2195 //collapsible : false,
2200 //buttonAlign:"center",
2201 closeClick : function(){
2202 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2205 handleButton("cancel");
2210 dlg.on("hide", handleHide);
2212 //dlg.addKeyListener(27, handleEsc);
2214 this.buttons = buttons;
2215 var bt = this.buttonText;
2216 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2217 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2218 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2219 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2221 bodyEl = dlg.body.createChild({
2223 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2224 '<textarea class="roo-mb-textarea"></textarea>' +
2225 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2227 msgEl = bodyEl.dom.firstChild;
2228 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2229 textboxEl.enableDisplayMode();
2230 textboxEl.addKeyListener([10,13], function(){
2231 if(dlg.isVisible() && opt && opt.buttons){
2234 }else if(opt.buttons.yes){
2235 handleButton("yes");
2239 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2240 textareaEl.enableDisplayMode();
2241 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2242 progressEl.enableDisplayMode();
2243 var pf = progressEl.dom.firstChild;
2245 pp = Roo.get(pf.firstChild);
2246 pp.setHeight(pf.offsetHeight);
2254 * Updates the message box body text
2255 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2256 * the XHTML-compliant non-breaking space character '&#160;')
2257 * @return {Roo.MessageBox} This message box
2259 updateText : function(text){
2260 if(!dlg.isVisible() && !opt.width){
2261 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2263 msgEl.innerHTML = text || ' ';
2265 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2266 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2268 Math.min(opt.width || cw , this.maxWidth),
2269 Math.max(opt.minWidth || this.minWidth, bwidth)
2272 activeTextEl.setWidth(w);
2274 if(dlg.isVisible()){
2275 dlg.fixedcenter = false;
2277 // to big, make it scroll. = But as usual stupid IE does not support
2280 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2281 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2282 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2284 bodyEl.dom.style.height = '';
2285 bodyEl.dom.style.overflowY = '';
2288 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2290 bodyEl.dom.style.overflowX = '';
2293 dlg.setContentSize(w, bodyEl.getHeight());
2294 if(dlg.isVisible()){
2295 dlg.fixedcenter = true;
2301 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2302 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2303 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2304 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2305 * @return {Roo.MessageBox} This message box
2307 updateProgress : function(value, text){
2309 this.updateText(text);
2311 if (pp) { // weird bug on my firefox - for some reason this is not defined
2312 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2318 * Returns true if the message box is currently displayed
2319 * @return {Boolean} True if the message box is visible, else false
2321 isVisible : function(){
2322 return dlg && dlg.isVisible();
2326 * Hides the message box if it is displayed
2329 if(this.isVisible()){
2335 * Displays a new message box, or reinitializes an existing message box, based on the config options
2336 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2337 * The following config object properties are supported:
2339 Property Type Description
2340 ---------- --------------- ------------------------------------------------------------------------------------
2341 animEl String/Element An id or Element from which the message box should animate as it opens and
2342 closes (defaults to undefined)
2343 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2344 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2345 closable Boolean False to hide the top-right close button (defaults to true). Note that
2346 progress and wait dialogs will ignore this property and always hide the
2347 close button as they can only be closed programmatically.
2348 cls String A custom CSS class to apply to the message box element
2349 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2350 displayed (defaults to 75)
2351 fn Function A callback function to execute after closing the dialog. The arguments to the
2352 function will be btn (the name of the button that was clicked, if applicable,
2353 e.g. "ok"), and text (the value of the active text field, if applicable).
2354 Progress and wait dialogs will ignore this option since they do not respond to
2355 user actions and can only be closed programmatically, so any required function
2356 should be called by the same code after it closes the dialog.
2357 icon String A CSS class that provides a background image to be used as an icon for
2358 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2359 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2360 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2361 modal Boolean False to allow user interaction with the page while the message box is
2362 displayed (defaults to true)
2363 msg String A string that will replace the existing message box body text (defaults
2364 to the XHTML-compliant non-breaking space character ' ')
2365 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2366 progress Boolean True to display a progress bar (defaults to false)
2367 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2368 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2369 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2370 title String The title text
2371 value String The string value to set into the active textbox element if displayed
2372 wait Boolean True to display a progress bar (defaults to false)
2373 width Number The width of the dialog in pixels
2380 msg: 'Please enter your address:',
2382 buttons: Roo.MessageBox.OKCANCEL,
2385 animEl: 'addAddressBtn'
2388 * @param {Object} config Configuration options
2389 * @return {Roo.MessageBox} This message box
2391 show : function(options)
2394 // this causes nightmares if you show one dialog after another
2395 // especially on callbacks..
2397 if(this.isVisible()){
2400 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2401 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2402 Roo.log("New Dialog Message:" + options.msg )
2403 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2404 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2407 var d = this.getDialog();
2409 d.setTitle(opt.title || " ");
2410 d.close.setDisplayed(opt.closable !== false);
2411 activeTextEl = textboxEl;
2412 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2417 textareaEl.setHeight(typeof opt.multiline == "number" ?
2418 opt.multiline : this.defaultTextHeight);
2419 activeTextEl = textareaEl;
2428 progressEl.setDisplayed(opt.progress === true);
2429 this.updateProgress(0);
2430 activeTextEl.dom.value = opt.value || "";
2432 dlg.setDefaultButton(activeTextEl);
2434 var bs = opt.buttons;
2438 }else if(bs && bs.yes){
2439 db = buttons["yes"];
2441 dlg.setDefaultButton(db);
2443 bwidth = updateButtons(opt.buttons);
2444 this.updateText(opt.msg);
2446 d.el.addClass(opt.cls);
2448 d.proxyDrag = opt.proxyDrag === true;
2449 d.modal = opt.modal !== false;
2450 d.mask = opt.modal !== false ? mask : false;
2452 // force it to the end of the z-index stack so it gets a cursor in FF
2453 document.body.appendChild(dlg.el.dom);
2454 d.animateTarget = null;
2455 d.show(options.animEl);
2461 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2462 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2463 * and closing the message box when the process is complete.
2464 * @param {String} title The title bar text
2465 * @param {String} msg The message box body text
2466 * @return {Roo.MessageBox} This message box
2468 progress : function(title, msg){
2475 minWidth: this.minProgressWidth,
2482 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2483 * If a callback function is passed it will be called after the user clicks the button, and the
2484 * id of the button that was clicked will be passed as the only parameter to the callback
2485 * (could also be the top-right close button).
2486 * @param {String} title The title bar text
2487 * @param {String} msg The message box body text
2488 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2489 * @param {Object} scope (optional) The scope of the callback function
2490 * @return {Roo.MessageBox} This message box
2492 alert : function(title, msg, fn, scope){
2505 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2506 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2507 * You are responsible for closing the message box when the process is complete.
2508 * @param {String} msg The message box body text
2509 * @param {String} title (optional) The title bar text
2510 * @return {Roo.MessageBox} This message box
2512 wait : function(msg, title){
2523 waitTimer = Roo.TaskMgr.start({
2525 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2533 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2534 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2535 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2536 * @param {String} title The title bar text
2537 * @param {String} msg The message box body text
2538 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2539 * @param {Object} scope (optional) The scope of the callback function
2540 * @return {Roo.MessageBox} This message box
2542 confirm : function(title, msg, fn, scope){
2546 buttons: this.YESNO,
2555 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2556 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2557 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2558 * (could also be the top-right close button) and the text that was entered will be passed as the two
2559 * parameters to the callback.
2560 * @param {String} title The title bar text
2561 * @param {String} msg The message box body text
2562 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2563 * @param {Object} scope (optional) The scope of the callback function
2564 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2565 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2566 * @return {Roo.MessageBox} This message box
2568 prompt : function(title, msg, fn, scope, multiline){
2572 buttons: this.OKCANCEL,
2577 multiline: multiline,
2584 * Button config that displays a single OK button
2589 * Button config that displays Yes and No buttons
2592 YESNO : {yes:true, no:true},
2594 * Button config that displays OK and Cancel buttons
2597 OKCANCEL : {ok:true, cancel:true},
2599 * Button config that displays Yes, No and Cancel buttons
2602 YESNOCANCEL : {yes:true, no:true, cancel:true},
2605 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2608 defaultTextHeight : 75,
2610 * The maximum width in pixels of the message box (defaults to 600)
2615 * The minimum width in pixels of the message box (defaults to 100)
2620 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2621 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2624 minProgressWidth : 250,
2626 * An object containing the default button text strings that can be overriden for localized language support.
2627 * Supported properties are: ok, cancel, yes and no.
2628 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2641 * Shorthand for {@link Roo.MessageBox}
2643 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2644 Roo.Msg = Roo.Msg || Roo.MessageBox;
2653 * @class Roo.bootstrap.Navbar
2654 * @extends Roo.bootstrap.Component
2655 * Bootstrap Navbar class
2656 * @cfg {Boolean} sidebar has side bar
2657 * @cfg {Boolean} bar is a bar?
2658 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2659 * @cfg {String} brand what is brand
2660 * @cfg {Boolean} inverse is inverted color
2661 * @cfg {String} type (nav | pills | tabs)
2662 * @cfg {Boolean} arrangement stacked | justified
2663 * @cfg {String} align (left | right) alignment
2664 * @cfg {String} brand_href href of the brand
2665 * @cfg {Boolean} main (true|false) main nav bar? default false
2666 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2670 * Create a new Navbar
2671 * @param {Object} config The config object
2675 Roo.bootstrap.Navbar = function(config){
2676 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2681 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2700 getAutoCreate : function(){
2705 if (this.sidebar === true) {
2713 if (this.bar === true) {
2721 cls: 'navbar-header',
2726 cls: 'navbar-toggle',
2727 'data-toggle': 'collapse',
2732 html: 'Toggle navigation'
2752 cls: 'collapse navbar-collapse'
2757 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2759 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2760 cfg.cls += ' navbar-' + this.position;
2761 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2764 if (this.brand !== '') {
2767 href: this.brand_href ? this.brand_href : '#',
2768 cls: 'navbar-brand',
2776 cfg.cls += ' main-nav';
2782 } else if (this.bar === false) {
2785 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2795 if (['tabs','pills'].indexOf(this.type)!==-1) {
2796 cfg.cn[0].cls += ' nav-' + this.type
2798 if (this.type!=='nav') {
2799 Roo.log('nav type must be nav/tabs/pills')
2801 cfg.cn[0].cls += ' navbar-nav'
2804 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2805 cfg.cn[0].cls += ' nav-' + this.arrangement;
2808 if (this.align === 'right') {
2809 cfg.cn[0].cls += ' navbar-right';
2812 cfg.cls += ' navbar-inverse';
2820 initEvents :function ()
2822 //Roo.log(this.el.select('.navbar-toggle',true));
2823 this.el.select('.navbar-toggle',true).on('click', function() {
2824 // Roo.log('click');
2825 this.el.select('.navbar-collapse',true).toggleClass('in');
2833 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2835 var size = this.el.getSize();
2836 this.maskEl.setSize(size.width, size.height);
2837 this.maskEl.enableDisplayMode("block");
2846 getChildContainer : function()
2848 if (this.bar === true) {
2849 return this.el.select('.collapse',true).first();
2881 * @class Roo.bootstrap.NavGroup
2882 * @extends Roo.bootstrap.Component
2883 * Bootstrap NavGroup class
2884 * @cfg {String} align left | right
2885 * @cfg {Boolean} inverse false | true
2886 * @cfg {String} type (nav|pills|tab) default nav
2887 * @cfg {String} navId - reference Id for navbar.
2891 * Create a new nav group
2892 * @param {Object} config The config object
2895 Roo.bootstrap.NavGroup = function(config){
2896 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2898 Roo.bootstrap.NavGroup.register(this);
2902 * Fires when the active item changes
2903 * @param {Roo.bootstrap.NavGroup} this
2904 * @param {Roo.bootstrap.Navbar.Item} item The item selected
2905 * @param {Roo.bootstrap.Navbar.Item} item The previously selected item
2912 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2923 getAutoCreate : function()
2925 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2932 if (['tabs','pills'].indexOf(this.type)!==-1) {
2933 cfg.cls += ' nav-' + this.type
2935 if (this.type!=='nav') {
2936 Roo.log('nav type must be nav/tabs/pills')
2938 cfg.cls += ' navbar-nav'
2941 if (this.parent().sidebar === true) {
2944 cls: 'dashboard-menu'
2950 if (this.form === true) {
2956 if (this.align === 'right') {
2957 cfg.cls += ' navbar-right';
2959 cfg.cls += ' navbar-left';
2963 if (this.align === 'right') {
2964 cfg.cls += ' navbar-right';
2968 cfg.cls += ' navbar-inverse';
2976 setActiveItem : function(item)
2979 Roo.each(this.navItems, function(v){
2984 v.setActive(false, true);
2991 item.setActive(true, true);
2992 this.fireEvent('changed', this, item, prev);
2998 register : function(item)
3000 this.navItems.push( item);
3001 item.navId = this.navId;
3004 getNavItem: function(tabId)
3007 Roo.each(this.navItems, function(e) {
3008 if (e.tabId == tabId) {
3020 Roo.apply(Roo.bootstrap.NavGroup, {
3024 register : function(navgrp)
3026 this.groups[navgrp.navId] = navgrp;
3029 get: function(navId) {
3030 return this.groups[navId];
3045 * @class Roo.bootstrap.Navbar.Item
3046 * @extends Roo.bootstrap.Component
3047 * Bootstrap Navbar.Button class
3048 * @cfg {String} href link to
3049 * @cfg {String} html content of button
3050 * @cfg {String} badge text inside badge
3051 * @cfg {String} glyphicon name of glyphicon
3052 * @cfg {String} icon name of font awesome icon
3053 * @cfg {Boolean} active Is item active
3054 * @cfg {Boolean} preventDefault (true | false) default false
3055 * @cfg {String} tabId the tab that this item activates.
3058 * Create a new Navbar Button
3059 * @param {Object} config The config object
3061 Roo.bootstrap.Navbar.Item = function(config){
3062 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3067 * The raw click event for the entire grid.
3068 * @param {Roo.EventObject} e
3073 * Fires when the active item active state changes
3074 * @param {Roo.bootstrap.Navbar.Item} this
3075 * @param {boolean} state the new state
3083 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
3091 preventDefault : false,
3094 getAutoCreate : function(){
3096 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3098 if (this.parent().parent().sidebar === true) {
3111 cfg.cn[0].html = this.html;
3115 this.cls += ' active';
3119 cfg.cn[0].cls += ' dropdown-toggle';
3120 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3124 cfg.cn[0].tag = 'a',
3125 cfg.cn[0].href = this.href;
3128 if (this.glyphicon) {
3129 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3133 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3145 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3155 if (this.glyphicon) {
3156 if(cfg.html){cfg.html = ' ' + this.html};
3160 cls: 'glyphicon glyphicon-' + this.glyphicon
3165 cfg.cn[0].html = this.html || cfg.cn[0].html ;
3170 cfg.cn[0].html += " <span class='caret'></span>";
3171 //}else if (!this.href) {
3172 // cfg.cn[0].tag='p';
3173 // cfg.cn[0].cls='navbar-text';
3176 cfg.cn[0].href=this.href||'#';
3177 cfg.cn[0].html=this.html;
3180 if (this.badge !== '') {
3183 cfg.cn[0].html + ' ',
3194 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3199 initEvents: function() {
3200 // Roo.log('init events?');
3201 // Roo.log(this.el.dom);
3202 this.el.select('a',true).on('click', this.onClick, this);
3203 // at this point parent should be available..
3204 this.parent().register(this);
3207 onClick : function(e)
3209 if(this.preventDefault){
3213 if(this.fireEvent('click', this, e) === false){
3217 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3218 if (typeof(this.parent().setActiveItem) !== 'undefined') {
3219 this.parent().setActiveItem(this);
3227 isActive: function () {
3230 setActive : function(state, fire)
3232 this.active = state;
3234 this.el.removeClass('active');
3235 } else if (!this.el.hasClass('active')) {
3236 this.el.addClass('active');
3239 this.fireEvent('changed', this, state);
3244 // this should not be here...
3257 * @class Roo.bootstrap.Row
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Row class (contains columns...)
3263 * @param {Object} config The config object
3266 Roo.bootstrap.Row = function(config){
3267 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3270 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3272 getAutoCreate : function(){
3291 * @class Roo.bootstrap.Element
3292 * @extends Roo.bootstrap.Component
3293 * Bootstrap Element class
3294 * @cfg {String} html contents of the element
3295 * @cfg {String} tag tag of the element
3296 * @cfg {String} cls class of the element
3299 * Create a new Element
3300 * @param {Object} config The config object
3303 Roo.bootstrap.Element = function(config){
3304 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3307 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
3314 getAutoCreate : function(){
3339 * @class Roo.bootstrap.Pagination
3340 * @extends Roo.bootstrap.Component
3341 * Bootstrap Pagination class
3342 * @cfg {String} size xs | sm | md | lg
3343 * @cfg {Boolean} inverse false | true
3346 * Create a new Pagination
3347 * @param {Object} config The config object
3350 Roo.bootstrap.Pagination = function(config){
3351 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3354 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
3360 getAutoCreate : function(){
3366 cfg.cls += ' inverse';
3372 cfg.cls += " " + this.cls;
3390 * @class Roo.bootstrap.PaginationItem
3391 * @extends Roo.bootstrap.Component
3392 * Bootstrap PaginationItem class
3393 * @cfg {String} html text
3394 * @cfg {String} href the link
3395 * @cfg {Boolean} preventDefault (true | false) default true
3396 * @cfg {Boolean} active (true | false) default false
3400 * Create a new PaginationItem
3401 * @param {Object} config The config object
3405 Roo.bootstrap.PaginationItem = function(config){
3406 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3411 * The raw click event for the entire grid.
3412 * @param {Roo.EventObject} e
3418 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
3422 preventDefault: true,
3426 getAutoCreate : function(){
3432 href : this.href ? this.href : '#',
3433 html : this.html ? this.html : ''
3443 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3449 initEvents: function() {
3451 this.el.on('click', this.onClick, this);
3454 onClick : function(e)
3456 Roo.log('PaginationItem on click ');
3457 if(this.preventDefault){
3461 this.fireEvent('click', this, e);
3477 * @class Roo.bootstrap.Slider
3478 * @extends Roo.bootstrap.Component
3479 * Bootstrap Slider class
3482 * Create a new Slider
3483 * @param {Object} config The config object
3486 Roo.bootstrap.Slider = function(config){
3487 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3490 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
3492 getAutoCreate : function(){
3496 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3500 cls: 'ui-slider-handle ui-state-default ui-corner-all'
3518 * @class Roo.bootstrap.Table
3519 * @extends Roo.bootstrap.Component
3520 * Bootstrap Table class
3521 * @cfg {String} cls table class
3522 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3523 * @cfg {String} bgcolor Specifies the background color for a table
3524 * @cfg {Number} border Specifies whether the table cells should have borders or not
3525 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3526 * @cfg {Number} cellspacing Specifies the space between cells
3527 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3528 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3529 * @cfg {String} sortable Specifies that the table should be sortable
3530 * @cfg {String} summary Specifies a summary of the content of a table
3531 * @cfg {Number} width Specifies the width of a table
3533 * @cfg {boolean} striped Should the rows be alternative striped
3534 * @cfg {boolean} bordered Add borders to the table
3535 * @cfg {boolean} hover Add hover highlighting
3536 * @cfg {boolean} condensed Format condensed
3537 * @cfg {boolean} responsive Format condensed
3543 * Create a new Table
3544 * @param {Object} config The config object
3547 Roo.bootstrap.Table = function(config){
3548 Roo.bootstrap.Table.superclass.constructor.call(this, config);
3551 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3552 this.sm = this.selModel;
3553 this.sm.xmodule = this.xmodule || false;
3555 if (this.cm && typeof(this.cm.config) == 'undefined') {
3556 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3557 this.cm = this.colModel;
3558 this.cm.xmodule = this.xmodule || false;
3561 this.store= Roo.factory(this.store, Roo.data);
3562 this.ds = this.store;
3563 this.ds.xmodule = this.xmodule || false;
3568 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
3590 getAutoCreate : function(){
3591 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3600 cfg.cls += ' table-striped';
3603 cfg.cls += ' table-hover';
3605 if (this.bordered) {
3606 cfg.cls += ' table-bordered';
3608 if (this.condensed) {
3609 cfg.cls += ' table-condensed';
3611 if (this.responsive) {
3612 cfg.cls += ' table-responsive';
3619 cfg.cls+= ' ' +this.cls;
3622 // this lot should be simplifed...
3625 cfg.align=this.align;
3628 cfg.bgcolor=this.bgcolor;
3631 cfg.border=this.border;
3633 if (this.cellpadding) {
3634 cfg.cellpadding=this.cellpadding;
3636 if (this.cellspacing) {
3637 cfg.cellspacing=this.cellspacing;
3640 cfg.frame=this.frame;
3643 cfg.rules=this.rules;
3645 if (this.sortable) {
3646 cfg.sortable=this.sortable;
3649 cfg.summary=this.summary;
3652 cfg.width=this.width;
3655 if(this.store || this.cm){
3656 cfg.cn.push(this.renderHeader());
3657 cfg.cn.push(this.renderBody());
3658 cfg.cn.push(this.renderFooter());
3660 cfg.cls+= ' TableGrid';
3666 // initTableGrid : function()
3675 // var cm = this.cm;
3677 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3680 // html: cm.getColumnHeader(i)
3684 // cfg.push(header);
3691 initEvents : function()
3693 if(!this.store || !this.cm){
3697 Roo.log('initEvents with ds!!!!');
3701 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3702 e.on('click', _this.sort, _this);
3704 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3705 // this.maskEl.enableDisplayMode("block");
3706 // this.maskEl.show();
3708 this.store.on('load', this.onLoad, this);
3709 this.store.on('beforeload', this.onBeforeLoad, this);
3717 sort : function(e,el)
3719 var col = Roo.get(el)
3721 if(!col.hasClass('sortable')){
3725 var sort = col.attr('sort');
3728 if(col.hasClass('glyphicon-arrow-up')){
3732 this.store.sortInfo = {field : sort, direction : dir};
3737 renderHeader : function()
3746 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3748 var config = cm.config[i];
3752 html: cm.getColumnHeader(i)
3755 if(typeof(config.dataIndex) != 'undefined'){
3756 c.sort = config.dataIndex;
3759 if(typeof(config.sortable) != 'undefined' && config.sortable){
3763 if(typeof(config.width) != 'undefined'){
3764 c.style = 'width:' + config.width + 'px';
3773 renderBody : function()
3783 renderFooter : function()
3795 Roo.log('ds onload');
3800 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3801 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3803 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3804 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3807 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3808 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3812 var tbody = this.el.select('tbody', true).first();
3816 if(this.store.getCount() > 0){
3817 this.store.data.each(function(d){
3823 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3824 var renderer = cm.getRenderer(i);
3825 var config = cm.config[i];
3829 if(typeof(renderer) !== 'undefined'){
3830 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3833 if(typeof(value) === 'object'){
3843 html: (typeof(value) === 'object') ? '' : value
3846 if(typeof(config.width) != 'undefined'){
3847 td.style = 'width:' + config.width + 'px';
3854 tbody.createChild(row);
3862 Roo.each(renders, function(r){
3863 _this.renderColumn(r);
3867 // if(this.loadMask){
3868 // this.maskEl.hide();
3872 onBeforeLoad : function()
3874 Roo.log('ds onBeforeLoad');
3878 // if(this.loadMask){
3879 // this.maskEl.show();
3885 this.el.select('tbody', true).first().dom.innerHTML = '';
3888 getSelectionModel : function(){
3890 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3892 return this.selModel;
3895 renderColumn : function(r)
3898 r.cfg.render(Roo.get(r.id));
3901 Roo.each(r.cfg.cn, function(c){
3906 _this.renderColumn(child);
3923 * @class Roo.bootstrap.TableCell
3924 * @extends Roo.bootstrap.Component
3925 * Bootstrap TableCell class
3926 * @cfg {String} html cell contain text
3927 * @cfg {String} cls cell class
3928 * @cfg {String} tag cell tag (td|th) default td
3929 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3930 * @cfg {String} align Aligns the content in a cell
3931 * @cfg {String} axis Categorizes cells
3932 * @cfg {String} bgcolor Specifies the background color of a cell
3933 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3934 * @cfg {Number} colspan Specifies the number of columns a cell should span
3935 * @cfg {String} headers Specifies one or more header cells a cell is related to
3936 * @cfg {Number} height Sets the height of a cell
3937 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3938 * @cfg {Number} rowspan Sets the number of rows a cell should span
3939 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3940 * @cfg {String} valign Vertical aligns the content in a cell
3941 * @cfg {Number} width Specifies the width of a cell
3944 * Create a new TableCell
3945 * @param {Object} config The config object
3948 Roo.bootstrap.TableCell = function(config){
3949 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3952 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3972 getAutoCreate : function(){
3973 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3993 cfg.align=this.align
3999 cfg.bgcolor=this.bgcolor
4002 cfg.charoff=this.charoff
4005 cfg.colspan=this.colspan
4008 cfg.headers=this.headers
4011 cfg.height=this.height
4014 cfg.nowrap=this.nowrap
4017 cfg.rowspan=this.rowspan
4020 cfg.scope=this.scope
4023 cfg.valign=this.valign
4026 cfg.width=this.width
4045 * @class Roo.bootstrap.TableRow
4046 * @extends Roo.bootstrap.Component
4047 * Bootstrap TableRow class
4048 * @cfg {String} cls row class
4049 * @cfg {String} align Aligns the content in a table row
4050 * @cfg {String} bgcolor Specifies a background color for a table row
4051 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4052 * @cfg {String} valign Vertical aligns the content in a table row
4055 * Create a new TableRow
4056 * @param {Object} config The config object
4059 Roo.bootstrap.TableRow = function(config){
4060 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4063 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
4071 getAutoCreate : function(){
4072 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4082 cfg.align = this.align;
4085 cfg.bgcolor = this.bgcolor;
4088 cfg.charoff = this.charoff;
4091 cfg.valign = this.valign;
4109 * @class Roo.bootstrap.TableBody
4110 * @extends Roo.bootstrap.Component
4111 * Bootstrap TableBody class
4112 * @cfg {String} cls element class
4113 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4114 * @cfg {String} align Aligns the content inside the element
4115 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4116 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4119 * Create a new TableBody
4120 * @param {Object} config The config object
4123 Roo.bootstrap.TableBody = function(config){
4124 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4127 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
4135 getAutoCreate : function(){
4136 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4150 cfg.align = this.align;
4153 cfg.charoff = this.charoff;
4156 cfg.valign = this.valign;
4163 // initEvents : function()
4170 // this.store = Roo.factory(this.store, Roo.data);
4171 // this.store.on('load', this.onLoad, this);
4173 // this.store.load();
4177 // onLoad: function ()
4179 // this.fireEvent('load', this);
4189 * Ext JS Library 1.1.1
4190 * Copyright(c) 2006-2007, Ext JS, LLC.
4192 * Originally Released Under LGPL - original licence link has changed is not relivant.
4195 * <script type="text/javascript">
4198 // as we use this in bootstrap.
4199 Roo.namespace('Roo.form');
4201 * @class Roo.form.Action
4202 * Internal Class used to handle form actions
4204 * @param {Roo.form.BasicForm} el The form element or its id
4205 * @param {Object} config Configuration options
4210 // define the action interface
4211 Roo.form.Action = function(form, options){
4213 this.options = options || {};
4216 * Client Validation Failed
4219 Roo.form.Action.CLIENT_INVALID = 'client';
4221 * Server Validation Failed
4224 Roo.form.Action.SERVER_INVALID = 'server';
4226 * Connect to Server Failed
4229 Roo.form.Action.CONNECT_FAILURE = 'connect';
4231 * Reading Data from Server Failed
4234 Roo.form.Action.LOAD_FAILURE = 'load';
4236 Roo.form.Action.prototype = {
4238 failureType : undefined,
4239 response : undefined,
4243 run : function(options){
4248 success : function(response){
4253 handleResponse : function(response){
4257 // default connection failure
4258 failure : function(response){
4260 this.response = response;
4261 this.failureType = Roo.form.Action.CONNECT_FAILURE;
4262 this.form.afterAction(this, false);
4265 processResponse : function(response){
4266 this.response = response;
4267 if(!response.responseText){
4270 this.result = this.handleResponse(response);
4274 // utility functions used internally
4275 getUrl : function(appendParams){
4276 var url = this.options.url || this.form.url || this.form.el.dom.action;
4278 var p = this.getParams();
4280 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4286 getMethod : function(){
4287 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4290 getParams : function(){
4291 var bp = this.form.baseParams;
4292 var p = this.options.params;
4294 if(typeof p == "object"){
4295 p = Roo.urlEncode(Roo.applyIf(p, bp));
4296 }else if(typeof p == 'string' && bp){
4297 p += '&' + Roo.urlEncode(bp);
4300 p = Roo.urlEncode(bp);
4305 createCallback : function(){
4307 success: this.success,
4308 failure: this.failure,
4310 timeout: (this.form.timeout*1000),
4311 upload: this.form.fileUpload ? this.success : undefined
4316 Roo.form.Action.Submit = function(form, options){
4317 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4320 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4323 haveProgress : false,
4324 uploadComplete : false,
4326 // uploadProgress indicator.
4327 uploadProgress : function()
4329 if (!this.form.progressUrl) {
4333 if (!this.haveProgress) {
4334 Roo.MessageBox.progress("Uploading", "Uploading");
4336 if (this.uploadComplete) {
4337 Roo.MessageBox.hide();
4341 this.haveProgress = true;
4343 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4345 var c = new Roo.data.Connection();
4347 url : this.form.progressUrl,
4352 success : function(req){
4353 //console.log(data);
4357 rdata = Roo.decode(req.responseText)
4359 Roo.log("Invalid data from server..");
4363 if (!rdata || !rdata.success) {
4365 Roo.MessageBox.alert(Roo.encode(rdata));
4368 var data = rdata.data;
4370 if (this.uploadComplete) {
4371 Roo.MessageBox.hide();
4376 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4377 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4380 this.uploadProgress.defer(2000,this);
4383 failure: function(data) {
4384 Roo.log('progress url failed ');
4395 // run get Values on the form, so it syncs any secondary forms.
4396 this.form.getValues();
4398 var o = this.options;
4399 var method = this.getMethod();
4400 var isPost = method == 'POST';
4401 if(o.clientValidation === false || this.form.isValid()){
4403 if (this.form.progressUrl) {
4404 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4405 (new Date() * 1) + '' + Math.random());
4410 Roo.Ajax.request(Roo.apply(this.createCallback(), {
4411 form:this.form.el.dom,
4412 url:this.getUrl(!isPost),
4414 params:isPost ? this.getParams() : null,
4415 isUpload: this.form.fileUpload
4418 this.uploadProgress();
4420 }else if (o.clientValidation !== false){ // client validation failed
4421 this.failureType = Roo.form.Action.CLIENT_INVALID;
4422 this.form.afterAction(this, false);
4426 success : function(response)
4428 this.uploadComplete= true;
4429 if (this.haveProgress) {
4430 Roo.MessageBox.hide();
4434 var result = this.processResponse(response);
4435 if(result === true || result.success){
4436 this.form.afterAction(this, true);
4440 this.form.markInvalid(result.errors);
4441 this.failureType = Roo.form.Action.SERVER_INVALID;
4443 this.form.afterAction(this, false);
4445 failure : function(response)
4447 this.uploadComplete= true;
4448 if (this.haveProgress) {
4449 Roo.MessageBox.hide();
4452 this.response = response;
4453 this.failureType = Roo.form.Action.CONNECT_FAILURE;
4454 this.form.afterAction(this, false);
4457 handleResponse : function(response){
4458 if(this.form.errorReader){
4459 var rs = this.form.errorReader.read(response);
4462 for(var i = 0, len = rs.records.length; i < len; i++) {
4463 var r = rs.records[i];
4467 if(errors.length < 1){
4471 success : rs.success,
4477 ret = Roo.decode(response.responseText);
4481 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4491 Roo.form.Action.Load = function(form, options){
4492 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4493 this.reader = this.form.reader;
4496 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4501 Roo.Ajax.request(Roo.apply(
4502 this.createCallback(), {
4503 method:this.getMethod(),
4504 url:this.getUrl(false),
4505 params:this.getParams()
4509 success : function(response){
4511 var result = this.processResponse(response);
4512 if(result === true || !result.success || !result.data){
4513 this.failureType = Roo.form.Action.LOAD_FAILURE;
4514 this.form.afterAction(this, false);
4517 this.form.clearInvalid();
4518 this.form.setValues(result.data);
4519 this.form.afterAction(this, true);
4522 handleResponse : function(response){
4523 if(this.form.reader){
4524 var rs = this.form.reader.read(response);
4525 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4527 success : rs.success,
4531 return Roo.decode(response.responseText);
4535 Roo.form.Action.ACTION_TYPES = {
4536 'load' : Roo.form.Action.Load,
4537 'submit' : Roo.form.Action.Submit
4546 * @class Roo.bootstrap.Form
4547 * @extends Roo.bootstrap.Component
4548 * Bootstrap Form class
4549 * @cfg {String} method GET | POST (default POST)
4550 * @cfg {String} labelAlign top | left (default top)
4551 * @cfg {String} align left | right - for navbars
4556 * @param {Object} config The config object
4560 Roo.bootstrap.Form = function(config){
4561 Roo.bootstrap.Form.superclass.constructor.call(this, config);
4564 * @event clientvalidation
4565 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4566 * @param {Form} this
4567 * @param {Boolean} valid true if the form has passed client-side validation
4569 clientvalidation: true,
4571 * @event beforeaction
4572 * Fires before any action is performed. Return false to cancel the action.
4573 * @param {Form} this
4574 * @param {Action} action The action to be performed
4578 * @event actionfailed
4579 * Fires when an action fails.
4580 * @param {Form} this
4581 * @param {Action} action The action that failed
4583 actionfailed : true,
4585 * @event actioncomplete
4586 * Fires when an action is completed.
4587 * @param {Form} this
4588 * @param {Action} action The action that completed
4590 actioncomplete : true
4595 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
4598 * @cfg {String} method
4599 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4604 * The URL to use for form actions if one isn't supplied in the action options.
4607 * @cfg {Boolean} fileUpload
4608 * Set to true if this form is a file upload.
4612 * @cfg {Object} baseParams
4613 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4617 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4621 * @cfg {Sting} align (left|right) for navbar forms
4626 activeAction : null,
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.
4633 waitMsgTarget : false,
4638 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4639 * element by passing it or its id or mask the form itself by passing in true.
4643 getAutoCreate : function(){
4647 method : this.method || 'POST',
4648 id : this.id || Roo.id(),
4651 if (this.parent().xtype.match(/^Nav/)) {
4652 cfg.cls = 'navbar-form navbar-' + this.align;
4656 if (this.labelAlign == 'left' ) {
4657 cfg.cls += ' form-horizontal';
4663 initEvents : function()
4665 this.el.on('submit', this.onSubmit, this);
4670 onSubmit : function(e){
4675 * Returns true if client-side validation on the form is successful.
4678 isValid : function(){
4679 var items = this.getItems();
4681 items.each(function(f){
4690 * Returns true if any fields in this form have changed since their original load.
4693 isDirty : function(){
4695 var items = this.getItems();
4696 items.each(function(f){
4706 * Performs a predefined action (submit or load) or custom actions you define on this form.
4707 * @param {String} actionName The name of the action type
4708 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
4709 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4710 * accept other config options):
4712 Property Type Description
4713 ---------------- --------------- ----------------------------------------------------------------------------------
4714 url String The url for the action (defaults to the form's url)
4715 method String The form method to use (defaults to the form's method, or POST if not defined)
4716 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
4717 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
4718 validate the form on the client (defaults to false)
4720 * @return {BasicForm} this
4722 doAction : function(action, options){
4723 if(typeof action == 'string'){
4724 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4726 if(this.fireEvent('beforeaction', this, action) !== false){
4727 this.beforeAction(action);
4728 action.run.defer(100, action);
4734 beforeAction : function(action){
4735 var o = action.options;
4737 // not really supported yet.. ??
4739 //if(this.waitMsgTarget === true){
4740 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4741 //}else if(this.waitMsgTarget){
4742 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4743 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4745 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4751 afterAction : function(action, success){
4752 this.activeAction = null;
4753 var o = action.options;
4755 //if(this.waitMsgTarget === true){
4757 //}else if(this.waitMsgTarget){
4758 // this.waitMsgTarget.unmask();
4760 // Roo.MessageBox.updateProgress(1);
4761 // Roo.MessageBox.hide();
4768 Roo.callback(o.success, o.scope, [this, action]);
4769 this.fireEvent('actioncomplete', this, action);
4773 // failure condition..
4774 // we have a scenario where updates need confirming.
4775 // eg. if a locking scenario exists..
4776 // we look for { errors : { needs_confirm : true }} in the response.
4778 (typeof(action.result) != 'undefined') &&
4779 (typeof(action.result.errors) != 'undefined') &&
4780 (typeof(action.result.errors.needs_confirm) != 'undefined')
4783 Roo.log("not supported yet");
4786 Roo.MessageBox.confirm(
4787 "Change requires confirmation",
4788 action.result.errorMsg,
4793 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4803 Roo.callback(o.failure, o.scope, [this, action]);
4804 // show an error message if no failed handler is set..
4805 if (!this.hasListener('actionfailed')) {
4806 Roo.log("need to add dialog support");
4808 Roo.MessageBox.alert("Error",
4809 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4810 action.result.errorMsg :
4811 "Saving Failed, please check your entries or try again"
4816 this.fireEvent('actionfailed', this, action);
4821 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4822 * @param {String} id The value to search for
4825 findField : function(id){
4826 var items = this.getItems();
4827 var field = items.get(id);
4829 items.each(function(f){
4830 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4837 return field || null;
4840 * Mark fields in this form invalid in bulk.
4841 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4842 * @return {BasicForm} this
4844 markInvalid : function(errors){
4845 if(errors instanceof Array){
4846 for(var i = 0, len = errors.length; i < len; i++){
4847 var fieldError = errors[i];
4848 var f = this.findField(fieldError.id);
4850 f.markInvalid(fieldError.msg);
4856 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4857 field.markInvalid(errors[id]);
4861 //Roo.each(this.childForms || [], function (f) {
4862 // f.markInvalid(errors);
4869 * Set values for fields in this form in bulk.
4870 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4871 * @return {BasicForm} this
4873 setValues : function(values){
4874 if(values instanceof Array){ // array of objects
4875 for(var i = 0, len = values.length; i < len; i++){
4877 var f = this.findField(v.id);
4879 f.setValue(v.value);
4880 if(this.trackResetOnLoad){
4881 f.originalValue = f.getValue();
4885 }else{ // object hash
4888 if(typeof values[id] != 'function' && (field = this.findField(id))){
4890 if (field.setFromData &&
4892 field.displayField &&
4893 // combos' with local stores can
4894 // be queried via setValue()
4895 // to set their value..
4896 (field.store && !field.store.isLocal)
4900 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4901 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4902 field.setFromData(sd);
4905 field.setValue(values[id]);
4909 if(this.trackResetOnLoad){
4910 field.originalValue = field.getValue();
4916 //Roo.each(this.childForms || [], function (f) {
4917 // f.setValues(values);
4924 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4925 * they are returned as an array.
4926 * @param {Boolean} asString
4929 getValues : function(asString){
4930 //if (this.childForms) {
4931 // copy values from the child forms
4932 // Roo.each(this.childForms, function (f) {
4933 // this.setValues(f.getValues());
4939 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4940 if(asString === true){
4943 return Roo.urlDecode(fs);
4947 * Returns the fields in this form as an object with key/value pairs.
4948 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4951 getFieldValues : function(with_hidden)
4953 var items = this.getItems();
4955 items.each(function(f){
4959 var v = f.getValue();
4960 if (f.inputType =='radio') {
4961 if (typeof(ret[f.getName()]) == 'undefined') {
4962 ret[f.getName()] = ''; // empty..
4965 if (!f.el.dom.checked) {
4973 // not sure if this supported any more..
4974 if ((typeof(v) == 'object') && f.getRawValue) {
4975 v = f.getRawValue() ; // dates..
4977 // combo boxes where name != hiddenName...
4978 if (f.name != f.getName()) {
4979 ret[f.name] = f.getRawValue();
4981 ret[f.getName()] = v;
4988 * Clears all invalid messages in this form.
4989 * @return {BasicForm} this
4991 clearInvalid : function(){
4992 var items = this.getItems();
4994 items.each(function(f){
5005 * @return {BasicForm} this
5008 var items = this.getItems();
5009 items.each(function(f){
5013 Roo.each(this.childForms || [], function (f) {
5020 getItems : function()
5022 var r=new Roo.util.MixedCollection(false, function(o){
5023 return o.id || (o.id = Roo.id());
5025 var iter = function(el) {
5032 Roo.each(el.items,function(e) {
5051 * Ext JS Library 1.1.1
5052 * Copyright(c) 2006-2007, Ext JS, LLC.
5054 * Originally Released Under LGPL - original licence link has changed is not relivant.
5057 * <script type="text/javascript">
5060 * @class Roo.form.VTypes
5061 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5064 Roo.form.VTypes = function(){
5065 // closure these in so they are only created once.
5066 var alpha = /^[a-zA-Z_]+$/;
5067 var alphanum = /^[a-zA-Z0-9_]+$/;
5068 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5069 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5071 // All these messages and functions are configurable
5074 * The function used to validate email addresses
5075 * @param {String} value The email address
5077 'email' : function(v){
5078 return email.test(v);
5081 * The error text to display when the email validation function returns false
5084 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5086 * The keystroke filter mask to be applied on email input
5089 'emailMask' : /[a-z0-9_\.\-@]/i,
5092 * The function used to validate URLs
5093 * @param {String} value The URL
5095 'url' : function(v){
5099 * The error text to display when the url validation function returns false
5102 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5105 * The function used to validate alpha values
5106 * @param {String} value The value
5108 'alpha' : function(v){
5109 return alpha.test(v);
5112 * The error text to display when the alpha validation function returns false
5115 'alphaText' : 'This field should only contain letters and _',
5117 * The keystroke filter mask to be applied on alpha input
5120 'alphaMask' : /[a-z_]/i,
5123 * The function used to validate alphanumeric values
5124 * @param {String} value The value
5126 'alphanum' : function(v){
5127 return alphanum.test(v);
5130 * The error text to display when the alphanumeric validation function returns false
5133 'alphanumText' : 'This field should only contain letters, numbers and _',
5135 * The keystroke filter mask to be applied on alphanumeric input
5138 'alphanumMask' : /[a-z0-9_]/i
5148 * @class Roo.bootstrap.Input
5149 * @extends Roo.bootstrap.Component
5150 * Bootstrap Input class
5151 * @cfg {Boolean} disabled is it disabled
5152 * @cfg {String} fieldLabel - the label associated
5153 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5154 * @cfg {String} name name of the input
5155 * @cfg {string} fieldLabel - the label associated
5156 * @cfg {string} inputType - input / file submit ...
5157 * @cfg {string} placeholder - placeholder to put in text.
5158 * @cfg {string} before - input group add on before
5159 * @cfg {string} after - input group add on after
5160 * @cfg {string} size - (lg|sm) or leave empty..
5161 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5162 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5163 * @cfg {Number} md colspan out of 12 for computer-sized screens
5164 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5165 * @cfg {string} value default value of the input
5166 * @cfg {Number} labelWidth set the width of label (0-12)
5167 * @cfg {String} labelAlign (top|left)
5168 * @cfg {Boolean} readOnly Specifies that the field should be read-only
5172 * Create a new Input
5173 * @param {Object} config The config object
5176 Roo.bootstrap.Input = function(config){
5177 Roo.bootstrap.Input.superclass.constructor.call(this, config);
5182 * Fires when this field receives input focus.
5183 * @param {Roo.form.Field} this
5188 * Fires when this field loses input focus.
5189 * @param {Roo.form.Field} this
5194 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
5195 * {@link Roo.EventObject#getKey} to determine which key was pressed.
5196 * @param {Roo.form.Field} this
5197 * @param {Roo.EventObject} e The event object
5202 * Fires just before the field blurs if the field value has changed.
5203 * @param {Roo.form.Field} this
5204 * @param {Mixed} newValue The new value
5205 * @param {Mixed} oldValue The original value
5210 * Fires after the field has been marked as invalid.
5211 * @param {Roo.form.Field} this
5212 * @param {String} msg The validation message
5217 * Fires after the field has been validated with no errors.
5218 * @param {Roo.form.Field} this
5223 * Fires after the key up
5224 * @param {Roo.form.Field} this
5225 * @param {Roo.EventObject} e The event Object
5231 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
5233 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5234 automatic validation (defaults to "keyup").
5236 validationEvent : "keyup",
5238 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5240 validateOnBlur : true,
5242 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5244 validationDelay : 250,
5246 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5248 focusClass : "x-form-focus", // not needed???
5252 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5254 invalidClass : "has-error",
5257 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5259 selectOnFocus : false,
5262 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5266 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5271 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5273 disableKeyFilter : false,
5276 * @cfg {Boolean} disabled True to disable the field (defaults to false).
5280 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5284 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5286 blankText : "This field is required",
5289 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5293 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5295 maxLength : Number.MAX_VALUE,
5297 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5299 minLengthText : "The minimum length for this field is {0}",
5301 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5303 maxLengthText : "The maximum length for this field is {0}",
5307 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5308 * If available, this function will be called only after the basic validators all return true, and will be passed the
5309 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5313 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5314 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5315 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
5319 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5342 parentLabelAlign : function()
5345 while (parent.parent()) {
5346 parent = parent.parent();
5347 if (typeof(parent.labelAlign) !='undefined') {
5348 return parent.labelAlign;
5355 getAutoCreate : function(){
5357 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5363 if(this.inputType != 'hidden'){
5364 cfg.cls = 'form-group' //input-group
5370 type : this.inputType,
5372 cls : 'form-control',
5373 placeholder : this.placeholder || ''
5377 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5378 input.maxLength = this.maxLength;
5381 if (this.disabled) {
5382 input.disabled=true;
5385 if (this.readOnly) {
5386 input.readonly=true;
5390 input.name = this.name;
5393 input.cls += ' input-' + this.size;
5396 ['xs','sm','md','lg'].map(function(size){
5397 if (settings[size]) {
5398 cfg.cls += ' col-' + size + '-' + settings[size];
5402 var inputblock = input;
5404 if (this.before || this.after) {
5407 cls : 'input-group',
5411 inputblock.cn.push({
5413 cls : 'input-group-addon',
5417 inputblock.cn.push(input);
5419 inputblock.cn.push({
5421 cls : 'input-group-addon',
5428 if (align ==='left' && this.fieldLabel.length) {
5429 Roo.log("left and has label");
5435 cls : 'control-label col-sm-' + this.labelWidth,
5436 html : this.fieldLabel
5440 cls : "col-sm-" + (12 - this.labelWidth),
5447 } else if ( this.fieldLabel.length) {
5453 //cls : 'input-group-addon',
5454 html : this.fieldLabel
5464 Roo.log(" no label && no align");
5473 Roo.log('input-parentType: ' + this.parentType);
5475 if (this.parentType === 'Navbar' && this.parent().bar) {
5476 cfg.cls += ' navbar-form';
5484 * return the real input element.
5486 inputEl: function ()
5488 return this.el.select('input.form-control',true).first();
5490 setDisabled : function(v)
5492 var i = this.inputEl().dom;
5494 i.removeAttribute('disabled');
5498 i.setAttribute('disabled','true');
5500 initEvents : function()
5503 this.inputEl().on("keydown" , this.fireKey, this);
5504 this.inputEl().on("focus", this.onFocus, this);
5505 this.inputEl().on("blur", this.onBlur, this);
5507 this.inputEl().relayEvent('keyup', this);
5509 // reference to original value for reset
5510 this.originalValue = this.getValue();
5511 //Roo.form.TextField.superclass.initEvents.call(this);
5512 if(this.validationEvent == 'keyup'){
5513 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5514 this.inputEl().on('keyup', this.filterValidation, this);
5516 else if(this.validationEvent !== false){
5517 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5520 if(this.selectOnFocus){
5521 this.on("focus", this.preFocus, this);
5524 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5525 this.inputEl().on("keypress", this.filterKeys, this);
5528 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
5529 this.el.on("click", this.autoSize, this);
5532 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5533 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5537 filterValidation : function(e){
5538 if(!e.isNavKeyPress()){
5539 this.validationTask.delay(this.validationDelay);
5543 * Validates the field value
5544 * @return {Boolean} True if the value is valid, else false
5546 validate : function(){
5547 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5548 if(this.disabled || this.validateValue(this.getRawValue())){
5549 this.clearInvalid();
5557 * Validates a value according to the field's validation rules and marks the field as invalid
5558 * if the validation fails
5559 * @param {Mixed} value The value to validate
5560 * @return {Boolean} True if the value is valid, else false
5562 validateValue : function(value){
5563 if(value.length < 1) { // if it's blank
5564 if(this.allowBlank){
5565 this.clearInvalid();
5568 this.markInvalid(this.blankText);
5572 if(value.length < this.minLength){
5573 this.markInvalid(String.format(this.minLengthText, this.minLength));
5576 if(value.length > this.maxLength){
5577 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5581 var vt = Roo.form.VTypes;
5582 if(!vt[this.vtype](value, this)){
5583 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5587 if(typeof this.validator == "function"){
5588 var msg = this.validator(value);
5590 this.markInvalid(msg);
5594 if(this.regex && !this.regex.test(value)){
5595 this.markInvalid(this.regexText);
5604 fireKey : function(e){
5605 //Roo.log('field ' + e.getKey());
5606 if(e.isNavKeyPress()){
5607 this.fireEvent("specialkey", this, e);
5610 focus : function (selectText){
5612 this.inputEl().focus();
5613 if(selectText === true){
5614 this.inputEl().dom.select();
5620 onFocus : function(){
5621 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5622 // this.el.addClass(this.focusClass);
5625 this.hasFocus = true;
5626 this.startValue = this.getValue();
5627 this.fireEvent("focus", this);
5631 beforeBlur : Roo.emptyFn,
5635 onBlur : function(){
5637 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5638 //this.el.removeClass(this.focusClass);
5640 this.hasFocus = false;
5641 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5644 var v = this.getValue();
5645 if(String(v) !== String(this.startValue)){
5646 this.fireEvent('change', this, v, this.startValue);
5648 this.fireEvent("blur", this);
5652 * Resets the current field value to the originally loaded value and clears any validation messages
5655 this.setValue(this.originalValue);
5656 this.clearInvalid();
5659 * Returns the name of the field
5660 * @return {Mixed} name The name field
5662 getName: function(){
5666 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
5667 * @return {Mixed} value The field value
5669 getValue : function(){
5670 return this.inputEl().getValue();
5673 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
5674 * @return {Mixed} value The field value
5676 getRawValue : function(){
5677 var v = this.inputEl().getValue();
5683 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
5684 * @param {Mixed} value The value to set
5686 setRawValue : function(v){
5687 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5690 selectText : function(start, end){
5691 var v = this.getRawValue();
5693 start = start === undefined ? 0 : start;
5694 end = end === undefined ? v.length : end;
5695 var d = this.inputEl().dom;
5696 if(d.setSelectionRange){
5697 d.setSelectionRange(start, end);
5698 }else if(d.createTextRange){
5699 var range = d.createTextRange();
5700 range.moveStart("character", start);
5701 range.moveEnd("character", v.length-end);
5708 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
5709 * @param {Mixed} value The value to set
5711 setValue : function(v){
5714 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5720 processValue : function(value){
5721 if(this.stripCharsRe){
5722 var newValue = value.replace(this.stripCharsRe, '');
5723 if(newValue !== value){
5724 this.setRawValue(newValue);
5731 preFocus : function(){
5733 if(this.selectOnFocus){
5734 this.inputEl().dom.select();
5737 filterKeys : function(e){
5739 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5742 var c = e.getCharCode(), cc = String.fromCharCode(c);
5743 if(Roo.isIE && (e.isSpecialKey() || !cc)){
5746 if(!this.maskRe.test(cc)){
5751 * Clear any invalid styles/messages for this field
5753 clearInvalid : function(){
5755 if(!this.el || this.preventMark){ // not rendered
5758 this.el.removeClass(this.invalidClass);
5760 switch(this.msgTarget){
5762 this.el.dom.qtip = '';
5765 this.el.dom.title = '';
5769 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5774 this.errorIcon.dom.qtip = '';
5775 this.errorIcon.hide();
5776 this.un('resize', this.alignErrorIcon, this);
5780 var t = Roo.getDom(this.msgTarget);
5782 t.style.display = 'none';
5786 this.fireEvent('valid', this);
5789 * Mark this field as invalid
5790 * @param {String} msg The validation message
5792 markInvalid : function(msg){
5793 if(!this.el || this.preventMark){ // not rendered
5796 this.el.addClass(this.invalidClass);
5798 msg = msg || this.invalidText;
5799 switch(this.msgTarget){
5801 this.el.dom.qtip = msg;
5802 this.el.dom.qclass = 'x-form-invalid-tip';
5803 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5804 Roo.QuickTips.enable();
5808 this.el.dom.title = msg;
5812 var elp = this.el.findParent('.x-form-element', 5, true);
5813 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5814 this.errorEl.setWidth(elp.getWidth(true)-20);
5816 this.errorEl.update(msg);
5817 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5820 if(!this.errorIcon){
5821 var elp = this.el.findParent('.x-form-element', 5, true);
5822 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5824 this.alignErrorIcon();
5825 this.errorIcon.dom.qtip = msg;
5826 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5827 this.errorIcon.show();
5828 this.on('resize', this.alignErrorIcon, this);
5831 var t = Roo.getDom(this.msgTarget);
5833 t.style.display = this.msgDisplay;
5837 this.fireEvent('invalid', this, msg);
5840 SafariOnKeyDown : function(event)
5842 // this is a workaround for a password hang bug on chrome/ webkit.
5844 var isSelectAll = false;
5846 if(this.inputEl().dom.selectionEnd > 0){
5847 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5849 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5850 event.preventDefault();
5855 if(isSelectAll){ // backspace and delete key
5857 event.preventDefault();
5858 // this is very hacky as keydown always get's upper case.
5860 var cc = String.fromCharCode(event.getCharCode());
5861 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5865 adjustWidth : function(tag, w){
5866 tag = tag.toLowerCase();
5867 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5868 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5872 if(tag == 'textarea'){
5875 }else if(Roo.isOpera){
5879 if(tag == 'textarea'){
5898 * @class Roo.bootstrap.TextArea
5899 * @extends Roo.bootstrap.Input
5900 * Bootstrap TextArea class
5901 * @cfg {Number} cols Specifies the visible width of a text area
5902 * @cfg {Number} rows Specifies the visible number of lines in a text area
5903 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5904 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5905 * @cfg {string} html text
5908 * Create a new TextArea
5909 * @param {Object} config The config object
5912 Roo.bootstrap.TextArea = function(config){
5913 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5917 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5927 getAutoCreate : function(){
5929 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5940 value : this.value || '',
5941 html: this.html || '',
5942 cls : 'form-control',
5943 placeholder : this.placeholder || ''
5947 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5948 input.maxLength = this.maxLength;
5952 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5956 input.cols = this.cols;
5959 if (this.readOnly) {
5960 input.readonly = true;
5964 input.name = this.name;
5968 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5972 ['xs','sm','md','lg'].map(function(size){
5973 if (settings[size]) {
5974 cfg.cls += ' col-' + size + '-' + settings[size];
5978 var inputblock = input;
5980 if (this.before || this.after) {
5983 cls : 'input-group',
5987 inputblock.cn.push({
5989 cls : 'input-group-addon',
5993 inputblock.cn.push(input);
5995 inputblock.cn.push({
5997 cls : 'input-group-addon',
6004 if (align ==='left' && this.fieldLabel.length) {
6005 Roo.log("left and has label");
6011 cls : 'control-label col-sm-' + this.labelWidth,
6012 html : this.fieldLabel
6016 cls : "col-sm-" + (12 - this.labelWidth),
6023 } else if ( this.fieldLabel.length) {
6029 //cls : 'input-group-addon',
6030 html : this.fieldLabel
6040 Roo.log(" no label && no align");
6050 if (this.disabled) {
6051 input.disabled=true;
6058 * return the real textarea element.
6060 inputEl: function ()
6062 return this.el.select('textarea.form-control',true).first();
6070 * trigger field - base class for combo..
6075 * @class Roo.bootstrap.TriggerField
6076 * @extends Roo.bootstrap.Input
6077 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6078 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6079 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6080 * for which you can provide a custom implementation. For example:
6082 var trigger = new Roo.bootstrap.TriggerField();
6083 trigger.onTriggerClick = myTriggerFn;
6084 trigger.applyTo('my-field');
6087 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6088 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6089 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
6090 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6092 * Create a new TriggerField.
6093 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6094 * to the base TextField)
6096 Roo.bootstrap.TriggerField = function(config){
6097 this.mimicing = false;
6098 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6101 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
6103 * @cfg {String} triggerClass A CSS class to apply to the trigger
6106 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6110 /** @cfg {Boolean} grow @hide */
6111 /** @cfg {Number} growMin @hide */
6112 /** @cfg {Number} growMax @hide */
6118 autoSize: Roo.emptyFn,
6125 actionMode : 'wrap',
6129 getAutoCreate : function(){
6131 var parent = this.parent();
6133 var align = this.parentLabelAlign();
6138 cls: 'form-group' //input-group
6145 type : this.inputType,
6146 cls : 'form-control',
6147 autocomplete: 'off',
6148 placeholder : this.placeholder || ''
6152 input.name = this.name;
6155 input.cls += ' input-' + this.size;
6158 if (this.disabled) {
6159 input.disabled=true;
6162 var inputblock = input;
6164 if (this.before || this.after) {
6167 cls : 'input-group',
6171 inputblock.cn.push({
6173 cls : 'input-group-addon',
6177 inputblock.cn.push(input);
6179 inputblock.cn.push({
6181 cls : 'input-group-addon',
6194 cls: 'form-hidden-field'
6202 Roo.log('multiple');
6210 cls: 'form-hidden-field'
6214 cls: 'select2-choices',
6218 cls: 'select2-search-field',
6231 cls: 'select2-container input-group',
6236 cls: 'typeahead typeahead-long dropdown-menu',
6237 style: 'display:none'
6245 cls : 'input-group-addon btn dropdown-toggle',
6253 cls: 'combobox-clear',
6267 combobox.cls += ' select2-container-multi';
6270 if (align ==='left' && this.fieldLabel.length) {
6272 Roo.log("left and has label");
6278 cls : 'control-label col-sm-' + this.labelWidth,
6279 html : this.fieldLabel
6283 cls : "col-sm-" + (12 - this.labelWidth),
6290 } else if ( this.fieldLabel.length) {
6296 //cls : 'input-group-addon',
6297 html : this.fieldLabel
6307 Roo.log(" no label && no align");
6314 ['xs','sm','md','lg'].map(function(size){
6315 if (settings[size]) {
6316 cfg.cls += ' col-' + size + '-' + settings[size];
6327 onResize : function(w, h){
6328 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6329 // if(typeof w == 'number'){
6330 // var x = w - this.trigger.getWidth();
6331 // this.inputEl().setWidth(this.adjustWidth('input', x));
6332 // this.trigger.setStyle('left', x+'px');
6337 adjustSize : Roo.BoxComponent.prototype.adjustSize,
6340 getResizeEl : function(){
6341 return this.inputEl();
6345 getPositionEl : function(){
6346 return this.inputEl();
6350 alignErrorIcon : function(){
6351 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6355 initEvents : function(){
6357 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6358 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6360 this.trigger = this.el.select('span.dropdown-toggle',true).first();
6361 if(this.hideTrigger){
6362 this.trigger.setDisplayed(false);
6364 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6368 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6371 //this.trigger.addClassOnOver('x-form-trigger-over');
6372 //this.trigger.addClassOnClick('x-form-trigger-click');
6375 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6380 initTrigger : function(){
6385 onDestroy : function(){
6387 this.trigger.removeAllListeners();
6388 // this.trigger.remove();
6391 // this.wrap.remove();
6393 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6397 onFocus : function(){
6398 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6401 this.wrap.addClass('x-trigger-wrap-focus');
6402 this.mimicing = true;
6403 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6404 if(this.monitorTab){
6405 this.el.on("keydown", this.checkTab, this);
6412 checkTab : function(e){
6413 if(e.getKey() == e.TAB){
6419 onBlur : function(){
6424 mimicBlur : function(e, t){
6426 if(!this.wrap.contains(t) && this.validateBlur()){
6433 triggerBlur : function(){
6434 this.mimicing = false;
6435 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6436 if(this.monitorTab){
6437 this.el.un("keydown", this.checkTab, this);
6439 //this.wrap.removeClass('x-trigger-wrap-focus');
6440 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6444 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6445 validateBlur : function(e, t){
6450 onDisable : function(){
6451 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6453 // this.wrap.addClass('x-item-disabled');
6458 onEnable : function(){
6459 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6461 // this.el.removeClass('x-item-disabled');
6466 onShow : function(){
6467 var ae = this.getActionEl();
6470 ae.dom.style.display = '';
6471 ae.dom.style.visibility = 'visible';
6477 onHide : function(){
6478 var ae = this.getActionEl();
6479 ae.dom.style.display = 'none';
6483 * The function that should handle the trigger's click event. This method does nothing by default until overridden
6484 * by an implementing function.
6486 * @param {EventObject} e
6488 onTriggerClick : Roo.emptyFn
6492 * Ext JS Library 1.1.1
6493 * Copyright(c) 2006-2007, Ext JS, LLC.
6495 * Originally Released Under LGPL - original licence link has changed is not relivant.
6498 * <script type="text/javascript">
6503 * @class Roo.data.SortTypes
6505 * Defines the default sorting (casting?) comparison functions used when sorting data.
6507 Roo.data.SortTypes = {
6509 * Default sort that does nothing
6510 * @param {Mixed} s The value being converted
6511 * @return {Mixed} The comparison value
6518 * The regular expression used to strip tags
6522 stripTagsRE : /<\/?[^>]+>/gi,
6525 * Strips all HTML tags to sort on text only
6526 * @param {Mixed} s The value being converted
6527 * @return {String} The comparison value
6529 asText : function(s){
6530 return String(s).replace(this.stripTagsRE, "");
6534 * Strips all HTML tags to sort on text only - Case insensitive
6535 * @param {Mixed} s The value being converted
6536 * @return {String} The comparison value
6538 asUCText : function(s){
6539 return String(s).toUpperCase().replace(this.stripTagsRE, "");
6543 * Case insensitive string
6544 * @param {Mixed} s The value being converted
6545 * @return {String} The comparison value
6547 asUCString : function(s) {
6548 return String(s).toUpperCase();
6553 * @param {Mixed} s The value being converted
6554 * @return {Number} The comparison value
6556 asDate : function(s) {
6560 if(s instanceof Date){
6563 return Date.parse(String(s));
6568 * @param {Mixed} s The value being converted
6569 * @return {Float} The comparison value
6571 asFloat : function(s) {
6572 var val = parseFloat(String(s).replace(/,/g, ""));
6573 if(isNaN(val)) val = 0;
6579 * @param {Mixed} s The value being converted
6580 * @return {Number} The comparison value
6582 asInt : function(s) {
6583 var val = parseInt(String(s).replace(/,/g, ""));
6584 if(isNaN(val)) val = 0;
6589 * Ext JS Library 1.1.1
6590 * Copyright(c) 2006-2007, Ext JS, LLC.
6592 * Originally Released Under LGPL - original licence link has changed is not relivant.
6595 * <script type="text/javascript">
6599 * @class Roo.data.Record
6600 * Instances of this class encapsulate both record <em>definition</em> information, and record
6601 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6602 * to access Records cached in an {@link Roo.data.Store} object.<br>
6604 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6605 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6608 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6610 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6611 * {@link #create}. The parameters are the same.
6612 * @param {Array} data An associative Array of data values keyed by the field name.
6613 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6614 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6615 * not specified an integer id is generated.
6617 Roo.data.Record = function(data, id){
6618 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6623 * Generate a constructor for a specific record layout.
6624 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6625 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6626 * Each field definition object may contain the following properties: <ul>
6627 * <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,
6628 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6629 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6630 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6631 * is being used, then this is a string containing the javascript expression to reference the data relative to
6632 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6633 * to the data item relative to the record element. If the mapping expression is the same as the field name,
6634 * this may be omitted.</p></li>
6635 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6636 * <ul><li>auto (Default, implies no conversion)</li>
6641 * <li>date</li></ul></p></li>
6642 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6643 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6644 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6645 * by the Reader into an object that will be stored in the Record. It is passed the
6646 * following parameters:<ul>
6647 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6649 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6651 * <br>usage:<br><pre><code>
6652 var TopicRecord = Roo.data.Record.create(
6653 {name: 'title', mapping: 'topic_title'},
6654 {name: 'author', mapping: 'username'},
6655 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6656 {name: 'lastPost', mapping: 'post_time', type: 'date'},
6657 {name: 'lastPoster', mapping: 'user2'},
6658 {name: 'excerpt', mapping: 'post_text'}
6661 var myNewRecord = new TopicRecord({
6662 title: 'Do my job please',
6665 lastPost: new Date(),
6666 lastPoster: 'Animal',
6667 excerpt: 'No way dude!'
6669 myStore.add(myNewRecord);
6674 Roo.data.Record.create = function(o){
6676 f.superclass.constructor.apply(this, arguments);
6678 Roo.extend(f, Roo.data.Record);
6679 var p = f.prototype;
6680 p.fields = new Roo.util.MixedCollection(false, function(field){
6683 for(var i = 0, len = o.length; i < len; i++){
6684 p.fields.add(new Roo.data.Field(o[i]));
6686 f.getField = function(name){
6687 return p.fields.get(name);
6692 Roo.data.Record.AUTO_ID = 1000;
6693 Roo.data.Record.EDIT = 'edit';
6694 Roo.data.Record.REJECT = 'reject';
6695 Roo.data.Record.COMMIT = 'commit';
6697 Roo.data.Record.prototype = {
6699 * Readonly flag - true if this record has been modified.
6708 join : function(store){
6713 * Set the named field to the specified value.
6714 * @param {String} name The name of the field to set.
6715 * @param {Object} value The value to set the field to.
6717 set : function(name, value){
6718 if(this.data[name] == value){
6725 if(typeof this.modified[name] == 'undefined'){
6726 this.modified[name] = this.data[name];
6728 this.data[name] = value;
6729 if(!this.editing && this.store){
6730 this.store.afterEdit(this);
6735 * Get the value of the named field.
6736 * @param {String} name The name of the field to get the value of.
6737 * @return {Object} The value of the field.
6739 get : function(name){
6740 return this.data[name];
6744 beginEdit : function(){
6745 this.editing = true;
6750 cancelEdit : function(){
6751 this.editing = false;
6752 delete this.modified;
6756 endEdit : function(){
6757 this.editing = false;
6758 if(this.dirty && this.store){
6759 this.store.afterEdit(this);
6764 * Usually called by the {@link Roo.data.Store} which owns the Record.
6765 * Rejects all changes made to the Record since either creation, or the last commit operation.
6766 * Modified fields are reverted to their original values.
6768 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6769 * of reject operations.
6771 reject : function(){
6772 var m = this.modified;
6774 if(typeof m[n] != "function"){
6775 this.data[n] = m[n];
6779 delete this.modified;
6780 this.editing = false;
6782 this.store.afterReject(this);
6787 * Usually called by the {@link Roo.data.Store} which owns the Record.
6788 * Commits all changes made to the Record since either creation, or the last commit operation.
6790 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6791 * of commit operations.
6793 commit : function(){
6795 delete this.modified;
6796 this.editing = false;
6798 this.store.afterCommit(this);
6803 hasError : function(){
6804 return this.error != null;
6808 clearError : function(){
6813 * Creates a copy of this record.
6814 * @param {String} id (optional) A new record id if you don't want to use this record's id
6817 copy : function(newId) {
6818 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6822 * Ext JS Library 1.1.1
6823 * Copyright(c) 2006-2007, Ext JS, LLC.
6825 * Originally Released Under LGPL - original licence link has changed is not relivant.
6828 * <script type="text/javascript">
6834 * @class Roo.data.Store
6835 * @extends Roo.util.Observable
6836 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6837 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6839 * 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
6840 * has no knowledge of the format of the data returned by the Proxy.<br>
6842 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6843 * instances from the data object. These records are cached and made available through accessor functions.
6845 * Creates a new Store.
6846 * @param {Object} config A config object containing the objects needed for the Store to access data,
6847 * and read the data into Records.
6849 Roo.data.Store = function(config){
6850 this.data = new Roo.util.MixedCollection(false);
6851 this.data.getKey = function(o){
6854 this.baseParams = {};
6861 "multisort" : "_multisort"
6864 if(config && config.data){
6865 this.inlineData = config.data;
6869 Roo.apply(this, config);
6871 if(this.reader){ // reader passed
6872 this.reader = Roo.factory(this.reader, Roo.data);
6873 this.reader.xmodule = this.xmodule || false;
6874 if(!this.recordType){
6875 this.recordType = this.reader.recordType;
6877 if(this.reader.onMetaChange){
6878 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6882 if(this.recordType){
6883 this.fields = this.recordType.prototype.fields;
6889 * @event datachanged
6890 * Fires when the data cache has changed, and a widget which is using this Store
6891 * as a Record cache should refresh its view.
6892 * @param {Store} this
6897 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6898 * @param {Store} this
6899 * @param {Object} meta The JSON metadata
6904 * Fires when Records have been added to the Store
6905 * @param {Store} this
6906 * @param {Roo.data.Record[]} records The array of Records added
6907 * @param {Number} index The index at which the record(s) were added
6912 * Fires when a Record has been removed from the Store
6913 * @param {Store} this
6914 * @param {Roo.data.Record} record The Record that was removed
6915 * @param {Number} index The index at which the record was removed
6920 * Fires when a Record has been updated
6921 * @param {Store} this
6922 * @param {Roo.data.Record} record The Record that was updated
6923 * @param {String} operation The update operation being performed. Value may be one of:
6925 Roo.data.Record.EDIT
6926 Roo.data.Record.REJECT
6927 Roo.data.Record.COMMIT
6933 * Fires when the data cache has been cleared.
6934 * @param {Store} this
6939 * Fires before a request is made for a new data object. If the beforeload handler returns false
6940 * the load action will be canceled.
6941 * @param {Store} this
6942 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6946 * @event beforeloadadd
6947 * Fires after a new set of Records has been loaded.
6948 * @param {Store} this
6949 * @param {Roo.data.Record[]} records The Records that were loaded
6950 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6952 beforeloadadd : true,
6955 * Fires after a new set of Records has been loaded, before they are added to the store.
6956 * @param {Store} this
6957 * @param {Roo.data.Record[]} records The Records that were loaded
6958 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6959 * @params {Object} return from reader
6963 * @event loadexception
6964 * Fires if an exception occurs in the Proxy during loading.
6965 * Called with the signature of the Proxy's "loadexception" event.
6966 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6969 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6970 * @param {Object} load options
6971 * @param {Object} jsonData from your request (normally this contains the Exception)
6973 loadexception : true
6977 this.proxy = Roo.factory(this.proxy, Roo.data);
6978 this.proxy.xmodule = this.xmodule || false;
6979 this.relayEvents(this.proxy, ["loadexception"]);
6981 this.sortToggle = {};
6982 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6984 Roo.data.Store.superclass.constructor.call(this);
6986 if(this.inlineData){
6987 this.loadData(this.inlineData);
6988 delete this.inlineData;
6992 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6994 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6995 * without a remote query - used by combo/forms at present.
6999 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7002 * @cfg {Array} data Inline data to be loaded when the store is initialized.
7005 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7006 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7009 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7010 * on any HTTP request
7013 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7016 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7020 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7021 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7026 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7027 * loaded or when a record is removed. (defaults to false).
7029 pruneModifiedRecords : false,
7035 * Add Records to the Store and fires the add event.
7036 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7038 add : function(records){
7039 records = [].concat(records);
7040 for(var i = 0, len = records.length; i < len; i++){
7041 records[i].join(this);
7043 var index = this.data.length;
7044 this.data.addAll(records);
7045 this.fireEvent("add", this, records, index);
7049 * Remove a Record from the Store and fires the remove event.
7050 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7052 remove : function(record){
7053 var index = this.data.indexOf(record);
7054 this.data.removeAt(index);
7055 if(this.pruneModifiedRecords){
7056 this.modified.remove(record);
7058 this.fireEvent("remove", this, record, index);
7062 * Remove all Records from the Store and fires the clear event.
7064 removeAll : function(){
7066 if(this.pruneModifiedRecords){
7069 this.fireEvent("clear", this);
7073 * Inserts Records to the Store at the given index and fires the add event.
7074 * @param {Number} index The start index at which to insert the passed Records.
7075 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7077 insert : function(index, records){
7078 records = [].concat(records);
7079 for(var i = 0, len = records.length; i < len; i++){
7080 this.data.insert(index, records[i]);
7081 records[i].join(this);
7083 this.fireEvent("add", this, records, index);
7087 * Get the index within the cache of the passed Record.
7088 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7089 * @return {Number} The index of the passed Record. Returns -1 if not found.
7091 indexOf : function(record){
7092 return this.data.indexOf(record);
7096 * Get the index within the cache of the Record with the passed id.
7097 * @param {String} id The id of the Record to find.
7098 * @return {Number} The index of the Record. Returns -1 if not found.
7100 indexOfId : function(id){
7101 return this.data.indexOfKey(id);
7105 * Get the Record with the specified id.
7106 * @param {String} id The id of the Record to find.
7107 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7109 getById : function(id){
7110 return this.data.key(id);
7114 * Get the Record at the specified index.
7115 * @param {Number} index The index of the Record to find.
7116 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7118 getAt : function(index){
7119 return this.data.itemAt(index);
7123 * Returns a range of Records between specified indices.
7124 * @param {Number} startIndex (optional) The starting index (defaults to 0)
7125 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7126 * @return {Roo.data.Record[]} An array of Records
7128 getRange : function(start, end){
7129 return this.data.getRange(start, end);
7133 storeOptions : function(o){
7134 o = Roo.apply({}, o);
7137 this.lastOptions = o;
7141 * Loads the Record cache from the configured Proxy using the configured Reader.
7143 * If using remote paging, then the first load call must specify the <em>start</em>
7144 * and <em>limit</em> properties in the options.params property to establish the initial
7145 * position within the dataset, and the number of Records to cache on each read from the Proxy.
7147 * <strong>It is important to note that for remote data sources, loading is asynchronous,
7148 * and this call will return before the new data has been loaded. Perform any post-processing
7149 * in a callback function, or in a "load" event handler.</strong>
7151 * @param {Object} options An object containing properties which control loading options:<ul>
7152 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7153 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7154 * passed the following arguments:<ul>
7155 * <li>r : Roo.data.Record[]</li>
7156 * <li>options: Options object from the load call</li>
7157 * <li>success: Boolean success indicator</li></ul></li>
7158 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7159 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7162 load : function(options){
7163 options = options || {};
7164 if(this.fireEvent("beforeload", this, options) !== false){
7165 this.storeOptions(options);
7166 var p = Roo.apply(options.params || {}, this.baseParams);
7167 // if meta was not loaded from remote source.. try requesting it.
7168 if (!this.reader.metaFromRemote) {
7171 if(this.sortInfo && this.remoteSort){
7172 var pn = this.paramNames;
7173 p[pn["sort"]] = this.sortInfo.field;
7174 p[pn["dir"]] = this.sortInfo.direction;
7176 if (this.multiSort) {
7177 var pn = this.paramNames;
7178 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7181 this.proxy.load(p, this.reader, this.loadRecords, this, options);
7186 * Reloads the Record cache from the configured Proxy using the configured Reader and
7187 * the options from the last load operation performed.
7188 * @param {Object} options (optional) An object containing properties which may override the options
7189 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7190 * the most recently used options are reused).
7192 reload : function(options){
7193 this.load(Roo.applyIf(options||{}, this.lastOptions));
7197 // Called as a callback by the Reader during a load operation.
7198 loadRecords : function(o, options, success){
7199 if(!o || success === false){
7200 if(success !== false){
7201 this.fireEvent("load", this, [], options, o);
7203 if(options.callback){
7204 options.callback.call(options.scope || this, [], options, false);
7208 // if data returned failure - throw an exception.
7209 if (o.success === false) {
7210 // show a message if no listener is registered.
7211 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7212 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7214 // loadmask wil be hooked into this..
7215 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7218 var r = o.records, t = o.totalRecords || r.length;
7220 this.fireEvent("beforeloadadd", this, r, options, o);
7222 if(!options || options.add !== true){
7223 if(this.pruneModifiedRecords){
7226 for(var i = 0, len = r.length; i < len; i++){
7230 this.data = this.snapshot;
7231 delete this.snapshot;
7234 this.data.addAll(r);
7235 this.totalLength = t;
7237 this.fireEvent("datachanged", this);
7239 this.totalLength = Math.max(t, this.data.length+r.length);
7242 this.fireEvent("load", this, r, options, o);
7243 if(options.callback){
7244 options.callback.call(options.scope || this, r, options, true);
7250 * Loads data from a passed data block. A Reader which understands the format of the data
7251 * must have been configured in the constructor.
7252 * @param {Object} data The data block from which to read the Records. The format of the data expected
7253 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7254 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7256 loadData : function(o, append){
7257 var r = this.reader.readRecords(o);
7258 this.loadRecords(r, {add: append}, true);
7262 * Gets the number of cached records.
7264 * <em>If using paging, this may not be the total size of the dataset. If the data object
7265 * used by the Reader contains the dataset size, then the getTotalCount() function returns
7266 * the data set size</em>
7268 getCount : function(){
7269 return this.data.length || 0;
7273 * Gets the total number of records in the dataset as returned by the server.
7275 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7276 * the dataset size</em>
7278 getTotalCount : function(){
7279 return this.totalLength || 0;
7283 * Returns the sort state of the Store as an object with two properties:
7285 field {String} The name of the field by which the Records are sorted
7286 direction {String} The sort order, "ASC" or "DESC"
7289 getSortState : function(){
7290 return this.sortInfo;
7294 applySort : function(){
7295 if(this.sortInfo && !this.remoteSort){
7296 var s = this.sortInfo, f = s.field;
7297 var st = this.fields.get(f).sortType;
7298 var fn = function(r1, r2){
7299 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7300 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7302 this.data.sort(s.direction, fn);
7303 if(this.snapshot && this.snapshot != this.data){
7304 this.snapshot.sort(s.direction, fn);
7310 * Sets the default sort column and order to be used by the next load operation.
7311 * @param {String} fieldName The name of the field to sort by.
7312 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7314 setDefaultSort : function(field, dir){
7315 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7320 * If remote sorting is used, the sort is performed on the server, and the cache is
7321 * reloaded. If local sorting is used, the cache is sorted internally.
7322 * @param {String} fieldName The name of the field to sort by.
7323 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7325 sort : function(fieldName, dir){
7326 var f = this.fields.get(fieldName);
7328 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7330 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7331 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7336 this.sortToggle[f.name] = dir;
7337 this.sortInfo = {field: f.name, direction: dir};
7338 if(!this.remoteSort){
7340 this.fireEvent("datachanged", this);
7342 this.load(this.lastOptions);
7347 * Calls the specified function for each of the Records in the cache.
7348 * @param {Function} fn The function to call. The Record is passed as the first parameter.
7349 * Returning <em>false</em> aborts and exits the iteration.
7350 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7352 each : function(fn, scope){
7353 this.data.each(fn, scope);
7357 * Gets all records modified since the last commit. Modified records are persisted across load operations
7358 * (e.g., during paging).
7359 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7361 getModifiedRecords : function(){
7362 return this.modified;
7366 createFilterFn : function(property, value, anyMatch){
7367 if(!value.exec){ // not a regex
7368 value = String(value);
7369 if(value.length == 0){
7372 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7375 return value.test(r.data[property]);
7380 * Sums the value of <i>property</i> for each record between start and end and returns the result.
7381 * @param {String} property A field on your records
7382 * @param {Number} start The record index to start at (defaults to 0)
7383 * @param {Number} end The last record index to include (defaults to length - 1)
7384 * @return {Number} The sum
7386 sum : function(property, start, end){
7387 var rs = this.data.items, v = 0;
7389 end = (end || end === 0) ? end : rs.length-1;
7391 for(var i = start; i <= end; i++){
7392 v += (rs[i].data[property] || 0);
7398 * Filter the records by a specified property.
7399 * @param {String} field A field on your records
7400 * @param {String/RegExp} value Either a string that the field
7401 * should start with or a RegExp to test against the field
7402 * @param {Boolean} anyMatch True to match any part not just the beginning
7404 filter : function(property, value, anyMatch){
7405 var fn = this.createFilterFn(property, value, anyMatch);
7406 return fn ? this.filterBy(fn) : this.clearFilter();
7410 * Filter by a function. The specified function will be called with each
7411 * record in this data source. If the function returns true the record is included,
7412 * otherwise it is filtered.
7413 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7414 * @param {Object} scope (optional) The scope of the function (defaults to this)
7416 filterBy : function(fn, scope){
7417 this.snapshot = this.snapshot || this.data;
7418 this.data = this.queryBy(fn, scope||this);
7419 this.fireEvent("datachanged", this);
7423 * Query the records by a specified property.
7424 * @param {String} field A field on your records
7425 * @param {String/RegExp} value Either a string that the field
7426 * should start with or a RegExp to test against the field
7427 * @param {Boolean} anyMatch True to match any part not just the beginning
7428 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7430 query : function(property, value, anyMatch){
7431 var fn = this.createFilterFn(property, value, anyMatch);
7432 return fn ? this.queryBy(fn) : this.data.clone();
7436 * Query by a function. The specified function will be called with each
7437 * record in this data source. If the function returns true the record is included
7439 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7440 * @param {Object} scope (optional) The scope of the function (defaults to this)
7441 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7443 queryBy : function(fn, scope){
7444 var data = this.snapshot || this.data;
7445 return data.filterBy(fn, scope||this);
7449 * Collects unique values for a particular dataIndex from this store.
7450 * @param {String} dataIndex The property to collect
7451 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7452 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7453 * @return {Array} An array of the unique values
7455 collect : function(dataIndex, allowNull, bypassFilter){
7456 var d = (bypassFilter === true && this.snapshot) ?
7457 this.snapshot.items : this.data.items;
7458 var v, sv, r = [], l = {};
7459 for(var i = 0, len = d.length; i < len; i++){
7460 v = d[i].data[dataIndex];
7462 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7471 * Revert to a view of the Record cache with no filtering applied.
7472 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7474 clearFilter : function(suppressEvent){
7475 if(this.snapshot && this.snapshot != this.data){
7476 this.data = this.snapshot;
7477 delete this.snapshot;
7478 if(suppressEvent !== true){
7479 this.fireEvent("datachanged", this);
7485 afterEdit : function(record){
7486 if(this.modified.indexOf(record) == -1){
7487 this.modified.push(record);
7489 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7493 afterReject : function(record){
7494 this.modified.remove(record);
7495 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7499 afterCommit : function(record){
7500 this.modified.remove(record);
7501 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7505 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7506 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7508 commitChanges : function(){
7509 var m = this.modified.slice(0);
7511 for(var i = 0, len = m.length; i < len; i++){
7517 * Cancel outstanding changes on all changed records.
7519 rejectChanges : function(){
7520 var m = this.modified.slice(0);
7522 for(var i = 0, len = m.length; i < len; i++){
7527 onMetaChange : function(meta, rtype, o){
7528 this.recordType = rtype;
7529 this.fields = rtype.prototype.fields;
7530 delete this.snapshot;
7531 this.sortInfo = meta.sortInfo || this.sortInfo;
7533 this.fireEvent('metachange', this, this.reader.meta);
7536 moveIndex : function(data, type)
7538 var index = this.indexOf(data);
7540 var newIndex = index + type;
7544 this.insert(newIndex, data);
7549 * Ext JS Library 1.1.1
7550 * Copyright(c) 2006-2007, Ext JS, LLC.
7552 * Originally Released Under LGPL - original licence link has changed is not relivant.
7555 * <script type="text/javascript">
7559 * @class Roo.data.SimpleStore
7560 * @extends Roo.data.Store
7561 * Small helper class to make creating Stores from Array data easier.
7562 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7563 * @cfg {Array} fields An array of field definition objects, or field name strings.
7564 * @cfg {Array} data The multi-dimensional array of data
7566 * @param {Object} config
7568 Roo.data.SimpleStore = function(config){
7569 Roo.data.SimpleStore.superclass.constructor.call(this, {
7571 reader: new Roo.data.ArrayReader({
7574 Roo.data.Record.create(config.fields)
7576 proxy : new Roo.data.MemoryProxy(config.data)
7580 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7582 * Ext JS Library 1.1.1
7583 * Copyright(c) 2006-2007, Ext JS, LLC.
7585 * Originally Released Under LGPL - original licence link has changed is not relivant.
7588 * <script type="text/javascript">
7593 * @extends Roo.data.Store
7594 * @class Roo.data.JsonStore
7595 * Small helper class to make creating Stores for JSON data easier. <br/>
7597 var store = new Roo.data.JsonStore({
7598 url: 'get-images.php',
7600 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7603 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7604 * JsonReader and HttpProxy (unless inline data is provided).</b>
7605 * @cfg {Array} fields An array of field definition objects, or field name strings.
7607 * @param {Object} config
7609 Roo.data.JsonStore = function(c){
7610 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7611 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7612 reader: new Roo.data.JsonReader(c, c.fields)
7615 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7617 * Ext JS Library 1.1.1
7618 * Copyright(c) 2006-2007, Ext JS, LLC.
7620 * Originally Released Under LGPL - original licence link has changed is not relivant.
7623 * <script type="text/javascript">
7627 Roo.data.Field = function(config){
7628 if(typeof config == "string"){
7629 config = {name: config};
7631 Roo.apply(this, config);
7637 var st = Roo.data.SortTypes;
7638 // named sortTypes are supported, here we look them up
7639 if(typeof this.sortType == "string"){
7640 this.sortType = st[this.sortType];
7643 // set default sortType for strings and dates
7647 this.sortType = st.asUCString;
7650 this.sortType = st.asDate;
7653 this.sortType = st.none;
7658 var stripRe = /[\$,%]/g;
7660 // prebuilt conversion function for this field, instead of
7661 // switching every time we're reading a value
7663 var cv, dateFormat = this.dateFormat;
7668 cv = function(v){ return v; };
7671 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7675 return v !== undefined && v !== null && v !== '' ?
7676 parseInt(String(v).replace(stripRe, ""), 10) : '';
7681 return v !== undefined && v !== null && v !== '' ?
7682 parseFloat(String(v).replace(stripRe, ""), 10) : '';
7687 cv = function(v){ return v === true || v === "true" || v == 1; };
7694 if(v instanceof Date){
7698 if(dateFormat == "timestamp"){
7699 return new Date(v*1000);
7701 return Date.parseDate(v, dateFormat);
7703 var parsed = Date.parse(v);
7704 return parsed ? new Date(parsed) : null;
7713 Roo.data.Field.prototype = {
7721 * Ext JS Library 1.1.1
7722 * Copyright(c) 2006-2007, Ext JS, LLC.
7724 * Originally Released Under LGPL - original licence link has changed is not relivant.
7727 * <script type="text/javascript">
7730 // Base class for reading structured data from a data source. This class is intended to be
7731 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7734 * @class Roo.data.DataReader
7735 * Base class for reading structured data from a data source. This class is intended to be
7736 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7739 Roo.data.DataReader = function(meta, recordType){
7743 this.recordType = recordType instanceof Array ?
7744 Roo.data.Record.create(recordType) : recordType;
7747 Roo.data.DataReader.prototype = {
7749 * Create an empty record
7750 * @param {Object} data (optional) - overlay some values
7751 * @return {Roo.data.Record} record created.
7753 newRow : function(d) {
7755 this.recordType.prototype.fields.each(function(c) {
7757 case 'int' : da[c.name] = 0; break;
7758 case 'date' : da[c.name] = new Date(); break;
7759 case 'float' : da[c.name] = 0.0; break;
7760 case 'boolean' : da[c.name] = false; break;
7761 default : da[c.name] = ""; break;
7765 return new this.recordType(Roo.apply(da, d));
7770 * Ext JS Library 1.1.1
7771 * Copyright(c) 2006-2007, Ext JS, LLC.
7773 * Originally Released Under LGPL - original licence link has changed is not relivant.
7776 * <script type="text/javascript">
7780 * @class Roo.data.DataProxy
7781 * @extends Roo.data.Observable
7782 * This class is an abstract base class for implementations which provide retrieval of
7783 * unformatted data objects.<br>
7785 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7786 * (of the appropriate type which knows how to parse the data object) to provide a block of
7787 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7789 * Custom implementations must implement the load method as described in
7790 * {@link Roo.data.HttpProxy#load}.
7792 Roo.data.DataProxy = function(){
7796 * Fires before a network request is made to retrieve a data object.
7797 * @param {Object} This DataProxy object.
7798 * @param {Object} params The params parameter to the load function.
7803 * Fires before the load method's callback is called.
7804 * @param {Object} This DataProxy object.
7805 * @param {Object} o The data object.
7806 * @param {Object} arg The callback argument object passed to the load function.
7810 * @event loadexception
7811 * Fires if an Exception occurs during data retrieval.
7812 * @param {Object} This DataProxy object.
7813 * @param {Object} o The data object.
7814 * @param {Object} arg The callback argument object passed to the load function.
7815 * @param {Object} e The Exception.
7817 loadexception : true
7819 Roo.data.DataProxy.superclass.constructor.call(this);
7822 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7825 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7829 * Ext JS Library 1.1.1
7830 * Copyright(c) 2006-2007, Ext JS, LLC.
7832 * Originally Released Under LGPL - original licence link has changed is not relivant.
7835 * <script type="text/javascript">
7838 * @class Roo.data.MemoryProxy
7839 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7840 * to the Reader when its load method is called.
7842 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7844 Roo.data.MemoryProxy = function(data){
7848 Roo.data.MemoryProxy.superclass.constructor.call(this);
7852 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7854 * Load data from the requested source (in this case an in-memory
7855 * data object passed to the constructor), read the data object into
7856 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7857 * process that block using the passed callback.
7858 * @param {Object} params This parameter is not used by the MemoryProxy class.
7859 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7860 * object into a block of Roo.data.Records.
7861 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7862 * The function must be passed <ul>
7863 * <li>The Record block object</li>
7864 * <li>The "arg" argument from the load function</li>
7865 * <li>A boolean success indicator</li>
7867 * @param {Object} scope The scope in which to call the callback
7868 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7870 load : function(params, reader, callback, scope, arg){
7871 params = params || {};
7874 result = reader.readRecords(this.data);
7876 this.fireEvent("loadexception", this, arg, null, e);
7877 callback.call(scope, null, arg, false);
7880 callback.call(scope, result, arg, true);
7884 update : function(params, records){
7889 * Ext JS Library 1.1.1
7890 * Copyright(c) 2006-2007, Ext JS, LLC.
7892 * Originally Released Under LGPL - original licence link has changed is not relivant.
7895 * <script type="text/javascript">
7898 * @class Roo.data.HttpProxy
7899 * @extends Roo.data.DataProxy
7900 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7901 * configured to reference a certain URL.<br><br>
7903 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7904 * from which the running page was served.<br><br>
7906 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7908 * Be aware that to enable the browser to parse an XML document, the server must set
7909 * the Content-Type header in the HTTP response to "text/xml".
7911 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7912 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7913 * will be used to make the request.
7915 Roo.data.HttpProxy = function(conn){
7916 Roo.data.HttpProxy.superclass.constructor.call(this);
7917 // is conn a conn config or a real conn?
7919 this.useAjax = !conn || !conn.events;
7923 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7924 // thse are take from connection...
7927 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7930 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7931 * extra parameters to each request made by this object. (defaults to undefined)
7934 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7935 * to each request made by this object. (defaults to undefined)
7938 * @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)
7941 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7944 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7950 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7954 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7955 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7956 * a finer-grained basis than the DataProxy events.
7958 getConnection : function(){
7959 return this.useAjax ? Roo.Ajax : this.conn;
7963 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7964 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7965 * process that block using the passed callback.
7966 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7967 * for the request to the remote server.
7968 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7969 * object into a block of Roo.data.Records.
7970 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7971 * The function must be passed <ul>
7972 * <li>The Record block object</li>
7973 * <li>The "arg" argument from the load function</li>
7974 * <li>A boolean success indicator</li>
7976 * @param {Object} scope The scope in which to call the callback
7977 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7979 load : function(params, reader, callback, scope, arg){
7980 if(this.fireEvent("beforeload", this, params) !== false){
7982 params : params || {},
7984 callback : callback,
7989 callback : this.loadResponse,
7993 Roo.applyIf(o, this.conn);
7994 if(this.activeRequest){
7995 Roo.Ajax.abort(this.activeRequest);
7997 this.activeRequest = Roo.Ajax.request(o);
7999 this.conn.request(o);
8002 callback.call(scope||this, null, arg, false);
8007 loadResponse : function(o, success, response){
8008 delete this.activeRequest;
8010 this.fireEvent("loadexception", this, o, response);
8011 o.request.callback.call(o.request.scope, null, o.request.arg, false);
8016 result = o.reader.read(response);
8018 this.fireEvent("loadexception", this, o, response, e);
8019 o.request.callback.call(o.request.scope, null, o.request.arg, false);
8023 this.fireEvent("load", this, o, o.request.arg);
8024 o.request.callback.call(o.request.scope, result, o.request.arg, true);
8028 update : function(dataSet){
8033 updateResponse : function(dataSet){
8038 * Ext JS Library 1.1.1
8039 * Copyright(c) 2006-2007, Ext JS, LLC.
8041 * Originally Released Under LGPL - original licence link has changed is not relivant.
8044 * <script type="text/javascript">
8048 * @class Roo.data.ScriptTagProxy
8049 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8050 * other than the originating domain of the running page.<br><br>
8052 * <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
8053 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8055 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8056 * source code that is used as the source inside a <script> tag.<br><br>
8058 * In order for the browser to process the returned data, the server must wrap the data object
8059 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8060 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8061 * depending on whether the callback name was passed:
8064 boolean scriptTag = false;
8065 String cb = request.getParameter("callback");
8068 response.setContentType("text/javascript");
8070 response.setContentType("application/x-json");
8072 Writer out = response.getWriter();
8074 out.write(cb + "(");
8076 out.print(dataBlock.toJsonString());
8083 * @param {Object} config A configuration object.
8085 Roo.data.ScriptTagProxy = function(config){
8086 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8087 Roo.apply(this, config);
8088 this.head = document.getElementsByTagName("head")[0];
8091 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8093 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8095 * @cfg {String} url The URL from which to request the data object.
8098 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8102 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8103 * the server the name of the callback function set up by the load call to process the returned data object.
8104 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8105 * javascript output which calls this named function passing the data object as its only parameter.
8107 callbackParam : "callback",
8109 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8110 * name to the request.
8115 * Load data from the configured URL, read the data object into
8116 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8117 * process that block using the passed callback.
8118 * @param {Object} params An object containing properties which are to be used as HTTP parameters
8119 * for the request to the remote server.
8120 * @param {Roo.data.DataReader} reader The Reader object which converts the data
8121 * object into a block of Roo.data.Records.
8122 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8123 * The function must be passed <ul>
8124 * <li>The Record block object</li>
8125 * <li>The "arg" argument from the load function</li>
8126 * <li>A boolean success indicator</li>
8128 * @param {Object} scope The scope in which to call the callback
8129 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8131 load : function(params, reader, callback, scope, arg){
8132 if(this.fireEvent("beforeload", this, params) !== false){
8134 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8137 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8139 url += "&_dc=" + (new Date().getTime());
8141 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8144 cb : "stcCallback"+transId,
8145 scriptId : "stcScript"+transId,
8149 callback : callback,
8155 window[trans.cb] = function(o){
8156 conn.handleResponse(o, trans);
8159 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8161 if(this.autoAbort !== false){
8165 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8167 var script = document.createElement("script");
8168 script.setAttribute("src", url);
8169 script.setAttribute("type", "text/javascript");
8170 script.setAttribute("id", trans.scriptId);
8171 this.head.appendChild(script);
8175 callback.call(scope||this, null, arg, false);
8180 isLoading : function(){
8181 return this.trans ? true : false;
8185 * Abort the current server request.
8188 if(this.isLoading()){
8189 this.destroyTrans(this.trans);
8194 destroyTrans : function(trans, isLoaded){
8195 this.head.removeChild(document.getElementById(trans.scriptId));
8196 clearTimeout(trans.timeoutId);
8198 window[trans.cb] = undefined;
8200 delete window[trans.cb];
8203 // if hasn't been loaded, wait for load to remove it to prevent script error
8204 window[trans.cb] = function(){
8205 window[trans.cb] = undefined;
8207 delete window[trans.cb];
8214 handleResponse : function(o, trans){
8216 this.destroyTrans(trans, true);
8219 result = trans.reader.readRecords(o);
8221 this.fireEvent("loadexception", this, o, trans.arg, e);
8222 trans.callback.call(trans.scope||window, null, trans.arg, false);
8225 this.fireEvent("load", this, o, trans.arg);
8226 trans.callback.call(trans.scope||window, result, trans.arg, true);
8230 handleFailure : function(trans){
8232 this.destroyTrans(trans, false);
8233 this.fireEvent("loadexception", this, null, trans.arg);
8234 trans.callback.call(trans.scope||window, null, trans.arg, false);
8238 * Ext JS Library 1.1.1
8239 * Copyright(c) 2006-2007, Ext JS, LLC.
8241 * Originally Released Under LGPL - original licence link has changed is not relivant.
8244 * <script type="text/javascript">
8248 * @class Roo.data.JsonReader
8249 * @extends Roo.data.DataReader
8250 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8251 * based on mappings in a provided Roo.data.Record constructor.
8253 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8254 * in the reply previously.
8259 var RecordDef = Roo.data.Record.create([
8260 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
8261 {name: 'occupation'} // This field will use "occupation" as the mapping.
8263 var myReader = new Roo.data.JsonReader({
8264 totalProperty: "results", // The property which contains the total dataset size (optional)
8265 root: "rows", // The property which contains an Array of row objects
8266 id: "id" // The property within each row object that provides an ID for the record (optional)
8270 * This would consume a JSON file like this:
8272 { 'results': 2, 'rows': [
8273 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8274 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8277 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8278 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8279 * paged from the remote server.
8280 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8281 * @cfg {String} root name of the property which contains the Array of row objects.
8282 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8284 * Create a new JsonReader
8285 * @param {Object} meta Metadata configuration options
8286 * @param {Object} recordType Either an Array of field definition objects,
8287 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8289 Roo.data.JsonReader = function(meta, recordType){
8292 // set some defaults:
8294 totalProperty: 'total',
8295 successProperty : 'success',
8300 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8302 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8305 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
8306 * Used by Store query builder to append _requestMeta to params.
8309 metaFromRemote : false,
8311 * This method is only used by a DataProxy which has retrieved data from a remote server.
8312 * @param {Object} response The XHR object which contains the JSON data in its responseText.
8313 * @return {Object} data A data block which is used by an Roo.data.Store object as
8314 * a cache of Roo.data.Records.
8316 read : function(response){
8317 var json = response.responseText;
8319 var o = /* eval:var:o */ eval("("+json+")");
8321 throw {message: "JsonReader.read: Json object not found"};
8327 this.metaFromRemote = true;
8328 this.meta = o.metaData;
8329 this.recordType = Roo.data.Record.create(o.metaData.fields);
8330 this.onMetaChange(this.meta, this.recordType, o);
8332 return this.readRecords(o);
8335 // private function a store will implement
8336 onMetaChange : function(meta, recordType, o){
8343 simpleAccess: function(obj, subsc) {
8350 getJsonAccessor: function(){
8352 return function(expr) {
8354 return(re.test(expr))
8355 ? new Function("obj", "return obj." + expr)
8365 * Create a data block containing Roo.data.Records from an XML document.
8366 * @param {Object} o An object which contains an Array of row objects in the property specified
8367 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8368 * which contains the total size of the dataset.
8369 * @return {Object} data A data block which is used by an Roo.data.Store object as
8370 * a cache of Roo.data.Records.
8372 readRecords : function(o){
8374 * After any data loads, the raw JSON data is available for further custom processing.
8378 var s = this.meta, Record = this.recordType,
8379 f = Record.prototype.fields, fi = f.items, fl = f.length;
8381 // Generate extraction functions for the totalProperty, the root, the id, and for each field
8383 if(s.totalProperty) {
8384 this.getTotal = this.getJsonAccessor(s.totalProperty);
8386 if(s.successProperty) {
8387 this.getSuccess = this.getJsonAccessor(s.successProperty);
8389 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8391 var g = this.getJsonAccessor(s.id);
8392 this.getId = function(rec) {
8394 return (r === undefined || r === "") ? null : r;
8397 this.getId = function(){return null;};
8400 for(var jj = 0; jj < fl; jj++){
8402 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8403 this.ef[jj] = this.getJsonAccessor(map);
8407 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8408 if(s.totalProperty){
8409 var vt = parseInt(this.getTotal(o), 10);
8414 if(s.successProperty){
8415 var vs = this.getSuccess(o);
8416 if(vs === false || vs === 'false'){
8421 for(var i = 0; i < c; i++){
8424 var id = this.getId(n);
8425 for(var j = 0; j < fl; j++){
8427 var v = this.ef[j](n);
8429 Roo.log('missing convert for ' + f.name);
8433 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8435 var record = new Record(values, id);
8437 records[i] = record;
8443 totalRecords : totalRecords
8448 * Ext JS Library 1.1.1
8449 * Copyright(c) 2006-2007, Ext JS, LLC.
8451 * Originally Released Under LGPL - original licence link has changed is not relivant.
8454 * <script type="text/javascript">
8458 * @class Roo.data.ArrayReader
8459 * @extends Roo.data.DataReader
8460 * Data reader class to create an Array of Roo.data.Record objects from an Array.
8461 * Each element of that Array represents a row of data fields. The
8462 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8463 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8467 var RecordDef = Roo.data.Record.create([
8468 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
8469 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
8471 var myReader = new Roo.data.ArrayReader({
8472 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
8476 * This would consume an Array like this:
8478 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8480 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8482 * Create a new JsonReader
8483 * @param {Object} meta Metadata configuration options.
8484 * @param {Object} recordType Either an Array of field definition objects
8485 * as specified to {@link Roo.data.Record#create},
8486 * or an {@link Roo.data.Record} object
8487 * created using {@link Roo.data.Record#create}.
8489 Roo.data.ArrayReader = function(meta, recordType){
8490 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8493 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8495 * Create a data block containing Roo.data.Records from an XML document.
8496 * @param {Object} o An Array of row objects which represents the dataset.
8497 * @return {Object} data A data block which is used by an Roo.data.Store object as
8498 * a cache of Roo.data.Records.
8500 readRecords : function(o){
8501 var sid = this.meta ? this.meta.id : null;
8502 var recordType = this.recordType, fields = recordType.prototype.fields;
8505 for(var i = 0; i < root.length; i++){
8508 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8509 for(var j = 0, jlen = fields.length; j < jlen; j++){
8510 var f = fields.items[j];
8511 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8512 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8516 var record = new recordType(values, id);
8518 records[records.length] = record;
8522 totalRecords : records.length
8531 * @class Roo.bootstrap.ComboBox
8532 * @extends Roo.bootstrap.TriggerField
8533 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8534 * @cfg {Boolean} append (true|false) default false
8536 * Create a new ComboBox.
8537 * @param {Object} config Configuration options
8539 Roo.bootstrap.ComboBox = function(config){
8540 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8544 * Fires when the dropdown list is expanded
8545 * @param {Roo.bootstrap.ComboBox} combo This combo box
8550 * Fires when the dropdown list is collapsed
8551 * @param {Roo.bootstrap.ComboBox} combo This combo box
8555 * @event beforeselect
8556 * Fires before a list item is selected. Return false to cancel the selection.
8557 * @param {Roo.bootstrap.ComboBox} combo This combo box
8558 * @param {Roo.data.Record} record The data record returned from the underlying store
8559 * @param {Number} index The index of the selected item in the dropdown list
8561 'beforeselect' : true,
8564 * Fires when a list item is selected
8565 * @param {Roo.bootstrap.ComboBox} combo This combo box
8566 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8567 * @param {Number} index The index of the selected item in the dropdown list
8571 * @event beforequery
8572 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8573 * The event object passed has these properties:
8574 * @param {Roo.bootstrap.ComboBox} combo This combo box
8575 * @param {String} query The query
8576 * @param {Boolean} forceAll true to force "all" query
8577 * @param {Boolean} cancel true to cancel the query
8578 * @param {Object} e The query event object
8580 'beforequery': true,
8583 * Fires when the 'add' icon is pressed (add a listener to enable add button)
8584 * @param {Roo.bootstrap.ComboBox} combo This combo box
8589 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8590 * @param {Roo.bootstrap.ComboBox} combo This combo box
8591 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8596 * Fires when the remove value from the combobox array
8597 * @param {Roo.bootstrap.ComboBox} combo This combo box
8604 this.selectedIndex = -1;
8605 if(this.mode == 'local'){
8606 if(config.queryDelay === undefined){
8607 this.queryDelay = 10;
8609 if(config.minChars === undefined){
8615 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8618 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8619 * rendering into an Roo.Editor, defaults to false)
8622 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8623 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8626 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8629 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8630 * the dropdown list (defaults to undefined, with no header element)
8634 * @cfg {String/Roo.Template} tpl The template to use to render the output
8638 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8640 listWidth: undefined,
8642 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8643 * mode = 'remote' or 'text' if mode = 'local')
8645 displayField: undefined,
8647 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8648 * mode = 'remote' or 'value' if mode = 'local').
8649 * Note: use of a valueField requires the user make a selection
8650 * in order for a value to be mapped.
8652 valueField: undefined,
8656 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8657 * field's data value (defaults to the underlying DOM element's name)
8659 hiddenName: undefined,
8661 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8665 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8667 selectedClass: 'active',
8670 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8674 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8675 * anchor positions (defaults to 'tl-bl')
8677 listAlign: 'tl-bl?',
8679 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8683 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
8684 * query specified by the allQuery config option (defaults to 'query')
8686 triggerAction: 'query',
8688 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8689 * (defaults to 4, does not apply if editable = false)
8693 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8694 * delay (typeAheadDelay) if it matches a known value (defaults to false)
8698 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8699 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8703 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8704 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
8708 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
8709 * when editable = true (defaults to false)
8711 selectOnFocus:false,
8713 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8715 queryParam: 'query',
8717 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
8718 * when mode = 'remote' (defaults to 'Loading...')
8720 loadingText: 'Loading...',
8722 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8726 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8730 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8731 * traditional select (defaults to true)
8735 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8739 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8743 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8744 * listWidth has a higher value)
8748 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8749 * allow the user to set arbitrary text into the field (defaults to false)
8751 forceSelection:false,
8753 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8754 * if typeAhead = true (defaults to 250)
8756 typeAheadDelay : 250,
8758 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8759 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8761 valueNotFoundText : undefined,
8763 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8768 * @cfg {Boolean} disableClear Disable showing of clear button.
8770 disableClear : false,
8772 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8774 alwaysQuery : false,
8777 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8791 // element that contains real text value.. (when hidden is used..)
8794 initEvents: function(){
8797 throw "can not find store for combo";
8799 this.store = Roo.factory(this.store, Roo.data);
8803 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8806 if(this.hiddenName){
8808 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8810 this.hiddenField.dom.value =
8811 this.hiddenValue !== undefined ? this.hiddenValue :
8812 this.value !== undefined ? this.value : '';
8814 // prevent input submission
8815 this.el.dom.removeAttribute('name');
8816 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8821 // this.el.dom.setAttribute('autocomplete', 'off');
8824 var cls = 'x-combo-list';
8825 this.list = this.el.select('ul.dropdown-menu',true).first();
8827 //this.list = new Roo.Layer({
8828 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8831 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8832 this.list.setWidth(lw);
8834 this.list.on('mouseover', this.onViewOver, this);
8835 this.list.on('mousemove', this.onViewMove, this);
8837 this.list.on('scroll', this.onViewScroll, this);
8840 this.list.swallowEvent('mousewheel');
8841 this.assetHeight = 0;
8844 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8845 this.assetHeight += this.header.getHeight();
8848 this.innerList = this.list.createChild({cls:cls+'-inner'});
8849 this.innerList.on('mouseover', this.onViewOver, this);
8850 this.innerList.on('mousemove', this.onViewMove, this);
8851 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8853 if(this.allowBlank && !this.pageSize && !this.disableClear){
8854 this.footer = this.list.createChild({cls:cls+'-ft'});
8855 this.pageTb = new Roo.Toolbar(this.footer);
8859 this.footer = this.list.createChild({cls:cls+'-ft'});
8860 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8861 {pageSize: this.pageSize});
8865 if (this.pageTb && this.allowBlank && !this.disableClear) {
8867 this.pageTb.add(new Roo.Toolbar.Fill(), {
8868 cls: 'x-btn-icon x-btn-clear',
8874 _this.onSelect(false, -1);
8879 this.assetHeight += this.footer.getHeight();
8884 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8887 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8888 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8890 //this.view.wrapEl.setDisplayed(false);
8891 this.view.on('click', this.onViewClick, this);
8895 this.store.on('beforeload', this.onBeforeLoad, this);
8896 this.store.on('load', this.onLoad, this);
8897 this.store.on('loadexception', this.onLoadException, this);
8900 this.resizer = new Roo.Resizable(this.list, {
8901 pinned:true, handles:'se'
8903 this.resizer.on('resize', function(r, w, h){
8904 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8906 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8907 this.restrictHeight();
8909 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8913 this.editable = true;
8914 this.setEditable(false);
8919 if (typeof(this.events.add.listeners) != 'undefined') {
8921 this.addicon = this.wrap.createChild(
8922 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8924 this.addicon.on('click', function(e) {
8925 this.fireEvent('add', this);
8928 if (typeof(this.events.edit.listeners) != 'undefined') {
8930 this.editicon = this.wrap.createChild(
8931 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8933 this.editicon.setStyle('margin-left', '40px');
8935 this.editicon.on('click', function(e) {
8937 // we fire even if inothing is selected..
8938 this.fireEvent('edit', this, this.lastData );
8944 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8946 this.inKeyMode = true;
8950 "down" : function(e){
8951 if(!this.isExpanded()){
8952 this.onTriggerClick();
8954 this.inKeyMode = true;
8959 "enter" : function(e){
8964 "esc" : function(e){
8968 "tab" : function(e){
8971 if(this.fireEvent("specialkey", this, e)){
8972 this.onViewClick(false);
8980 doRelay : function(foo, bar, hname){
8981 if(hname == 'down' || this.scope.isExpanded()){
8982 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8991 this.queryDelay = Math.max(this.queryDelay || 10,
8992 this.mode == 'local' ? 10 : 250);
8995 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8998 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9000 if(this.editable !== false){
9001 this.inputEl().on("keyup", this.onKeyUp, this);
9003 if(this.forceSelection){
9004 this.on('blur', this.doForce, this);
9008 this.choices = this.el.select('ul.select2-choices', true).first();
9009 this.searchField = this.el.select('ul li.select2-search-field', true).first();
9013 onDestroy : function(){
9015 this.view.setStore(null);
9016 this.view.el.removeAllListeners();
9017 this.view.el.remove();
9018 this.view.purgeListeners();
9021 this.list.dom.innerHTML = '';
9024 this.store.un('beforeload', this.onBeforeLoad, this);
9025 this.store.un('load', this.onLoad, this);
9026 this.store.un('loadexception', this.onLoadException, this);
9028 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9032 fireKey : function(e){
9033 if(e.isNavKeyPress() && !this.list.isVisible()){
9034 this.fireEvent("specialkey", this, e);
9039 onResize: function(w, h){
9040 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9042 // if(typeof w != 'number'){
9043 // // we do not handle it!?!?
9046 // var tw = this.trigger.getWidth();
9047 // // tw += this.addicon ? this.addicon.getWidth() : 0;
9048 // // tw += this.editicon ? this.editicon.getWidth() : 0;
9050 // this.inputEl().setWidth( this.adjustWidth('input', x));
9052 // //this.trigger.setStyle('left', x+'px');
9054 // if(this.list && this.listWidth === undefined){
9055 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9056 // this.list.setWidth(lw);
9057 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9065 * Allow or prevent the user from directly editing the field text. If false is passed,
9066 * the user will only be able to select from the items defined in the dropdown list. This method
9067 * is the runtime equivalent of setting the 'editable' config option at config time.
9068 * @param {Boolean} value True to allow the user to directly edit the field text
9070 setEditable : function(value){
9071 if(value == this.editable){
9074 this.editable = value;
9076 this.inputEl().dom.setAttribute('readOnly', true);
9077 this.inputEl().on('mousedown', this.onTriggerClick, this);
9078 this.inputEl().addClass('x-combo-noedit');
9080 this.inputEl().dom.setAttribute('readOnly', false);
9081 this.inputEl().un('mousedown', this.onTriggerClick, this);
9082 this.inputEl().removeClass('x-combo-noedit');
9088 onBeforeLoad : function(combo,opts){
9093 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9095 this.restrictHeight();
9096 this.selectedIndex = -1;
9100 onLoad : function(){
9102 this.hasQuery = false;
9108 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9109 this.loading.hide();
9112 if(this.store.getCount() > 0){
9114 this.restrictHeight();
9115 if(this.lastQuery == this.allQuery){
9117 this.inputEl().dom.select();
9119 if(!this.selectByValue(this.value, true)){
9120 this.select(0, true);
9124 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9125 this.taTask.delay(this.typeAheadDelay);
9129 this.onEmptyResults();
9135 onLoadException : function()
9137 this.hasQuery = false;
9139 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9140 this.loading.hide();
9144 Roo.log(this.store.reader.jsonData);
9145 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9147 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9153 onTypeAhead : function(){
9154 if(this.store.getCount() > 0){
9155 var r = this.store.getAt(0);
9156 var newValue = r.data[this.displayField];
9157 var len = newValue.length;
9158 var selStart = this.getRawValue().length;
9160 if(selStart != len){
9161 this.setRawValue(newValue);
9162 this.selectText(selStart, newValue.length);
9168 onSelect : function(record, index){
9170 if(this.fireEvent('beforeselect', this, record, index) !== false){
9172 this.setFromData(index > -1 ? record.data : false);
9175 this.fireEvent('select', this, record, index);
9180 * Returns the currently selected field value or empty string if no value is set.
9181 * @return {String} value The selected value
9183 getValue : function(){
9186 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9189 if(this.valueField){
9190 return typeof this.value != 'undefined' ? this.value : '';
9192 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9197 * Clears any text/value currently set in the field
9199 clearValue : function(){
9200 if(this.hiddenField){
9201 this.hiddenField.dom.value = '';
9204 this.setRawValue('');
9205 this.lastSelectionText = '';
9210 * Sets the specified value into the field. If the value finds a match, the corresponding record text
9211 * will be displayed in the field. If the value does not match the data value of an existing item,
9212 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9213 * Otherwise the field will be blank (although the value will still be set).
9214 * @param {String} value The value to match
9216 setValue : function(v){
9223 if(this.valueField){
9224 var r = this.findRecord(this.valueField, v);
9226 text = r.data[this.displayField];
9227 }else if(this.valueNotFoundText !== undefined){
9228 text = this.valueNotFoundText;
9231 this.lastSelectionText = text;
9232 if(this.hiddenField){
9233 this.hiddenField.dom.value = v;
9235 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9239 * @property {Object} the last set data for the element
9244 * Sets the value of the field based on a object which is related to the record format for the store.
9245 * @param {Object} value the value to set as. or false on reset?
9247 setFromData : function(o){
9254 var dv = ''; // display value
9255 var vv = ''; // value value..
9257 if (this.displayField) {
9258 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9260 // this is an error condition!!!
9261 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
9264 if(this.valueField){
9265 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9268 if(this.hiddenField){
9269 this.hiddenField.dom.value = vv;
9271 this.lastSelectionText = dv;
9272 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9276 // no hidden field.. - we store the value in 'value', but still display
9277 // display field!!!!
9278 this.lastSelectionText = dv;
9279 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9286 // overridden so that last data is reset..
9287 this.setValue(this.originalValue);
9288 this.clearInvalid();
9289 this.lastData = false;
9291 this.view.clearSelections();
9295 findRecord : function(prop, value){
9297 if(this.store.getCount() > 0){
9298 this.store.each(function(r){
9299 if(r.data[prop] == value){
9311 // returns hidden if it's set..
9312 if (!this.rendered) {return ''};
9313 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
9317 onViewMove : function(e, t){
9318 this.inKeyMode = false;
9322 onViewOver : function(e, t){
9323 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9326 var item = this.view.findItemFromChild(t);
9328 var index = this.view.indexOf(item);
9329 this.select(index, false);
9334 onViewClick : function(doFocus)
9336 var index = this.view.getSelectedIndexes()[0];
9337 var r = this.store.getAt(index);
9339 this.onSelect(r, index);
9341 if(doFocus !== false && !this.blockFocus){
9342 this.inputEl().focus();
9347 restrictHeight : function(){
9348 //this.innerList.dom.style.height = '';
9349 //var inner = this.innerList.dom;
9350 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9351 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9352 //this.list.beginUpdate();
9353 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9354 this.list.alignTo(this.inputEl(), this.listAlign);
9355 //this.list.endUpdate();
9359 onEmptyResults : function(){
9364 * Returns true if the dropdown list is expanded, else false.
9366 isExpanded : function(){
9367 return this.list.isVisible();
9371 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9372 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9373 * @param {String} value The data value of the item to select
9374 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9375 * selected item if it is not currently in view (defaults to true)
9376 * @return {Boolean} True if the value matched an item in the list, else false
9378 selectByValue : function(v, scrollIntoView){
9379 if(v !== undefined && v !== null){
9380 var r = this.findRecord(this.valueField || this.displayField, v);
9382 this.select(this.store.indexOf(r), scrollIntoView);
9390 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9391 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9392 * @param {Number} index The zero-based index of the list item to select
9393 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9394 * selected item if it is not currently in view (defaults to true)
9396 select : function(index, scrollIntoView){
9397 this.selectedIndex = index;
9398 this.view.select(index);
9399 if(scrollIntoView !== false){
9400 var el = this.view.getNode(index);
9402 //this.innerList.scrollChildIntoView(el, false);
9409 selectNext : function(){
9410 var ct = this.store.getCount();
9412 if(this.selectedIndex == -1){
9414 }else if(this.selectedIndex < ct-1){
9415 this.select(this.selectedIndex+1);
9421 selectPrev : function(){
9422 var ct = this.store.getCount();
9424 if(this.selectedIndex == -1){
9426 }else if(this.selectedIndex != 0){
9427 this.select(this.selectedIndex-1);
9433 onKeyUp : function(e){
9434 if(this.editable !== false && !e.isSpecialKey()){
9435 this.lastKey = e.getKey();
9436 this.dqTask.delay(this.queryDelay);
9441 validateBlur : function(){
9442 return !this.list || !this.list.isVisible();
9446 initQuery : function(){
9447 this.doQuery(this.getRawValue());
9451 doForce : function(){
9452 if(this.el.dom.value.length > 0){
9454 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9460 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
9461 * query allowing the query action to be canceled if needed.
9462 * @param {String} query The SQL query to execute
9463 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9464 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
9465 * saved in the current store (defaults to false)
9467 doQuery : function(q, forceAll){
9469 if(q === undefined || q === null){
9478 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9483 forceAll = qe.forceAll;
9484 if(forceAll === true || (q.length >= this.minChars)){
9486 this.hasQuery = true;
9488 if(this.lastQuery != q || this.alwaysQuery){
9490 if(this.mode == 'local'){
9491 this.selectedIndex = -1;
9493 this.store.clearFilter();
9495 this.store.filter(this.displayField, q);
9499 this.store.baseParams[this.queryParam] = q;
9501 var options = {params : this.getParams(q)};
9505 options.params.start = this.page * this.pageSize;
9508 this.store.load(options);
9512 this.selectedIndex = -1;
9517 this.loadNext = false;
9521 getParams : function(q){
9523 //p[this.queryParam] = q;
9527 p.limit = this.pageSize;
9533 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9535 collapse : function(){
9536 if(!this.isExpanded()){
9541 Roo.get(document).un('mousedown', this.collapseIf, this);
9542 Roo.get(document).un('mousewheel', this.collapseIf, this);
9543 if (!this.editable) {
9544 Roo.get(document).un('keydown', this.listKeyPress, this);
9546 this.fireEvent('collapse', this);
9550 collapseIf : function(e){
9551 var in_combo = e.within(this.el);
9552 var in_list = e.within(this.list);
9554 if (in_combo || in_list) {
9555 //e.stopPropagation();
9564 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9566 expand : function(){
9568 if(this.isExpanded() || !this.hasFocus){
9572 this.list.alignTo(this.inputEl(), this.listAlign);
9574 Roo.get(document).on('mousedown', this.collapseIf, this);
9575 Roo.get(document).on('mousewheel', this.collapseIf, this);
9576 if (!this.editable) {
9577 Roo.get(document).on('keydown', this.listKeyPress, this);
9580 this.fireEvent('expand', this);
9584 // Implements the default empty TriggerField.onTriggerClick function
9585 onTriggerClick : function()
9587 Roo.log('trigger click');
9594 this.loadNext = false;
9596 if(this.isExpanded()){
9598 if (!this.blockFocus) {
9599 this.inputEl().focus();
9603 this.hasFocus = true;
9604 if(this.triggerAction == 'all') {
9605 this.doQuery(this.allQuery, true);
9607 this.doQuery(this.getRawValue());
9609 if (!this.blockFocus) {
9610 this.inputEl().focus();
9614 listKeyPress : function(e)
9616 //Roo.log('listkeypress');
9617 // scroll to first matching element based on key pres..
9618 if (e.isSpecialKey()) {
9621 var k = String.fromCharCode(e.getKey()).toUpperCase();
9624 var csel = this.view.getSelectedNodes();
9625 var cselitem = false;
9627 var ix = this.view.indexOf(csel[0]);
9628 cselitem = this.store.getAt(ix);
9629 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9635 this.store.each(function(v) {
9637 // start at existing selection.
9638 if (cselitem.id == v.id) {
9644 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9645 match = this.store.indexOf(v);
9651 if (match === false) {
9652 return true; // no more action?
9655 this.view.select(match);
9656 var sn = Roo.get(this.view.getSelectedNodes()[0])
9657 //sn.scrollIntoView(sn.dom.parentNode, false);
9660 onViewScroll : function(e, t){
9662 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9666 this.hasQuery = true;
9668 this.loading = this.list.select('.loading', true).first();
9670 if(this.loading === null){
9671 this.list.createChild({
9673 cls: 'loading select2-more-results select2-active',
9674 html: 'Loading more results...'
9677 this.loading = this.list.select('.loading', true).first();
9679 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9681 this.loading.hide();
9684 this.loading.show();
9689 this.loadNext = true;
9691 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9696 addItem : function(o)
9698 var dv = ''; // display value
9700 if (this.displayField) {
9701 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9703 // this is an error condition!!!
9704 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
9711 var choice = this.choices.createChild({
9713 cls: 'select2-search-choice',
9722 cls: 'select2-search-choice-close',
9727 }, this.searchField);
9729 var close = choice.select('a.select2-search-choice-close', true).first()
9731 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9738 this.inputEl().dom.value = '';
9742 onRemoveItem : function(e, _self, o)
9744 Roo.log('remove item');
9745 var index = this.item.indexOf(o.data) * 1;
9748 Roo.log('not this item?!');
9752 this.item.splice(index, 1);
9757 this.fireEvent('remove', this);
9761 syncValue : function()
9763 if(!this.item.length){
9770 Roo.each(this.item, function(i){
9771 if(_this.valueField){
9772 value.push(i[_this.valueField]);
9779 this.value = value.join(',');
9781 if(this.hiddenField){
9782 this.hiddenField.dom.value = this.value;
9786 clearItem : function()
9794 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9804 * @cfg {Boolean} grow
9808 * @cfg {Number} growMin
9812 * @cfg {Number} growMax
9822 * Ext JS Library 1.1.1
9823 * Copyright(c) 2006-2007, Ext JS, LLC.
9825 * Originally Released Under LGPL - original licence link has changed is not relivant.
9828 * <script type="text/javascript">
9833 * @extends Roo.util.Observable
9834 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9835 * This class also supports single and multi selection modes. <br>
9836 * Create a data model bound view:
9838 var store = new Roo.data.Store(...);
9840 var view = new Roo.View({
9842 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9845 selectedClass: "ydataview-selected",
9849 // listen for node click?
9850 view.on("click", function(vw, index, node, e){
9851 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9855 dataModel.load("foobar.xml");
9857 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9859 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9860 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9862 * Note: old style constructor is still suported (container, template, config)
9866 * @param {Object} config The config object
9869 Roo.View = function(config, depreciated_tpl, depreciated_config){
9871 if (typeof(depreciated_tpl) == 'undefined') {
9872 // new way.. - universal constructor.
9873 Roo.apply(this, config);
9874 this.el = Roo.get(this.el);
9877 this.el = Roo.get(config);
9878 this.tpl = depreciated_tpl;
9879 Roo.apply(this, depreciated_config);
9881 this.wrapEl = this.el.wrap().wrap();
9882 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9885 if(typeof(this.tpl) == "string"){
9886 this.tpl = new Roo.Template(this.tpl);
9888 // support xtype ctors..
9889 this.tpl = new Roo.factory(this.tpl, Roo);
9901 * @event beforeclick
9902 * Fires before a click is processed. Returns false to cancel the default action.
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
9908 "beforeclick" : true,
9911 * Fires when a template node is 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
9920 * Fires when a template node is double 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
9928 * @event contextmenu
9929 * Fires when a template node is right clicked.
9930 * @param {Roo.View} this
9931 * @param {Number} index The index of the target node
9932 * @param {HTMLElement} node The target node
9933 * @param {Roo.EventObject} e The raw event object
9935 "contextmenu" : true,
9937 * @event selectionchange
9938 * Fires when the selected nodes change.
9939 * @param {Roo.View} this
9940 * @param {Array} selections Array of the selected nodes
9942 "selectionchange" : true,
9945 * @event beforeselect
9946 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9947 * @param {Roo.View} this
9948 * @param {HTMLElement} node The node to be selected
9949 * @param {Array} selections Array of currently selected nodes
9951 "beforeselect" : true,
9953 * @event preparedata
9954 * Fires on every row to render, to allow you to change the data.
9955 * @param {Roo.View} this
9956 * @param {Object} data to be rendered (change this)
9958 "preparedata" : true
9966 "click": this.onClick,
9967 "dblclick": this.onDblClick,
9968 "contextmenu": this.onContextMenu,
9972 this.selections = [];
9974 this.cmp = new Roo.CompositeElementLite([]);
9976 this.store = Roo.factory(this.store, Roo.data);
9977 this.setStore(this.store, true);
9980 if ( this.footer && this.footer.xtype) {
9982 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9984 this.footer.dataSource = this.store
9985 this.footer.container = fctr;
9986 this.footer = Roo.factory(this.footer, Roo);
9987 fctr.insertFirst(this.el);
9989 // this is a bit insane - as the paging toolbar seems to detach the el..
9990 // dom.parentNode.parentNode.parentNode
9991 // they get detached?
9995 Roo.View.superclass.constructor.call(this);
10000 Roo.extend(Roo.View, Roo.util.Observable, {
10003 * @cfg {Roo.data.Store} store Data store to load data from.
10008 * @cfg {String|Roo.Element} el The container element.
10013 * @cfg {String|Roo.Template} tpl The template used by this View
10017 * @cfg {String} dataName the named area of the template to use as the data area
10018 * Works with domtemplates roo-name="name"
10022 * @cfg {String} selectedClass The css class to add to selected nodes
10024 selectedClass : "x-view-selected",
10026 * @cfg {String} emptyText The empty text to show when nothing is loaded.
10031 * @cfg {String} text to display on mask (default Loading)
10035 * @cfg {Boolean} multiSelect Allow multiple selection
10037 multiSelect : false,
10039 * @cfg {Boolean} singleSelect Allow single selection
10041 singleSelect: false,
10044 * @cfg {Boolean} toggleSelect - selecting
10046 toggleSelect : false,
10049 * Returns the element this view is bound to.
10050 * @return {Roo.Element}
10052 getEl : function(){
10053 return this.wrapEl;
10059 * Refreshes the view. - called by datachanged on the store. - do not call directly.
10061 refresh : function(){
10062 Roo.log('refresh');
10065 // if we are using something like 'domtemplate', then
10066 // the what gets used is:
10067 // t.applySubtemplate(NAME, data, wrapping data..)
10068 // the outer template then get' applied with
10069 // the store 'extra data'
10070 // and the body get's added to the
10071 // roo-name="data" node?
10072 // <span class='roo-tpl-{name}'></span> ?????
10076 this.clearSelections();
10077 this.el.update("");
10079 var records = this.store.getRange();
10080 if(records.length < 1) {
10082 // is this valid?? = should it render a template??
10084 this.el.update(this.emptyText);
10088 if (this.dataName) {
10089 this.el.update(t.apply(this.store.meta)); //????
10090 el = this.el.child('.roo-tpl-' + this.dataName);
10093 for(var i = 0, len = records.length; i < len; i++){
10094 var data = this.prepareData(records[i].data, i, records[i]);
10095 this.fireEvent("preparedata", this, data, i, records[i]);
10096 html[html.length] = Roo.util.Format.trim(
10098 t.applySubtemplate(this.dataName, data, this.store.meta) :
10105 el.update(html.join(""));
10106 this.nodes = el.dom.childNodes;
10107 this.updateIndexes(0);
10112 * Function to override to reformat the data that is sent to
10113 * the template for each node.
10114 * DEPRICATED - use the preparedata event handler.
10115 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10116 * a JSON object for an UpdateManager bound view).
10118 prepareData : function(data, index, record)
10120 this.fireEvent("preparedata", this, data, index, record);
10124 onUpdate : function(ds, record){
10125 Roo.log('on update');
10126 this.clearSelections();
10127 var index = this.store.indexOf(record);
10128 var n = this.nodes[index];
10129 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10130 n.parentNode.removeChild(n);
10131 this.updateIndexes(index, index);
10137 onAdd : function(ds, records, index)
10139 Roo.log(['on Add', ds, records, index] );
10140 this.clearSelections();
10141 if(this.nodes.length == 0){
10145 var n = this.nodes[index];
10146 for(var i = 0, len = records.length; i < len; i++){
10147 var d = this.prepareData(records[i].data, i, records[i]);
10149 this.tpl.insertBefore(n, d);
10152 this.tpl.append(this.el, d);
10155 this.updateIndexes(index);
10158 onRemove : function(ds, record, index){
10159 Roo.log('onRemove');
10160 this.clearSelections();
10161 var el = this.dataName ?
10162 this.el.child('.roo-tpl-' + this.dataName) :
10165 el.dom.removeChild(this.nodes[index]);
10166 this.updateIndexes(index);
10170 * Refresh an individual node.
10171 * @param {Number} index
10173 refreshNode : function(index){
10174 this.onUpdate(this.store, this.store.getAt(index));
10177 updateIndexes : function(startIndex, endIndex){
10178 var ns = this.nodes;
10179 startIndex = startIndex || 0;
10180 endIndex = endIndex || ns.length - 1;
10181 for(var i = startIndex; i <= endIndex; i++){
10182 ns[i].nodeIndex = i;
10187 * Changes the data store this view uses and refresh the view.
10188 * @param {Store} store
10190 setStore : function(store, initial){
10191 if(!initial && this.store){
10192 this.store.un("datachanged", this.refresh);
10193 this.store.un("add", this.onAdd);
10194 this.store.un("remove", this.onRemove);
10195 this.store.un("update", this.onUpdate);
10196 this.store.un("clear", this.refresh);
10197 this.store.un("beforeload", this.onBeforeLoad);
10198 this.store.un("load", this.onLoad);
10199 this.store.un("loadexception", this.onLoad);
10203 store.on("datachanged", this.refresh, this);
10204 store.on("add", this.onAdd, this);
10205 store.on("remove", this.onRemove, this);
10206 store.on("update", this.onUpdate, this);
10207 store.on("clear", this.refresh, this);
10208 store.on("beforeload", this.onBeforeLoad, this);
10209 store.on("load", this.onLoad, this);
10210 store.on("loadexception", this.onLoad, this);
10218 * onbeforeLoad - masks the loading area.
10221 onBeforeLoad : function(store,opts)
10223 Roo.log('onBeforeLoad');
10225 this.el.update("");
10227 this.el.mask(this.mask ? this.mask : "Loading" );
10229 onLoad : function ()
10236 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10237 * @param {HTMLElement} node
10238 * @return {HTMLElement} The template node
10240 findItemFromChild : function(node){
10241 var el = this.dataName ?
10242 this.el.child('.roo-tpl-' + this.dataName,true) :
10245 if(!node || node.parentNode == el){
10248 var p = node.parentNode;
10249 while(p && p != el){
10250 if(p.parentNode == el){
10259 onClick : function(e){
10260 var item = this.findItemFromChild(e.getTarget());
10262 var index = this.indexOf(item);
10263 if(this.onItemClick(item, index, e) !== false){
10264 this.fireEvent("click", this, index, item, e);
10267 this.clearSelections();
10272 onContextMenu : function(e){
10273 var item = this.findItemFromChild(e.getTarget());
10275 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10280 onDblClick : function(e){
10281 var item = this.findItemFromChild(e.getTarget());
10283 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10287 onItemClick : function(item, index, e)
10289 if(this.fireEvent("beforeclick", this, index, item, e) === false){
10292 if (this.toggleSelect) {
10293 var m = this.isSelected(item) ? 'unselect' : 'select';
10296 _t[m](item, true, false);
10299 if(this.multiSelect || this.singleSelect){
10300 if(this.multiSelect && e.shiftKey && this.lastSelection){
10301 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10303 this.select(item, this.multiSelect && e.ctrlKey);
10304 this.lastSelection = item;
10306 e.preventDefault();
10312 * Get the number of selected nodes.
10315 getSelectionCount : function(){
10316 return this.selections.length;
10320 * Get the currently selected nodes.
10321 * @return {Array} An array of HTMLElements
10323 getSelectedNodes : function(){
10324 return this.selections;
10328 * Get the indexes of the selected nodes.
10331 getSelectedIndexes : function(){
10332 var indexes = [], s = this.selections;
10333 for(var i = 0, len = s.length; i < len; i++){
10334 indexes.push(s[i].nodeIndex);
10340 * Clear all selections
10341 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10343 clearSelections : function(suppressEvent){
10344 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10345 this.cmp.elements = this.selections;
10346 this.cmp.removeClass(this.selectedClass);
10347 this.selections = [];
10348 if(!suppressEvent){
10349 this.fireEvent("selectionchange", this, this.selections);
10355 * Returns true if the passed node is selected
10356 * @param {HTMLElement/Number} node The node or node index
10357 * @return {Boolean}
10359 isSelected : function(node){
10360 var s = this.selections;
10364 node = this.getNode(node);
10365 return s.indexOf(node) !== -1;
10370 * @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
10371 * @param {Boolean} keepExisting (optional) true to keep existing selections
10372 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10374 select : function(nodeInfo, keepExisting, suppressEvent){
10375 if(nodeInfo instanceof Array){
10377 this.clearSelections(true);
10379 for(var i = 0, len = nodeInfo.length; i < len; i++){
10380 this.select(nodeInfo[i], true, true);
10384 var node = this.getNode(nodeInfo);
10385 if(!node || this.isSelected(node)){
10386 return; // already selected.
10389 this.clearSelections(true);
10391 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10392 Roo.fly(node).addClass(this.selectedClass);
10393 this.selections.push(node);
10394 if(!suppressEvent){
10395 this.fireEvent("selectionchange", this, this.selections);
10403 * @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
10404 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10405 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10407 unselect : function(nodeInfo, keepExisting, suppressEvent)
10409 if(nodeInfo instanceof Array){
10410 Roo.each(this.selections, function(s) {
10411 this.unselect(s, nodeInfo);
10415 var node = this.getNode(nodeInfo);
10416 if(!node || !this.isSelected(node)){
10417 Roo.log("not selected");
10418 return; // not selected.
10422 Roo.each(this.selections, function(s) {
10424 Roo.fly(node).removeClass(this.selectedClass);
10431 this.selections= ns;
10432 this.fireEvent("selectionchange", this, this.selections);
10436 * Gets a template node.
10437 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10438 * @return {HTMLElement} The node or null if it wasn't found
10440 getNode : function(nodeInfo){
10441 if(typeof nodeInfo == "string"){
10442 return document.getElementById(nodeInfo);
10443 }else if(typeof nodeInfo == "number"){
10444 return this.nodes[nodeInfo];
10450 * Gets a range template nodes.
10451 * @param {Number} startIndex
10452 * @param {Number} endIndex
10453 * @return {Array} An array of nodes
10455 getNodes : function(start, end){
10456 var ns = this.nodes;
10457 start = start || 0;
10458 end = typeof end == "undefined" ? ns.length - 1 : end;
10461 for(var i = start; i <= end; i++){
10465 for(var i = start; i >= end; i--){
10473 * Finds the index of the passed node
10474 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10475 * @return {Number} The index of the node or -1
10477 indexOf : function(node){
10478 node = this.getNode(node);
10479 if(typeof node.nodeIndex == "number"){
10480 return node.nodeIndex;
10482 var ns = this.nodes;
10483 for(var i = 0, len = ns.length; i < len; i++){
10494 * based on jquery fullcalendar
10498 Roo.bootstrap = Roo.bootstrap || {};
10500 * @class Roo.bootstrap.Calendar
10501 * @extends Roo.bootstrap.Component
10502 * Bootstrap Calendar class
10503 * @cfg {Boolean} loadMask (true|false) default false
10504 * @cfg {Object} header generate the user specific header of the calendar, default false
10507 * Create a new Container
10508 * @param {Object} config The config object
10513 Roo.bootstrap.Calendar = function(config){
10514 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10518 * Fires when a date is selected
10519 * @param {DatePicker} this
10520 * @param {Date} date The selected date
10524 * @event monthchange
10525 * Fires when the displayed month changes
10526 * @param {DatePicker} this
10527 * @param {Date} date The selected month
10529 'monthchange': true,
10531 * @event evententer
10532 * Fires when mouse over an event
10533 * @param {Calendar} this
10534 * @param {event} Event
10536 'evententer': true,
10538 * @event eventleave
10539 * Fires when the mouse leaves an
10540 * @param {Calendar} this
10543 'eventleave': true,
10545 * @event eventclick
10546 * Fires when the mouse click an
10547 * @param {Calendar} this
10556 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
10559 * @cfg {Number} startDay
10560 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10568 getAutoCreate : function(){
10571 var fc_button = function(name, corner, style, content ) {
10572 return Roo.apply({},{
10574 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
10576 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10579 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10590 style : 'width:100%',
10597 cls : 'fc-header-left',
10599 fc_button('prev', 'left', 'arrow', '‹' ),
10600 fc_button('next', 'right', 'arrow', '›' ),
10601 { tag: 'span', cls: 'fc-header-space' },
10602 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
10610 cls : 'fc-header-center',
10614 cls: 'fc-header-title',
10617 html : 'month / year'
10625 cls : 'fc-header-right',
10627 /* fc_button('month', 'left', '', 'month' ),
10628 fc_button('week', '', '', 'week' ),
10629 fc_button('day', 'right', '', 'day' )
10641 header = this.header;
10644 var cal_heads = function() {
10646 // fixme - handle this.
10648 for (var i =0; i < Date.dayNames.length; i++) {
10649 var d = Date.dayNames[i];
10652 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10653 html : d.substring(0,3)
10657 ret[0].cls += ' fc-first';
10658 ret[6].cls += ' fc-last';
10661 var cal_cell = function(n) {
10664 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10669 cls: 'fc-day-number',
10673 cls: 'fc-day-content',
10677 style: 'position: relative;' // height: 17px;
10689 var cal_rows = function() {
10692 for (var r = 0; r < 6; r++) {
10699 for (var i =0; i < Date.dayNames.length; i++) {
10700 var d = Date.dayNames[i];
10701 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10704 row.cn[0].cls+=' fc-first';
10705 row.cn[0].cn[0].style = 'min-height:90px';
10706 row.cn[6].cls+=' fc-last';
10710 ret[0].cls += ' fc-first';
10711 ret[4].cls += ' fc-prev-last';
10712 ret[5].cls += ' fc-last';
10719 cls: 'fc-border-separate',
10720 style : 'width:100%',
10728 cls : 'fc-first fc-last',
10746 cls : 'fc-content',
10747 style : "position: relative;",
10750 cls : 'fc-view fc-view-month fc-grid',
10751 style : 'position: relative',
10752 unselectable : 'on',
10755 cls : 'fc-event-container',
10756 style : 'position:absolute;z-index:8;top:0;left:0;'
10774 initEvents : function()
10777 throw "can not find store for calendar";
10783 style: "text-align:center",
10787 style: "background-color:white;width:50%;margin:250 auto",
10791 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10802 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10804 var size = this.el.select('.fc-content', true).first().getSize();
10805 this.maskEl.setSize(size.width, size.height);
10806 this.maskEl.enableDisplayMode("block");
10807 if(!this.loadMask){
10808 this.maskEl.hide();
10811 this.store = Roo.factory(this.store, Roo.data);
10812 this.store.on('load', this.onLoad, this);
10813 this.store.on('beforeload', this.onBeforeLoad, this);
10817 this.cells = this.el.select('.fc-day',true);
10818 //Roo.log(this.cells);
10819 this.textNodes = this.el.query('.fc-day-number');
10820 this.cells.addClassOnOver('fc-state-hover');
10822 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10823 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10824 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10825 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10827 this.on('monthchange', this.onMonthChange, this);
10829 this.update(new Date().clearTime());
10832 resize : function() {
10833 var sz = this.el.getSize();
10835 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10836 this.el.select('.fc-day-content div',true).setHeight(34);
10841 showPrevMonth : function(e){
10842 this.update(this.activeDate.add("mo", -1));
10844 showToday : function(e){
10845 this.update(new Date().clearTime());
10848 showNextMonth : function(e){
10849 this.update(this.activeDate.add("mo", 1));
10853 showPrevYear : function(){
10854 this.update(this.activeDate.add("y", -1));
10858 showNextYear : function(){
10859 this.update(this.activeDate.add("y", 1));
10864 update : function(date)
10866 var vd = this.activeDate;
10867 this.activeDate = date;
10868 // if(vd && this.el){
10869 // var t = date.getTime();
10870 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10871 // Roo.log('using add remove');
10873 // this.fireEvent('monthchange', this, date);
10875 // this.cells.removeClass("fc-state-highlight");
10876 // this.cells.each(function(c){
10877 // if(c.dateValue == t){
10878 // c.addClass("fc-state-highlight");
10879 // setTimeout(function(){
10880 // try{c.dom.firstChild.focus();}catch(e){}
10890 var days = date.getDaysInMonth();
10892 var firstOfMonth = date.getFirstDateOfMonth();
10893 var startingPos = firstOfMonth.getDay()-this.startDay;
10895 if(startingPos < this.startDay){
10899 var pm = date.add(Date.MONTH, -1);
10900 var prevStart = pm.getDaysInMonth()-startingPos;
10902 this.cells = this.el.select('.fc-day',true);
10903 this.textNodes = this.el.query('.fc-day-number');
10904 this.cells.addClassOnOver('fc-state-hover');
10906 var cells = this.cells.elements;
10907 var textEls = this.textNodes;
10909 Roo.each(cells, function(cell){
10910 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10913 days += startingPos;
10915 // convert everything to numbers so it's fast
10916 var day = 86400000;
10917 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10920 //Roo.log(prevStart);
10922 var today = new Date().clearTime().getTime();
10923 var sel = date.clearTime().getTime();
10924 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10925 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10926 var ddMatch = this.disabledDatesRE;
10927 var ddText = this.disabledDatesText;
10928 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10929 var ddaysText = this.disabledDaysText;
10930 var format = this.format;
10932 var setCellClass = function(cal, cell){
10934 //Roo.log('set Cell Class');
10936 var t = d.getTime();
10940 cell.dateValue = t;
10942 cell.className += " fc-today";
10943 cell.className += " fc-state-highlight";
10944 cell.title = cal.todayText;
10947 // disable highlight in other month..
10948 //cell.className += " fc-state-highlight";
10953 cell.className = " fc-state-disabled";
10954 cell.title = cal.minText;
10958 cell.className = " fc-state-disabled";
10959 cell.title = cal.maxText;
10963 if(ddays.indexOf(d.getDay()) != -1){
10964 cell.title = ddaysText;
10965 cell.className = " fc-state-disabled";
10968 if(ddMatch && format){
10969 var fvalue = d.dateFormat(format);
10970 if(ddMatch.test(fvalue)){
10971 cell.title = ddText.replace("%0", fvalue);
10972 cell.className = " fc-state-disabled";
10976 if (!cell.initialClassName) {
10977 cell.initialClassName = cell.dom.className;
10980 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10985 for(; i < startingPos; i++) {
10986 textEls[i].innerHTML = (++prevStart);
10987 d.setDate(d.getDate()+1);
10989 cells[i].className = "fc-past fc-other-month";
10990 setCellClass(this, cells[i]);
10995 for(; i < days; i++){
10996 intDay = i - startingPos + 1;
10997 textEls[i].innerHTML = (intDay);
10998 d.setDate(d.getDate()+1);
11000 cells[i].className = ''; // "x-date-active";
11001 setCellClass(this, cells[i]);
11005 for(; i < 42; i++) {
11006 textEls[i].innerHTML = (++extraDays);
11007 d.setDate(d.getDate()+1);
11009 cells[i].className = "fc-future fc-other-month";
11010 setCellClass(this, cells[i]);
11013 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11015 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11017 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11018 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11020 if(totalRows != 6){
11021 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11022 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11025 this.fireEvent('monthchange', this, date);
11029 if(!this.internalRender){
11030 var main = this.el.dom.firstChild;
11031 var w = main.offsetWidth;
11032 this.el.setWidth(w + this.el.getBorderWidth("lr"));
11033 Roo.fly(main).setWidth(w);
11034 this.internalRender = true;
11035 // opera does not respect the auto grow header center column
11036 // then, after it gets a width opera refuses to recalculate
11037 // without a second pass
11038 if(Roo.isOpera && !this.secondPass){
11039 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11040 this.secondPass = true;
11041 this.update.defer(10, this, [date]);
11048 findCell : function(dt) {
11049 dt = dt.clearTime().getTime();
11051 this.cells.each(function(c){
11052 //Roo.log("check " +c.dateValue + '?=' + dt);
11053 if(c.dateValue == dt){
11063 findCells : function(ev) {
11064 var s = ev.start.clone().clearTime().getTime();
11066 var e= ev.end.clone().clearTime().getTime();
11069 this.cells.each(function(c){
11070 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11072 if(c.dateValue > e){
11075 if(c.dateValue < s){
11084 findBestRow: function(cells)
11088 for (var i =0 ; i < cells.length;i++) {
11089 ret = Math.max(cells[i].rows || 0,ret);
11096 addItem : function(ev)
11098 // look for vertical location slot in
11099 var cells = this.findCells(ev);
11101 ev.row = this.findBestRow(cells);
11103 // work out the location.
11107 for(var i =0; i < cells.length; i++) {
11115 if (crow.start.getY() == cells[i].getY()) {
11117 crow.end = cells[i];
11133 for (var i = 0; i < cells.length;i++) {
11134 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11138 this.calevents.push(ev);
11141 clearEvents: function() {
11143 if(!this.calevents){
11147 Roo.each(this.cells.elements, function(c){
11151 Roo.each(this.calevents, function(e) {
11152 Roo.each(e.els, function(el) {
11153 el.un('mouseenter' ,this.onEventEnter, this);
11154 el.un('mouseleave' ,this.onEventLeave, this);
11161 renderEvents: function()
11163 // first make sure there is enough space..
11165 this.cells.each(function(c) {
11167 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11170 for (var e = 0; e < this.calevents.length; e++) {
11171 var ev = this.calevents[e];
11172 var cells = ev.cells;
11173 var rows = ev.rows;
11175 for(var i =0; i < rows.length; i++) {
11178 // how many rows should it span..
11181 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11182 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11184 unselectable : "on",
11187 cls: 'fc-event-inner',
11191 // cls: 'fc-event-time',
11192 // html : cells.length > 1 ? '' : ev.time
11196 cls: 'fc-event-title',
11197 html : String.format('{0}', ev.title)
11204 cls: 'ui-resizable-handle ui-resizable-e',
11205 html : '  '
11211 cfg.cls += ' fc-event-start';
11213 if ((i+1) == rows.length) {
11214 cfg.cls += ' fc-event-end';
11217 var ctr = this.el.select('.fc-event-container',true).first();
11218 var cg = ctr.createChild(cfg);
11220 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11221 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11222 cg.on('click', this.onEventClick, this, ev);
11226 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11227 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11229 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
11230 cg.setWidth(ebox.right - sbox.x -2);
11238 onEventEnter: function (e, el,event,d) {
11239 this.fireEvent('evententer', this, el, event);
11242 onEventLeave: function (e, el,event,d) {
11243 this.fireEvent('eventleave', this, el, event);
11246 onEventClick: function (e, el,event,d) {
11247 this.fireEvent('eventclick', this, el, event);
11250 onMonthChange: function () {
11254 onLoad: function ()
11256 this.calevents = [];
11259 if(this.store.getCount() > 0){
11260 this.store.data.each(function(d){
11263 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11264 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11265 time : d.data.start_time,
11266 title : d.data.title,
11267 description : d.data.description,
11268 venue : d.data.venue
11273 this.renderEvents();
11276 this.maskEl.hide();
11280 onBeforeLoad: function()
11282 this.clearEvents();
11285 this.maskEl.show();
11299 * @class Roo.bootstrap.Popover
11300 * @extends Roo.bootstrap.Component
11301 * Bootstrap Popover class
11302 * @cfg {String} html contents of the popover (or false to use children..)
11303 * @cfg {String} title of popover (or false to hide)
11304 * @cfg {String} placement how it is placed
11305 * @cfg {String} trigger click || hover (or false to trigger manually)
11306 * @cfg {String} over what (parent or false to trigger manually.)
11309 * Create a new Popover
11310 * @param {Object} config The config object
11313 Roo.bootstrap.Popover = function(config){
11314 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11317 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
11319 title: 'Fill in a title',
11322 placement : 'right',
11323 trigger : 'hover', // hover
11327 can_build_overlaid : false,
11329 getChildContainer : function()
11331 return this.el.select('.popover-content',true).first();
11334 getAutoCreate : function(){
11335 Roo.log('make popover?');
11337 cls : 'popover roo-dynamic',
11338 style: 'display:block',
11344 cls : 'popover-inner',
11348 cls: 'popover-title',
11352 cls : 'popover-content',
11363 setTitle: function(str)
11365 this.el.select('.popover-title',true).first().dom.innerHTML = str;
11367 setContent: function(str)
11369 this.el.select('.popover-content',true).first().dom.innerHTML = str;
11371 // as it get's added to the bottom of the page.
11372 onRender : function(ct, position)
11374 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11376 var cfg = Roo.apply({}, this.getAutoCreate());
11380 cfg.cls += ' ' + this.cls;
11383 cfg.style = this.style;
11385 Roo.log("adding to ")
11386 this.el = Roo.get(document.body).createChild(cfg, position);
11392 initEvents : function()
11394 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11395 this.el.enableDisplayMode('block');
11397 if (this.over === false) {
11400 if (this.triggers === false) {
11403 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11404 var triggers = this.trigger ? this.trigger.split(' ') : [];
11405 Roo.each(triggers, function(trigger) {
11407 if (trigger == 'click') {
11408 on_el.on('click', this.toggle, this);
11409 } else if (trigger != 'manual') {
11410 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
11411 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11413 on_el.on(eventIn ,this.enter, this);
11414 on_el.on(eventOut, this.leave, this);
11425 toggle : function () {
11426 this.hoverState == 'in' ? this.leave() : this.enter();
11429 enter : function () {
11432 clearTimeout(this.timeout);
11434 this.hoverState = 'in'
11436 if (!this.delay || !this.delay.show) {
11441 this.timeout = setTimeout(function () {
11442 if (_t.hoverState == 'in') {
11445 }, this.delay.show)
11447 leave : function() {
11448 clearTimeout(this.timeout);
11450 this.hoverState = 'out'
11452 if (!this.delay || !this.delay.hide) {
11457 this.timeout = setTimeout(function () {
11458 if (_t.hoverState == 'out') {
11461 }, this.delay.hide)
11464 show : function (on_el)
11467 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11470 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11471 if (this.html !== false) {
11472 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11474 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11475 if (!this.title.length) {
11476 this.el.select('.popover-title',true).hide();
11479 var placement = typeof this.placement == 'function' ?
11480 this.placement.call(this, this.el, on_el) :
11483 var autoToken = /\s?auto?\s?/i;
11484 var autoPlace = autoToken.test(placement);
11486 placement = placement.replace(autoToken, '') || 'top';
11490 //this.el.setXY([0,0]);
11492 this.el.dom.style.display='block';
11493 this.el.addClass(placement);
11495 //this.el.appendTo(on_el);
11497 var p = this.getPosition();
11498 var box = this.el.getBox();
11503 var align = Roo.bootstrap.Popover.alignment[placement]
11504 this.el.alignTo(on_el, align[0],align[1]);
11505 //var arrow = this.el.select('.arrow',true).first();
11506 //arrow.set(align[2],
11508 this.el.addClass('in');
11509 this.hoverState = null;
11511 if (this.el.hasClass('fade')) {
11518 this.el.setXY([0,0]);
11519 this.el.removeClass('in');
11526 Roo.bootstrap.Popover.alignment = {
11527 'left' : ['r-l', [-10,0], 'right'],
11528 'right' : ['l-r', [10,0], 'left'],
11529 'bottom' : ['t-b', [0,10], 'top'],
11530 'top' : [ 'b-t', [0,-10], 'bottom']
11541 * @class Roo.bootstrap.Progress
11542 * @extends Roo.bootstrap.Component
11543 * Bootstrap Progress class
11544 * @cfg {Boolean} striped striped of the progress bar
11545 * @cfg {Boolean} active animated of the progress bar
11549 * Create a new Progress
11550 * @param {Object} config The config object
11553 Roo.bootstrap.Progress = function(config){
11554 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11557 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
11562 getAutoCreate : function(){
11570 cfg.cls += ' progress-striped';
11574 cfg.cls += ' active';
11593 * @class Roo.bootstrap.ProgressBar
11594 * @extends Roo.bootstrap.Component
11595 * Bootstrap ProgressBar class
11596 * @cfg {Number} aria_valuenow aria-value now
11597 * @cfg {Number} aria_valuemin aria-value min
11598 * @cfg {Number} aria_valuemax aria-value max
11599 * @cfg {String} label label for the progress bar
11600 * @cfg {String} panel (success | info | warning | danger )
11601 * @cfg {String} role role of the progress bar
11602 * @cfg {String} sr_only text
11606 * Create a new ProgressBar
11607 * @param {Object} config The config object
11610 Roo.bootstrap.ProgressBar = function(config){
11611 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11614 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
11618 aria_valuemax : 100,
11624 getAutoCreate : function()
11629 cls: 'progress-bar',
11630 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11642 cfg.role = this.role;
11645 if(this.aria_valuenow){
11646 cfg['aria-valuenow'] = this.aria_valuenow;
11649 if(this.aria_valuemin){
11650 cfg['aria-valuemin'] = this.aria_valuemin;
11653 if(this.aria_valuemax){
11654 cfg['aria-valuemax'] = this.aria_valuemax;
11657 if(this.label && !this.sr_only){
11658 cfg.html = this.label;
11662 cfg.cls += ' progress-bar-' + this.panel;
11668 update : function(aria_valuenow)
11670 this.aria_valuenow = aria_valuenow;
11672 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11687 * @class Roo.bootstrap.TabPanel
11688 * @extends Roo.bootstrap.Component
11689 * Bootstrap TabPanel class
11690 * @cfg {Boolean} active panel active
11691 * @cfg {String} html panel content
11692 * @cfg {String} tabId tab relate id
11693 * @cfg {String} navId The navbar which triggers show hide
11697 * Create a new TabPanel
11698 * @param {Object} config The config object
11701 Roo.bootstrap.TabPanel = function(config){
11702 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11706 * Fires when the active status changes
11707 * @param {Roo.bootstrap.TabPanel} this
11708 * @param {Boolean} state the new state
11715 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
11722 getAutoCreate : function(){
11726 html: this.html || ''
11730 cfg.cls += ' active';
11734 cfg.tabId = this.tabId;
11739 onRender : function(ct, position)
11741 // Roo.log("Call onRender: " + this.xtype);
11743 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11745 if (this.navId && this.tabId) {
11746 var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11748 Roo.log("could not find navID:" + this.navId + ", tabId: " + this.tabId);
11750 item.on('changed', function(item, state) {
11751 this.setActive(state);
11757 setActive: function(state)
11759 Roo.log("panel - set active " + this.tabId + "=" + state);
11761 this.active = state;
11763 this.el.removeClass('active');
11765 } else if (!this.el.hasClass('active')) {
11766 this.el.addClass('active');
11768 this.fireEvent('changed', this, state);
11785 * @class Roo.bootstrap.DateField
11786 * @extends Roo.bootstrap.Input
11787 * Bootstrap DateField class
11788 * @cfg {Number} weekStart default 0
11789 * @cfg {Number} weekStart default 0
11790 * @cfg {Number} viewMode default empty, (months|years)
11791 * @cfg {Number} minViewMode default empty, (months|years)
11792 * @cfg {Number} startDate default -Infinity
11793 * @cfg {Number} endDate default Infinity
11794 * @cfg {Boolean} todayHighlight default false
11795 * @cfg {Boolean} todayBtn default false
11796 * @cfg {Boolean} calendarWeeks default false
11797 * @cfg {Object} daysOfWeekDisabled default empty
11799 * @cfg {Boolean} keyboardNavigation default true
11800 * @cfg {String} language default en
11803 * Create a new DateField
11804 * @param {Object} config The config object
11807 Roo.bootstrap.DateField = function(config){
11808 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11812 * Fires when this field show.
11813 * @param {Roo.bootstrap.DateField} this
11814 * @param {Mixed} date The date value
11819 * Fires when this field hide.
11820 * @param {Roo.bootstrap.DateField} this
11821 * @param {Mixed} date The date value
11826 * Fires when select a date.
11827 * @param {Roo.bootstrap.DateField} this
11828 * @param {Mixed} date The date value
11834 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11837 * @cfg {String} format
11838 * The default date format string which can be overriden for localization support. The format must be
11839 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11843 * @cfg {String} altFormats
11844 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11845 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11847 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11855 todayHighlight : false,
11861 keyboardNavigation: true,
11863 calendarWeeks: false,
11865 startDate: -Infinity,
11869 daysOfWeekDisabled: [],
11873 UTCDate: function()
11875 return new Date(Date.UTC.apply(Date, arguments));
11878 UTCToday: function()
11880 var today = new Date();
11881 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11884 getDate: function() {
11885 var d = this.getUTCDate();
11886 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11889 getUTCDate: function() {
11893 setDate: function(d) {
11894 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11897 setUTCDate: function(d) {
11899 this.setValue(this.formatDate(this.date));
11902 onRender: function(ct, position)
11905 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11907 this.language = this.language || 'en';
11908 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11909 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11911 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11912 this.format = this.format || 'm/d/y';
11913 this.isInline = false;
11914 this.isInput = true;
11915 this.component = this.el.select('.add-on', true).first() || false;
11916 this.component = (this.component && this.component.length === 0) ? false : this.component;
11917 this.hasInput = this.component && this.inputEL().length;
11919 if (typeof(this.minViewMode === 'string')) {
11920 switch (this.minViewMode) {
11922 this.minViewMode = 1;
11925 this.minViewMode = 2;
11928 this.minViewMode = 0;
11933 if (typeof(this.viewMode === 'string')) {
11934 switch (this.viewMode) {
11947 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11949 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11951 this.picker().on('mousedown', this.onMousedown, this);
11952 this.picker().on('click', this.onClick, this);
11954 this.picker().addClass('datepicker-dropdown');
11956 this.startViewMode = this.viewMode;
11959 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11960 if(!this.calendarWeeks){
11965 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11966 v.attr('colspan', function(i, val){
11967 return parseInt(val) + 1;
11972 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11974 this.setStartDate(this.startDate);
11975 this.setEndDate(this.endDate);
11977 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11984 if(this.isInline) {
11989 picker : function()
11991 return this.el.select('.datepicker', true).first();
11994 fillDow: function()
11996 var dowCnt = this.weekStart;
12005 if(this.calendarWeeks){
12013 while (dowCnt < this.weekStart + 7) {
12017 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12021 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12024 fillMonths: function()
12027 var months = this.picker().select('>.datepicker-months td', true).first();
12029 months.dom.innerHTML = '';
12035 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12038 months.createChild(month);
12043 update: function(){
12045 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12047 if (this.date < this.startDate) {
12048 this.viewDate = new Date(this.startDate);
12049 } else if (this.date > this.endDate) {
12050 this.viewDate = new Date(this.endDate);
12052 this.viewDate = new Date(this.date);
12059 var d = new Date(this.viewDate),
12060 year = d.getUTCFullYear(),
12061 month = d.getUTCMonth(),
12062 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12063 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12064 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12065 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12066 currentDate = this.date && this.date.valueOf(),
12067 today = this.UTCToday();
12069 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12071 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12073 // this.picker.select('>tfoot th.today').
12074 // .text(dates[this.language].today)
12075 // .toggle(this.todayBtn !== false);
12077 this.updateNavArrows();
12080 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12082 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12084 prevMonth.setUTCDate(day);
12086 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12088 var nextMonth = new Date(prevMonth);
12090 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12092 nextMonth = nextMonth.valueOf();
12094 var fillMonths = false;
12096 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12098 while(prevMonth.valueOf() < nextMonth) {
12101 if (prevMonth.getUTCDay() === this.weekStart) {
12103 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12111 if(this.calendarWeeks){
12112 // ISO 8601: First week contains first thursday.
12113 // ISO also states week starts on Monday, but we can be more abstract here.
12115 // Start of current week: based on weekstart/current date
12116 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12117 // Thursday of this week
12118 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12119 // First Thursday of year, year from thursday
12120 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12121 // Calendar week: ms between thursdays, div ms per day, div 7 days
12122 calWeek = (th - yth) / 864e5 / 7 + 1;
12124 fillMonths.cn.push({
12132 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12134 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12137 if (this.todayHighlight &&
12138 prevMonth.getUTCFullYear() == today.getFullYear() &&
12139 prevMonth.getUTCMonth() == today.getMonth() &&
12140 prevMonth.getUTCDate() == today.getDate()) {
12141 clsName += ' today';
12144 if (currentDate && prevMonth.valueOf() === currentDate) {
12145 clsName += ' active';
12148 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12149 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12150 clsName += ' disabled';
12153 fillMonths.cn.push({
12155 cls: 'day ' + clsName,
12156 html: prevMonth.getDate()
12159 prevMonth.setDate(prevMonth.getDate()+1);
12162 var currentYear = this.date && this.date.getUTCFullYear();
12163 var currentMonth = this.date && this.date.getUTCMonth();
12165 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12167 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12168 v.removeClass('active');
12170 if(currentYear === year && k === currentMonth){
12171 v.addClass('active');
12174 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12175 v.addClass('disabled');
12181 year = parseInt(year/10, 10) * 10;
12183 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12185 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12188 for (var i = -1; i < 11; i++) {
12189 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12191 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12199 showMode: function(dir) {
12201 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12203 Roo.each(this.picker().select('>div',true).elements, function(v){
12204 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12207 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12212 if(this.isInline) return;
12214 this.picker().removeClass(['bottom', 'top']);
12216 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12218 * place to the top of element!
12222 this.picker().addClass('top');
12223 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12228 this.picker().addClass('bottom');
12230 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12233 parseDate : function(value){
12234 if(!value || value instanceof Date){
12237 var v = Date.parseDate(value, this.format);
12238 if (!v && this.useIso) {
12239 v = Date.parseDate(value, 'Y-m-d');
12241 if(!v && this.altFormats){
12242 if(!this.altFormatsArray){
12243 this.altFormatsArray = this.altFormats.split("|");
12245 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12246 v = Date.parseDate(value, this.altFormatsArray[i]);
12252 formatDate : function(date, fmt){
12253 return (!date || !(date instanceof Date)) ?
12254 date : date.dateFormat(fmt || this.format);
12257 onFocus : function()
12259 Roo.bootstrap.DateField.superclass.onFocus.call(this);
12263 onBlur : function()
12265 Roo.bootstrap.DateField.superclass.onBlur.call(this);
12271 this.picker().show();
12275 this.fireEvent('show', this, this.date);
12280 if(this.isInline) return;
12281 this.picker().hide();
12282 this.viewMode = this.startViewMode;
12285 this.fireEvent('hide', this, this.date);
12289 onMousedown: function(e){
12290 e.stopPropagation();
12291 e.preventDefault();
12294 keyup: function(e){
12295 Roo.bootstrap.DateField.superclass.keyup.call(this);
12300 setValue: function(v){
12301 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12303 this.fireEvent('select', this, this.date);
12307 fireKey: function(e){
12308 if (!this.picker().isVisible()){
12309 if (e.keyCode == 27) // allow escape to hide and re-show picker
12313 var dateChanged = false,
12315 newDate, newViewDate;
12319 e.preventDefault();
12323 if (!this.keyboardNavigation) break;
12324 dir = e.keyCode == 37 ? -1 : 1;
12327 newDate = this.moveYear(this.date, dir);
12328 newViewDate = this.moveYear(this.viewDate, dir);
12329 } else if (e.shiftKey){
12330 newDate = this.moveMonth(this.date, dir);
12331 newViewDate = this.moveMonth(this.viewDate, dir);
12333 newDate = new Date(this.date);
12334 newDate.setUTCDate(this.date.getUTCDate() + dir);
12335 newViewDate = new Date(this.viewDate);
12336 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12338 if (this.dateWithinRange(newDate)){
12339 this.date = newDate;
12340 this.viewDate = newViewDate;
12341 this.setValue(this.formatDate(this.date));
12343 e.preventDefault();
12344 dateChanged = true;
12349 if (!this.keyboardNavigation) break;
12350 dir = e.keyCode == 38 ? -1 : 1;
12352 newDate = this.moveYear(this.date, dir);
12353 newViewDate = this.moveYear(this.viewDate, dir);
12354 } else if (e.shiftKey){
12355 newDate = this.moveMonth(this.date, dir);
12356 newViewDate = this.moveMonth(this.viewDate, dir);
12358 newDate = new Date(this.date);
12359 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12360 newViewDate = new Date(this.viewDate);
12361 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12363 if (this.dateWithinRange(newDate)){
12364 this.date = newDate;
12365 this.viewDate = newViewDate;
12366 this.setValue(this.formatDate(this.date));
12368 e.preventDefault();
12369 dateChanged = true;
12373 this.setValue(this.formatDate(this.date));
12375 e.preventDefault();
12378 this.setValue(this.formatDate(this.date));
12385 onClick: function(e) {
12386 e.stopPropagation();
12387 e.preventDefault();
12389 var target = e.getTarget();
12391 if(target.nodeName.toLowerCase() === 'i'){
12392 target = Roo.get(target).dom.parentNode;
12395 var nodeName = target.nodeName;
12396 var className = target.className;
12397 var html = target.innerHTML;
12399 switch(nodeName.toLowerCase()) {
12401 switch(className) {
12407 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12408 switch(this.viewMode){
12410 this.viewDate = this.moveMonth(this.viewDate, dir);
12414 this.viewDate = this.moveYear(this.viewDate, dir);
12420 var date = new Date();
12421 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12423 this.setValue(this.formatDate(this.date));
12429 if (className.indexOf('disabled') === -1) {
12430 this.viewDate.setUTCDate(1);
12431 if (className.indexOf('month') !== -1) {
12432 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12434 var year = parseInt(html, 10) || 0;
12435 this.viewDate.setUTCFullYear(year);
12444 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12445 var day = parseInt(html, 10) || 1;
12446 var year = this.viewDate.getUTCFullYear(),
12447 month = this.viewDate.getUTCMonth();
12449 if (className.indexOf('old') !== -1) {
12456 } else if (className.indexOf('new') !== -1) {
12464 this.date = this.UTCDate(year, month, day,0,0,0,0);
12465 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12467 this.setValue(this.formatDate(this.date));
12474 setStartDate: function(startDate){
12475 this.startDate = startDate || -Infinity;
12476 if (this.startDate !== -Infinity) {
12477 this.startDate = this.parseDate(this.startDate);
12480 this.updateNavArrows();
12483 setEndDate: function(endDate){
12484 this.endDate = endDate || Infinity;
12485 if (this.endDate !== Infinity) {
12486 this.endDate = this.parseDate(this.endDate);
12489 this.updateNavArrows();
12492 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12493 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12494 if (typeof(this.daysOfWeekDisabled) !== 'object') {
12495 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12497 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12498 return parseInt(d, 10);
12501 this.updateNavArrows();
12504 updateNavArrows: function() {
12505 var d = new Date(this.viewDate),
12506 year = d.getUTCFullYear(),
12507 month = d.getUTCMonth();
12509 Roo.each(this.picker().select('.prev', true).elements, function(v){
12511 switch (this.viewMode) {
12514 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12520 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12527 Roo.each(this.picker().select('.next', true).elements, function(v){
12529 switch (this.viewMode) {
12532 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12538 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12546 moveMonth: function(date, dir){
12547 if (!dir) return date;
12548 var new_date = new Date(date.valueOf()),
12549 day = new_date.getUTCDate(),
12550 month = new_date.getUTCMonth(),
12551 mag = Math.abs(dir),
12553 dir = dir > 0 ? 1 : -1;
12556 // If going back one month, make sure month is not current month
12557 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12559 return new_date.getUTCMonth() == month;
12561 // If going forward one month, make sure month is as expected
12562 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12564 return new_date.getUTCMonth() != new_month;
12566 new_month = month + dir;
12567 new_date.setUTCMonth(new_month);
12568 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12569 if (new_month < 0 || new_month > 11)
12570 new_month = (new_month + 12) % 12;
12572 // For magnitudes >1, move one month at a time...
12573 for (var i=0; i<mag; i++)
12574 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12575 new_date = this.moveMonth(new_date, dir);
12576 // ...then reset the day, keeping it in the new month
12577 new_month = new_date.getUTCMonth();
12578 new_date.setUTCDate(day);
12580 return new_month != new_date.getUTCMonth();
12583 // Common date-resetting loop -- if date is beyond end of month, make it
12586 new_date.setUTCDate(--day);
12587 new_date.setUTCMonth(new_month);
12592 moveYear: function(date, dir){
12593 return this.moveMonth(date, dir*12);
12596 dateWithinRange: function(date){
12597 return date >= this.startDate && date <= this.endDate;
12601 remove: function() {
12602 this.picker().remove();
12607 Roo.apply(Roo.bootstrap.DateField, {
12618 html: '<i class="icon-arrow-left"/>'
12628 html: '<i class="icon-arrow-right"/>'
12670 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12671 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12672 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12673 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12674 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12687 navFnc: 'FullYear',
12692 navFnc: 'FullYear',
12697 Roo.apply(Roo.bootstrap.DateField, {
12701 cls: 'datepicker dropdown-menu',
12705 cls: 'datepicker-days',
12709 cls: 'table-condensed',
12711 Roo.bootstrap.DateField.head,
12715 Roo.bootstrap.DateField.footer
12722 cls: 'datepicker-months',
12726 cls: 'table-condensed',
12728 Roo.bootstrap.DateField.head,
12729 Roo.bootstrap.DateField.content,
12730 Roo.bootstrap.DateField.footer
12737 cls: 'datepicker-years',
12741 cls: 'table-condensed',
12743 Roo.bootstrap.DateField.head,
12744 Roo.bootstrap.DateField.content,
12745 Roo.bootstrap.DateField.footer
12764 * @class Roo.bootstrap.TimeField
12765 * @extends Roo.bootstrap.Input
12766 * Bootstrap DateField class
12770 * Create a new TimeField
12771 * @param {Object} config The config object
12774 Roo.bootstrap.TimeField = function(config){
12775 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12779 * Fires when this field show.
12780 * @param {Roo.bootstrap.DateField} this
12781 * @param {Mixed} date The date value
12786 * Fires when this field hide.
12787 * @param {Roo.bootstrap.DateField} this
12788 * @param {Mixed} date The date value
12793 * Fires when select a date.
12794 * @param {Roo.bootstrap.DateField} this
12795 * @param {Mixed} date The date value
12801 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
12804 * @cfg {String} format
12805 * The default time format string which can be overriden for localization support. The format must be
12806 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12810 onRender: function(ct, position)
12813 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12815 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12817 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12819 this.pop = this.picker().select('>.datepicker-time',true).first();
12820 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12822 this.picker().on('mousedown', this.onMousedown, this);
12823 this.picker().on('click', this.onClick, this);
12825 this.picker().addClass('datepicker-dropdown');
12830 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12831 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12832 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12833 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12834 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12835 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12839 fireKey: function(e){
12840 if (!this.picker().isVisible()){
12841 if (e.keyCode == 27) // allow escape to hide and re-show picker
12846 e.preventDefault();
12854 this.onTogglePeriod();
12857 this.onIncrementMinutes();
12860 this.onDecrementMinutes();
12869 onClick: function(e) {
12870 e.stopPropagation();
12871 e.preventDefault();
12874 picker : function()
12876 return this.el.select('.datepicker', true).first();
12879 fillTime: function()
12881 var time = this.pop.select('tbody', true).first();
12883 time.dom.innerHTML = '';
12898 cls: 'hours-up glyphicon glyphicon-chevron-up'
12918 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12939 cls: 'timepicker-hour',
12954 cls: 'timepicker-minute',
12969 cls: 'btn btn-primary period',
12991 cls: 'hours-down glyphicon glyphicon-chevron-down'
13011 cls: 'minutes-down glyphicon glyphicon-chevron-down'
13029 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13036 var hours = this.time.getHours();
13037 var minutes = this.time.getMinutes();
13050 hours = hours - 12;
13054 hours = '0' + hours;
13058 minutes = '0' + minutes;
13061 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13062 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13063 this.pop.select('button', true).first().dom.innerHTML = period;
13069 this.picker().removeClass(['bottom', 'top']);
13071 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13073 * place to the top of element!
13077 this.picker().addClass('top');
13078 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13083 this.picker().addClass('bottom');
13085 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13088 onFocus : function()
13090 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13094 onBlur : function()
13096 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13102 this.picker().show();
13107 this.fireEvent('show', this, this.date);
13112 this.picker().hide();
13115 this.fireEvent('hide', this, this.date);
13118 setTime : function()
13121 this.setValue(this.time.format(this.format));
13123 this.fireEvent('select', this, this.date);
13128 onMousedown: function(e){
13129 e.stopPropagation();
13130 e.preventDefault();
13133 onIncrementHours: function()
13135 Roo.log('onIncrementHours');
13136 this.time = this.time.add(Date.HOUR, 1);
13141 onDecrementHours: function()
13143 Roo.log('onDecrementHours');
13144 this.time = this.time.add(Date.HOUR, -1);
13148 onIncrementMinutes: function()
13150 Roo.log('onIncrementMinutes');
13151 this.time = this.time.add(Date.MINUTE, 1);
13155 onDecrementMinutes: function()
13157 Roo.log('onDecrementMinutes');
13158 this.time = this.time.add(Date.MINUTE, -1);
13162 onTogglePeriod: function()
13164 Roo.log('onTogglePeriod');
13165 this.time = this.time.add(Date.HOUR, 12);
13172 Roo.apply(Roo.bootstrap.TimeField, {
13202 cls: 'btn btn-info ok',
13214 Roo.apply(Roo.bootstrap.TimeField, {
13218 cls: 'datepicker dropdown-menu',
13222 cls: 'datepicker-time',
13226 cls: 'table-condensed',
13228 Roo.bootstrap.TimeField.content,
13229 Roo.bootstrap.TimeField.footer
13248 * @class Roo.bootstrap.CheckBox
13249 * @extends Roo.bootstrap.Input
13250 * Bootstrap CheckBox class
13252 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13253 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13254 * @cfg {String} boxLabel The text that appears beside the checkbox
13255 * @cfg {Boolean} checked initnal the element
13258 * Create a new CheckBox
13259 * @param {Object} config The config object
13262 Roo.bootstrap.CheckBox = function(config){
13263 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13268 * Fires when the element is checked or unchecked.
13269 * @param {Roo.bootstrap.CheckBox} this This input
13270 * @param {Boolean} checked The new checked value
13276 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
13278 inputType: 'checkbox',
13284 getAutoCreate : function()
13286 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13292 cfg.cls = 'form-group' //input-group
13297 type : this.inputType,
13298 value : (!this.checked) ? this.valueOff : this.inputValue,
13300 placeholder : this.placeholder || ''
13304 if (this.disabled) {
13305 input.disabled=true;
13309 input.checked = this.checked;
13313 input.name = this.name;
13317 input.cls += ' input-' + this.size;
13321 ['xs','sm','md','lg'].map(function(size){
13322 if (settings[size]) {
13323 cfg.cls += ' col-' + size + '-' + settings[size];
13327 var inputblock = input;
13329 if (this.before || this.after) {
13332 cls : 'input-group',
13336 inputblock.cn.push({
13338 cls : 'input-group-addon',
13342 inputblock.cn.push(input);
13344 inputblock.cn.push({
13346 cls : 'input-group-addon',
13353 if (align ==='left' && this.fieldLabel.length) {
13354 Roo.log("left and has label");
13360 cls : 'control-label col-md-' + this.labelWidth,
13361 html : this.fieldLabel
13365 cls : "col-md-" + (12 - this.labelWidth),
13372 } else if ( this.fieldLabel.length) {
13377 tag: this.boxLabel ? 'span' : 'label',
13379 cls: 'control-label box-input-label',
13380 //cls : 'input-group-addon',
13381 html : this.fieldLabel
13391 Roo.log(" no label && no align");
13406 html: this.boxLabel
13415 * return the real input element.
13417 inputEl: function ()
13419 return this.el.select('input.form-box',true).first();
13424 return this.el.select('label.control-label',true).first();
13427 initEvents : function()
13429 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13431 this.inputEl().on('click', this.onClick, this);
13435 onClick : function()
13437 this.setChecked(!this.checked);
13440 setChecked : function(state,suppressEvent)
13442 this.checked = state;
13444 this.inputEl().dom.checked = state;
13446 if(suppressEvent !== true){
13447 this.fireEvent('check', this, state);
13450 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13454 setValue : function(v,suppressEvent)
13456 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13470 * @class Roo.bootstrap.Radio
13471 * @extends Roo.bootstrap.CheckBox
13472 * Bootstrap Radio class
13475 * Create a new Radio
13476 * @param {Object} config The config object
13479 Roo.bootstrap.Radio = function(config){
13480 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13484 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
13486 inputType: 'radio',
13490 getAutoCreate : function()
13492 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13498 cfg.cls = 'form-group' //input-group
13503 type : this.inputType,
13504 value : (!this.checked) ? this.valueOff : this.inputValue,
13506 placeholder : this.placeholder || ''
13510 if (this.disabled) {
13511 input.disabled=true;
13515 input.checked = this.checked;
13519 input.name = this.name;
13523 input.cls += ' input-' + this.size;
13527 ['xs','sm','md','lg'].map(function(size){
13528 if (settings[size]) {
13529 cfg.cls += ' col-' + size + '-' + settings[size];
13533 var inputblock = input;
13535 if (this.before || this.after) {
13538 cls : 'input-group',
13542 inputblock.cn.push({
13544 cls : 'input-group-addon',
13548 inputblock.cn.push(input);
13550 inputblock.cn.push({
13552 cls : 'input-group-addon',
13559 if (align ==='left' && this.fieldLabel.length) {
13560 Roo.log("left and has label");
13566 cls : 'control-label col-md-' + this.labelWidth,
13567 html : this.fieldLabel
13571 cls : "col-md-" + (12 - this.labelWidth),
13578 } else if ( this.fieldLabel.length) {
13585 cls: 'control-label box-input-label',
13586 //cls : 'input-group-addon',
13587 html : this.fieldLabel
13597 Roo.log(" no label && no align");
13612 html: this.boxLabel
13620 onClick : function()
13622 this.setChecked(true);
13625 setChecked : function(state,suppressEvent)
13628 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13629 v.dom.checked = false;
13633 this.checked = state;
13634 this.inputEl().dom.checked = state;
13636 if(suppressEvent !== true){
13637 this.fireEvent('check', this, state);
13640 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13644 getGroupValue : function()
13647 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13648 if(v.dom.checked == true){
13649 value = v.dom.value;
13657 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13658 * @return {Mixed} value The field value
13660 getValue : function(){
13661 return this.getGroupValue();
13667 //<script type="text/javascript">
13670 * Based Ext JS Library 1.1.1
13671 * Copyright(c) 2006-2007, Ext JS, LLC.
13677 * @class Roo.HtmlEditorCore
13678 * @extends Roo.Component
13679 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13681 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13684 Roo.HtmlEditorCore = function(config){
13687 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13690 * @event initialize
13691 * Fires when the editor is fully initialized (including the iframe)
13692 * @param {Roo.HtmlEditorCore} this
13697 * Fires when the editor is first receives the focus. Any insertion must wait
13698 * until after this event.
13699 * @param {Roo.HtmlEditorCore} this
13703 * @event beforesync
13704 * Fires before the textarea is updated with content from the editor iframe. Return false
13705 * to cancel the sync.
13706 * @param {Roo.HtmlEditorCore} this
13707 * @param {String} html
13711 * @event beforepush
13712 * Fires before the iframe editor is updated with content from the textarea. Return false
13713 * to cancel the push.
13714 * @param {Roo.HtmlEditorCore} this
13715 * @param {String} html
13720 * Fires when the textarea is updated with content from the editor iframe.
13721 * @param {Roo.HtmlEditorCore} this
13722 * @param {String} html
13727 * Fires when the iframe editor is updated with content from the textarea.
13728 * @param {Roo.HtmlEditorCore} this
13729 * @param {String} html
13734 * @event editorevent
13735 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13736 * @param {Roo.HtmlEditorCore} this
13744 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
13748 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
13754 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
13759 * @cfg {Number} height (in pixels)
13763 * @cfg {Number} width (in pixels)
13768 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13771 stylesheets: false,
13776 // private properties
13777 validationEvent : false,
13779 initialized : false,
13781 sourceEditMode : false,
13782 onFocus : Roo.emptyFn,
13784 hideMode:'offsets',
13792 * Protected method that will not generally be called directly. It
13793 * is called when the editor initializes the iframe with HTML contents. Override this method if you
13794 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13796 getDocMarkup : function(){
13799 Roo.log(this.stylesheets);
13801 // inherit styels from page...??
13802 if (this.stylesheets === false) {
13804 Roo.get(document.head).select('style').each(function(node) {
13805 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13808 Roo.get(document.head).select('link').each(function(node) {
13809 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13812 } else if (!this.stylesheets.length) {
13814 st = '<style type="text/css">' +
13815 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13818 Roo.each(this.stylesheets, function(s) {
13819 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13824 st += '<style type="text/css">' +
13825 'IMG { cursor: pointer } ' +
13829 return '<html><head>' + st +
13830 //<style type="text/css">' +
13831 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13833 ' </head><body class="roo-htmleditor-body"></body></html>';
13837 onRender : function(ct, position)
13840 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13841 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13844 this.el.dom.style.border = '0 none';
13845 this.el.dom.setAttribute('tabIndex', -1);
13846 this.el.addClass('x-hidden hide');
13850 if(Roo.isIE){ // fix IE 1px bogus margin
13851 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13855 this.frameId = Roo.id();
13859 var iframe = this.owner.wrap.createChild({
13861 cls: 'form-control', // bootstrap..
13863 name: this.frameId,
13864 frameBorder : 'no',
13865 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13870 this.iframe = iframe.dom;
13872 this.assignDocWin();
13874 this.doc.designMode = 'on';
13877 this.doc.write(this.getDocMarkup());
13881 var task = { // must defer to wait for browser to be ready
13883 //console.log("run task?" + this.doc.readyState);
13884 this.assignDocWin();
13885 if(this.doc.body || this.doc.readyState == 'complete'){
13887 this.doc.designMode="on";
13891 Roo.TaskMgr.stop(task);
13892 this.initEditor.defer(10, this);
13899 Roo.TaskMgr.start(task);
13906 onResize : function(w, h)
13908 Roo.log('resize: ' +w + ',' + h );
13909 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13913 if(typeof w == 'number'){
13915 this.iframe.style.width = w + 'px';
13917 if(typeof h == 'number'){
13919 this.iframe.style.height = h + 'px';
13921 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13928 * Toggles the editor between standard and source edit mode.
13929 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13931 toggleSourceEdit : function(sourceEditMode){
13933 this.sourceEditMode = sourceEditMode === true;
13935 if(this.sourceEditMode){
13937 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13940 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13941 //this.iframe.className = '';
13944 //this.setSize(this.owner.wrap.getSize());
13945 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13952 * Protected method that will not generally be called directly. If you need/want
13953 * custom HTML cleanup, this is the method you should override.
13954 * @param {String} html The HTML to be cleaned
13955 * return {String} The cleaned HTML
13957 cleanHtml : function(html){
13958 html = String(html);
13959 if(html.length > 5){
13960 if(Roo.isSafari){ // strip safari nonsense
13961 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13964 if(html == ' '){
13971 * HTML Editor -> Textarea
13972 * Protected method that will not generally be called directly. Syncs the contents
13973 * of the editor iframe with the textarea.
13975 syncValue : function(){
13976 if(this.initialized){
13977 var bd = (this.doc.body || this.doc.documentElement);
13978 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13979 var html = bd.innerHTML;
13981 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13982 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13984 html = '<div style="'+m[0]+'">' + html + '</div>';
13987 html = this.cleanHtml(html);
13988 // fix up the special chars.. normaly like back quotes in word...
13989 // however we do not want to do this with chinese..
13990 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13991 var cc = b.charCodeAt();
13993 (cc >= 0x4E00 && cc < 0xA000 ) ||
13994 (cc >= 0x3400 && cc < 0x4E00 ) ||
13995 (cc >= 0xf900 && cc < 0xfb00 )
14001 if(this.owner.fireEvent('beforesync', this, html) !== false){
14002 this.el.dom.value = html;
14003 this.owner.fireEvent('sync', this, html);
14009 * Protected method that will not generally be called directly. Pushes the value of the textarea
14010 * into the iframe editor.
14012 pushValue : function(){
14013 if(this.initialized){
14014 var v = this.el.dom.value.trim();
14016 // if(v.length < 1){
14020 if(this.owner.fireEvent('beforepush', this, v) !== false){
14021 var d = (this.doc.body || this.doc.documentElement);
14023 this.cleanUpPaste();
14024 this.el.dom.value = d.innerHTML;
14025 this.owner.fireEvent('push', this, v);
14031 deferFocus : function(){
14032 this.focus.defer(10, this);
14036 focus : function(){
14037 if(this.win && !this.sourceEditMode){
14044 assignDocWin: function()
14046 var iframe = this.iframe;
14049 this.doc = iframe.contentWindow.document;
14050 this.win = iframe.contentWindow;
14052 if (!Roo.get(this.frameId)) {
14055 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14056 this.win = Roo.get(this.frameId).dom.contentWindow;
14061 initEditor : function(){
14062 //console.log("INIT EDITOR");
14063 this.assignDocWin();
14067 this.doc.designMode="on";
14069 this.doc.write(this.getDocMarkup());
14072 var dbody = (this.doc.body || this.doc.documentElement);
14073 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14074 // this copies styles from the containing element into thsi one..
14075 // not sure why we need all of this..
14076 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14077 ss['background-attachment'] = 'fixed'; // w3c
14078 dbody.bgProperties = 'fixed'; // ie
14079 Roo.DomHelper.applyStyles(dbody, ss);
14080 Roo.EventManager.on(this.doc, {
14081 //'mousedown': this.onEditorEvent,
14082 'mouseup': this.onEditorEvent,
14083 'dblclick': this.onEditorEvent,
14084 'click': this.onEditorEvent,
14085 'keyup': this.onEditorEvent,
14090 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14092 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14093 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14095 this.initialized = true;
14097 this.owner.fireEvent('initialize', this);
14102 onDestroy : function(){
14108 //for (var i =0; i < this.toolbars.length;i++) {
14109 // // fixme - ask toolbars for heights?
14110 // this.toolbars[i].onDestroy();
14113 //this.wrap.dom.innerHTML = '';
14114 //this.wrap.remove();
14119 onFirstFocus : function(){
14121 this.assignDocWin();
14124 this.activated = true;
14127 if(Roo.isGecko){ // prevent silly gecko errors
14129 var s = this.win.getSelection();
14130 if(!s.focusNode || s.focusNode.nodeType != 3){
14131 var r = s.getRangeAt(0);
14132 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14137 this.execCmd('useCSS', true);
14138 this.execCmd('styleWithCSS', false);
14141 this.owner.fireEvent('activate', this);
14145 adjustFont: function(btn){
14146 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14147 //if(Roo.isSafari){ // safari
14150 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14151 if(Roo.isSafari){ // safari
14152 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14153 v = (v < 10) ? 10 : v;
14154 v = (v > 48) ? 48 : v;
14155 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14160 v = Math.max(1, v+adjust);
14162 this.execCmd('FontSize', v );
14165 onEditorEvent : function(e){
14166 this.owner.fireEvent('editorevent', this, e);
14167 // this.updateToolbar();
14168 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14171 insertTag : function(tg)
14173 // could be a bit smarter... -> wrap the current selected tRoo..
14174 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14176 range = this.createRange(this.getSelection());
14177 var wrappingNode = this.doc.createElement(tg.toLowerCase());
14178 wrappingNode.appendChild(range.extractContents());
14179 range.insertNode(wrappingNode);
14186 this.execCmd("formatblock", tg);
14190 insertText : function(txt)
14194 var range = this.createRange();
14195 range.deleteContents();
14196 //alert(Sender.getAttribute('label'));
14198 range.insertNode(this.doc.createTextNode(txt));
14204 * Executes a Midas editor command on the editor document and performs necessary focus and
14205 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14206 * @param {String} cmd The Midas command
14207 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14209 relayCmd : function(cmd, value){
14211 this.execCmd(cmd, value);
14212 this.owner.fireEvent('editorevent', this);
14213 //this.updateToolbar();
14214 this.owner.deferFocus();
14218 * Executes a Midas editor command directly on the editor document.
14219 * For visual commands, you should use {@link #relayCmd} instead.
14220 * <b>This should only be called after the editor is initialized.</b>
14221 * @param {String} cmd The Midas command
14222 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14224 execCmd : function(cmd, value){
14225 this.doc.execCommand(cmd, false, value === undefined ? null : value);
14232 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14234 * @param {String} text | dom node..
14236 insertAtCursor : function(text)
14241 if(!this.activated){
14247 var r = this.doc.selection.createRange();
14258 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14262 // from jquery ui (MIT licenced)
14264 var win = this.win;
14266 if (win.getSelection && win.getSelection().getRangeAt) {
14267 range = win.getSelection().getRangeAt(0);
14268 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14269 range.insertNode(node);
14270 } else if (win.document.selection && win.document.selection.createRange) {
14271 // no firefox support
14272 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14273 win.document.selection.createRange().pasteHTML(txt);
14275 // no firefox support
14276 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14277 this.execCmd('InsertHTML', txt);
14286 mozKeyPress : function(e){
14288 var c = e.getCharCode(), cmd;
14291 c = String.fromCharCode(c).toLowerCase();
14305 this.cleanUpPaste.defer(100, this);
14313 e.preventDefault();
14321 fixKeys : function(){ // load time branching for fastest keydown performance
14323 return function(e){
14324 var k = e.getKey(), r;
14327 r = this.doc.selection.createRange();
14330 r.pasteHTML('    ');
14337 r = this.doc.selection.createRange();
14339 var target = r.parentElement();
14340 if(!target || target.tagName.toLowerCase() != 'li'){
14342 r.pasteHTML('<br />');
14348 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14349 this.cleanUpPaste.defer(100, this);
14355 }else if(Roo.isOpera){
14356 return function(e){
14357 var k = e.getKey();
14361 this.execCmd('InsertHTML','    ');
14364 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14365 this.cleanUpPaste.defer(100, this);
14370 }else if(Roo.isSafari){
14371 return function(e){
14372 var k = e.getKey();
14376 this.execCmd('InsertText','\t');
14380 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14381 this.cleanUpPaste.defer(100, this);
14389 getAllAncestors: function()
14391 var p = this.getSelectedNode();
14394 a.push(p); // push blank onto stack..
14395 p = this.getParentElement();
14399 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14403 a.push(this.doc.body);
14407 lastSelNode : false,
14410 getSelection : function()
14412 this.assignDocWin();
14413 return Roo.isIE ? this.doc.selection : this.win.getSelection();
14416 getSelectedNode: function()
14418 // this may only work on Gecko!!!
14420 // should we cache this!!!!
14425 var range = this.createRange(this.getSelection()).cloneRange();
14428 var parent = range.parentElement();
14430 var testRange = range.duplicate();
14431 testRange.moveToElementText(parent);
14432 if (testRange.inRange(range)) {
14435 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14438 parent = parent.parentElement;
14443 // is ancestor a text element.
14444 var ac = range.commonAncestorContainer;
14445 if (ac.nodeType == 3) {
14446 ac = ac.parentNode;
14449 var ar = ac.childNodes;
14452 var other_nodes = [];
14453 var has_other_nodes = false;
14454 for (var i=0;i<ar.length;i++) {
14455 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
14458 // fullly contained node.
14460 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14465 // probably selected..
14466 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14467 other_nodes.push(ar[i]);
14471 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
14476 has_other_nodes = true;
14478 if (!nodes.length && other_nodes.length) {
14479 nodes= other_nodes;
14481 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14487 createRange: function(sel)
14489 // this has strange effects when using with
14490 // top toolbar - not sure if it's a great idea.
14491 //this.editor.contentWindow.focus();
14492 if (typeof sel != "undefined") {
14494 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14496 return this.doc.createRange();
14499 return this.doc.createRange();
14502 getParentElement: function()
14505 this.assignDocWin();
14506 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14508 var range = this.createRange(sel);
14511 var p = range.commonAncestorContainer;
14512 while (p.nodeType == 3) { // text node
14523 * Range intersection.. the hard stuff...
14527 * [ -- selected range --- ]
14531 * if end is before start or hits it. fail.
14532 * if start is after end or hits it fail.
14534 * if either hits (but other is outside. - then it's not
14540 // @see http://www.thismuchiknow.co.uk/?p=64.
14541 rangeIntersectsNode : function(range, node)
14543 var nodeRange = node.ownerDocument.createRange();
14545 nodeRange.selectNode(node);
14547 nodeRange.selectNodeContents(node);
14550 var rangeStartRange = range.cloneRange();
14551 rangeStartRange.collapse(true);
14553 var rangeEndRange = range.cloneRange();
14554 rangeEndRange.collapse(false);
14556 var nodeStartRange = nodeRange.cloneRange();
14557 nodeStartRange.collapse(true);
14559 var nodeEndRange = nodeRange.cloneRange();
14560 nodeEndRange.collapse(false);
14562 return rangeStartRange.compareBoundaryPoints(
14563 Range.START_TO_START, nodeEndRange) == -1 &&
14564 rangeEndRange.compareBoundaryPoints(
14565 Range.START_TO_START, nodeStartRange) == 1;
14569 rangeCompareNode : function(range, node)
14571 var nodeRange = node.ownerDocument.createRange();
14573 nodeRange.selectNode(node);
14575 nodeRange.selectNodeContents(node);
14579 range.collapse(true);
14581 nodeRange.collapse(true);
14583 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14584 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
14586 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14588 var nodeIsBefore = ss == 1;
14589 var nodeIsAfter = ee == -1;
14591 if (nodeIsBefore && nodeIsAfter)
14593 if (!nodeIsBefore && nodeIsAfter)
14594 return 1; //right trailed.
14596 if (nodeIsBefore && !nodeIsAfter)
14597 return 2; // left trailed.
14602 // private? - in a new class?
14603 cleanUpPaste : function()
14605 // cleans up the whole document..
14606 Roo.log('cleanuppaste');
14608 this.cleanUpChildren(this.doc.body);
14609 var clean = this.cleanWordChars(this.doc.body.innerHTML);
14610 if (clean != this.doc.body.innerHTML) {
14611 this.doc.body.innerHTML = clean;
14616 cleanWordChars : function(input) {// change the chars to hex code
14617 var he = Roo.HtmlEditorCore;
14619 var output = input;
14620 Roo.each(he.swapCodes, function(sw) {
14621 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14623 output = output.replace(swapper, sw[1]);
14630 cleanUpChildren : function (n)
14632 if (!n.childNodes.length) {
14635 for (var i = n.childNodes.length-1; i > -1 ; i--) {
14636 this.cleanUpChild(n.childNodes[i]);
14643 cleanUpChild : function (node)
14646 //console.log(node);
14647 if (node.nodeName == "#text") {
14648 // clean up silly Windows -- stuff?
14651 if (node.nodeName == "#comment") {
14652 node.parentNode.removeChild(node);
14653 // clean up silly Windows -- stuff?
14657 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14659 node.parentNode.removeChild(node);
14664 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14666 // remove <a name=....> as rendering on yahoo mailer is borked with this.
14667 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14669 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14670 // remove_keep_children = true;
14673 if (remove_keep_children) {
14674 this.cleanUpChildren(node);
14675 // inserts everything just before this node...
14676 while (node.childNodes.length) {
14677 var cn = node.childNodes[0];
14678 node.removeChild(cn);
14679 node.parentNode.insertBefore(cn, node);
14681 node.parentNode.removeChild(node);
14685 if (!node.attributes || !node.attributes.length) {
14686 this.cleanUpChildren(node);
14690 function cleanAttr(n,v)
14693 if (v.match(/^\./) || v.match(/^\//)) {
14696 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14699 if (v.match(/^#/)) {
14702 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14703 node.removeAttribute(n);
14707 function cleanStyle(n,v)
14709 if (v.match(/expression/)) { //XSS?? should we even bother..
14710 node.removeAttribute(n);
14713 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14714 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14717 var parts = v.split(/;/);
14720 Roo.each(parts, function(p) {
14721 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14725 var l = p.split(':').shift().replace(/\s+/g,'');
14726 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14728 if ( cblack.indexOf(l) > -1) {
14729 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14730 //node.removeAttribute(n);
14734 // only allow 'c whitelisted system attributes'
14735 if ( cwhite.length && cwhite.indexOf(l) < 0) {
14736 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14737 //node.removeAttribute(n);
14747 if (clean.length) {
14748 node.setAttribute(n, clean.join(';'));
14750 node.removeAttribute(n);
14756 for (var i = node.attributes.length-1; i > -1 ; i--) {
14757 var a = node.attributes[i];
14760 if (a.name.toLowerCase().substr(0,2)=='on') {
14761 node.removeAttribute(a.name);
14764 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14765 node.removeAttribute(a.name);
14768 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14769 cleanAttr(a.name,a.value); // fixme..
14772 if (a.name == 'style') {
14773 cleanStyle(a.name,a.value);
14776 /// clean up MS crap..
14777 // tecnically this should be a list of valid class'es..
14780 if (a.name == 'class') {
14781 if (a.value.match(/^Mso/)) {
14782 node.className = '';
14785 if (a.value.match(/body/)) {
14786 node.className = '';
14797 this.cleanUpChildren(node);
14803 // hide stuff that is not compatible
14817 * @event specialkey
14821 * @cfg {String} fieldClass @hide
14824 * @cfg {String} focusClass @hide
14827 * @cfg {String} autoCreate @hide
14830 * @cfg {String} inputType @hide
14833 * @cfg {String} invalidClass @hide
14836 * @cfg {String} invalidText @hide
14839 * @cfg {String} msgFx @hide
14842 * @cfg {String} validateOnBlur @hide
14846 Roo.HtmlEditorCore.white = [
14847 'area', 'br', 'img', 'input', 'hr', 'wbr',
14849 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14850 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14851 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14852 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14853 'table', 'ul', 'xmp',
14855 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14858 'dir', 'menu', 'ol', 'ul', 'dl',
14864 Roo.HtmlEditorCore.black = [
14865 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14867 'base', 'basefont', 'bgsound', 'blink', 'body',
14868 'frame', 'frameset', 'head', 'html', 'ilayer',
14869 'iframe', 'layer', 'link', 'meta', 'object',
14870 'script', 'style' ,'title', 'xml' // clean later..
14872 Roo.HtmlEditorCore.clean = [
14873 'script', 'style', 'title', 'xml'
14875 Roo.HtmlEditorCore.remove = [
14880 Roo.HtmlEditorCore.ablack = [
14884 Roo.HtmlEditorCore.aclean = [
14885 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14889 Roo.HtmlEditorCore.pwhite= [
14890 'http', 'https', 'mailto'
14893 // white listed style attributes.
14894 Roo.HtmlEditorCore.cwhite= [
14895 // 'text-align', /// default is to allow most things..
14901 // black listed style attributes.
14902 Roo.HtmlEditorCore.cblack= [
14903 // 'font-size' -- this can be set by the project
14907 Roo.HtmlEditorCore.swapCodes =[
14926 * @class Roo.bootstrap.HtmlEditor
14927 * @extends Roo.bootstrap.TextArea
14928 * Bootstrap HtmlEditor class
14931 * Create a new HtmlEditor
14932 * @param {Object} config The config object
14935 Roo.bootstrap.HtmlEditor = function(config){
14936 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14937 if (!this.toolbars) {
14938 this.toolbars = [];
14940 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14943 * @event initialize
14944 * Fires when the editor is fully initialized (including the iframe)
14945 * @param {HtmlEditor} this
14950 * Fires when the editor is first receives the focus. Any insertion must wait
14951 * until after this event.
14952 * @param {HtmlEditor} this
14956 * @event beforesync
14957 * Fires before the textarea is updated with content from the editor iframe. Return false
14958 * to cancel the sync.
14959 * @param {HtmlEditor} this
14960 * @param {String} html
14964 * @event beforepush
14965 * Fires before the iframe editor is updated with content from the textarea. Return false
14966 * to cancel the push.
14967 * @param {HtmlEditor} this
14968 * @param {String} html
14973 * Fires when the textarea is updated with content from the editor iframe.
14974 * @param {HtmlEditor} this
14975 * @param {String} html
14980 * Fires when the iframe editor is updated with content from the textarea.
14981 * @param {HtmlEditor} this
14982 * @param {String} html
14986 * @event editmodechange
14987 * Fires when the editor switches edit modes
14988 * @param {HtmlEditor} this
14989 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14991 editmodechange: true,
14993 * @event editorevent
14994 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14995 * @param {HtmlEditor} this
14999 * @event firstfocus
15000 * Fires when on first focus - needed by toolbars..
15001 * @param {HtmlEditor} this
15006 * Auto save the htmlEditor value as a file into Events
15007 * @param {HtmlEditor} this
15011 * @event savedpreview
15012 * preview the saved version of htmlEditor
15013 * @param {HtmlEditor} this
15020 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
15024 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15029 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
15034 * @cfg {Number} height (in pixels)
15038 * @cfg {Number} width (in pixels)
15043 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15046 stylesheets: false,
15051 // private properties
15052 validationEvent : false,
15054 initialized : false,
15057 onFocus : Roo.emptyFn,
15059 hideMode:'offsets',
15062 tbContainer : false,
15064 toolbarContainer :function() {
15065 return this.wrap.select('.x-html-editor-tb',true).first();
15069 * Protected method that will not generally be called directly. It
15070 * is called when the editor creates its toolbar. Override this method if you need to
15071 * add custom toolbar buttons.
15072 * @param {HtmlEditor} editor
15074 createToolbar : function(){
15076 Roo.log("create toolbars");
15078 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15079 this.toolbars[0].render(this.toolbarContainer());
15083 // if (!editor.toolbars || !editor.toolbars.length) {
15084 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15087 // for (var i =0 ; i < editor.toolbars.length;i++) {
15088 // editor.toolbars[i] = Roo.factory(
15089 // typeof(editor.toolbars[i]) == 'string' ?
15090 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
15091 // Roo.bootstrap.HtmlEditor);
15092 // editor.toolbars[i].init(editor);
15098 onRender : function(ct, position)
15100 // Roo.log("Call onRender: " + this.xtype);
15102 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15104 this.wrap = this.inputEl().wrap({
15105 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15108 this.editorcore.onRender(ct, position);
15110 if (this.resizable) {
15111 this.resizeEl = new Roo.Resizable(this.wrap, {
15115 minHeight : this.height,
15116 height: this.height,
15117 handles : this.resizable,
15120 resize : function(r, w, h) {
15121 _t.onResize(w,h); // -something
15127 this.createToolbar(this);
15130 if(!this.width && this.resizable){
15131 this.setSize(this.wrap.getSize());
15133 if (this.resizeEl) {
15134 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15135 // should trigger onReize..
15141 onResize : function(w, h)
15143 Roo.log('resize: ' +w + ',' + h );
15144 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15148 if(this.inputEl() ){
15149 if(typeof w == 'number'){
15150 var aw = w - this.wrap.getFrameWidth('lr');
15151 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15154 if(typeof h == 'number'){
15155 var tbh = -11; // fixme it needs to tool bar size!
15156 for (var i =0; i < this.toolbars.length;i++) {
15157 // fixme - ask toolbars for heights?
15158 tbh += this.toolbars[i].el.getHeight();
15159 //if (this.toolbars[i].footer) {
15160 // tbh += this.toolbars[i].footer.el.getHeight();
15168 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15169 ah -= 5; // knock a few pixes off for look..
15170 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15174 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15175 this.editorcore.onResize(ew,eh);
15180 * Toggles the editor between standard and source edit mode.
15181 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15183 toggleSourceEdit : function(sourceEditMode)
15185 this.editorcore.toggleSourceEdit(sourceEditMode);
15187 if(this.editorcore.sourceEditMode){
15188 Roo.log('editor - showing textarea');
15191 // Roo.log(this.syncValue());
15193 this.inputEl().removeClass('hide');
15194 this.inputEl().dom.removeAttribute('tabIndex');
15195 this.inputEl().focus();
15197 Roo.log('editor - hiding textarea');
15199 // Roo.log(this.pushValue());
15202 this.inputEl().addClass('hide');
15203 this.inputEl().dom.setAttribute('tabIndex', -1);
15204 //this.deferFocus();
15207 if(this.resizable){
15208 this.setSize(this.wrap.getSize());
15211 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15214 // private (for BoxComponent)
15215 adjustSize : Roo.BoxComponent.prototype.adjustSize,
15217 // private (for BoxComponent)
15218 getResizeEl : function(){
15222 // private (for BoxComponent)
15223 getPositionEl : function(){
15228 initEvents : function(){
15229 this.originalValue = this.getValue();
15233 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15236 // markInvalid : Roo.emptyFn,
15238 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15241 // clearInvalid : Roo.emptyFn,
15243 setValue : function(v){
15244 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15245 this.editorcore.pushValue();
15250 deferFocus : function(){
15251 this.focus.defer(10, this);
15255 focus : function(){
15256 this.editorcore.focus();
15262 onDestroy : function(){
15268 for (var i =0; i < this.toolbars.length;i++) {
15269 // fixme - ask toolbars for heights?
15270 this.toolbars[i].onDestroy();
15273 this.wrap.dom.innerHTML = '';
15274 this.wrap.remove();
15279 onFirstFocus : function(){
15280 //Roo.log("onFirstFocus");
15281 this.editorcore.onFirstFocus();
15282 for (var i =0; i < this.toolbars.length;i++) {
15283 this.toolbars[i].onFirstFocus();
15289 syncValue : function()
15291 this.editorcore.syncValue();
15294 pushValue : function()
15296 this.editorcore.pushValue();
15300 // hide stuff that is not compatible
15314 * @event specialkey
15318 * @cfg {String} fieldClass @hide
15321 * @cfg {String} focusClass @hide
15324 * @cfg {String} autoCreate @hide
15327 * @cfg {String} inputType @hide
15330 * @cfg {String} invalidClass @hide
15333 * @cfg {String} invalidText @hide
15336 * @cfg {String} msgFx @hide
15339 * @cfg {String} validateOnBlur @hide
15350 * @class Roo.bootstrap.HtmlEditorToolbar1
15355 new Roo.bootstrap.HtmlEditor({
15358 new Roo.bootstrap.HtmlEditorToolbar1({
15359 disable : { fonts: 1 , format: 1, ..., ... , ...],
15365 * @cfg {Object} disable List of elements to disable..
15366 * @cfg {Array} btns List of additional buttons.
15370 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15373 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15376 Roo.apply(this, config);
15378 // default disabled, based on 'good practice'..
15379 this.disable = this.disable || {};
15380 Roo.applyIf(this.disable, {
15383 specialElements : true
15385 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15387 this.editor = config.editor;
15388 this.editorcore = config.editor.editorcore;
15390 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15392 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15393 // dont call parent... till later.
15395 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
15401 editorcore : false,
15406 "h1","h2","h3","h4","h5","h6",
15408 "abbr", "acronym", "address", "cite", "samp", "var",
15412 onRender : function(ct, position)
15414 // Roo.log("Call onRender: " + this.xtype);
15416 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15418 this.el.dom.style.marginBottom = '0';
15420 var editorcore = this.editorcore;
15421 var editor= this.editor;
15424 var btn = function(id,cmd , toggle, handler){
15426 var event = toggle ? 'toggle' : 'click';
15431 xns: Roo.bootstrap,
15434 enableToggle:toggle !== false,
15436 pressed : toggle ? false : null,
15439 a.listeners[toggle ? 'toggle' : 'click'] = function() {
15440 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
15449 xns: Roo.bootstrap,
15450 glyphicon : 'font',
15454 xns: Roo.bootstrap,
15458 Roo.each(this.formats, function(f) {
15459 style.menu.items.push({
15461 xns: Roo.bootstrap,
15462 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15467 editorcore.insertTag(this.tagname);
15474 children.push(style);
15477 btn('bold',false,true);
15478 btn('italic',false,true);
15479 btn('align-left', 'justifyleft',true);
15480 btn('align-center', 'justifycenter',true);
15481 btn('align-right' , 'justifyright',true);
15482 btn('link', false, false, function(btn) {
15483 //Roo.log("create link?");
15484 var url = prompt(this.createLinkText, this.defaultLinkValue);
15485 if(url && url != 'http:/'+'/'){
15486 this.editorcore.relayCmd('createlink', url);
15489 btn('list','insertunorderedlist',true);
15490 btn('pencil', false,true, function(btn){
15493 this.toggleSourceEdit(btn.pressed);
15499 xns: Roo.bootstrap,
15504 xns: Roo.bootstrap,
15509 cog.menu.items.push({
15511 xns: Roo.bootstrap,
15512 html : Clean styles,
15517 editorcore.insertTag(this.tagname);
15526 this.xtype = 'Navbar';
15528 for(var i=0;i< children.length;i++) {
15530 this.buttons.add(this.addxtypeChild(children[i]));
15534 editor.on('editorevent', this.updateToolbar, this);
15536 onBtnClick : function(id)
15538 this.editorcore.relayCmd(id);
15539 this.editorcore.focus();
15543 * Protected method that will not generally be called directly. It triggers
15544 * a toolbar update by reading the markup state of the current selection in the editor.
15546 updateToolbar: function(){
15548 if(!this.editorcore.activated){
15549 this.editor.onFirstFocus(); // is this neeed?
15553 var btns = this.buttons;
15554 var doc = this.editorcore.doc;
15555 btns.get('bold').setActive(doc.queryCommandState('bold'));
15556 btns.get('italic').setActive(doc.queryCommandState('italic'));
15557 //btns.get('underline').setActive(doc.queryCommandState('underline'));
15559 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15560 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15561 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15563 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15564 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15567 var ans = this.editorcore.getAllAncestors();
15568 if (this.formatCombo) {
15571 var store = this.formatCombo.store;
15572 this.formatCombo.setValue("");
15573 for (var i =0; i < ans.length;i++) {
15574 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15576 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15584 // hides menus... - so this cant be on a menu...
15585 Roo.bootstrap.MenuMgr.hideAll();
15587 Roo.bootstrap.MenuMgr.hideAll();
15588 //this.editorsyncValue();
15590 onFirstFocus: function() {
15591 this.buttons.each(function(item){
15595 toggleSourceEdit : function(sourceEditMode){
15598 if(sourceEditMode){
15599 Roo.log("disabling buttons");
15600 this.buttons.each( function(item){
15601 if(item.cmd != 'pencil'){
15607 Roo.log("enabling buttons");
15608 if(this.editorcore.initialized){
15609 this.buttons.each( function(item){
15615 Roo.log("calling toggole on editor");
15616 // tell the editor that it's been pressed..
15617 this.editor.toggleSourceEdit(sourceEditMode);
15627 * @class Roo.bootstrap.Table.AbstractSelectionModel
15628 * @extends Roo.util.Observable
15629 * Abstract base class for grid SelectionModels. It provides the interface that should be
15630 * implemented by descendant classes. This class should not be directly instantiated.
15633 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15634 this.locked = false;
15635 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15639 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
15640 /** @ignore Called by the grid automatically. Do not call directly. */
15641 init : function(grid){
15647 * Locks the selections.
15650 this.locked = true;
15654 * Unlocks the selections.
15656 unlock : function(){
15657 this.locked = false;
15661 * Returns true if the selections are locked.
15662 * @return {Boolean}
15664 isLocked : function(){
15665 return this.locked;
15669 * @class Roo.bootstrap.Table.ColumnModel
15670 * @extends Roo.util.Observable
15671 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15672 * the columns in the table.
15675 * @param {Object} config An Array of column config objects. See this class's
15676 * config objects for details.
15678 Roo.bootstrap.Table.ColumnModel = function(config){
15680 * The config passed into the constructor
15682 this.config = config;
15685 // if no id, create one
15686 // if the column does not have a dataIndex mapping,
15687 // map it to the order it is in the config
15688 for(var i = 0, len = config.length; i < len; i++){
15690 if(typeof c.dataIndex == "undefined"){
15693 if(typeof c.renderer == "string"){
15694 c.renderer = Roo.util.Format[c.renderer];
15696 if(typeof c.id == "undefined"){
15699 // if(c.editor && c.editor.xtype){
15700 // c.editor = Roo.factory(c.editor, Roo.grid);
15702 // if(c.editor && c.editor.isFormField){
15703 // c.editor = new Roo.grid.GridEditor(c.editor);
15706 this.lookup[c.id] = c;
15710 * The width of columns which have no width specified (defaults to 100)
15713 this.defaultWidth = 100;
15716 * Default sortable of columns which have no sortable specified (defaults to false)
15719 this.defaultSortable = false;
15723 * @event widthchange
15724 * Fires when the width of a column changes.
15725 * @param {ColumnModel} this
15726 * @param {Number} columnIndex The column index
15727 * @param {Number} newWidth The new width
15729 "widthchange": true,
15731 * @event headerchange
15732 * Fires when the text of a header changes.
15733 * @param {ColumnModel} this
15734 * @param {Number} columnIndex The column index
15735 * @param {Number} newText The new header text
15737 "headerchange": true,
15739 * @event hiddenchange
15740 * Fires when a column is hidden or "unhidden".
15741 * @param {ColumnModel} this
15742 * @param {Number} columnIndex The column index
15743 * @param {Boolean} hidden true if hidden, false otherwise
15745 "hiddenchange": true,
15747 * @event columnmoved
15748 * Fires when a column is moved.
15749 * @param {ColumnModel} this
15750 * @param {Number} oldIndex
15751 * @param {Number} newIndex
15753 "columnmoved" : true,
15755 * @event columlockchange
15756 * Fires when a column's locked state is changed
15757 * @param {ColumnModel} this
15758 * @param {Number} colIndex
15759 * @param {Boolean} locked true if locked
15761 "columnlockchange" : true
15763 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15765 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15767 * @cfg {String} header The header text to display in the Grid view.
15770 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15771 * {@link Roo.data.Record} definition from which to draw the column's value. If not
15772 * specified, the column's index is used as an index into the Record's data Array.
15775 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15776 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15779 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15780 * Defaults to the value of the {@link #defaultSortable} property.
15781 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15784 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
15787 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
15790 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15793 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15796 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15797 * given the cell's data value. See {@link #setRenderer}. If not specified, the
15798 * default renderer uses the raw data value.
15801 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
15805 * Returns the id of the column at the specified index.
15806 * @param {Number} index The column index
15807 * @return {String} the id
15809 getColumnId : function(index){
15810 return this.config[index].id;
15814 * Returns the column for a specified id.
15815 * @param {String} id The column id
15816 * @return {Object} the column
15818 getColumnById : function(id){
15819 return this.lookup[id];
15824 * Returns the column for a specified dataIndex.
15825 * @param {String} dataIndex The column dataIndex
15826 * @return {Object|Boolean} the column or false if not found
15828 getColumnByDataIndex: function(dataIndex){
15829 var index = this.findColumnIndex(dataIndex);
15830 return index > -1 ? this.config[index] : false;
15834 * Returns the index for a specified column id.
15835 * @param {String} id The column id
15836 * @return {Number} the index, or -1 if not found
15838 getIndexById : function(id){
15839 for(var i = 0, len = this.config.length; i < len; i++){
15840 if(this.config[i].id == id){
15848 * Returns the index for a specified column dataIndex.
15849 * @param {String} dataIndex The column dataIndex
15850 * @return {Number} the index, or -1 if not found
15853 findColumnIndex : function(dataIndex){
15854 for(var i = 0, len = this.config.length; i < len; i++){
15855 if(this.config[i].dataIndex == dataIndex){
15863 moveColumn : function(oldIndex, newIndex){
15864 var c = this.config[oldIndex];
15865 this.config.splice(oldIndex, 1);
15866 this.config.splice(newIndex, 0, c);
15867 this.dataMap = null;
15868 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15871 isLocked : function(colIndex){
15872 return this.config[colIndex].locked === true;
15875 setLocked : function(colIndex, value, suppressEvent){
15876 if(this.isLocked(colIndex) == value){
15879 this.config[colIndex].locked = value;
15880 if(!suppressEvent){
15881 this.fireEvent("columnlockchange", this, colIndex, value);
15885 getTotalLockedWidth : function(){
15886 var totalWidth = 0;
15887 for(var i = 0; i < this.config.length; i++){
15888 if(this.isLocked(i) && !this.isHidden(i)){
15889 this.totalWidth += this.getColumnWidth(i);
15895 getLockedCount : function(){
15896 for(var i = 0, len = this.config.length; i < len; i++){
15897 if(!this.isLocked(i)){
15904 * Returns the number of columns.
15907 getColumnCount : function(visibleOnly){
15908 if(visibleOnly === true){
15910 for(var i = 0, len = this.config.length; i < len; i++){
15911 if(!this.isHidden(i)){
15917 return this.config.length;
15921 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15922 * @param {Function} fn
15923 * @param {Object} scope (optional)
15924 * @return {Array} result
15926 getColumnsBy : function(fn, scope){
15928 for(var i = 0, len = this.config.length; i < len; i++){
15929 var c = this.config[i];
15930 if(fn.call(scope||this, c, i) === true){
15938 * Returns true if the specified column is sortable.
15939 * @param {Number} col The column index
15940 * @return {Boolean}
15942 isSortable : function(col){
15943 if(typeof this.config[col].sortable == "undefined"){
15944 return this.defaultSortable;
15946 return this.config[col].sortable;
15950 * Returns the rendering (formatting) function defined for the column.
15951 * @param {Number} col The column index.
15952 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15954 getRenderer : function(col){
15955 if(!this.config[col].renderer){
15956 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15958 return this.config[col].renderer;
15962 * Sets the rendering (formatting) function for a column.
15963 * @param {Number} col The column index
15964 * @param {Function} fn The function to use to process the cell's raw data
15965 * to return HTML markup for the grid view. The render function is called with
15966 * the following parameters:<ul>
15967 * <li>Data value.</li>
15968 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15969 * <li>css A CSS style string to apply to the table cell.</li>
15970 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15971 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15972 * <li>Row index</li>
15973 * <li>Column index</li>
15974 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15976 setRenderer : function(col, fn){
15977 this.config[col].renderer = fn;
15981 * Returns the width for the specified column.
15982 * @param {Number} col The column index
15985 getColumnWidth : function(col){
15986 return this.config[col].width * 1 || this.defaultWidth;
15990 * Sets the width for a column.
15991 * @param {Number} col The column index
15992 * @param {Number} width The new width
15994 setColumnWidth : function(col, width, suppressEvent){
15995 this.config[col].width = width;
15996 this.totalWidth = null;
15997 if(!suppressEvent){
15998 this.fireEvent("widthchange", this, col, width);
16003 * Returns the total width of all columns.
16004 * @param {Boolean} includeHidden True to include hidden column widths
16007 getTotalWidth : function(includeHidden){
16008 if(!this.totalWidth){
16009 this.totalWidth = 0;
16010 for(var i = 0, len = this.config.length; i < len; i++){
16011 if(includeHidden || !this.isHidden(i)){
16012 this.totalWidth += this.getColumnWidth(i);
16016 return this.totalWidth;
16020 * Returns the header for the specified column.
16021 * @param {Number} col The column index
16024 getColumnHeader : function(col){
16025 return this.config[col].header;
16029 * Sets the header for a column.
16030 * @param {Number} col The column index
16031 * @param {String} header The new header
16033 setColumnHeader : function(col, header){
16034 this.config[col].header = header;
16035 this.fireEvent("headerchange", this, col, header);
16039 * Returns the tooltip for the specified column.
16040 * @param {Number} col The column index
16043 getColumnTooltip : function(col){
16044 return this.config[col].tooltip;
16047 * Sets the tooltip for a column.
16048 * @param {Number} col The column index
16049 * @param {String} tooltip The new tooltip
16051 setColumnTooltip : function(col, tooltip){
16052 this.config[col].tooltip = tooltip;
16056 * Returns the dataIndex for the specified column.
16057 * @param {Number} col The column index
16060 getDataIndex : function(col){
16061 return this.config[col].dataIndex;
16065 * Sets the dataIndex for a column.
16066 * @param {Number} col The column index
16067 * @param {Number} dataIndex The new dataIndex
16069 setDataIndex : function(col, dataIndex){
16070 this.config[col].dataIndex = dataIndex;
16076 * Returns true if the cell is editable.
16077 * @param {Number} colIndex The column index
16078 * @param {Number} rowIndex The row index
16079 * @return {Boolean}
16081 isCellEditable : function(colIndex, rowIndex){
16082 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16086 * Returns the editor defined for the cell/column.
16087 * return false or null to disable editing.
16088 * @param {Number} colIndex The column index
16089 * @param {Number} rowIndex The row index
16092 getCellEditor : function(colIndex, rowIndex){
16093 return this.config[colIndex].editor;
16097 * Sets if a column is editable.
16098 * @param {Number} col The column index
16099 * @param {Boolean} editable True if the column is editable
16101 setEditable : function(col, editable){
16102 this.config[col].editable = editable;
16107 * Returns true if the column is hidden.
16108 * @param {Number} colIndex The column index
16109 * @return {Boolean}
16111 isHidden : function(colIndex){
16112 return this.config[colIndex].hidden;
16117 * Returns true if the column width cannot be changed
16119 isFixed : function(colIndex){
16120 return this.config[colIndex].fixed;
16124 * Returns true if the column can be resized
16125 * @return {Boolean}
16127 isResizable : function(colIndex){
16128 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16131 * Sets if a column is hidden.
16132 * @param {Number} colIndex The column index
16133 * @param {Boolean} hidden True if the column is hidden
16135 setHidden : function(colIndex, hidden){
16136 this.config[colIndex].hidden = hidden;
16137 this.totalWidth = null;
16138 this.fireEvent("hiddenchange", this, colIndex, hidden);
16142 * Sets the editor for a column.
16143 * @param {Number} col The column index
16144 * @param {Object} editor The editor object
16146 setEditor : function(col, editor){
16147 this.config[col].editor = editor;
16151 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16152 if(typeof value == "string" && value.length < 1){
16158 // Alias for backwards compatibility
16159 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16162 * @extends Roo.bootstrap.Table.AbstractSelectionModel
16163 * @class Roo.bootstrap.Table.RowSelectionModel
16164 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16165 * It supports multiple selections and keyboard selection/navigation.
16167 * @param {Object} config
16170 Roo.bootstrap.Table.RowSelectionModel = function(config){
16171 Roo.apply(this, config);
16172 this.selections = new Roo.util.MixedCollection(false, function(o){
16177 this.lastActive = false;
16181 * @event selectionchange
16182 * Fires when the selection changes
16183 * @param {SelectionModel} this
16185 "selectionchange" : true,
16187 * @event afterselectionchange
16188 * Fires after the selection changes (eg. by key press or clicking)
16189 * @param {SelectionModel} this
16191 "afterselectionchange" : true,
16193 * @event beforerowselect
16194 * Fires when a row is selected being selected, return false to cancel.
16195 * @param {SelectionModel} this
16196 * @param {Number} rowIndex The selected index
16197 * @param {Boolean} keepExisting False if other selections will be cleared
16199 "beforerowselect" : true,
16202 * Fires when a row is selected.
16203 * @param {SelectionModel} this
16204 * @param {Number} rowIndex The selected index
16205 * @param {Roo.data.Record} r The record
16207 "rowselect" : true,
16209 * @event rowdeselect
16210 * Fires when a row is deselected.
16211 * @param {SelectionModel} this
16212 * @param {Number} rowIndex The selected index
16214 "rowdeselect" : true
16216 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16217 this.locked = false;
16220 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
16222 * @cfg {Boolean} singleSelect
16223 * True to allow selection of only one row at a time (defaults to false)
16225 singleSelect : false,
16228 initEvents : function(){
16230 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16231 this.grid.on("mousedown", this.handleMouseDown, this);
16232 }else{ // allow click to work like normal
16233 this.grid.on("rowclick", this.handleDragableRowClick, this);
16236 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16237 "up" : function(e){
16239 this.selectPrevious(e.shiftKey);
16240 }else if(this.last !== false && this.lastActive !== false){
16241 var last = this.last;
16242 this.selectRange(this.last, this.lastActive-1);
16243 this.grid.getView().focusRow(this.lastActive);
16244 if(last !== false){
16248 this.selectFirstRow();
16250 this.fireEvent("afterselectionchange", this);
16252 "down" : function(e){
16254 this.selectNext(e.shiftKey);
16255 }else if(this.last !== false && this.lastActive !== false){
16256 var last = this.last;
16257 this.selectRange(this.last, this.lastActive+1);
16258 this.grid.getView().focusRow(this.lastActive);
16259 if(last !== false){
16263 this.selectFirstRow();
16265 this.fireEvent("afterselectionchange", this);
16270 var view = this.grid.view;
16271 view.on("refresh", this.onRefresh, this);
16272 view.on("rowupdated", this.onRowUpdated, this);
16273 view.on("rowremoved", this.onRemove, this);
16277 onRefresh : function(){
16278 var ds = this.grid.dataSource, i, v = this.grid.view;
16279 var s = this.selections;
16280 s.each(function(r){
16281 if((i = ds.indexOfId(r.id)) != -1){
16290 onRemove : function(v, index, r){
16291 this.selections.remove(r);
16295 onRowUpdated : function(v, index, r){
16296 if(this.isSelected(r)){
16297 v.onRowSelect(index);
16303 * @param {Array} records The records to select
16304 * @param {Boolean} keepExisting (optional) True to keep existing selections
16306 selectRecords : function(records, keepExisting){
16308 this.clearSelections();
16310 var ds = this.grid.dataSource;
16311 for(var i = 0, len = records.length; i < len; i++){
16312 this.selectRow(ds.indexOf(records[i]), true);
16317 * Gets the number of selected rows.
16320 getCount : function(){
16321 return this.selections.length;
16325 * Selects the first row in the grid.
16327 selectFirstRow : function(){
16332 * Select the last row.
16333 * @param {Boolean} keepExisting (optional) True to keep existing selections
16335 selectLastRow : function(keepExisting){
16336 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16340 * Selects the row immediately following the last selected row.
16341 * @param {Boolean} keepExisting (optional) True to keep existing selections
16343 selectNext : function(keepExisting){
16344 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16345 this.selectRow(this.last+1, keepExisting);
16346 this.grid.getView().focusRow(this.last);
16351 * Selects the row that precedes the last selected row.
16352 * @param {Boolean} keepExisting (optional) True to keep existing selections
16354 selectPrevious : function(keepExisting){
16356 this.selectRow(this.last-1, keepExisting);
16357 this.grid.getView().focusRow(this.last);
16362 * Returns the selected records
16363 * @return {Array} Array of selected records
16365 getSelections : function(){
16366 return [].concat(this.selections.items);
16370 * Returns the first selected record.
16373 getSelected : function(){
16374 return this.selections.itemAt(0);
16379 * Clears all selections.
16381 clearSelections : function(fast){
16382 if(this.locked) return;
16384 var ds = this.grid.dataSource;
16385 var s = this.selections;
16386 s.each(function(r){
16387 this.deselectRow(ds.indexOfId(r.id));
16391 this.selections.clear();
16398 * Selects all rows.
16400 selectAll : function(){
16401 if(this.locked) return;
16402 this.selections.clear();
16403 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16404 this.selectRow(i, true);
16409 * Returns True if there is a selection.
16410 * @return {Boolean}
16412 hasSelection : function(){
16413 return this.selections.length > 0;
16417 * Returns True if the specified row is selected.
16418 * @param {Number/Record} record The record or index of the record to check
16419 * @return {Boolean}
16421 isSelected : function(index){
16422 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16423 return (r && this.selections.key(r.id) ? true : false);
16427 * Returns True if the specified record id is selected.
16428 * @param {String} id The id of record to check
16429 * @return {Boolean}
16431 isIdSelected : function(id){
16432 return (this.selections.key(id) ? true : false);
16436 handleMouseDown : function(e, t){
16437 var view = this.grid.getView(), rowIndex;
16438 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16441 if(e.shiftKey && this.last !== false){
16442 var last = this.last;
16443 this.selectRange(last, rowIndex, e.ctrlKey);
16444 this.last = last; // reset the last
16445 view.focusRow(rowIndex);
16447 var isSelected = this.isSelected(rowIndex);
16448 if(e.button !== 0 && isSelected){
16449 view.focusRow(rowIndex);
16450 }else if(e.ctrlKey && isSelected){
16451 this.deselectRow(rowIndex);
16452 }else if(!isSelected){
16453 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16454 view.focusRow(rowIndex);
16457 this.fireEvent("afterselectionchange", this);
16460 handleDragableRowClick : function(grid, rowIndex, e)
16462 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16463 this.selectRow(rowIndex, false);
16464 grid.view.focusRow(rowIndex);
16465 this.fireEvent("afterselectionchange", this);
16470 * Selects multiple rows.
16471 * @param {Array} rows Array of the indexes of the row to select
16472 * @param {Boolean} keepExisting (optional) True to keep existing selections
16474 selectRows : function(rows, keepExisting){
16476 this.clearSelections();
16478 for(var i = 0, len = rows.length; i < len; i++){
16479 this.selectRow(rows[i], true);
16484 * Selects a range of rows. All rows in between startRow and endRow are also selected.
16485 * @param {Number} startRow The index of the first row in the range
16486 * @param {Number} endRow The index of the last row in the range
16487 * @param {Boolean} keepExisting (optional) True to retain existing selections
16489 selectRange : function(startRow, endRow, keepExisting){
16490 if(this.locked) return;
16492 this.clearSelections();
16494 if(startRow <= endRow){
16495 for(var i = startRow; i <= endRow; i++){
16496 this.selectRow(i, true);
16499 for(var i = startRow; i >= endRow; i--){
16500 this.selectRow(i, true);
16506 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16507 * @param {Number} startRow The index of the first row in the range
16508 * @param {Number} endRow The index of the last row in the range
16510 deselectRange : function(startRow, endRow, preventViewNotify){
16511 if(this.locked) return;
16512 for(var i = startRow; i <= endRow; i++){
16513 this.deselectRow(i, preventViewNotify);
16519 * @param {Number} row The index of the row to select
16520 * @param {Boolean} keepExisting (optional) True to keep existing selections
16522 selectRow : function(index, keepExisting, preventViewNotify){
16523 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16524 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16525 if(!keepExisting || this.singleSelect){
16526 this.clearSelections();
16528 var r = this.grid.dataSource.getAt(index);
16529 this.selections.add(r);
16530 this.last = this.lastActive = index;
16531 if(!preventViewNotify){
16532 this.grid.getView().onRowSelect(index);
16534 this.fireEvent("rowselect", this, index, r);
16535 this.fireEvent("selectionchange", this);
16541 * @param {Number} row The index of the row to deselect
16543 deselectRow : function(index, preventViewNotify){
16544 if(this.locked) return;
16545 if(this.last == index){
16548 if(this.lastActive == index){
16549 this.lastActive = false;
16551 var r = this.grid.dataSource.getAt(index);
16552 this.selections.remove(r);
16553 if(!preventViewNotify){
16554 this.grid.getView().onRowDeselect(index);
16556 this.fireEvent("rowdeselect", this, index);
16557 this.fireEvent("selectionchange", this);
16561 restoreLast : function(){
16563 this.last = this._last;
16568 acceptsNav : function(row, col, cm){
16569 return !cm.isHidden(col) && cm.isCellEditable(col, row);
16573 onEditorKey : function(field, e){
16574 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16579 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16581 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16583 }else if(k == e.ENTER && !e.ctrlKey){
16587 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16589 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16591 }else if(k == e.ESC){
16595 g.startEditing(newCell[0], newCell[1]);
16606 * @class Roo.bootstrap.MessageBar
16607 * @extends Roo.bootstrap.Component
16608 * Bootstrap MessageBar class
16609 * @cfg {String} html contents of the MessageBar
16610 * @cfg {String} weight (info | success | warning | danger) default info
16611 * @cfg {String} beforeClass insert the bar before the given class
16612 * @cfg {Boolean} closable (true | false) default false
16613 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16616 * Create a new Element
16617 * @param {Object} config The config object
16620 Roo.bootstrap.MessageBar = function(config){
16621 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16624 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
16630 beforeClass: 'bootstrap-sticky-wrap',
16632 getAutoCreate : function(){
16636 cls: 'alert alert-dismissable alert-' + this.weight,
16641 html: this.html || ''
16647 cfg.cls += ' alert-messages-fixed';
16661 onRender : function(ct, position)
16663 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16666 var cfg = Roo.apply({}, this.getAutoCreate());
16670 cfg.cls += ' ' + this.cls;
16673 cfg.style = this.style;
16675 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16677 this.el.setVisibilityMode(Roo.Element.DISPLAY);
16680 this.el.select('>button.close').on('click', this.hide, this);
16686 if (!this.rendered) {
16692 this.fireEvent('show', this);
16698 if (!this.rendered) {
16704 this.fireEvent('hide', this);
16707 update : function()
16709 // var e = this.el.dom.firstChild;
16711 // if(this.closable){
16712 // e = e.nextSibling;
16715 // e.data = this.html || '';
16717 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';