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
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| 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)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa (ban|check|...) font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1968 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1970 this.el.on("mouseover", this.onMouseOver, this);
1971 this.el.on("mouseout", this.onMouseOut, this);
1975 findTargetItem : function(e){
1976 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1980 //Roo.log(t); Roo.log(t.id);
1982 //Roo.log(this.menuitems);
1983 return this.menuitems.get(t.id);
1985 //return this.items.get(t.menuItemId);
1990 onClick : function(e){
1991 Roo.log("menu.onClick");
1992 var t = this.findTargetItem(e);
1993 if(!t || t.isContainer){
1998 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1999 if(t == this.activeItem && t.shouldDeactivate(e)){
2000 this.activeItem.deactivate();
2001 delete this.activeItem;
2005 this.setActiveItem(t, true);
2013 Roo.log('pass click event');
2017 this.fireEvent("click", this, t, e);
2021 onMouseOver : function(e){
2022 var t = this.findTargetItem(e);
2025 // if(t.canActivate && !t.disabled){
2026 // this.setActiveItem(t, true);
2030 this.fireEvent("mouseover", this, e, t);
2032 isVisible : function(){
2033 return !this.hidden;
2035 onMouseOut : function(e){
2036 var t = this.findTargetItem(e);
2039 // if(t == this.activeItem && t.shouldDeactivate(e)){
2040 // this.activeItem.deactivate();
2041 // delete this.activeItem;
2044 this.fireEvent("mouseout", this, e, t);
2049 * Displays this menu relative to another element
2050 * @param {String/HTMLElement/Roo.Element} element The element to align to
2051 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052 * the element (defaults to this.defaultAlign)
2053 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2055 show : function(el, pos, parentMenu){
2056 this.parentMenu = parentMenu;
2060 this.fireEvent("beforeshow", this);
2061 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2064 * Displays this menu at a specific xy position
2065 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 showAt : function(xy, parentMenu, /* private: */_e){
2069 this.parentMenu = parentMenu;
2074 this.fireEvent("beforeshow", this);
2075 //xy = this.el.adjustForConstraints(xy);
2079 this.hideMenuItems();
2080 this.hidden = false;
2081 this.triggerEl.addClass('open');
2083 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2089 this.fireEvent("show", this);
2095 this.doFocus.defer(50, this);
2099 doFocus : function(){
2101 this.focusEl.focus();
2106 * Hides this menu and optionally all parent menus
2107 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2109 hide : function(deep){
2111 this.hideMenuItems();
2112 if(this.el && this.isVisible()){
2113 this.fireEvent("beforehide", this);
2114 if(this.activeItem){
2115 this.activeItem.deactivate();
2116 this.activeItem = null;
2118 this.triggerEl.removeClass('open');;
2120 this.fireEvent("hide", this);
2122 if(deep === true && this.parentMenu){
2123 this.parentMenu.hide(true);
2127 onTriggerPress : function(e)
2130 Roo.log('trigger press');
2131 //Roo.log(e.getTarget());
2132 // Roo.log(this.triggerEl.dom);
2133 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2137 if (this.isVisible()) {
2142 this.show(this.triggerEl, false, false);
2151 hideMenuItems : function()
2153 //$(backdrop).remove()
2154 Roo.select('.open',true).each(function(aa) {
2156 aa.removeClass('open');
2157 //var parent = getParent($(this))
2158 //var relatedTarget = { relatedTarget: this }
2160 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161 //if (e.isDefaultPrevented()) return
2162 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2165 addxtypeChild : function (tree, cntr) {
2166 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2168 this.menuitems.add(comp);
2189 * @class Roo.bootstrap.MenuItem
2190 * @extends Roo.bootstrap.Component
2191 * Bootstrap MenuItem class
2192 * @cfg {String} html the menu label
2193 * @cfg {String} href the link
2194 * @cfg {Boolean} preventDefault (true | false) default true
2195 * @cfg {Boolean} isContainer (true | false) default false
2199 * Create a new MenuItem
2200 * @param {Object} config The config object
2204 Roo.bootstrap.MenuItem = function(config){
2205 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2210 * The raw click event for the entire grid.
2211 * @param {Roo.bootstrap.MenuItem} this
2212 * @param {Roo.EventObject} e
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2222 preventDefault: true,
2223 isContainer : false,
2225 getAutoCreate : function(){
2227 if(this.isContainer){
2230 cls: 'dropdown-menu-item'
2236 cls: 'dropdown-menu-item',
2245 if (this.parent().type == 'treeview') {
2246 cfg.cls = 'treeview-menu';
2249 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2254 initEvents: function() {
2256 //this.el.select('a').on('click', this.onClick, this);
2259 onClick : function(e)
2261 Roo.log('item on click ');
2262 //if(this.preventDefault){
2263 // e.preventDefault();
2265 //this.parent().hideMenuItems();
2267 this.fireEvent('click', this, e);
2286 * @class Roo.bootstrap.MenuSeparator
2287 * @extends Roo.bootstrap.Component
2288 * Bootstrap MenuSeparator class
2291 * Create a new MenuItem
2292 * @param {Object} config The config object
2296 Roo.bootstrap.MenuSeparator = function(config){
2297 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2302 getAutoCreate : function(){
2321 * @class Roo.bootstrap.Modal
2322 * @extends Roo.bootstrap.Component
2323 * Bootstrap Modal class
2324 * @cfg {String} title Title of dialog
2325 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2327 * @cfg {Boolean} specificTitle default false
2328 * @cfg {Array} buttons Array of buttons or standard button set..
2329 * @cfg {String} buttonPosition (left|right|center) default right
2330 * @cfg {Boolean} animate default true
2331 * @cfg {Boolean} allow_close default true
2334 * Create a new Modal Dialog
2335 * @param {Object} config The config object
2338 Roo.bootstrap.Modal = function(config){
2339 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2344 * The raw btnclick event for the button
2345 * @param {Roo.EventObject} e
2349 this.buttons = this.buttons || [];
2352 this.tmpl = Roo.factory(this.tmpl);
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2359 title : 'test dialog',
2369 specificTitle: false,
2371 buttonPosition: 'right',
2385 onRender : function(ct, position)
2387 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2390 var cfg = Roo.apply({}, this.getAutoCreate());
2393 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2395 //if (!cfg.name.length) {
2399 cfg.cls += ' ' + this.cls;
2402 cfg.style = this.style;
2404 this.el = Roo.get(document.body).createChild(cfg, position);
2406 //var type = this.el.dom.type;
2411 if(this.tabIndex !== undefined){
2412 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2416 this.bodyEl = this.el.select('.modal-body',true).first();
2417 this.closeEl = this.el.select('.modal-header .close', true).first();
2418 this.footerEl = this.el.select('.modal-footer',true).first();
2419 this.titleEl = this.el.select('.modal-title',true).first();
2423 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424 this.maskEl.enableDisplayMode("block");
2426 //this.el.addClass("x-dlg-modal");
2428 if (this.buttons.length) {
2429 Roo.each(this.buttons, function(bb) {
2430 var b = Roo.apply({}, bb);
2431 b.xns = b.xns || Roo.bootstrap;
2432 b.xtype = b.xtype || 'Button';
2433 if (typeof(b.listeners) == 'undefined') {
2434 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2437 var btn = Roo.factory(b);
2439 btn.onRender(this.el.select('.modal-footer div').first());
2443 // render the children.
2446 if(typeof(this.items) != 'undefined'){
2447 var items = this.items;
2450 for(var i =0;i < items.length;i++) {
2451 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2455 this.items = nitems;
2457 // where are these used - they used to be body/close/footer
2461 //this.el.addClass([this.fieldClass, this.cls]);
2465 getAutoCreate : function(){
2470 html : this.html || ''
2475 cls : 'modal-title',
2479 if(this.specificTitle){
2485 if (this.allow_close) {
2496 style : 'display: none',
2499 cls: "modal-dialog",
2502 cls : "modal-content",
2505 cls : 'modal-header',
2510 cls : 'modal-footer',
2514 cls: 'btn-' + this.buttonPosition
2531 modal.cls += ' fade';
2537 getChildContainer : function() {
2542 getButtonContainer : function() {
2543 return this.el.select('.modal-footer div',true).first();
2546 initEvents : function()
2548 if (this.allow_close) {
2549 this.closeEl.on('click', this.hide, this);
2554 window.addEventListener("resize", function() { _this.resize(); } );
2560 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2565 if (!this.rendered) {
2569 this.el.setStyle('display', 'block');
2573 (function(){ _this.el.addClass('in'); }).defer(50);
2575 this.el.addClass('in');
2578 // not sure how we can show data in here..
2580 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2583 Roo.get(document.body).addClass("x-body-masked");
2584 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2586 this.el.setStyle('zIndex', '10001');
2588 this.fireEvent('show', this);
2595 Roo.get(document.body).removeClass("x-body-masked");
2596 this.el.removeClass('in');
2600 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2602 this.el.setStyle('display', 'none');
2605 this.fireEvent('hide', this);
2608 addButton : function(str, cb)
2612 var b = Roo.apply({}, { html : str } );
2613 b.xns = b.xns || Roo.bootstrap;
2614 b.xtype = b.xtype || 'Button';
2615 if (typeof(b.listeners) == 'undefined') {
2616 b.listeners = { click : cb.createDelegate(this) };
2619 var btn = Roo.factory(b);
2621 btn.onRender(this.el.select('.modal-footer div').first());
2627 setDefaultButton : function(btn)
2629 //this.el.select('.modal-footer').()
2631 resizeTo: function(w,h)
2635 setContentSize : function(w, h)
2639 onButtonClick: function(btn,e)
2642 this.fireEvent('btnclick', btn.name, e);
2645 * Set the title of the Dialog
2646 * @param {String} str new Title
2648 setTitle: function(str) {
2649 this.titleEl.dom.innerHTML = str;
2652 * Set the body of the Dialog
2653 * @param {String} str new Title
2655 setBody: function(str) {
2656 this.bodyEl.dom.innerHTML = str;
2659 * Set the body of the Dialog using the template
2660 * @param {Obj} data - apply this data to the template and replace the body contents.
2662 applyBody: function(obj)
2665 Roo.log("Error - using apply Body without a template");
2668 this.tmpl.overwrite(this.bodyEl, obj);
2674 Roo.apply(Roo.bootstrap.Modal, {
2676 * Button config that displays a single OK button
2685 * Button config that displays Yes and No buttons
2701 * Button config that displays OK and Cancel buttons
2716 * Button config that displays Yes, No and Cancel buttons
2739 * messagebox - can be used as a replace
2743 * @class Roo.MessageBox
2744 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2753 // process text value...
2757 // Show a dialog using config options:
2759 title:'Save Changes?',
2760 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761 buttons: Roo.Msg.YESNOCANCEL,
2768 Roo.bootstrap.MessageBox = function(){
2769 var dlg, opt, mask, waitTimer;
2770 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771 var buttons, activeTextEl, bwidth;
2775 var handleButton = function(button){
2777 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2781 var handleHide = function(){
2783 dlg.el.removeClass(opt.cls);
2786 // Roo.TaskMgr.stop(waitTimer);
2787 // waitTimer = null;
2792 var updateButtons = function(b){
2795 buttons["ok"].hide();
2796 buttons["cancel"].hide();
2797 buttons["yes"].hide();
2798 buttons["no"].hide();
2799 //dlg.footer.dom.style.display = 'none';
2802 dlg.footerEl.dom.style.display = '';
2803 for(var k in buttons){
2804 if(typeof buttons[k] != "function"){
2807 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808 width += buttons[k].el.getWidth()+15;
2818 var handleEsc = function(d, k, e){
2819 if(opt && opt.closable !== false){
2829 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830 * @return {Roo.BasicDialog} The BasicDialog element
2832 getDialog : function(){
2834 dlg = new Roo.bootstrap.Modal( {
2837 //constraintoviewport:false,
2839 //collapsible : false,
2844 //buttonAlign:"center",
2845 closeClick : function(){
2846 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2849 handleButton("cancel");
2854 dlg.on("hide", handleHide);
2856 //dlg.addKeyListener(27, handleEsc);
2858 this.buttons = buttons;
2859 var bt = this.buttonText;
2860 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2865 bodyEl = dlg.bodyEl.createChild({
2867 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868 '<textarea class="roo-mb-textarea"></textarea>' +
2869 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2871 msgEl = bodyEl.dom.firstChild;
2872 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873 textboxEl.enableDisplayMode();
2874 textboxEl.addKeyListener([10,13], function(){
2875 if(dlg.isVisible() && opt && opt.buttons){
2878 }else if(opt.buttons.yes){
2879 handleButton("yes");
2883 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884 textareaEl.enableDisplayMode();
2885 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886 progressEl.enableDisplayMode();
2887 var pf = progressEl.dom.firstChild;
2889 pp = Roo.get(pf.firstChild);
2890 pp.setHeight(pf.offsetHeight);
2898 * Updates the message box body text
2899 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900 * the XHTML-compliant non-breaking space character '&#160;')
2901 * @return {Roo.MessageBox} This message box
2903 updateText : function(text){
2904 if(!dlg.isVisible() && !opt.width){
2905 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2907 msgEl.innerHTML = text || ' ';
2909 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2912 Math.min(opt.width || cw , this.maxWidth),
2913 Math.max(opt.minWidth || this.minWidth, bwidth)
2916 activeTextEl.setWidth(w);
2918 if(dlg.isVisible()){
2919 dlg.fixedcenter = false;
2921 // to big, make it scroll. = But as usual stupid IE does not support
2924 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2928 bodyEl.dom.style.height = '';
2929 bodyEl.dom.style.overflowY = '';
2932 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2934 bodyEl.dom.style.overflowX = '';
2937 dlg.setContentSize(w, bodyEl.getHeight());
2938 if(dlg.isVisible()){
2939 dlg.fixedcenter = true;
2945 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2946 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949 * @return {Roo.MessageBox} This message box
2951 updateProgress : function(value, text){
2953 this.updateText(text);
2955 if (pp) { // weird bug on my firefox - for some reason this is not defined
2956 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2962 * Returns true if the message box is currently displayed
2963 * @return {Boolean} True if the message box is visible, else false
2965 isVisible : function(){
2966 return dlg && dlg.isVisible();
2970 * Hides the message box if it is displayed
2973 if(this.isVisible()){
2979 * Displays a new message box, or reinitializes an existing message box, based on the config options
2980 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981 * The following config object properties are supported:
2983 Property Type Description
2984 ---------- --------------- ------------------------------------------------------------------------------------
2985 animEl String/Element An id or Element from which the message box should animate as it opens and
2986 closes (defaults to undefined)
2987 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable Boolean False to hide the top-right close button (defaults to true). Note that
2990 progress and wait dialogs will ignore this property and always hide the
2991 close button as they can only be closed programmatically.
2992 cls String A custom CSS class to apply to the message box element
2993 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2994 displayed (defaults to 75)
2995 fn Function A callback function to execute after closing the dialog. The arguments to the
2996 function will be btn (the name of the button that was clicked, if applicable,
2997 e.g. "ok"), and text (the value of the active text field, if applicable).
2998 Progress and wait dialogs will ignore this option since they do not respond to
2999 user actions and can only be closed programmatically, so any required function
3000 should be called by the same code after it closes the dialog.
3001 icon String A CSS class that provides a background image to be used as an icon for
3002 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3004 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3005 modal Boolean False to allow user interaction with the page while the message box is
3006 displayed (defaults to true)
3007 msg String A string that will replace the existing message box body text (defaults
3008 to the XHTML-compliant non-breaking space character ' ')
3009 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3010 progress Boolean True to display a progress bar (defaults to false)
3011 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3014 title String The title text
3015 value String The string value to set into the active textbox element if displayed
3016 wait Boolean True to display a progress bar (defaults to false)
3017 width Number The width of the dialog in pixels
3024 msg: 'Please enter your address:',
3026 buttons: Roo.MessageBox.OKCANCEL,
3029 animEl: 'addAddressBtn'
3032 * @param {Object} config Configuration options
3033 * @return {Roo.MessageBox} This message box
3035 show : function(options)
3038 // this causes nightmares if you show one dialog after another
3039 // especially on callbacks..
3041 if(this.isVisible()){
3044 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3046 Roo.log("New Dialog Message:" + options.msg )
3047 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3051 var d = this.getDialog();
3053 d.setTitle(opt.title || " ");
3054 d.closeEl.setDisplayed(opt.closable !== false);
3055 activeTextEl = textboxEl;
3056 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3061 textareaEl.setHeight(typeof opt.multiline == "number" ?
3062 opt.multiline : this.defaultTextHeight);
3063 activeTextEl = textareaEl;
3072 progressEl.setDisplayed(opt.progress === true);
3073 this.updateProgress(0);
3074 activeTextEl.dom.value = opt.value || "";
3076 dlg.setDefaultButton(activeTextEl);
3078 var bs = opt.buttons;
3082 }else if(bs && bs.yes){
3083 db = buttons["yes"];
3085 dlg.setDefaultButton(db);
3087 bwidth = updateButtons(opt.buttons);
3088 this.updateText(opt.msg);
3090 d.el.addClass(opt.cls);
3092 d.proxyDrag = opt.proxyDrag === true;
3093 d.modal = opt.modal !== false;
3094 d.mask = opt.modal !== false ? mask : false;
3096 // force it to the end of the z-index stack so it gets a cursor in FF
3097 document.body.appendChild(dlg.el.dom);
3098 d.animateTarget = null;
3099 d.show(options.animEl);
3105 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3106 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107 * and closing the message box when the process is complete.
3108 * @param {String} title The title bar text
3109 * @param {String} msg The message box body text
3110 * @return {Roo.MessageBox} This message box
3112 progress : function(title, msg){
3119 minWidth: this.minProgressWidth,
3126 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127 * If a callback function is passed it will be called after the user clicks the button, and the
3128 * id of the button that was clicked will be passed as the only parameter to the callback
3129 * (could also be the top-right close button).
3130 * @param {String} title The title bar text
3131 * @param {String} msg The message box body text
3132 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133 * @param {Object} scope (optional) The scope of the callback function
3134 * @return {Roo.MessageBox} This message box
3136 alert : function(title, msg, fn, scope){
3149 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3150 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151 * You are responsible for closing the message box when the process is complete.
3152 * @param {String} msg The message box body text
3153 * @param {String} title (optional) The title bar text
3154 * @return {Roo.MessageBox} This message box
3156 wait : function(msg, title){
3167 waitTimer = Roo.TaskMgr.start({
3169 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3177 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180 * @param {String} title The title bar text
3181 * @param {String} msg The message box body text
3182 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183 * @param {Object} scope (optional) The scope of the callback function
3184 * @return {Roo.MessageBox} This message box
3186 confirm : function(title, msg, fn, scope){
3190 buttons: this.YESNO,
3199 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3201 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202 * (could also be the top-right close button) and the text that was entered will be passed as the two
3203 * parameters to the callback.
3204 * @param {String} title The title bar text
3205 * @param {String} msg The message box body text
3206 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207 * @param {Object} scope (optional) The scope of the callback function
3208 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210 * @return {Roo.MessageBox} This message box
3212 prompt : function(title, msg, fn, scope, multiline){
3216 buttons: this.OKCANCEL,
3221 multiline: multiline,
3228 * Button config that displays a single OK button
3233 * Button config that displays Yes and No buttons
3236 YESNO : {yes:true, no:true},
3238 * Button config that displays OK and Cancel buttons
3241 OKCANCEL : {ok:true, cancel:true},
3243 * Button config that displays Yes, No and Cancel buttons
3246 YESNOCANCEL : {yes:true, no:true, cancel:true},
3249 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3252 defaultTextHeight : 75,
3254 * The maximum width in pixels of the message box (defaults to 600)
3259 * The minimum width in pixels of the message box (defaults to 100)
3264 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3265 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3268 minProgressWidth : 250,
3270 * An object containing the default button text strings that can be overriden for localized language support.
3271 * Supported properties are: ok, cancel, yes and no.
3272 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3285 * Shorthand for {@link Roo.MessageBox}
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3297 * @class Roo.bootstrap.Navbar
3298 * @extends Roo.bootstrap.Component
3299 * Bootstrap Navbar class
3302 * Create a new Navbar
3303 * @param {Object} config The config object
3307 Roo.bootstrap.Navbar = function(config){
3308 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3321 getAutoCreate : function(){
3324 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3328 initEvents :function ()
3330 //Roo.log(this.el.select('.navbar-toggle',true));
3331 this.el.select('.navbar-toggle',true).on('click', function() {
3332 // Roo.log('click');
3333 this.el.select('.navbar-collapse',true).toggleClass('in');
3341 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3343 var size = this.el.getSize();
3344 this.maskEl.setSize(size.width, size.height);
3345 this.maskEl.enableDisplayMode("block");
3354 getChildContainer : function()
3356 if (this.el.select('.collapse').getCount()) {
3357 return this.el.select('.collapse',true).first();
3390 * @class Roo.bootstrap.NavSimplebar
3391 * @extends Roo.bootstrap.Navbar
3392 * Bootstrap Sidebar class
3394 * @cfg {Boolean} inverse is inverted color
3396 * @cfg {String} type (nav | pills | tabs)
3397 * @cfg {Boolean} arrangement stacked | justified
3398 * @cfg {String} align (left | right) alignment
3400 * @cfg {Boolean} main (true|false) main nav bar? default false
3401 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3403 * @cfg {String} tag (header|footer|nav|div) default is nav
3409 * Create a new Sidebar
3410 * @param {Object} config The config object
3414 Roo.bootstrap.NavSimplebar = function(config){
3415 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3434 getAutoCreate : function(){
3438 tag : this.tag || 'div',
3451 this.type = this.type || 'nav';
3452 if (['tabs','pills'].indexOf(this.type)!==-1) {
3453 cfg.cn[0].cls += ' nav-' + this.type
3457 if (this.type!=='nav') {
3458 Roo.log('nav type must be nav/tabs/pills')
3460 cfg.cn[0].cls += ' navbar-nav'
3466 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467 cfg.cn[0].cls += ' nav-' + this.arrangement;
3471 if (this.align === 'right') {
3472 cfg.cn[0].cls += ' navbar-right';
3476 cfg.cls += ' navbar-inverse';
3503 * @class Roo.bootstrap.NavHeaderbar
3504 * @extends Roo.bootstrap.NavSimplebar
3505 * Bootstrap Sidebar class
3507 * @cfg {String} brand what is brand
3508 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509 * @cfg {String} brand_href href of the brand
3510 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3511 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3516 * Create a new Sidebar
3517 * @param {Object} config The config object
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3533 desktopCenter : false,
3536 getAutoCreate : function(){
3539 tag: this.nav || 'nav',
3546 if (this.desktopCenter) {
3547 cn.push({cls : 'container', cn : []});
3554 cls: 'navbar-header',
3559 cls: 'navbar-toggle',
3560 'data-toggle': 'collapse',
3565 html: 'Toggle navigation'
3587 cls: 'collapse navbar-collapse',
3591 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3593 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594 cfg.cls += ' navbar-' + this.position;
3596 // tag can override this..
3598 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3601 if (this.brand !== '') {
3604 href: this.brand_href ? this.brand_href : '#',
3605 cls: 'navbar-brand',
3613 cfg.cls += ' main-nav';
3621 getHeaderChildContainer : function()
3623 if (this.el.select('.navbar-header').getCount()) {
3624 return this.el.select('.navbar-header',true).first();
3627 return this.getChildContainer();
3631 initEvents : function()
3633 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3635 if (this.autohide) {
3640 Roo.get(document).on('scroll',function(e) {
3641 var ns = Roo.get(document).getScroll().top;
3642 var os = prevScroll;
3646 ft.removeClass('slideDown');
3647 ft.addClass('slideUp');
3650 ft.removeClass('slideUp');
3651 ft.addClass('slideDown');
3672 * @class Roo.bootstrap.NavSidebar
3673 * @extends Roo.bootstrap.Navbar
3674 * Bootstrap Sidebar class
3677 * Create a new Sidebar
3678 * @param {Object} config The config object
3682 Roo.bootstrap.NavSidebar = function(config){
3683 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3688 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3690 getAutoCreate : function(){
3695 cls: 'sidebar sidebar-nav'
3717 * @class Roo.bootstrap.NavGroup
3718 * @extends Roo.bootstrap.Component
3719 * Bootstrap NavGroup class
3720 * @cfg {String} align (left|right)
3721 * @cfg {Boolean} inverse
3722 * @cfg {String} type (nav|pills|tab) default nav
3723 * @cfg {String} navId - reference Id for navbar.
3727 * Create a new nav group
3728 * @param {Object} config The config object
3731 Roo.bootstrap.NavGroup = function(config){
3732 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3735 Roo.bootstrap.NavGroup.register(this);
3739 * Fires when the active item changes
3740 * @param {Roo.bootstrap.NavGroup} this
3741 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3760 getAutoCreate : function()
3762 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3769 if (['tabs','pills'].indexOf(this.type)!==-1) {
3770 cfg.cls += ' nav-' + this.type
3772 if (this.type!=='nav') {
3773 Roo.log('nav type must be nav/tabs/pills')
3775 cfg.cls += ' navbar-nav'
3778 if (this.parent().sidebar) {
3781 cls: 'dashboard-menu sidebar-menu'
3787 if (this.form === true) {
3793 if (this.align === 'right') {
3794 cfg.cls += ' navbar-right';
3796 cfg.cls += ' navbar-left';
3800 if (this.align === 'right') {
3801 cfg.cls += ' navbar-right';
3805 cfg.cls += ' navbar-inverse';
3813 * sets the active Navigation item
3814 * @param {Roo.bootstrap.NavItem} the new current navitem
3816 setActiveItem : function(item)
3819 Roo.each(this.navItems, function(v){
3824 v.setActive(false, true);
3831 item.setActive(true, true);
3832 this.fireEvent('changed', this, item, prev);
3837 * gets the active Navigation item
3838 * @return {Roo.bootstrap.NavItem} the current navitem
3840 getActive : function()
3844 Roo.each(this.navItems, function(v){
3855 indexOfNav : function()
3859 Roo.each(this.navItems, function(v,i){
3870 * adds a Navigation item
3871 * @param {Roo.bootstrap.NavItem} the navitem to add
3873 addItem : function(cfg)
3875 var cn = new Roo.bootstrap.NavItem(cfg);
3877 cn.parentId = this.id;
3878 cn.onRender(this.el, null);
3882 * register a Navigation item
3883 * @param {Roo.bootstrap.NavItem} the navitem to add
3885 register : function(item)
3887 this.navItems.push( item);
3888 item.navId = this.navId;
3893 * clear all the Navigation item
3896 clearAll : function()
3899 this.el.dom.innerHTML = '';
3902 getNavItem: function(tabId)
3905 Roo.each(this.navItems, function(e) {
3906 if (e.tabId == tabId) {
3916 setActiveNext : function()
3918 var i = this.indexOfNav(this.getActive());
3919 if (i > this.navItems.length) {
3922 this.setActiveItem(this.navItems[i+1]);
3924 setActivePrev : function()
3926 var i = this.indexOfNav(this.getActive());
3930 this.setActiveItem(this.navItems[i-1]);
3932 clearWasActive : function(except) {
3933 Roo.each(this.navItems, function(e) {
3934 if (e.tabId != except.tabId && e.was_active) {
3935 e.was_active = false;
3942 getWasActive : function ()
3945 Roo.each(this.navItems, function(e) {
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3964 * register a Navigation Group
3965 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3967 register : function(navgrp)
3969 this.groups[navgrp.navId] = navgrp;
3973 * fetch a Navigation Group based on the navigation ID
3974 * @param {string} the navgroup to add
3975 * @returns {Roo.bootstrap.NavGroup} the navgroup
3977 get: function(navId) {
3978 if (typeof(this.groups[navId]) == 'undefined') {
3980 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3982 return this.groups[navId] ;
3997 * @class Roo.bootstrap.NavItem
3998 * @extends Roo.bootstrap.Component
3999 * Bootstrap Navbar.NavItem class
4000 * @cfg {String} href link to
4001 * @cfg {String} html content of button
4002 * @cfg {String} badge text inside badge
4003 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004 * @cfg {String} glyphicon name of glyphicon
4005 * @cfg {String} icon name of font awesome icon
4006 * @cfg {Boolean} active Is item active
4007 * @cfg {Boolean} disabled Is item disabled
4009 * @cfg {Boolean} preventDefault (true | false) default false
4010 * @cfg {String} tabId the tab that this item activates.
4011 * @cfg {String} tagtype (a|span) render as a href or span?
4012 * @cfg {Boolean} animateRef (true|false) link to element default false
4015 * Create a new Navbar Item
4016 * @param {Object} config The config object
4018 Roo.bootstrap.NavItem = function(config){
4019 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4024 * The raw click event for the entire grid.
4025 * @param {Roo.EventObject} e
4030 * Fires when the active item active state changes
4031 * @param {Roo.bootstrap.NavItem} this
4032 * @param {boolean} state the new state
4038 * Fires when scroll to element
4039 * @param {Roo.bootstrap.NavItem} this
4040 * @param {Object} options
4041 * @param {Roo.EventObject} e
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4057 preventDefault : false,
4064 getAutoCreate : function(){
4072 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4074 if (this.disabled) {
4075 cfg.cls += ' disabled';
4078 if (this.href || this.html || this.glyphicon || this.icon) {
4082 href : this.href || "#",
4083 html: this.html || ''
4088 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4091 if(this.glyphicon) {
4092 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4097 cfg.cn[0].html += " <span class='caret'></span>";
4101 if (this.badge !== '') {
4103 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4111 initEvents: function()
4113 if (typeof (this.menu) != 'undefined') {
4114 this.menu.parentType = this.xtype;
4115 this.menu.triggerEl = this.el;
4116 this.menu = this.addxtype(Roo.apply({}, this.menu));
4119 this.el.select('a',true).on('click', this.onClick, this);
4121 if(this.tagtype == 'span'){
4122 this.el.select('span',true).on('click', this.onClick, this);
4125 // at this point parent should be available..
4126 this.parent().register(this);
4129 onClick : function(e)
4132 this.preventDefault ||
4139 if (this.disabled) {
4143 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144 if (tg && tg.transition) {
4145 Roo.log("waiting for the transitionend");
4151 //Roo.log("fire event clicked");
4152 if(this.fireEvent('click', this, e) === false){
4156 if(this.tagtype == 'span'){
4160 //Roo.log(this.href);
4161 var ael = this.el.select('a',true).first();
4164 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167 return; // ignore... - it's a 'hash' to another page.
4171 this.scrollToElement(e);
4175 var p = this.parent();
4177 if (['tabs','pills'].indexOf(p.type)!==-1) {
4178 if (typeof(p.setActiveItem) !== 'undefined') {
4179 p.setActiveItem(this);
4183 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185 // remove the collapsed menu expand...
4186 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4190 isActive: function () {
4193 setActive : function(state, fire, is_was_active)
4195 if (this.active && !state && this.navId) {
4196 this.was_active = true;
4197 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4199 nv.clearWasActive(this);
4203 this.active = state;
4206 this.el.removeClass('active');
4207 } else if (!this.el.hasClass('active')) {
4208 this.el.addClass('active');
4211 this.fireEvent('changed', this, state);
4214 // show a panel if it's registered and related..
4216 if (!this.navId || !this.tabId || !state || is_was_active) {
4220 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4224 var pan = tg.getPanelByName(this.tabId);
4228 // if we can not flip to new panel - go back to old nav highlight..
4229 if (false == tg.showPanel(pan)) {
4230 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4232 var onav = nv.getWasActive();
4234 onav.setActive(true, false, true);
4243 // this should not be here...
4244 setDisabled : function(state)
4246 this.disabled = state;
4248 this.el.removeClass('disabled');
4249 } else if (!this.el.hasClass('disabled')) {
4250 this.el.addClass('disabled');
4256 * Fetch the element to display the tooltip on.
4257 * @return {Roo.Element} defaults to this.el
4259 tooltipEl : function()
4261 return this.el.select('' + this.tagtype + '', true).first();
4264 scrollToElement : function(e)
4266 var c = document.body;
4269 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4271 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272 c = document.documentElement;
4275 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4281 var o = target.calcOffsetsTo(c);
4288 this.fireEvent('scrollto', this, options, e);
4290 Roo.get(c).scrollTo('top', options.value, true);
4303 * <span> icon </span>
4304 * <span> text </span>
4305 * <span>badge </span>
4309 * @class Roo.bootstrap.NavSidebarItem
4310 * @extends Roo.bootstrap.NavItem
4311 * Bootstrap Navbar.NavSidebarItem class
4312 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4314 * Create a new Navbar Button
4315 * @param {Object} config The config object
4317 Roo.bootstrap.NavSidebarItem = function(config){
4318 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4323 * The raw click event for the entire grid.
4324 * @param {Roo.EventObject} e
4329 * Fires when the active item active state changes
4330 * @param {Roo.bootstrap.NavSidebarItem} this
4331 * @param {boolean} state the new state
4339 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4341 badgeWeight : 'default',
4343 getAutoCreate : function(){
4348 href : this.href || '#',
4360 html : this.html || ''
4365 cfg.cls += ' active';
4368 if (this.disabled) {
4369 cfg.cls += ' disabled';
4373 if (this.glyphicon || this.icon) {
4374 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4375 a.cn.push({ tag : 'i', cls : c }) ;
4380 if (this.badge !== '') {
4382 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4386 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4387 a.cls += 'dropdown-toggle treeview' ;
4398 initEvents : function()
4400 this.el.on('click', this.onClick, this);
4402 if(this.badge !== ''){
4403 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4408 onClick : function(e)
4415 if(this.preventDefault){
4419 this.fireEvent('click', this);
4422 disable : function()
4424 this.setDisabled(true);
4429 this.setDisabled(false);
4432 setDisabled : function(state)
4434 if(this.disabled == state){
4438 this.disabled = state;
4441 this.el.addClass('disabled');
4445 this.el.removeClass('disabled');
4450 setActive : function(state)
4452 if(this.active == state){
4456 this.active = state;
4459 this.el.addClass('active');
4463 this.el.removeClass('active');
4468 isActive: function ()
4473 setBadge : function(str)
4479 this.badgeEl.dom.innerHTML = str;
4496 * @class Roo.bootstrap.Row
4497 * @extends Roo.bootstrap.Component
4498 * Bootstrap Row class (contains columns...)
4502 * @param {Object} config The config object
4505 Roo.bootstrap.Row = function(config){
4506 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4509 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4511 getAutoCreate : function(){
4530 * @class Roo.bootstrap.Element
4531 * @extends Roo.bootstrap.Component
4532 * Bootstrap Element class
4533 * @cfg {String} html contents of the element
4534 * @cfg {String} tag tag of the element
4535 * @cfg {String} cls class of the element
4536 * @cfg {Boolean} preventDefault (true|false) default false
4537 * @cfg {Boolean} clickable (true|false) default false
4540 * Create a new Element
4541 * @param {Object} config The config object
4544 Roo.bootstrap.Element = function(config){
4545 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4551 * When a element is chick
4552 * @param {Roo.bootstrap.Element} this
4553 * @param {Roo.EventObject} e
4559 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4564 preventDefault: false,
4567 getAutoCreate : function(){
4578 initEvents: function()
4580 Roo.bootstrap.Element.superclass.initEvents.call(this);
4583 this.el.on('click', this.onClick, this);
4588 onClick : function(e)
4590 if(this.preventDefault){
4594 this.fireEvent('click', this, e);
4597 getValue : function()
4599 return this.el.dom.innerHTML;
4602 setValue : function(value)
4604 this.el.dom.innerHTML = value;
4619 * @class Roo.bootstrap.Pagination
4620 * @extends Roo.bootstrap.Component
4621 * Bootstrap Pagination class
4622 * @cfg {String} size xs | sm | md | lg
4623 * @cfg {Boolean} inverse false | true
4626 * Create a new Pagination
4627 * @param {Object} config The config object
4630 Roo.bootstrap.Pagination = function(config){
4631 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4634 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4640 getAutoCreate : function(){
4646 cfg.cls += ' inverse';
4652 cfg.cls += " " + this.cls;
4670 * @class Roo.bootstrap.PaginationItem
4671 * @extends Roo.bootstrap.Component
4672 * Bootstrap PaginationItem class
4673 * @cfg {String} html text
4674 * @cfg {String} href the link
4675 * @cfg {Boolean} preventDefault (true | false) default true
4676 * @cfg {Boolean} active (true | false) default false
4677 * @cfg {Boolean} disabled default false
4681 * Create a new PaginationItem
4682 * @param {Object} config The config object
4686 Roo.bootstrap.PaginationItem = function(config){
4687 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4692 * The raw click event for the entire grid.
4693 * @param {Roo.EventObject} e
4699 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4703 preventDefault: true,
4708 getAutoCreate : function(){
4714 href : this.href ? this.href : '#',
4715 html : this.html ? this.html : ''
4725 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4729 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4735 initEvents: function() {
4737 this.el.on('click', this.onClick, this);
4740 onClick : function(e)
4742 Roo.log('PaginationItem on click ');
4743 if(this.preventDefault){
4751 this.fireEvent('click', this, e);
4767 * @class Roo.bootstrap.Slider
4768 * @extends Roo.bootstrap.Component
4769 * Bootstrap Slider class
4772 * Create a new Slider
4773 * @param {Object} config The config object
4776 Roo.bootstrap.Slider = function(config){
4777 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4780 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4782 getAutoCreate : function(){
4786 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4790 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4802 * Ext JS Library 1.1.1
4803 * Copyright(c) 2006-2007, Ext JS, LLC.
4805 * Originally Released Under LGPL - original licence link has changed is not relivant.
4808 * <script type="text/javascript">
4813 * @class Roo.grid.ColumnModel
4814 * @extends Roo.util.Observable
4815 * This is the default implementation of a ColumnModel used by the Grid. It defines
4816 * the columns in the grid.
4819 var colModel = new Roo.grid.ColumnModel([
4820 {header: "Ticker", width: 60, sortable: true, locked: true},
4821 {header: "Company Name", width: 150, sortable: true},
4822 {header: "Market Cap.", width: 100, sortable: true},
4823 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4824 {header: "Employees", width: 100, sortable: true, resizable: false}
4829 * The config options listed for this class are options which may appear in each
4830 * individual column definition.
4831 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4833 * @param {Object} config An Array of column config objects. See this class's
4834 * config objects for details.
4836 Roo.grid.ColumnModel = function(config){
4838 * The config passed into the constructor
4840 this.config = config;
4843 // if no id, create one
4844 // if the column does not have a dataIndex mapping,
4845 // map it to the order it is in the config
4846 for(var i = 0, len = config.length; i < len; i++){
4848 if(typeof c.dataIndex == "undefined"){
4851 if(typeof c.renderer == "string"){
4852 c.renderer = Roo.util.Format[c.renderer];
4854 if(typeof c.id == "undefined"){
4857 if(c.editor && c.editor.xtype){
4858 c.editor = Roo.factory(c.editor, Roo.grid);
4860 if(c.editor && c.editor.isFormField){
4861 c.editor = new Roo.grid.GridEditor(c.editor);
4863 this.lookup[c.id] = c;
4867 * The width of columns which have no width specified (defaults to 100)
4870 this.defaultWidth = 100;
4873 * Default sortable of columns which have no sortable specified (defaults to false)
4876 this.defaultSortable = false;
4880 * @event widthchange
4881 * Fires when the width of a column changes.
4882 * @param {ColumnModel} this
4883 * @param {Number} columnIndex The column index
4884 * @param {Number} newWidth The new width
4886 "widthchange": true,
4888 * @event headerchange
4889 * Fires when the text of a header changes.
4890 * @param {ColumnModel} this
4891 * @param {Number} columnIndex The column index
4892 * @param {Number} newText The new header text
4894 "headerchange": true,
4896 * @event hiddenchange
4897 * Fires when a column is hidden or "unhidden".
4898 * @param {ColumnModel} this
4899 * @param {Number} columnIndex The column index
4900 * @param {Boolean} hidden true if hidden, false otherwise
4902 "hiddenchange": true,
4904 * @event columnmoved
4905 * Fires when a column is moved.
4906 * @param {ColumnModel} this
4907 * @param {Number} oldIndex
4908 * @param {Number} newIndex
4910 "columnmoved" : true,
4912 * @event columlockchange
4913 * Fires when a column's locked state is changed
4914 * @param {ColumnModel} this
4915 * @param {Number} colIndex
4916 * @param {Boolean} locked true if locked
4918 "columnlockchange" : true
4920 Roo.grid.ColumnModel.superclass.constructor.call(this);
4922 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4924 * @cfg {String} header The header text to display in the Grid view.
4927 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4928 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4929 * specified, the column's index is used as an index into the Record's data Array.
4932 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4933 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4936 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4937 * Defaults to the value of the {@link #defaultSortable} property.
4938 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4941 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4944 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4947 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4950 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4953 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4954 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4955 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4956 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4959 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4962 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4965 * @cfg {String} cursor (Optional)
4968 * @cfg {String} tooltip (Optional)
4971 * Returns the id of the column at the specified index.
4972 * @param {Number} index The column index
4973 * @return {String} the id
4975 getColumnId : function(index){
4976 return this.config[index].id;
4980 * Returns the column for a specified id.
4981 * @param {String} id The column id
4982 * @return {Object} the column
4984 getColumnById : function(id){
4985 return this.lookup[id];
4990 * Returns the column for a specified dataIndex.
4991 * @param {String} dataIndex The column dataIndex
4992 * @return {Object|Boolean} the column or false if not found
4994 getColumnByDataIndex: function(dataIndex){
4995 var index = this.findColumnIndex(dataIndex);
4996 return index > -1 ? this.config[index] : false;
5000 * Returns the index for a specified column id.
5001 * @param {String} id The column id
5002 * @return {Number} the index, or -1 if not found
5004 getIndexById : function(id){
5005 for(var i = 0, len = this.config.length; i < len; i++){
5006 if(this.config[i].id == id){
5014 * Returns the index for a specified column dataIndex.
5015 * @param {String} dataIndex The column dataIndex
5016 * @return {Number} the index, or -1 if not found
5019 findColumnIndex : function(dataIndex){
5020 for(var i = 0, len = this.config.length; i < len; i++){
5021 if(this.config[i].dataIndex == dataIndex){
5029 moveColumn : function(oldIndex, newIndex){
5030 var c = this.config[oldIndex];
5031 this.config.splice(oldIndex, 1);
5032 this.config.splice(newIndex, 0, c);
5033 this.dataMap = null;
5034 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5037 isLocked : function(colIndex){
5038 return this.config[colIndex].locked === true;
5041 setLocked : function(colIndex, value, suppressEvent){
5042 if(this.isLocked(colIndex) == value){
5045 this.config[colIndex].locked = value;
5047 this.fireEvent("columnlockchange", this, colIndex, value);
5051 getTotalLockedWidth : function(){
5053 for(var i = 0; i < this.config.length; i++){
5054 if(this.isLocked(i) && !this.isHidden(i)){
5055 this.totalWidth += this.getColumnWidth(i);
5061 getLockedCount : function(){
5062 for(var i = 0, len = this.config.length; i < len; i++){
5063 if(!this.isLocked(i)){
5070 * Returns the number of columns.
5073 getColumnCount : function(visibleOnly){
5074 if(visibleOnly === true){
5076 for(var i = 0, len = this.config.length; i < len; i++){
5077 if(!this.isHidden(i)){
5083 return this.config.length;
5087 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5088 * @param {Function} fn
5089 * @param {Object} scope (optional)
5090 * @return {Array} result
5092 getColumnsBy : function(fn, scope){
5094 for(var i = 0, len = this.config.length; i < len; i++){
5095 var c = this.config[i];
5096 if(fn.call(scope||this, c, i) === true){
5104 * Returns true if the specified column is sortable.
5105 * @param {Number} col The column index
5108 isSortable : function(col){
5109 if(typeof this.config[col].sortable == "undefined"){
5110 return this.defaultSortable;
5112 return this.config[col].sortable;
5116 * Returns the rendering (formatting) function defined for the column.
5117 * @param {Number} col The column index.
5118 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5120 getRenderer : function(col){
5121 if(!this.config[col].renderer){
5122 return Roo.grid.ColumnModel.defaultRenderer;
5124 return this.config[col].renderer;
5128 * Sets the rendering (formatting) function for a column.
5129 * @param {Number} col The column index
5130 * @param {Function} fn The function to use to process the cell's raw data
5131 * to return HTML markup for the grid view. The render function is called with
5132 * the following parameters:<ul>
5133 * <li>Data value.</li>
5134 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5135 * <li>css A CSS style string to apply to the table cell.</li>
5136 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5137 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5138 * <li>Row index</li>
5139 * <li>Column index</li>
5140 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5142 setRenderer : function(col, fn){
5143 this.config[col].renderer = fn;
5147 * Returns the width for the specified column.
5148 * @param {Number} col The column index
5151 getColumnWidth : function(col){
5152 return this.config[col].width * 1 || this.defaultWidth;
5156 * Sets the width for a column.
5157 * @param {Number} col The column index
5158 * @param {Number} width The new width
5160 setColumnWidth : function(col, width, suppressEvent){
5161 this.config[col].width = width;
5162 this.totalWidth = null;
5164 this.fireEvent("widthchange", this, col, width);
5169 * Returns the total width of all columns.
5170 * @param {Boolean} includeHidden True to include hidden column widths
5173 getTotalWidth : function(includeHidden){
5174 if(!this.totalWidth){
5175 this.totalWidth = 0;
5176 for(var i = 0, len = this.config.length; i < len; i++){
5177 if(includeHidden || !this.isHidden(i)){
5178 this.totalWidth += this.getColumnWidth(i);
5182 return this.totalWidth;
5186 * Returns the header for the specified column.
5187 * @param {Number} col The column index
5190 getColumnHeader : function(col){
5191 return this.config[col].header;
5195 * Sets the header for a column.
5196 * @param {Number} col The column index
5197 * @param {String} header The new header
5199 setColumnHeader : function(col, header){
5200 this.config[col].header = header;
5201 this.fireEvent("headerchange", this, col, header);
5205 * Returns the tooltip for the specified column.
5206 * @param {Number} col The column index
5209 getColumnTooltip : function(col){
5210 return this.config[col].tooltip;
5213 * Sets the tooltip for a column.
5214 * @param {Number} col The column index
5215 * @param {String} tooltip The new tooltip
5217 setColumnTooltip : function(col, tooltip){
5218 this.config[col].tooltip = tooltip;
5222 * Returns the dataIndex for the specified column.
5223 * @param {Number} col The column index
5226 getDataIndex : function(col){
5227 return this.config[col].dataIndex;
5231 * Sets the dataIndex for a column.
5232 * @param {Number} col The column index
5233 * @param {Number} dataIndex The new dataIndex
5235 setDataIndex : function(col, dataIndex){
5236 this.config[col].dataIndex = dataIndex;
5242 * Returns true if the cell is editable.
5243 * @param {Number} colIndex The column index
5244 * @param {Number} rowIndex The row index
5247 isCellEditable : function(colIndex, rowIndex){
5248 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5252 * Returns the editor defined for the cell/column.
5253 * return false or null to disable editing.
5254 * @param {Number} colIndex The column index
5255 * @param {Number} rowIndex The row index
5258 getCellEditor : function(colIndex, rowIndex){
5259 return this.config[colIndex].editor;
5263 * Sets if a column is editable.
5264 * @param {Number} col The column index
5265 * @param {Boolean} editable True if the column is editable
5267 setEditable : function(col, editable){
5268 this.config[col].editable = editable;
5273 * Returns true if the column is hidden.
5274 * @param {Number} colIndex The column index
5277 isHidden : function(colIndex){
5278 return this.config[colIndex].hidden;
5283 * Returns true if the column width cannot be changed
5285 isFixed : function(colIndex){
5286 return this.config[colIndex].fixed;
5290 * Returns true if the column can be resized
5293 isResizable : function(colIndex){
5294 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5297 * Sets if a column is hidden.
5298 * @param {Number} colIndex The column index
5299 * @param {Boolean} hidden True if the column is hidden
5301 setHidden : function(colIndex, hidden){
5302 this.config[colIndex].hidden = hidden;
5303 this.totalWidth = null;
5304 this.fireEvent("hiddenchange", this, colIndex, hidden);
5308 * Sets the editor for a column.
5309 * @param {Number} col The column index
5310 * @param {Object} editor The editor object
5312 setEditor : function(col, editor){
5313 this.config[col].editor = editor;
5317 Roo.grid.ColumnModel.defaultRenderer = function(value){
5318 if(typeof value == "string" && value.length < 1){
5324 // Alias for backwards compatibility
5325 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5328 * Ext JS Library 1.1.1
5329 * Copyright(c) 2006-2007, Ext JS, LLC.
5331 * Originally Released Under LGPL - original licence link has changed is not relivant.
5334 * <script type="text/javascript">
5338 * @class Roo.LoadMask
5339 * A simple utility class for generically masking elements while loading data. If the element being masked has
5340 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5341 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5342 * element's UpdateManager load indicator and will be destroyed after the initial load.
5344 * Create a new LoadMask
5345 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5346 * @param {Object} config The config object
5348 Roo.LoadMask = function(el, config){
5349 this.el = Roo.get(el);
5350 Roo.apply(this, config);
5352 this.store.on('beforeload', this.onBeforeLoad, this);
5353 this.store.on('load', this.onLoad, this);
5354 this.store.on('loadexception', this.onLoadException, this);
5355 this.removeMask = false;
5357 var um = this.el.getUpdateManager();
5358 um.showLoadIndicator = false; // disable the default indicator
5359 um.on('beforeupdate', this.onBeforeLoad, this);
5360 um.on('update', this.onLoad, this);
5361 um.on('failure', this.onLoad, this);
5362 this.removeMask = true;
5366 Roo.LoadMask.prototype = {
5368 * @cfg {Boolean} removeMask
5369 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5370 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5374 * The text to display in a centered loading message box (defaults to 'Loading...')
5378 * @cfg {String} msgCls
5379 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5381 msgCls : 'x-mask-loading',
5384 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5390 * Disables the mask to prevent it from being displayed
5392 disable : function(){
5393 this.disabled = true;
5397 * Enables the mask so that it can be displayed
5399 enable : function(){
5400 this.disabled = false;
5403 onLoadException : function()
5407 if (typeof(arguments[3]) != 'undefined') {
5408 Roo.MessageBox.alert("Error loading",arguments[3]);
5412 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5413 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5422 this.el.unmask(this.removeMask);
5427 this.el.unmask(this.removeMask);
5431 onBeforeLoad : function(){
5433 this.el.mask(this.msg, this.msgCls);
5438 destroy : function(){
5440 this.store.un('beforeload', this.onBeforeLoad, this);
5441 this.store.un('load', this.onLoad, this);
5442 this.store.un('loadexception', this.onLoadException, this);
5444 var um = this.el.getUpdateManager();
5445 um.un('beforeupdate', this.onBeforeLoad, this);
5446 um.un('update', this.onLoad, this);
5447 um.un('failure', this.onLoad, this);
5458 * @class Roo.bootstrap.Table
5459 * @extends Roo.bootstrap.Component
5460 * Bootstrap Table class
5461 * @cfg {String} cls table class
5462 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5463 * @cfg {String} bgcolor Specifies the background color for a table
5464 * @cfg {Number} border Specifies whether the table cells should have borders or not
5465 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5466 * @cfg {Number} cellspacing Specifies the space between cells
5467 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5468 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5469 * @cfg {String} sortable Specifies that the table should be sortable
5470 * @cfg {String} summary Specifies a summary of the content of a table
5471 * @cfg {Number} width Specifies the width of a table
5472 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5474 * @cfg {boolean} striped Should the rows be alternative striped
5475 * @cfg {boolean} bordered Add borders to the table
5476 * @cfg {boolean} hover Add hover highlighting
5477 * @cfg {boolean} condensed Format condensed
5478 * @cfg {boolean} responsive Format condensed
5479 * @cfg {Boolean} loadMask (true|false) default false
5480 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5481 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5482 * @cfg {Boolean} rowSelection (true|false) default false
5483 * @cfg {Boolean} cellSelection (true|false) default false
5484 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5488 * Create a new Table
5489 * @param {Object} config The config object
5492 Roo.bootstrap.Table = function(config){
5493 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5496 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5497 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5498 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5499 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5503 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5504 this.sm = this.selModel;
5505 this.sm.xmodule = this.xmodule || false;
5507 if (this.cm && typeof(this.cm.config) == 'undefined') {
5508 this.colModel = new Roo.grid.ColumnModel(this.cm);
5509 this.cm = this.colModel;
5510 this.cm.xmodule = this.xmodule || false;
5513 this.store= Roo.factory(this.store, Roo.data);
5514 this.ds = this.store;
5515 this.ds.xmodule = this.xmodule || false;
5518 if (this.footer && this.store) {
5519 this.footer.dataSource = this.ds;
5520 this.footer = Roo.factory(this.footer);
5527 * Fires when a cell is clicked
5528 * @param {Roo.bootstrap.Table} this
5529 * @param {Roo.Element} el
5530 * @param {Number} rowIndex
5531 * @param {Number} columnIndex
5532 * @param {Roo.EventObject} e
5536 * @event celldblclick
5537 * Fires when a cell is double clicked
5538 * @param {Roo.bootstrap.Table} this
5539 * @param {Roo.Element} el
5540 * @param {Number} rowIndex
5541 * @param {Number} columnIndex
5542 * @param {Roo.EventObject} e
5544 "celldblclick" : true,
5547 * Fires when a row is clicked
5548 * @param {Roo.bootstrap.Table} this
5549 * @param {Roo.Element} el
5550 * @param {Number} rowIndex
5551 * @param {Roo.EventObject} e
5555 * @event rowdblclick
5556 * Fires when a row is double clicked
5557 * @param {Roo.bootstrap.Table} this
5558 * @param {Roo.Element} el
5559 * @param {Number} rowIndex
5560 * @param {Roo.EventObject} e
5562 "rowdblclick" : true,
5565 * Fires when a mouseover occur
5566 * @param {Roo.bootstrap.Table} this
5567 * @param {Roo.Element} el
5568 * @param {Number} rowIndex
5569 * @param {Number} columnIndex
5570 * @param {Roo.EventObject} e
5575 * Fires when a mouseout occur
5576 * @param {Roo.bootstrap.Table} this
5577 * @param {Roo.Element} el
5578 * @param {Number} rowIndex
5579 * @param {Number} columnIndex
5580 * @param {Roo.EventObject} e
5585 * Fires when a row is rendered, so you can change add a style to it.
5586 * @param {Roo.bootstrap.Table} this
5587 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5591 * @event rowsrendered
5592 * Fires when all the rows have been rendered
5593 * @param {Roo.bootstrap.Table} this
5595 'rowsrendered' : true
5600 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5625 rowSelection : false,
5626 cellSelection : false,
5629 // Roo.Element - the tbody
5632 getAutoCreate : function(){
5633 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5642 cfg.cls += ' table-striped';
5646 cfg.cls += ' table-hover';
5648 if (this.bordered) {
5649 cfg.cls += ' table-bordered';
5651 if (this.condensed) {
5652 cfg.cls += ' table-condensed';
5654 if (this.responsive) {
5655 cfg.cls += ' table-responsive';
5659 cfg.cls+= ' ' +this.cls;
5662 // this lot should be simplifed...
5665 cfg.align=this.align;
5668 cfg.bgcolor=this.bgcolor;
5671 cfg.border=this.border;
5673 if (this.cellpadding) {
5674 cfg.cellpadding=this.cellpadding;
5676 if (this.cellspacing) {
5677 cfg.cellspacing=this.cellspacing;
5680 cfg.frame=this.frame;
5683 cfg.rules=this.rules;
5685 if (this.sortable) {
5686 cfg.sortable=this.sortable;
5689 cfg.summary=this.summary;
5692 cfg.width=this.width;
5695 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5698 if(this.store || this.cm){
5699 if(this.headerShow){
5700 cfg.cn.push(this.renderHeader());
5703 cfg.cn.push(this.renderBody());
5705 if(this.footerShow){
5706 cfg.cn.push(this.renderFooter());
5709 cfg.cls+= ' TableGrid';
5712 return { cn : [ cfg ] };
5715 initEvents : function()
5717 if(!this.store || !this.cm){
5721 //Roo.log('initEvents with ds!!!!');
5723 this.mainBody = this.el.select('tbody', true).first();
5728 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5729 e.on('click', _this.sort, _this);
5732 this.el.on("click", this.onClick, this);
5733 this.el.on("dblclick", this.onDblClick, this);
5735 // why is this done????? = it breaks dialogs??
5736 //this.parent().el.setStyle('position', 'relative');
5740 this.footer.parentId = this.id;
5741 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5744 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5746 this.store.on('load', this.onLoad, this);
5747 this.store.on('beforeload', this.onBeforeLoad, this);
5748 this.store.on('update', this.onUpdate, this);
5749 this.store.on('add', this.onAdd, this);
5753 onMouseover : function(e, el)
5755 var cell = Roo.get(el);
5761 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5762 cell = cell.findParent('td', false, true);
5765 var row = cell.findParent('tr', false, true);
5766 var cellIndex = cell.dom.cellIndex;
5767 var rowIndex = row.dom.rowIndex - 1; // start from 0
5769 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5773 onMouseout : function(e, el)
5775 var cell = Roo.get(el);
5781 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5782 cell = cell.findParent('td', false, true);
5785 var row = cell.findParent('tr', false, true);
5786 var cellIndex = cell.dom.cellIndex;
5787 var rowIndex = row.dom.rowIndex - 1; // start from 0
5789 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5793 onClick : function(e, el)
5795 var cell = Roo.get(el);
5797 if(!cell || (!this.cellSelection && !this.rowSelection)){
5801 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5802 cell = cell.findParent('td', false, true);
5805 if(!cell || typeof(cell) == 'undefined'){
5809 var row = cell.findParent('tr', false, true);
5811 if(!row || typeof(row) == 'undefined'){
5815 var cellIndex = cell.dom.cellIndex;
5816 var rowIndex = this.getRowIndex(row);
5818 // why??? - should these not be based on SelectionModel?
5819 if(this.cellSelection){
5820 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5823 if(this.rowSelection){
5824 this.fireEvent('rowclick', this, row, rowIndex, e);
5830 onDblClick : function(e,el)
5832 var cell = Roo.get(el);
5834 if(!cell || (!this.CellSelection && !this.RowSelection)){
5838 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5839 cell = cell.findParent('td', false, true);
5842 if(!cell || typeof(cell) == 'undefined'){
5846 var row = cell.findParent('tr', false, true);
5848 if(!row || typeof(row) == 'undefined'){
5852 var cellIndex = cell.dom.cellIndex;
5853 var rowIndex = this.getRowIndex(row);
5855 if(this.CellSelection){
5856 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5859 if(this.RowSelection){
5860 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5864 sort : function(e,el)
5866 var col = Roo.get(el);
5868 if(!col.hasClass('sortable')){
5872 var sort = col.attr('sort');
5875 if(col.hasClass('glyphicon-arrow-up')){
5879 this.store.sortInfo = {field : sort, direction : dir};
5882 Roo.log("calling footer first");
5883 this.footer.onClick('first');
5886 this.store.load({ params : { start : 0 } });
5890 renderHeader : function()
5899 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5901 var config = cm.config[i];
5906 html: cm.getColumnHeader(i)
5911 if(typeof(config.lgHeader) != 'undefined'){
5912 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5915 if(typeof(config.mdHeader) != 'undefined'){
5916 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5919 if(typeof(config.smHeader) != 'undefined'){
5920 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5923 if(typeof(config.xsHeader) != 'undefined'){
5924 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5931 if(typeof(config.tooltip) != 'undefined'){
5932 c.tooltip = config.tooltip;
5935 if(typeof(config.colspan) != 'undefined'){
5936 c.colspan = config.colspan;
5939 if(typeof(config.hidden) != 'undefined' && config.hidden){
5940 c.style += ' display:none;';
5943 if(typeof(config.dataIndex) != 'undefined'){
5944 c.sort = config.dataIndex;
5947 if(typeof(config.sortable) != 'undefined' && config.sortable){
5951 if(typeof(config.align) != 'undefined' && config.align.length){
5952 c.style += ' text-align:' + config.align + ';';
5955 if(typeof(config.width) != 'undefined'){
5956 c.style += ' width:' + config.width + 'px;';
5959 if(typeof(config.cls) != 'undefined'){
5960 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5969 renderBody : function()
5979 colspan : this.cm.getColumnCount()
5989 renderFooter : function()
5999 colspan : this.cm.getColumnCount()
6013 Roo.log('ds onload');
6018 var ds = this.store;
6020 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6021 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6023 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6024 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6027 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6028 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6032 var tbody = this.mainBody;
6034 if(ds.getCount() > 0){
6035 ds.data.each(function(d,rowIndex){
6036 var row = this.renderRow(cm, ds, rowIndex);
6038 tbody.createChild(row);
6042 if(row.cellObjects.length){
6043 Roo.each(row.cellObjects, function(r){
6044 _this.renderCellObject(r);
6051 Roo.each(this.el.select('tbody td', true).elements, function(e){
6052 e.on('mouseover', _this.onMouseover, _this);
6055 Roo.each(this.el.select('tbody td', true).elements, function(e){
6056 e.on('mouseout', _this.onMouseout, _this);
6058 this.fireEvent('rowsrendered', this);
6059 //if(this.loadMask){
6060 // this.maskEl.hide();
6065 onUpdate : function(ds,record)
6067 this.refreshRow(record);
6070 onRemove : function(ds, record, index, isUpdate){
6071 if(isUpdate !== true){
6072 this.fireEvent("beforerowremoved", this, index, record);
6074 var bt = this.mainBody.dom;
6076 var rows = this.el.select('tbody > tr', true).elements;
6078 if(typeof(rows[index]) != 'undefined'){
6079 bt.removeChild(rows[index].dom);
6082 // if(bt.rows[index]){
6083 // bt.removeChild(bt.rows[index]);
6086 if(isUpdate !== true){
6087 //this.stripeRows(index);
6088 //this.syncRowHeights(index, index);
6090 this.fireEvent("rowremoved", this, index, record);
6094 onAdd : function(ds, records, rowIndex)
6096 //Roo.log('on Add called');
6097 // - note this does not handle multiple adding very well..
6098 var bt = this.mainBody.dom;
6099 for (var i =0 ; i < records.length;i++) {
6100 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6101 //Roo.log(records[i]);
6102 //Roo.log(this.store.getAt(rowIndex+i));
6103 this.insertRow(this.store, rowIndex + i, false);
6110 refreshRow : function(record){
6111 var ds = this.store, index;
6112 if(typeof record == 'number'){
6114 record = ds.getAt(index);
6116 index = ds.indexOf(record);
6118 this.insertRow(ds, index, true);
6119 this.onRemove(ds, record, index+1, true);
6120 //this.syncRowHeights(index, index);
6122 this.fireEvent("rowupdated", this, index, record);
6125 insertRow : function(dm, rowIndex, isUpdate){
6128 this.fireEvent("beforerowsinserted", this, rowIndex);
6130 //var s = this.getScrollState();
6131 var row = this.renderRow(this.cm, this.store, rowIndex);
6132 // insert before rowIndex..
6133 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6137 if(row.cellObjects.length){
6138 Roo.each(row.cellObjects, function(r){
6139 _this.renderCellObject(r);
6144 this.fireEvent("rowsinserted", this, rowIndex);
6145 //this.syncRowHeights(firstRow, lastRow);
6146 //this.stripeRows(firstRow);
6153 getRowDom : function(rowIndex)
6155 var rows = this.el.select('tbody > tr', true).elements;
6157 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6160 // returns the object tree for a tr..
6163 renderRow : function(cm, ds, rowIndex)
6166 var d = ds.getAt(rowIndex);
6173 var cellObjects = [];
6175 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6176 var config = cm.config[i];
6178 var renderer = cm.getRenderer(i);
6182 if(typeof(renderer) !== 'undefined'){
6183 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6185 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6186 // and are rendered into the cells after the row is rendered - using the id for the element.
6188 if(typeof(value) === 'object'){
6198 rowIndex : rowIndex,
6203 this.fireEvent('rowclass', this, rowcfg);
6207 cls : rowcfg.rowClass,
6209 html: (typeof(value) === 'object') ? '' : value
6216 if(typeof(config.colspan) != 'undefined'){
6217 td.colspan = config.colspan;
6220 if(typeof(config.hidden) != 'undefined' && config.hidden){
6221 td.style += ' display:none;';
6224 if(typeof(config.align) != 'undefined' && config.align.length){
6225 td.style += ' text-align:' + config.align + ';';
6228 if(typeof(config.width) != 'undefined'){
6229 td.style += ' width:' + config.width + 'px;';
6232 if(typeof(config.cursor) != 'undefined'){
6233 td.style += ' cursor:' + config.cursor + ';';
6236 if(typeof(config.cls) != 'undefined'){
6237 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6244 row.cellObjects = cellObjects;
6252 onBeforeLoad : function()
6254 //Roo.log('ds onBeforeLoad');
6258 //if(this.loadMask){
6259 // this.maskEl.show();
6267 this.el.select('tbody', true).first().dom.innerHTML = '';
6270 * Show or hide a row.
6271 * @param {Number} rowIndex to show or hide
6272 * @param {Boolean} state hide
6274 setRowVisibility : function(rowIndex, state)
6276 var bt = this.mainBody.dom;
6278 var rows = this.el.select('tbody > tr', true).elements;
6280 if(typeof(rows[rowIndex]) == 'undefined'){
6283 rows[rowIndex].dom.style.display = state ? '' : 'none';
6287 getSelectionModel : function(){
6289 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6291 return this.selModel;
6294 * Render the Roo.bootstrap object from renderder
6296 renderCellObject : function(r)
6300 var t = r.cfg.render(r.container);
6303 Roo.each(r.cfg.cn, function(c){
6305 container: t.getChildContainer(),
6308 _this.renderCellObject(child);
6313 getRowIndex : function(row)
6317 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6340 * @class Roo.bootstrap.TableCell
6341 * @extends Roo.bootstrap.Component
6342 * Bootstrap TableCell class
6343 * @cfg {String} html cell contain text
6344 * @cfg {String} cls cell class
6345 * @cfg {String} tag cell tag (td|th) default td
6346 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6347 * @cfg {String} align Aligns the content in a cell
6348 * @cfg {String} axis Categorizes cells
6349 * @cfg {String} bgcolor Specifies the background color of a cell
6350 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6351 * @cfg {Number} colspan Specifies the number of columns a cell should span
6352 * @cfg {String} headers Specifies one or more header cells a cell is related to
6353 * @cfg {Number} height Sets the height of a cell
6354 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6355 * @cfg {Number} rowspan Sets the number of rows a cell should span
6356 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6357 * @cfg {String} valign Vertical aligns the content in a cell
6358 * @cfg {Number} width Specifies the width of a cell
6361 * Create a new TableCell
6362 * @param {Object} config The config object
6365 Roo.bootstrap.TableCell = function(config){
6366 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6369 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6389 getAutoCreate : function(){
6390 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6410 cfg.align=this.align
6416 cfg.bgcolor=this.bgcolor
6419 cfg.charoff=this.charoff
6422 cfg.colspan=this.colspan
6425 cfg.headers=this.headers
6428 cfg.height=this.height
6431 cfg.nowrap=this.nowrap
6434 cfg.rowspan=this.rowspan
6437 cfg.scope=this.scope
6440 cfg.valign=this.valign
6443 cfg.width=this.width
6462 * @class Roo.bootstrap.TableRow
6463 * @extends Roo.bootstrap.Component
6464 * Bootstrap TableRow class
6465 * @cfg {String} cls row class
6466 * @cfg {String} align Aligns the content in a table row
6467 * @cfg {String} bgcolor Specifies a background color for a table row
6468 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6469 * @cfg {String} valign Vertical aligns the content in a table row
6472 * Create a new TableRow
6473 * @param {Object} config The config object
6476 Roo.bootstrap.TableRow = function(config){
6477 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6480 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6488 getAutoCreate : function(){
6489 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6499 cfg.align = this.align;
6502 cfg.bgcolor = this.bgcolor;
6505 cfg.charoff = this.charoff;
6508 cfg.valign = this.valign;
6526 * @class Roo.bootstrap.TableBody
6527 * @extends Roo.bootstrap.Component
6528 * Bootstrap TableBody class
6529 * @cfg {String} cls element class
6530 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6531 * @cfg {String} align Aligns the content inside the element
6532 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6533 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6536 * Create a new TableBody
6537 * @param {Object} config The config object
6540 Roo.bootstrap.TableBody = function(config){
6541 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6544 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6552 getAutoCreate : function(){
6553 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6567 cfg.align = this.align;
6570 cfg.charoff = this.charoff;
6573 cfg.valign = this.valign;
6580 // initEvents : function()
6587 // this.store = Roo.factory(this.store, Roo.data);
6588 // this.store.on('load', this.onLoad, this);
6590 // this.store.load();
6594 // onLoad: function ()
6596 // this.fireEvent('load', this);
6606 * Ext JS Library 1.1.1
6607 * Copyright(c) 2006-2007, Ext JS, LLC.
6609 * Originally Released Under LGPL - original licence link has changed is not relivant.
6612 * <script type="text/javascript">
6615 // as we use this in bootstrap.
6616 Roo.namespace('Roo.form');
6618 * @class Roo.form.Action
6619 * Internal Class used to handle form actions
6621 * @param {Roo.form.BasicForm} el The form element or its id
6622 * @param {Object} config Configuration options
6627 // define the action interface
6628 Roo.form.Action = function(form, options){
6630 this.options = options || {};
6633 * Client Validation Failed
6636 Roo.form.Action.CLIENT_INVALID = 'client';
6638 * Server Validation Failed
6641 Roo.form.Action.SERVER_INVALID = 'server';
6643 * Connect to Server Failed
6646 Roo.form.Action.CONNECT_FAILURE = 'connect';
6648 * Reading Data from Server Failed
6651 Roo.form.Action.LOAD_FAILURE = 'load';
6653 Roo.form.Action.prototype = {
6655 failureType : undefined,
6656 response : undefined,
6660 run : function(options){
6665 success : function(response){
6670 handleResponse : function(response){
6674 // default connection failure
6675 failure : function(response){
6677 this.response = response;
6678 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6679 this.form.afterAction(this, false);
6682 processResponse : function(response){
6683 this.response = response;
6684 if(!response.responseText){
6687 this.result = this.handleResponse(response);
6691 // utility functions used internally
6692 getUrl : function(appendParams){
6693 var url = this.options.url || this.form.url || this.form.el.dom.action;
6695 var p = this.getParams();
6697 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6703 getMethod : function(){
6704 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6707 getParams : function(){
6708 var bp = this.form.baseParams;
6709 var p = this.options.params;
6711 if(typeof p == "object"){
6712 p = Roo.urlEncode(Roo.applyIf(p, bp));
6713 }else if(typeof p == 'string' && bp){
6714 p += '&' + Roo.urlEncode(bp);
6717 p = Roo.urlEncode(bp);
6722 createCallback : function(){
6724 success: this.success,
6725 failure: this.failure,
6727 timeout: (this.form.timeout*1000),
6728 upload: this.form.fileUpload ? this.success : undefined
6733 Roo.form.Action.Submit = function(form, options){
6734 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6737 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6740 haveProgress : false,
6741 uploadComplete : false,
6743 // uploadProgress indicator.
6744 uploadProgress : function()
6746 if (!this.form.progressUrl) {
6750 if (!this.haveProgress) {
6751 Roo.MessageBox.progress("Uploading", "Uploading");
6753 if (this.uploadComplete) {
6754 Roo.MessageBox.hide();
6758 this.haveProgress = true;
6760 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6762 var c = new Roo.data.Connection();
6764 url : this.form.progressUrl,
6769 success : function(req){
6770 //console.log(data);
6774 rdata = Roo.decode(req.responseText)
6776 Roo.log("Invalid data from server..");
6780 if (!rdata || !rdata.success) {
6782 Roo.MessageBox.alert(Roo.encode(rdata));
6785 var data = rdata.data;
6787 if (this.uploadComplete) {
6788 Roo.MessageBox.hide();
6793 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6794 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6797 this.uploadProgress.defer(2000,this);
6800 failure: function(data) {
6801 Roo.log('progress url failed ');
6812 // run get Values on the form, so it syncs any secondary forms.
6813 this.form.getValues();
6815 var o = this.options;
6816 var method = this.getMethod();
6817 var isPost = method == 'POST';
6818 if(o.clientValidation === false || this.form.isValid()){
6820 if (this.form.progressUrl) {
6821 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6822 (new Date() * 1) + '' + Math.random());
6827 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6828 form:this.form.el.dom,
6829 url:this.getUrl(!isPost),
6831 params:isPost ? this.getParams() : null,
6832 isUpload: this.form.fileUpload
6835 this.uploadProgress();
6837 }else if (o.clientValidation !== false){ // client validation failed
6838 this.failureType = Roo.form.Action.CLIENT_INVALID;
6839 this.form.afterAction(this, false);
6843 success : function(response)
6845 this.uploadComplete= true;
6846 if (this.haveProgress) {
6847 Roo.MessageBox.hide();
6851 var result = this.processResponse(response);
6852 if(result === true || result.success){
6853 this.form.afterAction(this, true);
6857 this.form.markInvalid(result.errors);
6858 this.failureType = Roo.form.Action.SERVER_INVALID;
6860 this.form.afterAction(this, false);
6862 failure : function(response)
6864 this.uploadComplete= true;
6865 if (this.haveProgress) {
6866 Roo.MessageBox.hide();
6869 this.response = response;
6870 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6871 this.form.afterAction(this, false);
6874 handleResponse : function(response){
6875 if(this.form.errorReader){
6876 var rs = this.form.errorReader.read(response);
6879 for(var i = 0, len = rs.records.length; i < len; i++) {
6880 var r = rs.records[i];
6884 if(errors.length < 1){
6888 success : rs.success,
6894 ret = Roo.decode(response.responseText);
6898 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6908 Roo.form.Action.Load = function(form, options){
6909 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6910 this.reader = this.form.reader;
6913 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6918 Roo.Ajax.request(Roo.apply(
6919 this.createCallback(), {
6920 method:this.getMethod(),
6921 url:this.getUrl(false),
6922 params:this.getParams()
6926 success : function(response){
6928 var result = this.processResponse(response);
6929 if(result === true || !result.success || !result.data){
6930 this.failureType = Roo.form.Action.LOAD_FAILURE;
6931 this.form.afterAction(this, false);
6934 this.form.clearInvalid();
6935 this.form.setValues(result.data);
6936 this.form.afterAction(this, true);
6939 handleResponse : function(response){
6940 if(this.form.reader){
6941 var rs = this.form.reader.read(response);
6942 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6944 success : rs.success,
6948 return Roo.decode(response.responseText);
6952 Roo.form.Action.ACTION_TYPES = {
6953 'load' : Roo.form.Action.Load,
6954 'submit' : Roo.form.Action.Submit
6963 * @class Roo.bootstrap.Form
6964 * @extends Roo.bootstrap.Component
6965 * Bootstrap Form class
6966 * @cfg {String} method GET | POST (default POST)
6967 * @cfg {String} labelAlign top | left (default top)
6968 * @cfg {String} align left | right - for navbars
6969 * @cfg {Boolean} loadMask load mask when submit (default true)
6974 * @param {Object} config The config object
6978 Roo.bootstrap.Form = function(config){
6979 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6982 * @event clientvalidation
6983 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6984 * @param {Form} this
6985 * @param {Boolean} valid true if the form has passed client-side validation
6987 clientvalidation: true,
6989 * @event beforeaction
6990 * Fires before any action is performed. Return false to cancel the action.
6991 * @param {Form} this
6992 * @param {Action} action The action to be performed
6996 * @event actionfailed
6997 * Fires when an action fails.
6998 * @param {Form} this
6999 * @param {Action} action The action that failed
7001 actionfailed : true,
7003 * @event actioncomplete
7004 * Fires when an action is completed.
7005 * @param {Form} this
7006 * @param {Action} action The action that completed
7008 actioncomplete : true
7013 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7016 * @cfg {String} method
7017 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7022 * The URL to use for form actions if one isn't supplied in the action options.
7025 * @cfg {Boolean} fileUpload
7026 * Set to true if this form is a file upload.
7030 * @cfg {Object} baseParams
7031 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7035 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7039 * @cfg {Sting} align (left|right) for navbar forms
7044 activeAction : null,
7047 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7048 * element by passing it or its id or mask the form itself by passing in true.
7051 waitMsgTarget : false,
7055 getAutoCreate : function(){
7059 method : this.method || 'POST',
7060 id : this.id || Roo.id(),
7063 if (this.parent().xtype.match(/^Nav/)) {
7064 cfg.cls = 'navbar-form navbar-' + this.align;
7068 if (this.labelAlign == 'left' ) {
7069 cfg.cls += ' form-horizontal';
7075 initEvents : function()
7077 this.el.on('submit', this.onSubmit, this);
7078 // this was added as random key presses on the form where triggering form submit.
7079 this.el.on('keypress', function(e) {
7080 if (e.getCharCode() != 13) {
7083 // we might need to allow it for textareas.. and some other items.
7084 // check e.getTarget().
7086 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7090 Roo.log("keypress blocked");
7098 onSubmit : function(e){
7103 * Returns true if client-side validation on the form is successful.
7106 isValid : function(){
7107 var items = this.getItems();
7109 items.each(function(f){
7118 * Returns true if any fields in this form have changed since their original load.
7121 isDirty : function(){
7123 var items = this.getItems();
7124 items.each(function(f){
7134 * Performs a predefined action (submit or load) or custom actions you define on this form.
7135 * @param {String} actionName The name of the action type
7136 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7137 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7138 * accept other config options):
7140 Property Type Description
7141 ---------------- --------------- ----------------------------------------------------------------------------------
7142 url String The url for the action (defaults to the form's url)
7143 method String The form method to use (defaults to the form's method, or POST if not defined)
7144 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7145 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7146 validate the form on the client (defaults to false)
7148 * @return {BasicForm} this
7150 doAction : function(action, options){
7151 if(typeof action == 'string'){
7152 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7154 if(this.fireEvent('beforeaction', this, action) !== false){
7155 this.beforeAction(action);
7156 action.run.defer(100, action);
7162 beforeAction : function(action){
7163 var o = action.options;
7166 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7168 // not really supported yet.. ??
7170 //if(this.waitMsgTarget === true){
7171 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7172 //}else if(this.waitMsgTarget){
7173 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7174 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7176 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7182 afterAction : function(action, success){
7183 this.activeAction = null;
7184 var o = action.options;
7186 //if(this.waitMsgTarget === true){
7188 //}else if(this.waitMsgTarget){
7189 // this.waitMsgTarget.unmask();
7191 // Roo.MessageBox.updateProgress(1);
7192 // Roo.MessageBox.hide();
7199 Roo.callback(o.success, o.scope, [this, action]);
7200 this.fireEvent('actioncomplete', this, action);
7204 // failure condition..
7205 // we have a scenario where updates need confirming.
7206 // eg. if a locking scenario exists..
7207 // we look for { errors : { needs_confirm : true }} in the response.
7209 (typeof(action.result) != 'undefined') &&
7210 (typeof(action.result.errors) != 'undefined') &&
7211 (typeof(action.result.errors.needs_confirm) != 'undefined')
7214 Roo.log("not supported yet");
7217 Roo.MessageBox.confirm(
7218 "Change requires confirmation",
7219 action.result.errorMsg,
7224 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7234 Roo.callback(o.failure, o.scope, [this, action]);
7235 // show an error message if no failed handler is set..
7236 if (!this.hasListener('actionfailed')) {
7237 Roo.log("need to add dialog support");
7239 Roo.MessageBox.alert("Error",
7240 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7241 action.result.errorMsg :
7242 "Saving Failed, please check your entries or try again"
7247 this.fireEvent('actionfailed', this, action);
7252 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7253 * @param {String} id The value to search for
7256 findField : function(id){
7257 var items = this.getItems();
7258 var field = items.get(id);
7260 items.each(function(f){
7261 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7268 return field || null;
7271 * Mark fields in this form invalid in bulk.
7272 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7273 * @return {BasicForm} this
7275 markInvalid : function(errors){
7276 if(errors instanceof Array){
7277 for(var i = 0, len = errors.length; i < len; i++){
7278 var fieldError = errors[i];
7279 var f = this.findField(fieldError.id);
7281 f.markInvalid(fieldError.msg);
7287 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7288 field.markInvalid(errors[id]);
7292 //Roo.each(this.childForms || [], function (f) {
7293 // f.markInvalid(errors);
7300 * Set values for fields in this form in bulk.
7301 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7302 * @return {BasicForm} this
7304 setValues : function(values){
7305 if(values instanceof Array){ // array of objects
7306 for(var i = 0, len = values.length; i < len; i++){
7308 var f = this.findField(v.id);
7310 f.setValue(v.value);
7311 if(this.trackResetOnLoad){
7312 f.originalValue = f.getValue();
7316 }else{ // object hash
7319 if(typeof values[id] != 'function' && (field = this.findField(id))){
7321 if (field.setFromData &&
7323 field.displayField &&
7324 // combos' with local stores can
7325 // be queried via setValue()
7326 // to set their value..
7327 (field.store && !field.store.isLocal)
7331 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7332 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7333 field.setFromData(sd);
7336 field.setValue(values[id]);
7340 if(this.trackResetOnLoad){
7341 field.originalValue = field.getValue();
7347 //Roo.each(this.childForms || [], function (f) {
7348 // f.setValues(values);
7355 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7356 * they are returned as an array.
7357 * @param {Boolean} asString
7360 getValues : function(asString){
7361 //if (this.childForms) {
7362 // copy values from the child forms
7363 // Roo.each(this.childForms, function (f) {
7364 // this.setValues(f.getValues());
7370 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7371 if(asString === true){
7374 return Roo.urlDecode(fs);
7378 * Returns the fields in this form as an object with key/value pairs.
7379 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7382 getFieldValues : function(with_hidden)
7384 var items = this.getItems();
7386 items.each(function(f){
7390 var v = f.getValue();
7391 if (f.inputType =='radio') {
7392 if (typeof(ret[f.getName()]) == 'undefined') {
7393 ret[f.getName()] = ''; // empty..
7396 if (!f.el.dom.checked) {
7404 // not sure if this supported any more..
7405 if ((typeof(v) == 'object') && f.getRawValue) {
7406 v = f.getRawValue() ; // dates..
7408 // combo boxes where name != hiddenName...
7409 if (f.name != f.getName()) {
7410 ret[f.name] = f.getRawValue();
7412 ret[f.getName()] = v;
7419 * Clears all invalid messages in this form.
7420 * @return {BasicForm} this
7422 clearInvalid : function(){
7423 var items = this.getItems();
7425 items.each(function(f){
7436 * @return {BasicForm} this
7439 var items = this.getItems();
7440 items.each(function(f){
7444 Roo.each(this.childForms || [], function (f) {
7451 getItems : function()
7453 var r=new Roo.util.MixedCollection(false, function(o){
7454 return o.id || (o.id = Roo.id());
7456 var iter = function(el) {
7463 Roo.each(el.items,function(e) {
7483 * Ext JS Library 1.1.1
7484 * Copyright(c) 2006-2007, Ext JS, LLC.
7486 * Originally Released Under LGPL - original licence link has changed is not relivant.
7489 * <script type="text/javascript">
7492 * @class Roo.form.VTypes
7493 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7496 Roo.form.VTypes = function(){
7497 // closure these in so they are only created once.
7498 var alpha = /^[a-zA-Z_]+$/;
7499 var alphanum = /^[a-zA-Z0-9_]+$/;
7500 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7501 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7503 // All these messages and functions are configurable
7506 * The function used to validate email addresses
7507 * @param {String} value The email address
7509 'email' : function(v){
7510 return email.test(v);
7513 * The error text to display when the email validation function returns false
7516 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7518 * The keystroke filter mask to be applied on email input
7521 'emailMask' : /[a-z0-9_\.\-@]/i,
7524 * The function used to validate URLs
7525 * @param {String} value The URL
7527 'url' : function(v){
7531 * The error text to display when the url validation function returns false
7534 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7537 * The function used to validate alpha values
7538 * @param {String} value The value
7540 'alpha' : function(v){
7541 return alpha.test(v);
7544 * The error text to display when the alpha validation function returns false
7547 'alphaText' : 'This field should only contain letters and _',
7549 * The keystroke filter mask to be applied on alpha input
7552 'alphaMask' : /[a-z_]/i,
7555 * The function used to validate alphanumeric values
7556 * @param {String} value The value
7558 'alphanum' : function(v){
7559 return alphanum.test(v);
7562 * The error text to display when the alphanumeric validation function returns false
7565 'alphanumText' : 'This field should only contain letters, numbers and _',
7567 * The keystroke filter mask to be applied on alphanumeric input
7570 'alphanumMask' : /[a-z0-9_]/i
7580 * @class Roo.bootstrap.Input
7581 * @extends Roo.bootstrap.Component
7582 * Bootstrap Input class
7583 * @cfg {Boolean} disabled is it disabled
7584 * @cfg {String} fieldLabel - the label associated
7585 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7586 * @cfg {String} name name of the input
7587 * @cfg {string} fieldLabel - the label associated
7588 * @cfg {string} inputType - input / file submit ...
7589 * @cfg {string} placeholder - placeholder to put in text.
7590 * @cfg {string} before - input group add on before
7591 * @cfg {string} after - input group add on after
7592 * @cfg {string} size - (lg|sm) or leave empty..
7593 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7594 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7595 * @cfg {Number} md colspan out of 12 for computer-sized screens
7596 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7597 * @cfg {string} value default value of the input
7598 * @cfg {Number} labelWidth set the width of label (0-12)
7599 * @cfg {String} labelAlign (top|left)
7600 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7601 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7603 * @cfg {String} align (left|center|right) Default left
7604 * @cfg {Boolean} forceFeedback (true|false) Default false
7610 * Create a new Input
7611 * @param {Object} config The config object
7614 Roo.bootstrap.Input = function(config){
7615 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7620 * Fires when this field receives input focus.
7621 * @param {Roo.form.Field} this
7626 * Fires when this field loses input focus.
7627 * @param {Roo.form.Field} this
7632 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7633 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7634 * @param {Roo.form.Field} this
7635 * @param {Roo.EventObject} e The event object
7640 * Fires just before the field blurs if the field value has changed.
7641 * @param {Roo.form.Field} this
7642 * @param {Mixed} newValue The new value
7643 * @param {Mixed} oldValue The original value
7648 * Fires after the field has been marked as invalid.
7649 * @param {Roo.form.Field} this
7650 * @param {String} msg The validation message
7655 * Fires after the field has been validated with no errors.
7656 * @param {Roo.form.Field} this
7661 * Fires after the key up
7662 * @param {Roo.form.Field} this
7663 * @param {Roo.EventObject} e The event Object
7669 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7671 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7672 automatic validation (defaults to "keyup").
7674 validationEvent : "keyup",
7676 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7678 validateOnBlur : true,
7680 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7682 validationDelay : 250,
7684 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7686 focusClass : "x-form-focus", // not needed???
7690 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7692 invalidClass : "has-warning",
7695 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7697 validClass : "has-success",
7700 * @cfg {Boolean} hasFeedback (true|false) default true
7705 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7707 invalidFeedbackClass : "glyphicon-warning-sign",
7710 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7712 validFeedbackClass : "glyphicon-ok",
7715 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7717 selectOnFocus : false,
7720 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7724 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7729 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7731 disableKeyFilter : false,
7734 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7738 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7742 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7744 blankText : "This field is required",
7747 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7751 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7753 maxLength : Number.MAX_VALUE,
7755 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7757 minLengthText : "The minimum length for this field is {0}",
7759 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7761 maxLengthText : "The maximum length for this field is {0}",
7765 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7766 * If available, this function will be called only after the basic validators all return true, and will be passed the
7767 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7771 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7772 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7773 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7777 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7781 autocomplete: false,
7800 formatedValue : false,
7801 forceFeedback : false,
7803 parentLabelAlign : function()
7806 while (parent.parent()) {
7807 parent = parent.parent();
7808 if (typeof(parent.labelAlign) !='undefined') {
7809 return parent.labelAlign;
7816 getAutoCreate : function(){
7818 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7824 if(this.inputType != 'hidden'){
7825 cfg.cls = 'form-group' //input-group
7831 type : this.inputType,
7833 cls : 'form-control',
7834 placeholder : this.placeholder || '',
7835 autocomplete : this.autocomplete || 'new-password'
7840 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7843 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7844 input.maxLength = this.maxLength;
7847 if (this.disabled) {
7848 input.disabled=true;
7851 if (this.readOnly) {
7852 input.readonly=true;
7856 input.name = this.name;
7859 input.cls += ' input-' + this.size;
7862 ['xs','sm','md','lg'].map(function(size){
7863 if (settings[size]) {
7864 cfg.cls += ' col-' + size + '-' + settings[size];
7868 var inputblock = input;
7872 cls: 'glyphicon form-control-feedback'
7875 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7878 cls : 'has-feedback',
7886 if (this.before || this.after) {
7889 cls : 'input-group',
7893 if (this.before && typeof(this.before) == 'string') {
7895 inputblock.cn.push({
7897 cls : 'roo-input-before input-group-addon',
7901 if (this.before && typeof(this.before) == 'object') {
7902 this.before = Roo.factory(this.before);
7903 Roo.log(this.before);
7904 inputblock.cn.push({
7906 cls : 'roo-input-before input-group-' +
7907 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7911 inputblock.cn.push(input);
7913 if (this.after && typeof(this.after) == 'string') {
7914 inputblock.cn.push({
7916 cls : 'roo-input-after input-group-addon',
7920 if (this.after && typeof(this.after) == 'object') {
7921 this.after = Roo.factory(this.after);
7922 Roo.log(this.after);
7923 inputblock.cn.push({
7925 cls : 'roo-input-after input-group-' +
7926 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7930 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7931 inputblock.cls += ' has-feedback';
7932 inputblock.cn.push(feedback);
7936 if (align ==='left' && this.fieldLabel.length) {
7937 Roo.log("left and has label");
7943 cls : 'control-label col-sm-' + this.labelWidth,
7944 html : this.fieldLabel
7948 cls : "col-sm-" + (12 - this.labelWidth),
7955 } else if ( this.fieldLabel.length) {
7961 //cls : 'input-group-addon',
7962 html : this.fieldLabel
7972 Roo.log(" no label && no align");
7981 Roo.log('input-parentType: ' + this.parentType);
7983 if (this.parentType === 'Navbar' && this.parent().bar) {
7984 cfg.cls += ' navbar-form';
7992 * return the real input element.
7994 inputEl: function ()
7996 return this.el.select('input.form-control',true).first();
7999 tooltipEl : function()
8001 return this.inputEl();
8004 setDisabled : function(v)
8006 var i = this.inputEl().dom;
8008 i.removeAttribute('disabled');
8012 i.setAttribute('disabled','true');
8014 initEvents : function()
8017 this.inputEl().on("keydown" , this.fireKey, this);
8018 this.inputEl().on("focus", this.onFocus, this);
8019 this.inputEl().on("blur", this.onBlur, this);
8021 this.inputEl().relayEvent('keyup', this);
8023 // reference to original value for reset
8024 this.originalValue = this.getValue();
8025 //Roo.form.TextField.superclass.initEvents.call(this);
8026 if(this.validationEvent == 'keyup'){
8027 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8028 this.inputEl().on('keyup', this.filterValidation, this);
8030 else if(this.validationEvent !== false){
8031 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8034 if(this.selectOnFocus){
8035 this.on("focus", this.preFocus, this);
8038 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8039 this.inputEl().on("keypress", this.filterKeys, this);
8042 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8043 this.el.on("click", this.autoSize, this);
8046 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8047 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8050 if (typeof(this.before) == 'object') {
8051 this.before.render(this.el.select('.roo-input-before',true).first());
8053 if (typeof(this.after) == 'object') {
8054 this.after.render(this.el.select('.roo-input-after',true).first());
8059 filterValidation : function(e){
8060 if(!e.isNavKeyPress()){
8061 this.validationTask.delay(this.validationDelay);
8065 * Validates the field value
8066 * @return {Boolean} True if the value is valid, else false
8068 validate : function(){
8069 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8070 if(this.disabled || this.validateValue(this.getRawValue())){
8081 * Validates a value according to the field's validation rules and marks the field as invalid
8082 * if the validation fails
8083 * @param {Mixed} value The value to validate
8084 * @return {Boolean} True if the value is valid, else false
8086 validateValue : function(value){
8087 if(value.length < 1) { // if it's blank
8088 if(this.allowBlank){
8094 if(value.length < this.minLength){
8097 if(value.length > this.maxLength){
8101 var vt = Roo.form.VTypes;
8102 if(!vt[this.vtype](value, this)){
8106 if(typeof this.validator == "function"){
8107 var msg = this.validator(value);
8113 if(this.regex && !this.regex.test(value)){
8123 fireKey : function(e){
8124 //Roo.log('field ' + e.getKey());
8125 if(e.isNavKeyPress()){
8126 this.fireEvent("specialkey", this, e);
8129 focus : function (selectText){
8131 this.inputEl().focus();
8132 if(selectText === true){
8133 this.inputEl().dom.select();
8139 onFocus : function(){
8140 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8141 // this.el.addClass(this.focusClass);
8144 this.hasFocus = true;
8145 this.startValue = this.getValue();
8146 this.fireEvent("focus", this);
8150 beforeBlur : Roo.emptyFn,
8154 onBlur : function(){
8156 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8157 //this.el.removeClass(this.focusClass);
8159 this.hasFocus = false;
8160 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8163 var v = this.getValue();
8164 if(String(v) !== String(this.startValue)){
8165 this.fireEvent('change', this, v, this.startValue);
8167 this.fireEvent("blur", this);
8171 * Resets the current field value to the originally loaded value and clears any validation messages
8174 this.setValue(this.originalValue);
8178 * Returns the name of the field
8179 * @return {Mixed} name The name field
8181 getName: function(){
8185 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8186 * @return {Mixed} value The field value
8188 getValue : function(){
8190 var v = this.inputEl().getValue();
8195 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8196 * @return {Mixed} value The field value
8198 getRawValue : function(){
8199 var v = this.inputEl().getValue();
8205 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8206 * @param {Mixed} value The value to set
8208 setRawValue : function(v){
8209 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8212 selectText : function(start, end){
8213 var v = this.getRawValue();
8215 start = start === undefined ? 0 : start;
8216 end = end === undefined ? v.length : end;
8217 var d = this.inputEl().dom;
8218 if(d.setSelectionRange){
8219 d.setSelectionRange(start, end);
8220 }else if(d.createTextRange){
8221 var range = d.createTextRange();
8222 range.moveStart("character", start);
8223 range.moveEnd("character", v.length-end);
8230 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8231 * @param {Mixed} value The value to set
8233 setValue : function(v){
8236 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8242 processValue : function(value){
8243 if(this.stripCharsRe){
8244 var newValue = value.replace(this.stripCharsRe, '');
8245 if(newValue !== value){
8246 this.setRawValue(newValue);
8253 preFocus : function(){
8255 if(this.selectOnFocus){
8256 this.inputEl().dom.select();
8259 filterKeys : function(e){
8261 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8264 var c = e.getCharCode(), cc = String.fromCharCode(c);
8265 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8268 if(!this.maskRe.test(cc)){
8273 * Clear any invalid styles/messages for this field
8275 clearInvalid : function(){
8277 if(!this.el || this.preventMark){ // not rendered
8280 this.el.removeClass(this.invalidClass);
8282 this.fireEvent('valid', this);
8286 * Mark this field as valid
8288 markValid : function(){
8289 if(!this.el || this.preventMark){ // not rendered
8293 this.el.removeClass([this.invalidClass, this.validClass]);
8295 var feedback = this.el.select('.form-control-feedback', true).first();
8298 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8301 if(this.disabled || this.allowBlank){
8305 this.el.addClass(this.validClass);
8307 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8309 var feedback = this.el.select('.form-control-feedback', true).first();
8312 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8313 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8318 this.fireEvent('valid', this);
8322 * Mark this field as invalid
8323 * @param {String} msg The validation message
8325 markInvalid : function(msg){
8326 if(!this.el || this.preventMark){ // not rendered
8330 this.el.removeClass([this.invalidClass, this.validClass]);
8332 var feedback = this.el.select('.form-control-feedback', true).first();
8335 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8338 if(this.disabled || this.allowBlank){
8342 this.el.addClass(this.invalidClass);
8344 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8346 var feedback = this.el.select('.form-control-feedback', true).first();
8349 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8351 if(this.getValue().length || this.forceFeedback){
8352 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8359 this.fireEvent('invalid', this, msg);
8362 SafariOnKeyDown : function(event)
8364 // this is a workaround for a password hang bug on chrome/ webkit.
8366 var isSelectAll = false;
8368 if(this.inputEl().dom.selectionEnd > 0){
8369 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8371 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8372 event.preventDefault();
8377 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8379 event.preventDefault();
8380 // this is very hacky as keydown always get's upper case.
8382 var cc = String.fromCharCode(event.getCharCode());
8383 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8387 adjustWidth : function(tag, w){
8388 tag = tag.toLowerCase();
8389 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8390 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8394 if(tag == 'textarea'){
8397 }else if(Roo.isOpera){
8401 if(tag == 'textarea'){
8420 * @class Roo.bootstrap.TextArea
8421 * @extends Roo.bootstrap.Input
8422 * Bootstrap TextArea class
8423 * @cfg {Number} cols Specifies the visible width of a text area
8424 * @cfg {Number} rows Specifies the visible number of lines in a text area
8425 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8426 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8427 * @cfg {string} html text
8430 * Create a new TextArea
8431 * @param {Object} config The config object
8434 Roo.bootstrap.TextArea = function(config){
8435 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8439 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8449 getAutoCreate : function(){
8451 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8462 value : this.value || '',
8463 html: this.html || '',
8464 cls : 'form-control',
8465 placeholder : this.placeholder || ''
8469 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8470 input.maxLength = this.maxLength;
8474 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8478 input.cols = this.cols;
8481 if (this.readOnly) {
8482 input.readonly = true;
8486 input.name = this.name;
8490 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8494 ['xs','sm','md','lg'].map(function(size){
8495 if (settings[size]) {
8496 cfg.cls += ' col-' + size + '-' + settings[size];
8500 var inputblock = input;
8502 if(this.hasFeedback && !this.allowBlank){
8506 cls: 'glyphicon form-control-feedback'
8510 cls : 'has-feedback',
8519 if (this.before || this.after) {
8522 cls : 'input-group',
8526 inputblock.cn.push({
8528 cls : 'input-group-addon',
8533 inputblock.cn.push(input);
8535 if(this.hasFeedback && !this.allowBlank){
8536 inputblock.cls += ' has-feedback';
8537 inputblock.cn.push(feedback);
8541 inputblock.cn.push({
8543 cls : 'input-group-addon',
8550 if (align ==='left' && this.fieldLabel.length) {
8551 Roo.log("left and has label");
8557 cls : 'control-label col-sm-' + this.labelWidth,
8558 html : this.fieldLabel
8562 cls : "col-sm-" + (12 - this.labelWidth),
8569 } else if ( this.fieldLabel.length) {
8575 //cls : 'input-group-addon',
8576 html : this.fieldLabel
8586 Roo.log(" no label && no align");
8596 if (this.disabled) {
8597 input.disabled=true;
8604 * return the real textarea element.
8606 inputEl: function ()
8608 return this.el.select('textarea.form-control',true).first();
8616 * trigger field - base class for combo..
8621 * @class Roo.bootstrap.TriggerField
8622 * @extends Roo.bootstrap.Input
8623 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8624 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8625 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8626 * for which you can provide a custom implementation. For example:
8628 var trigger = new Roo.bootstrap.TriggerField();
8629 trigger.onTriggerClick = myTriggerFn;
8630 trigger.applyTo('my-field');
8633 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8634 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8635 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8636 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8637 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8640 * Create a new TriggerField.
8641 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8642 * to the base TextField)
8644 Roo.bootstrap.TriggerField = function(config){
8645 this.mimicing = false;
8646 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8649 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8651 * @cfg {String} triggerClass A CSS class to apply to the trigger
8654 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8659 * @cfg {Boolean} removable (true|false) special filter default false
8663 /** @cfg {Boolean} grow @hide */
8664 /** @cfg {Number} growMin @hide */
8665 /** @cfg {Number} growMax @hide */
8671 autoSize: Roo.emptyFn,
8678 actionMode : 'wrap',
8683 getAutoCreate : function(){
8685 var align = this.labelAlign || this.parentLabelAlign();
8690 cls: 'form-group' //input-group
8697 type : this.inputType,
8698 cls : 'form-control',
8699 autocomplete: 'new-password',
8700 placeholder : this.placeholder || ''
8704 input.name = this.name;
8707 input.cls += ' input-' + this.size;
8710 if (this.disabled) {
8711 input.disabled=true;
8714 var inputblock = input;
8716 if(this.hasFeedback && !this.allowBlank){
8720 cls: 'glyphicon form-control-feedback'
8723 if(this.removable && !this.editable && !this.tickable){
8725 cls : 'has-feedback',
8731 cls : 'roo-combo-removable-btn close'
8738 cls : 'has-feedback',
8747 if(this.removable && !this.editable && !this.tickable){
8749 cls : 'roo-removable',
8755 cls : 'roo-combo-removable-btn close'
8762 if (this.before || this.after) {
8765 cls : 'input-group',
8769 inputblock.cn.push({
8771 cls : 'input-group-addon',
8776 inputblock.cn.push(input);
8778 if(this.hasFeedback && !this.allowBlank){
8779 inputblock.cls += ' has-feedback';
8780 inputblock.cn.push(feedback);
8784 inputblock.cn.push({
8786 cls : 'input-group-addon',
8799 cls: 'form-hidden-field'
8807 Roo.log('multiple');
8815 cls: 'form-hidden-field'
8819 cls: 'select2-choices',
8823 cls: 'select2-search-field',
8836 cls: 'select2-container input-group',
8841 // cls: 'typeahead typeahead-long dropdown-menu',
8842 // style: 'display:none'
8847 if(!this.multiple && this.showToggleBtn){
8853 if (this.caret != false) {
8856 cls: 'fa fa-' + this.caret
8863 cls : 'input-group-addon btn dropdown-toggle',
8868 cls: 'combobox-clear',
8882 combobox.cls += ' select2-container-multi';
8885 if (align ==='left' && this.fieldLabel.length) {
8887 Roo.log("left and has label");
8893 cls : 'control-label col-sm-' + this.labelWidth,
8894 html : this.fieldLabel
8898 cls : "col-sm-" + (12 - this.labelWidth),
8905 } else if ( this.fieldLabel.length) {
8911 //cls : 'input-group-addon',
8912 html : this.fieldLabel
8922 Roo.log(" no label && no align");
8929 ['xs','sm','md','lg'].map(function(size){
8930 if (settings[size]) {
8931 cfg.cls += ' col-' + size + '-' + settings[size];
8942 onResize : function(w, h){
8943 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8944 // if(typeof w == 'number'){
8945 // var x = w - this.trigger.getWidth();
8946 // this.inputEl().setWidth(this.adjustWidth('input', x));
8947 // this.trigger.setStyle('left', x+'px');
8952 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8955 getResizeEl : function(){
8956 return this.inputEl();
8960 getPositionEl : function(){
8961 return this.inputEl();
8965 alignErrorIcon : function(){
8966 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8970 initEvents : function(){
8974 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8975 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8976 if(!this.multiple && this.showToggleBtn){
8977 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8978 if(this.hideTrigger){
8979 this.trigger.setDisplayed(false);
8981 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8985 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8988 if(this.removable && !this.editable && !this.tickable){
8989 var close = this.closeTriggerEl();
8992 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8993 close.on('click', this.removeBtnClick, this, close);
8997 //this.trigger.addClassOnOver('x-form-trigger-over');
8998 //this.trigger.addClassOnClick('x-form-trigger-click');
9001 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9005 closeTriggerEl : function()
9007 var close = this.el.select('.roo-combo-removable-btn', true).first();
9008 return close ? close : false;
9011 removeBtnClick : function(e, h, el)
9015 if(this.fireEvent("remove", this) !== false){
9020 createList : function()
9022 this.list = Roo.get(document.body).createChild({
9024 cls: 'typeahead typeahead-long dropdown-menu',
9025 style: 'display:none'
9028 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9033 initTrigger : function(){
9038 onDestroy : function(){
9040 this.trigger.removeAllListeners();
9041 // this.trigger.remove();
9044 // this.wrap.remove();
9046 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9050 onFocus : function(){
9051 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9054 this.wrap.addClass('x-trigger-wrap-focus');
9055 this.mimicing = true;
9056 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9057 if(this.monitorTab){
9058 this.el.on("keydown", this.checkTab, this);
9065 checkTab : function(e){
9066 if(e.getKey() == e.TAB){
9072 onBlur : function(){
9077 mimicBlur : function(e, t){
9079 if(!this.wrap.contains(t) && this.validateBlur()){
9086 triggerBlur : function(){
9087 this.mimicing = false;
9088 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9089 if(this.monitorTab){
9090 this.el.un("keydown", this.checkTab, this);
9092 //this.wrap.removeClass('x-trigger-wrap-focus');
9093 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9097 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9098 validateBlur : function(e, t){
9103 onDisable : function(){
9104 this.inputEl().dom.disabled = true;
9105 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9107 // this.wrap.addClass('x-item-disabled');
9112 onEnable : function(){
9113 this.inputEl().dom.disabled = false;
9114 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9116 // this.el.removeClass('x-item-disabled');
9121 onShow : function(){
9122 var ae = this.getActionEl();
9125 ae.dom.style.display = '';
9126 ae.dom.style.visibility = 'visible';
9132 onHide : function(){
9133 var ae = this.getActionEl();
9134 ae.dom.style.display = 'none';
9138 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9139 * by an implementing function.
9141 * @param {EventObject} e
9143 onTriggerClick : Roo.emptyFn
9147 * Ext JS Library 1.1.1
9148 * Copyright(c) 2006-2007, Ext JS, LLC.
9150 * Originally Released Under LGPL - original licence link has changed is not relivant.
9153 * <script type="text/javascript">
9158 * @class Roo.data.SortTypes
9160 * Defines the default sorting (casting?) comparison functions used when sorting data.
9162 Roo.data.SortTypes = {
9164 * Default sort that does nothing
9165 * @param {Mixed} s The value being converted
9166 * @return {Mixed} The comparison value
9173 * The regular expression used to strip tags
9177 stripTagsRE : /<\/?[^>]+>/gi,
9180 * Strips all HTML tags to sort on text only
9181 * @param {Mixed} s The value being converted
9182 * @return {String} The comparison value
9184 asText : function(s){
9185 return String(s).replace(this.stripTagsRE, "");
9189 * Strips all HTML tags to sort on text only - Case insensitive
9190 * @param {Mixed} s The value being converted
9191 * @return {String} The comparison value
9193 asUCText : function(s){
9194 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9198 * Case insensitive string
9199 * @param {Mixed} s The value being converted
9200 * @return {String} The comparison value
9202 asUCString : function(s) {
9203 return String(s).toUpperCase();
9208 * @param {Mixed} s The value being converted
9209 * @return {Number} The comparison value
9211 asDate : function(s) {
9215 if(s instanceof Date){
9218 return Date.parse(String(s));
9223 * @param {Mixed} s The value being converted
9224 * @return {Float} The comparison value
9226 asFloat : function(s) {
9227 var val = parseFloat(String(s).replace(/,/g, ""));
9228 if(isNaN(val)) val = 0;
9234 * @param {Mixed} s The value being converted
9235 * @return {Number} The comparison value
9237 asInt : function(s) {
9238 var val = parseInt(String(s).replace(/,/g, ""));
9239 if(isNaN(val)) val = 0;
9244 * Ext JS Library 1.1.1
9245 * Copyright(c) 2006-2007, Ext JS, LLC.
9247 * Originally Released Under LGPL - original licence link has changed is not relivant.
9250 * <script type="text/javascript">
9254 * @class Roo.data.Record
9255 * Instances of this class encapsulate both record <em>definition</em> information, and record
9256 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9257 * to access Records cached in an {@link Roo.data.Store} object.<br>
9259 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9260 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9263 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9265 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9266 * {@link #create}. The parameters are the same.
9267 * @param {Array} data An associative Array of data values keyed by the field name.
9268 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9269 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9270 * not specified an integer id is generated.
9272 Roo.data.Record = function(data, id){
9273 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9278 * Generate a constructor for a specific record layout.
9279 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9280 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9281 * Each field definition object may contain the following properties: <ul>
9282 * <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,
9283 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9284 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9285 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9286 * is being used, then this is a string containing the javascript expression to reference the data relative to
9287 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9288 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9289 * this may be omitted.</p></li>
9290 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9291 * <ul><li>auto (Default, implies no conversion)</li>
9296 * <li>date</li></ul></p></li>
9297 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9298 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9299 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9300 * by the Reader into an object that will be stored in the Record. It is passed the
9301 * following parameters:<ul>
9302 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9304 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9306 * <br>usage:<br><pre><code>
9307 var TopicRecord = Roo.data.Record.create(
9308 {name: 'title', mapping: 'topic_title'},
9309 {name: 'author', mapping: 'username'},
9310 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9311 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9312 {name: 'lastPoster', mapping: 'user2'},
9313 {name: 'excerpt', mapping: 'post_text'}
9316 var myNewRecord = new TopicRecord({
9317 title: 'Do my job please',
9320 lastPost: new Date(),
9321 lastPoster: 'Animal',
9322 excerpt: 'No way dude!'
9324 myStore.add(myNewRecord);
9329 Roo.data.Record.create = function(o){
9331 f.superclass.constructor.apply(this, arguments);
9333 Roo.extend(f, Roo.data.Record);
9334 var p = f.prototype;
9335 p.fields = new Roo.util.MixedCollection(false, function(field){
9338 for(var i = 0, len = o.length; i < len; i++){
9339 p.fields.add(new Roo.data.Field(o[i]));
9341 f.getField = function(name){
9342 return p.fields.get(name);
9347 Roo.data.Record.AUTO_ID = 1000;
9348 Roo.data.Record.EDIT = 'edit';
9349 Roo.data.Record.REJECT = 'reject';
9350 Roo.data.Record.COMMIT = 'commit';
9352 Roo.data.Record.prototype = {
9354 * Readonly flag - true if this record has been modified.
9363 join : function(store){
9368 * Set the named field to the specified value.
9369 * @param {String} name The name of the field to set.
9370 * @param {Object} value The value to set the field to.
9372 set : function(name, value){
9373 if(this.data[name] == value){
9380 if(typeof this.modified[name] == 'undefined'){
9381 this.modified[name] = this.data[name];
9383 this.data[name] = value;
9384 if(!this.editing && this.store){
9385 this.store.afterEdit(this);
9390 * Get the value of the named field.
9391 * @param {String} name The name of the field to get the value of.
9392 * @return {Object} The value of the field.
9394 get : function(name){
9395 return this.data[name];
9399 beginEdit : function(){
9400 this.editing = true;
9405 cancelEdit : function(){
9406 this.editing = false;
9407 delete this.modified;
9411 endEdit : function(){
9412 this.editing = false;
9413 if(this.dirty && this.store){
9414 this.store.afterEdit(this);
9419 * Usually called by the {@link Roo.data.Store} which owns the Record.
9420 * Rejects all changes made to the Record since either creation, or the last commit operation.
9421 * Modified fields are reverted to their original values.
9423 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9424 * of reject operations.
9426 reject : function(){
9427 var m = this.modified;
9429 if(typeof m[n] != "function"){
9430 this.data[n] = m[n];
9434 delete this.modified;
9435 this.editing = false;
9437 this.store.afterReject(this);
9442 * Usually called by the {@link Roo.data.Store} which owns the Record.
9443 * Commits all changes made to the Record since either creation, or the last commit operation.
9445 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9446 * of commit operations.
9448 commit : function(){
9450 delete this.modified;
9451 this.editing = false;
9453 this.store.afterCommit(this);
9458 hasError : function(){
9459 return this.error != null;
9463 clearError : function(){
9468 * Creates a copy of this record.
9469 * @param {String} id (optional) A new record id if you don't want to use this record's id
9472 copy : function(newId) {
9473 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9477 * Ext JS Library 1.1.1
9478 * Copyright(c) 2006-2007, Ext JS, LLC.
9480 * Originally Released Under LGPL - original licence link has changed is not relivant.
9483 * <script type="text/javascript">
9489 * @class Roo.data.Store
9490 * @extends Roo.util.Observable
9491 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9492 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9494 * 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
9495 * has no knowledge of the format of the data returned by the Proxy.<br>
9497 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9498 * instances from the data object. These records are cached and made available through accessor functions.
9500 * Creates a new Store.
9501 * @param {Object} config A config object containing the objects needed for the Store to access data,
9502 * and read the data into Records.
9504 Roo.data.Store = function(config){
9505 this.data = new Roo.util.MixedCollection(false);
9506 this.data.getKey = function(o){
9509 this.baseParams = {};
9516 "multisort" : "_multisort"
9519 if(config && config.data){
9520 this.inlineData = config.data;
9524 Roo.apply(this, config);
9526 if(this.reader){ // reader passed
9527 this.reader = Roo.factory(this.reader, Roo.data);
9528 this.reader.xmodule = this.xmodule || false;
9529 if(!this.recordType){
9530 this.recordType = this.reader.recordType;
9532 if(this.reader.onMetaChange){
9533 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9537 if(this.recordType){
9538 this.fields = this.recordType.prototype.fields;
9544 * @event datachanged
9545 * Fires when the data cache has changed, and a widget which is using this Store
9546 * as a Record cache should refresh its view.
9547 * @param {Store} this
9552 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9553 * @param {Store} this
9554 * @param {Object} meta The JSON metadata
9559 * Fires when Records have been added to the Store
9560 * @param {Store} this
9561 * @param {Roo.data.Record[]} records The array of Records added
9562 * @param {Number} index The index at which the record(s) were added
9567 * Fires when a Record has been removed from the Store
9568 * @param {Store} this
9569 * @param {Roo.data.Record} record The Record that was removed
9570 * @param {Number} index The index at which the record was removed
9575 * Fires when a Record has been updated
9576 * @param {Store} this
9577 * @param {Roo.data.Record} record The Record that was updated
9578 * @param {String} operation The update operation being performed. Value may be one of:
9580 Roo.data.Record.EDIT
9581 Roo.data.Record.REJECT
9582 Roo.data.Record.COMMIT
9588 * Fires when the data cache has been cleared.
9589 * @param {Store} this
9594 * Fires before a request is made for a new data object. If the beforeload handler returns false
9595 * the load action will be canceled.
9596 * @param {Store} this
9597 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9601 * @event beforeloadadd
9602 * Fires after a new set of Records has been loaded.
9603 * @param {Store} this
9604 * @param {Roo.data.Record[]} records The Records that were loaded
9605 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9607 beforeloadadd : true,
9610 * Fires after a new set of Records has been loaded, before they are added to the store.
9611 * @param {Store} this
9612 * @param {Roo.data.Record[]} records The Records that were loaded
9613 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9614 * @params {Object} return from reader
9618 * @event loadexception
9619 * Fires if an exception occurs in the Proxy during loading.
9620 * Called with the signature of the Proxy's "loadexception" event.
9621 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9624 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9625 * @param {Object} load options
9626 * @param {Object} jsonData from your request (normally this contains the Exception)
9628 loadexception : true
9632 this.proxy = Roo.factory(this.proxy, Roo.data);
9633 this.proxy.xmodule = this.xmodule || false;
9634 this.relayEvents(this.proxy, ["loadexception"]);
9636 this.sortToggle = {};
9637 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9639 Roo.data.Store.superclass.constructor.call(this);
9641 if(this.inlineData){
9642 this.loadData(this.inlineData);
9643 delete this.inlineData;
9647 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9649 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9650 * without a remote query - used by combo/forms at present.
9654 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9657 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9660 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9661 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9664 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9665 * on any HTTP request
9668 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9671 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9675 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9676 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9681 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9682 * loaded or when a record is removed. (defaults to false).
9684 pruneModifiedRecords : false,
9690 * Add Records to the Store and fires the add event.
9691 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9693 add : function(records){
9694 records = [].concat(records);
9695 for(var i = 0, len = records.length; i < len; i++){
9696 records[i].join(this);
9698 var index = this.data.length;
9699 this.data.addAll(records);
9700 this.fireEvent("add", this, records, index);
9704 * Remove a Record from the Store and fires the remove event.
9705 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9707 remove : function(record){
9708 var index = this.data.indexOf(record);
9709 this.data.removeAt(index);
9710 if(this.pruneModifiedRecords){
9711 this.modified.remove(record);
9713 this.fireEvent("remove", this, record, index);
9717 * Remove all Records from the Store and fires the clear event.
9719 removeAll : function(){
9721 if(this.pruneModifiedRecords){
9724 this.fireEvent("clear", this);
9728 * Inserts Records to the Store at the given index and fires the add event.
9729 * @param {Number} index The start index at which to insert the passed Records.
9730 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9732 insert : function(index, records){
9733 records = [].concat(records);
9734 for(var i = 0, len = records.length; i < len; i++){
9735 this.data.insert(index, records[i]);
9736 records[i].join(this);
9738 this.fireEvent("add", this, records, index);
9742 * Get the index within the cache of the passed Record.
9743 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9744 * @return {Number} The index of the passed Record. Returns -1 if not found.
9746 indexOf : function(record){
9747 return this.data.indexOf(record);
9751 * Get the index within the cache of the Record with the passed id.
9752 * @param {String} id The id of the Record to find.
9753 * @return {Number} The index of the Record. Returns -1 if not found.
9755 indexOfId : function(id){
9756 return this.data.indexOfKey(id);
9760 * Get the Record with the specified id.
9761 * @param {String} id The id of the Record to find.
9762 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9764 getById : function(id){
9765 return this.data.key(id);
9769 * Get the Record at the specified index.
9770 * @param {Number} index The index of the Record to find.
9771 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9773 getAt : function(index){
9774 return this.data.itemAt(index);
9778 * Returns a range of Records between specified indices.
9779 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9780 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9781 * @return {Roo.data.Record[]} An array of Records
9783 getRange : function(start, end){
9784 return this.data.getRange(start, end);
9788 storeOptions : function(o){
9789 o = Roo.apply({}, o);
9792 this.lastOptions = o;
9796 * Loads the Record cache from the configured Proxy using the configured Reader.
9798 * If using remote paging, then the first load call must specify the <em>start</em>
9799 * and <em>limit</em> properties in the options.params property to establish the initial
9800 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9802 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9803 * and this call will return before the new data has been loaded. Perform any post-processing
9804 * in a callback function, or in a "load" event handler.</strong>
9806 * @param {Object} options An object containing properties which control loading options:<ul>
9807 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9808 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9809 * passed the following arguments:<ul>
9810 * <li>r : Roo.data.Record[]</li>
9811 * <li>options: Options object from the load call</li>
9812 * <li>success: Boolean success indicator</li></ul></li>
9813 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9814 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9817 load : function(options){
9818 options = options || {};
9819 if(this.fireEvent("beforeload", this, options) !== false){
9820 this.storeOptions(options);
9821 var p = Roo.apply(options.params || {}, this.baseParams);
9822 // if meta was not loaded from remote source.. try requesting it.
9823 if (!this.reader.metaFromRemote) {
9826 if(this.sortInfo && this.remoteSort){
9827 var pn = this.paramNames;
9828 p[pn["sort"]] = this.sortInfo.field;
9829 p[pn["dir"]] = this.sortInfo.direction;
9831 if (this.multiSort) {
9832 var pn = this.paramNames;
9833 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9836 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9841 * Reloads the Record cache from the configured Proxy using the configured Reader and
9842 * the options from the last load operation performed.
9843 * @param {Object} options (optional) An object containing properties which may override the options
9844 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9845 * the most recently used options are reused).
9847 reload : function(options){
9848 this.load(Roo.applyIf(options||{}, this.lastOptions));
9852 // Called as a callback by the Reader during a load operation.
9853 loadRecords : function(o, options, success){
9854 if(!o || success === false){
9855 if(success !== false){
9856 this.fireEvent("load", this, [], options, o);
9858 if(options.callback){
9859 options.callback.call(options.scope || this, [], options, false);
9863 // if data returned failure - throw an exception.
9864 if (o.success === false) {
9865 // show a message if no listener is registered.
9866 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9867 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9869 // loadmask wil be hooked into this..
9870 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9873 var r = o.records, t = o.totalRecords || r.length;
9875 this.fireEvent("beforeloadadd", this, r, options, o);
9877 if(!options || options.add !== true){
9878 if(this.pruneModifiedRecords){
9881 for(var i = 0, len = r.length; i < len; i++){
9885 this.data = this.snapshot;
9886 delete this.snapshot;
9889 this.data.addAll(r);
9890 this.totalLength = t;
9892 this.fireEvent("datachanged", this);
9894 this.totalLength = Math.max(t, this.data.length+r.length);
9897 this.fireEvent("load", this, r, options, o);
9898 if(options.callback){
9899 options.callback.call(options.scope || this, r, options, true);
9905 * Loads data from a passed data block. A Reader which understands the format of the data
9906 * must have been configured in the constructor.
9907 * @param {Object} data The data block from which to read the Records. The format of the data expected
9908 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9909 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9911 loadData : function(o, append){
9912 var r = this.reader.readRecords(o);
9913 this.loadRecords(r, {add: append}, true);
9917 * Gets the number of cached records.
9919 * <em>If using paging, this may not be the total size of the dataset. If the data object
9920 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9921 * the data set size</em>
9923 getCount : function(){
9924 return this.data.length || 0;
9928 * Gets the total number of records in the dataset as returned by the server.
9930 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9931 * the dataset size</em>
9933 getTotalCount : function(){
9934 return this.totalLength || 0;
9938 * Returns the sort state of the Store as an object with two properties:
9940 field {String} The name of the field by which the Records are sorted
9941 direction {String} The sort order, "ASC" or "DESC"
9944 getSortState : function(){
9945 return this.sortInfo;
9949 applySort : function(){
9950 if(this.sortInfo && !this.remoteSort){
9951 var s = this.sortInfo, f = s.field;
9952 var st = this.fields.get(f).sortType;
9953 var fn = function(r1, r2){
9954 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9955 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9957 this.data.sort(s.direction, fn);
9958 if(this.snapshot && this.snapshot != this.data){
9959 this.snapshot.sort(s.direction, fn);
9965 * Sets the default sort column and order to be used by the next load operation.
9966 * @param {String} fieldName The name of the field to sort by.
9967 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9969 setDefaultSort : function(field, dir){
9970 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9975 * If remote sorting is used, the sort is performed on the server, and the cache is
9976 * reloaded. If local sorting is used, the cache is sorted internally.
9977 * @param {String} fieldName The name of the field to sort by.
9978 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9980 sort : function(fieldName, dir){
9981 var f = this.fields.get(fieldName);
9983 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9985 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9986 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9991 this.sortToggle[f.name] = dir;
9992 this.sortInfo = {field: f.name, direction: dir};
9993 if(!this.remoteSort){
9995 this.fireEvent("datachanged", this);
9997 this.load(this.lastOptions);
10002 * Calls the specified function for each of the Records in the cache.
10003 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10004 * Returning <em>false</em> aborts and exits the iteration.
10005 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10007 each : function(fn, scope){
10008 this.data.each(fn, scope);
10012 * Gets all records modified since the last commit. Modified records are persisted across load operations
10013 * (e.g., during paging).
10014 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10016 getModifiedRecords : function(){
10017 return this.modified;
10021 createFilterFn : function(property, value, anyMatch){
10022 if(!value.exec){ // not a regex
10023 value = String(value);
10024 if(value.length == 0){
10027 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10029 return function(r){
10030 return value.test(r.data[property]);
10035 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10036 * @param {String} property A field on your records
10037 * @param {Number} start The record index to start at (defaults to 0)
10038 * @param {Number} end The last record index to include (defaults to length - 1)
10039 * @return {Number} The sum
10041 sum : function(property, start, end){
10042 var rs = this.data.items, v = 0;
10043 start = start || 0;
10044 end = (end || end === 0) ? end : rs.length-1;
10046 for(var i = start; i <= end; i++){
10047 v += (rs[i].data[property] || 0);
10053 * Filter the records by a specified property.
10054 * @param {String} field A field on your records
10055 * @param {String/RegExp} value Either a string that the field
10056 * should start with or a RegExp to test against the field
10057 * @param {Boolean} anyMatch True to match any part not just the beginning
10059 filter : function(property, value, anyMatch){
10060 var fn = this.createFilterFn(property, value, anyMatch);
10061 return fn ? this.filterBy(fn) : this.clearFilter();
10065 * Filter by a function. The specified function will be called with each
10066 * record in this data source. If the function returns true the record is included,
10067 * otherwise it is filtered.
10068 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10069 * @param {Object} scope (optional) The scope of the function (defaults to this)
10071 filterBy : function(fn, scope){
10072 this.snapshot = this.snapshot || this.data;
10073 this.data = this.queryBy(fn, scope||this);
10074 this.fireEvent("datachanged", this);
10078 * Query the records by a specified property.
10079 * @param {String} field A field on your records
10080 * @param {String/RegExp} value Either a string that the field
10081 * should start with or a RegExp to test against the field
10082 * @param {Boolean} anyMatch True to match any part not just the beginning
10083 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10085 query : function(property, value, anyMatch){
10086 var fn = this.createFilterFn(property, value, anyMatch);
10087 return fn ? this.queryBy(fn) : this.data.clone();
10091 * Query by a function. The specified function will be called with each
10092 * record in this data source. If the function returns true the record is included
10094 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10095 * @param {Object} scope (optional) The scope of the function (defaults to this)
10096 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10098 queryBy : function(fn, scope){
10099 var data = this.snapshot || this.data;
10100 return data.filterBy(fn, scope||this);
10104 * Collects unique values for a particular dataIndex from this store.
10105 * @param {String} dataIndex The property to collect
10106 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10107 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10108 * @return {Array} An array of the unique values
10110 collect : function(dataIndex, allowNull, bypassFilter){
10111 var d = (bypassFilter === true && this.snapshot) ?
10112 this.snapshot.items : this.data.items;
10113 var v, sv, r = [], l = {};
10114 for(var i = 0, len = d.length; i < len; i++){
10115 v = d[i].data[dataIndex];
10117 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10126 * Revert to a view of the Record cache with no filtering applied.
10127 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10129 clearFilter : function(suppressEvent){
10130 if(this.snapshot && this.snapshot != this.data){
10131 this.data = this.snapshot;
10132 delete this.snapshot;
10133 if(suppressEvent !== true){
10134 this.fireEvent("datachanged", this);
10140 afterEdit : function(record){
10141 if(this.modified.indexOf(record) == -1){
10142 this.modified.push(record);
10144 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10148 afterReject : function(record){
10149 this.modified.remove(record);
10150 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10154 afterCommit : function(record){
10155 this.modified.remove(record);
10156 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10160 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10161 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10163 commitChanges : function(){
10164 var m = this.modified.slice(0);
10165 this.modified = [];
10166 for(var i = 0, len = m.length; i < len; i++){
10172 * Cancel outstanding changes on all changed records.
10174 rejectChanges : function(){
10175 var m = this.modified.slice(0);
10176 this.modified = [];
10177 for(var i = 0, len = m.length; i < len; i++){
10182 onMetaChange : function(meta, rtype, o){
10183 this.recordType = rtype;
10184 this.fields = rtype.prototype.fields;
10185 delete this.snapshot;
10186 this.sortInfo = meta.sortInfo || this.sortInfo;
10187 this.modified = [];
10188 this.fireEvent('metachange', this, this.reader.meta);
10191 moveIndex : function(data, type)
10193 var index = this.indexOf(data);
10195 var newIndex = index + type;
10199 this.insert(newIndex, data);
10204 * Ext JS Library 1.1.1
10205 * Copyright(c) 2006-2007, Ext JS, LLC.
10207 * Originally Released Under LGPL - original licence link has changed is not relivant.
10210 * <script type="text/javascript">
10214 * @class Roo.data.SimpleStore
10215 * @extends Roo.data.Store
10216 * Small helper class to make creating Stores from Array data easier.
10217 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10218 * @cfg {Array} fields An array of field definition objects, or field name strings.
10219 * @cfg {Array} data The multi-dimensional array of data
10221 * @param {Object} config
10223 Roo.data.SimpleStore = function(config){
10224 Roo.data.SimpleStore.superclass.constructor.call(this, {
10226 reader: new Roo.data.ArrayReader({
10229 Roo.data.Record.create(config.fields)
10231 proxy : new Roo.data.MemoryProxy(config.data)
10235 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10237 * Ext JS Library 1.1.1
10238 * Copyright(c) 2006-2007, Ext JS, LLC.
10240 * Originally Released Under LGPL - original licence link has changed is not relivant.
10243 * <script type="text/javascript">
10248 * @extends Roo.data.Store
10249 * @class Roo.data.JsonStore
10250 * Small helper class to make creating Stores for JSON data easier. <br/>
10252 var store = new Roo.data.JsonStore({
10253 url: 'get-images.php',
10255 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10258 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10259 * JsonReader and HttpProxy (unless inline data is provided).</b>
10260 * @cfg {Array} fields An array of field definition objects, or field name strings.
10262 * @param {Object} config
10264 Roo.data.JsonStore = function(c){
10265 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10266 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10267 reader: new Roo.data.JsonReader(c, c.fields)
10270 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10272 * Ext JS Library 1.1.1
10273 * Copyright(c) 2006-2007, Ext JS, LLC.
10275 * Originally Released Under LGPL - original licence link has changed is not relivant.
10278 * <script type="text/javascript">
10282 Roo.data.Field = function(config){
10283 if(typeof config == "string"){
10284 config = {name: config};
10286 Roo.apply(this, config);
10289 this.type = "auto";
10292 var st = Roo.data.SortTypes;
10293 // named sortTypes are supported, here we look them up
10294 if(typeof this.sortType == "string"){
10295 this.sortType = st[this.sortType];
10298 // set default sortType for strings and dates
10299 if(!this.sortType){
10302 this.sortType = st.asUCString;
10305 this.sortType = st.asDate;
10308 this.sortType = st.none;
10313 var stripRe = /[\$,%]/g;
10315 // prebuilt conversion function for this field, instead of
10316 // switching every time we're reading a value
10318 var cv, dateFormat = this.dateFormat;
10323 cv = function(v){ return v; };
10326 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10330 return v !== undefined && v !== null && v !== '' ?
10331 parseInt(String(v).replace(stripRe, ""), 10) : '';
10336 return v !== undefined && v !== null && v !== '' ?
10337 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10342 cv = function(v){ return v === true || v === "true" || v == 1; };
10349 if(v instanceof Date){
10353 if(dateFormat == "timestamp"){
10354 return new Date(v*1000);
10356 return Date.parseDate(v, dateFormat);
10358 var parsed = Date.parse(v);
10359 return parsed ? new Date(parsed) : null;
10368 Roo.data.Field.prototype = {
10376 * Ext JS Library 1.1.1
10377 * Copyright(c) 2006-2007, Ext JS, LLC.
10379 * Originally Released Under LGPL - original licence link has changed is not relivant.
10382 * <script type="text/javascript">
10385 // Base class for reading structured data from a data source. This class is intended to be
10386 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10389 * @class Roo.data.DataReader
10390 * Base class for reading structured data from a data source. This class is intended to be
10391 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10394 Roo.data.DataReader = function(meta, recordType){
10398 this.recordType = recordType instanceof Array ?
10399 Roo.data.Record.create(recordType) : recordType;
10402 Roo.data.DataReader.prototype = {
10404 * Create an empty record
10405 * @param {Object} data (optional) - overlay some values
10406 * @return {Roo.data.Record} record created.
10408 newRow : function(d) {
10410 this.recordType.prototype.fields.each(function(c) {
10412 case 'int' : da[c.name] = 0; break;
10413 case 'date' : da[c.name] = new Date(); break;
10414 case 'float' : da[c.name] = 0.0; break;
10415 case 'boolean' : da[c.name] = false; break;
10416 default : da[c.name] = ""; break;
10420 return new this.recordType(Roo.apply(da, d));
10425 * Ext JS Library 1.1.1
10426 * Copyright(c) 2006-2007, Ext JS, LLC.
10428 * Originally Released Under LGPL - original licence link has changed is not relivant.
10431 * <script type="text/javascript">
10435 * @class Roo.data.DataProxy
10436 * @extends Roo.data.Observable
10437 * This class is an abstract base class for implementations which provide retrieval of
10438 * unformatted data objects.<br>
10440 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10441 * (of the appropriate type which knows how to parse the data object) to provide a block of
10442 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10444 * Custom implementations must implement the load method as described in
10445 * {@link Roo.data.HttpProxy#load}.
10447 Roo.data.DataProxy = function(){
10450 * @event beforeload
10451 * Fires before a network request is made to retrieve a data object.
10452 * @param {Object} This DataProxy object.
10453 * @param {Object} params The params parameter to the load function.
10458 * Fires before the load method's callback is called.
10459 * @param {Object} This DataProxy object.
10460 * @param {Object} o The data object.
10461 * @param {Object} arg The callback argument object passed to the load function.
10465 * @event loadexception
10466 * Fires if an Exception occurs during data retrieval.
10467 * @param {Object} This DataProxy object.
10468 * @param {Object} o The data object.
10469 * @param {Object} arg The callback argument object passed to the load function.
10470 * @param {Object} e The Exception.
10472 loadexception : true
10474 Roo.data.DataProxy.superclass.constructor.call(this);
10477 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10480 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10484 * Ext JS Library 1.1.1
10485 * Copyright(c) 2006-2007, Ext JS, LLC.
10487 * Originally Released Under LGPL - original licence link has changed is not relivant.
10490 * <script type="text/javascript">
10493 * @class Roo.data.MemoryProxy
10494 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10495 * to the Reader when its load method is called.
10497 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10499 Roo.data.MemoryProxy = function(data){
10503 Roo.data.MemoryProxy.superclass.constructor.call(this);
10507 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10509 * Load data from the requested source (in this case an in-memory
10510 * data object passed to the constructor), read the data object into
10511 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10512 * process that block using the passed callback.
10513 * @param {Object} params This parameter is not used by the MemoryProxy class.
10514 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10515 * object into a block of Roo.data.Records.
10516 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10517 * The function must be passed <ul>
10518 * <li>The Record block object</li>
10519 * <li>The "arg" argument from the load function</li>
10520 * <li>A boolean success indicator</li>
10522 * @param {Object} scope The scope in which to call the callback
10523 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10525 load : function(params, reader, callback, scope, arg){
10526 params = params || {};
10529 result = reader.readRecords(this.data);
10531 this.fireEvent("loadexception", this, arg, null, e);
10532 callback.call(scope, null, arg, false);
10535 callback.call(scope, result, arg, true);
10539 update : function(params, records){
10544 * Ext JS Library 1.1.1
10545 * Copyright(c) 2006-2007, Ext JS, LLC.
10547 * Originally Released Under LGPL - original licence link has changed is not relivant.
10550 * <script type="text/javascript">
10553 * @class Roo.data.HttpProxy
10554 * @extends Roo.data.DataProxy
10555 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10556 * configured to reference a certain URL.<br><br>
10558 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10559 * from which the running page was served.<br><br>
10561 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10563 * Be aware that to enable the browser to parse an XML document, the server must set
10564 * the Content-Type header in the HTTP response to "text/xml".
10566 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10567 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10568 * will be used to make the request.
10570 Roo.data.HttpProxy = function(conn){
10571 Roo.data.HttpProxy.superclass.constructor.call(this);
10572 // is conn a conn config or a real conn?
10574 this.useAjax = !conn || !conn.events;
10578 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10579 // thse are take from connection...
10582 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10585 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10586 * extra parameters to each request made by this object. (defaults to undefined)
10589 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10590 * to each request made by this object. (defaults to undefined)
10593 * @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)
10596 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10599 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10605 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10609 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10610 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10611 * a finer-grained basis than the DataProxy events.
10613 getConnection : function(){
10614 return this.useAjax ? Roo.Ajax : this.conn;
10618 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10619 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10620 * process that block using the passed callback.
10621 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10622 * for the request to the remote server.
10623 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10624 * object into a block of Roo.data.Records.
10625 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10626 * The function must be passed <ul>
10627 * <li>The Record block object</li>
10628 * <li>The "arg" argument from the load function</li>
10629 * <li>A boolean success indicator</li>
10631 * @param {Object} scope The scope in which to call the callback
10632 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10634 load : function(params, reader, callback, scope, arg){
10635 if(this.fireEvent("beforeload", this, params) !== false){
10637 params : params || {},
10639 callback : callback,
10644 callback : this.loadResponse,
10648 Roo.applyIf(o, this.conn);
10649 if(this.activeRequest){
10650 Roo.Ajax.abort(this.activeRequest);
10652 this.activeRequest = Roo.Ajax.request(o);
10654 this.conn.request(o);
10657 callback.call(scope||this, null, arg, false);
10662 loadResponse : function(o, success, response){
10663 delete this.activeRequest;
10665 this.fireEvent("loadexception", this, o, response);
10666 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10671 result = o.reader.read(response);
10673 this.fireEvent("loadexception", this, o, response, e);
10674 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10678 this.fireEvent("load", this, o, o.request.arg);
10679 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10683 update : function(dataSet){
10688 updateResponse : function(dataSet){
10693 * Ext JS Library 1.1.1
10694 * Copyright(c) 2006-2007, Ext JS, LLC.
10696 * Originally Released Under LGPL - original licence link has changed is not relivant.
10699 * <script type="text/javascript">
10703 * @class Roo.data.ScriptTagProxy
10704 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10705 * other than the originating domain of the running page.<br><br>
10707 * <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
10708 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10710 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10711 * source code that is used as the source inside a <script> tag.<br><br>
10713 * In order for the browser to process the returned data, the server must wrap the data object
10714 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10715 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10716 * depending on whether the callback name was passed:
10719 boolean scriptTag = false;
10720 String cb = request.getParameter("callback");
10723 response.setContentType("text/javascript");
10725 response.setContentType("application/x-json");
10727 Writer out = response.getWriter();
10729 out.write(cb + "(");
10731 out.print(dataBlock.toJsonString());
10738 * @param {Object} config A configuration object.
10740 Roo.data.ScriptTagProxy = function(config){
10741 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10742 Roo.apply(this, config);
10743 this.head = document.getElementsByTagName("head")[0];
10746 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10748 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10750 * @cfg {String} url The URL from which to request the data object.
10753 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10757 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10758 * the server the name of the callback function set up by the load call to process the returned data object.
10759 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10760 * javascript output which calls this named function passing the data object as its only parameter.
10762 callbackParam : "callback",
10764 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10765 * name to the request.
10770 * Load data from the configured URL, read the data object into
10771 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10772 * process that block using the passed callback.
10773 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10774 * for the request to the remote server.
10775 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10776 * object into a block of Roo.data.Records.
10777 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10778 * The function must be passed <ul>
10779 * <li>The Record block object</li>
10780 * <li>The "arg" argument from the load function</li>
10781 * <li>A boolean success indicator</li>
10783 * @param {Object} scope The scope in which to call the callback
10784 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10786 load : function(params, reader, callback, scope, arg){
10787 if(this.fireEvent("beforeload", this, params) !== false){
10789 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10791 var url = this.url;
10792 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10794 url += "&_dc=" + (new Date().getTime());
10796 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10799 cb : "stcCallback"+transId,
10800 scriptId : "stcScript"+transId,
10804 callback : callback,
10810 window[trans.cb] = function(o){
10811 conn.handleResponse(o, trans);
10814 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10816 if(this.autoAbort !== false){
10820 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10822 var script = document.createElement("script");
10823 script.setAttribute("src", url);
10824 script.setAttribute("type", "text/javascript");
10825 script.setAttribute("id", trans.scriptId);
10826 this.head.appendChild(script);
10828 this.trans = trans;
10830 callback.call(scope||this, null, arg, false);
10835 isLoading : function(){
10836 return this.trans ? true : false;
10840 * Abort the current server request.
10842 abort : function(){
10843 if(this.isLoading()){
10844 this.destroyTrans(this.trans);
10849 destroyTrans : function(trans, isLoaded){
10850 this.head.removeChild(document.getElementById(trans.scriptId));
10851 clearTimeout(trans.timeoutId);
10853 window[trans.cb] = undefined;
10855 delete window[trans.cb];
10858 // if hasn't been loaded, wait for load to remove it to prevent script error
10859 window[trans.cb] = function(){
10860 window[trans.cb] = undefined;
10862 delete window[trans.cb];
10869 handleResponse : function(o, trans){
10870 this.trans = false;
10871 this.destroyTrans(trans, true);
10874 result = trans.reader.readRecords(o);
10876 this.fireEvent("loadexception", this, o, trans.arg, e);
10877 trans.callback.call(trans.scope||window, null, trans.arg, false);
10880 this.fireEvent("load", this, o, trans.arg);
10881 trans.callback.call(trans.scope||window, result, trans.arg, true);
10885 handleFailure : function(trans){
10886 this.trans = false;
10887 this.destroyTrans(trans, false);
10888 this.fireEvent("loadexception", this, null, trans.arg);
10889 trans.callback.call(trans.scope||window, null, trans.arg, false);
10893 * Ext JS Library 1.1.1
10894 * Copyright(c) 2006-2007, Ext JS, LLC.
10896 * Originally Released Under LGPL - original licence link has changed is not relivant.
10899 * <script type="text/javascript">
10903 * @class Roo.data.JsonReader
10904 * @extends Roo.data.DataReader
10905 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10906 * based on mappings in a provided Roo.data.Record constructor.
10908 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10909 * in the reply previously.
10914 var RecordDef = Roo.data.Record.create([
10915 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10916 {name: 'occupation'} // This field will use "occupation" as the mapping.
10918 var myReader = new Roo.data.JsonReader({
10919 totalProperty: "results", // The property which contains the total dataset size (optional)
10920 root: "rows", // The property which contains an Array of row objects
10921 id: "id" // The property within each row object that provides an ID for the record (optional)
10925 * This would consume a JSON file like this:
10927 { 'results': 2, 'rows': [
10928 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10929 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10932 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10933 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10934 * paged from the remote server.
10935 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10936 * @cfg {String} root name of the property which contains the Array of row objects.
10937 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10938 * @cfg {Array} fields Array of field definition objects
10940 * Create a new JsonReader
10941 * @param {Object} meta Metadata configuration options
10942 * @param {Object} recordType Either an Array of field definition objects,
10943 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10945 Roo.data.JsonReader = function(meta, recordType){
10948 // set some defaults:
10949 Roo.applyIf(meta, {
10950 totalProperty: 'total',
10951 successProperty : 'success',
10956 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10958 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10961 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10962 * Used by Store query builder to append _requestMeta to params.
10965 metaFromRemote : false,
10967 * This method is only used by a DataProxy which has retrieved data from a remote server.
10968 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10969 * @return {Object} data A data block which is used by an Roo.data.Store object as
10970 * a cache of Roo.data.Records.
10972 read : function(response){
10973 var json = response.responseText;
10975 var o = /* eval:var:o */ eval("("+json+")");
10977 throw {message: "JsonReader.read: Json object not found"};
10983 this.metaFromRemote = true;
10984 this.meta = o.metaData;
10985 this.recordType = Roo.data.Record.create(o.metaData.fields);
10986 this.onMetaChange(this.meta, this.recordType, o);
10988 return this.readRecords(o);
10991 // private function a store will implement
10992 onMetaChange : function(meta, recordType, o){
10999 simpleAccess: function(obj, subsc) {
11006 getJsonAccessor: function(){
11008 return function(expr) {
11010 return(re.test(expr))
11011 ? new Function("obj", "return obj." + expr)
11016 return Roo.emptyFn;
11021 * Create a data block containing Roo.data.Records from an XML document.
11022 * @param {Object} o An object which contains an Array of row objects in the property specified
11023 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11024 * which contains the total size of the dataset.
11025 * @return {Object} data A data block which is used by an Roo.data.Store object as
11026 * a cache of Roo.data.Records.
11028 readRecords : function(o){
11030 * After any data loads, the raw JSON data is available for further custom processing.
11034 var s = this.meta, Record = this.recordType,
11035 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11037 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11039 if(s.totalProperty) {
11040 this.getTotal = this.getJsonAccessor(s.totalProperty);
11042 if(s.successProperty) {
11043 this.getSuccess = this.getJsonAccessor(s.successProperty);
11045 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11047 var g = this.getJsonAccessor(s.id);
11048 this.getId = function(rec) {
11050 return (r === undefined || r === "") ? null : r;
11053 this.getId = function(){return null;};
11056 for(var jj = 0; jj < fl; jj++){
11058 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11059 this.ef[jj] = this.getJsonAccessor(map);
11063 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11064 if(s.totalProperty){
11065 var vt = parseInt(this.getTotal(o), 10);
11070 if(s.successProperty){
11071 var vs = this.getSuccess(o);
11072 if(vs === false || vs === 'false'){
11077 for(var i = 0; i < c; i++){
11080 var id = this.getId(n);
11081 for(var j = 0; j < fl; j++){
11083 var v = this.ef[j](n);
11085 Roo.log('missing convert for ' + f.name);
11089 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11091 var record = new Record(values, id);
11093 records[i] = record;
11099 totalRecords : totalRecords
11104 * Ext JS Library 1.1.1
11105 * Copyright(c) 2006-2007, Ext JS, LLC.
11107 * Originally Released Under LGPL - original licence link has changed is not relivant.
11110 * <script type="text/javascript">
11114 * @class Roo.data.ArrayReader
11115 * @extends Roo.data.DataReader
11116 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11117 * Each element of that Array represents a row of data fields. The
11118 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11119 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11123 var RecordDef = Roo.data.Record.create([
11124 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11125 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11127 var myReader = new Roo.data.ArrayReader({
11128 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11132 * This would consume an Array like this:
11134 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11136 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11138 * Create a new JsonReader
11139 * @param {Object} meta Metadata configuration options.
11140 * @param {Object} recordType Either an Array of field definition objects
11141 * as specified to {@link Roo.data.Record#create},
11142 * or an {@link Roo.data.Record} object
11143 * created using {@link Roo.data.Record#create}.
11145 Roo.data.ArrayReader = function(meta, recordType){
11146 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11149 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11151 * Create a data block containing Roo.data.Records from an XML document.
11152 * @param {Object} o An Array of row objects which represents the dataset.
11153 * @return {Object} data A data block which is used by an Roo.data.Store object as
11154 * a cache of Roo.data.Records.
11156 readRecords : function(o){
11157 var sid = this.meta ? this.meta.id : null;
11158 var recordType = this.recordType, fields = recordType.prototype.fields;
11161 for(var i = 0; i < root.length; i++){
11164 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11165 for(var j = 0, jlen = fields.length; j < jlen; j++){
11166 var f = fields.items[j];
11167 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11168 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11170 values[f.name] = v;
11172 var record = new recordType(values, id);
11174 records[records.length] = record;
11178 totalRecords : records.length
11187 * @class Roo.bootstrap.ComboBox
11188 * @extends Roo.bootstrap.TriggerField
11189 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11190 * @cfg {Boolean} append (true|false) default false
11191 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11192 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11193 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11194 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11195 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11196 * @cfg {Boolean} animate default true
11197 * @cfg {Boolean} emptyResultText only for touch device
11199 * Create a new ComboBox.
11200 * @param {Object} config Configuration options
11202 Roo.bootstrap.ComboBox = function(config){
11203 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11207 * Fires when the dropdown list is expanded
11208 * @param {Roo.bootstrap.ComboBox} combo This combo box
11213 * Fires when the dropdown list is collapsed
11214 * @param {Roo.bootstrap.ComboBox} combo This combo box
11218 * @event beforeselect
11219 * Fires before a list item is selected. Return false to cancel the selection.
11220 * @param {Roo.bootstrap.ComboBox} combo This combo box
11221 * @param {Roo.data.Record} record The data record returned from the underlying store
11222 * @param {Number} index The index of the selected item in the dropdown list
11224 'beforeselect' : true,
11227 * Fires when a list item is selected
11228 * @param {Roo.bootstrap.ComboBox} combo This combo box
11229 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11230 * @param {Number} index The index of the selected item in the dropdown list
11234 * @event beforequery
11235 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11236 * The event object passed has these properties:
11237 * @param {Roo.bootstrap.ComboBox} combo This combo box
11238 * @param {String} query The query
11239 * @param {Boolean} forceAll true to force "all" query
11240 * @param {Boolean} cancel true to cancel the query
11241 * @param {Object} e The query event object
11243 'beforequery': true,
11246 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11247 * @param {Roo.bootstrap.ComboBox} combo This combo box
11252 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11253 * @param {Roo.bootstrap.ComboBox} combo This combo box
11254 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11259 * Fires when the remove value from the combobox array
11260 * @param {Roo.bootstrap.ComboBox} combo This combo box
11264 * @event specialfilter
11265 * Fires when specialfilter
11266 * @param {Roo.bootstrap.ComboBox} combo This combo box
11268 'specialfilter' : true
11273 this.tickItems = [];
11275 this.selectedIndex = -1;
11276 if(this.mode == 'local'){
11277 if(config.queryDelay === undefined){
11278 this.queryDelay = 10;
11280 if(config.minChars === undefined){
11286 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11289 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11290 * rendering into an Roo.Editor, defaults to false)
11293 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11294 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11297 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11300 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11301 * the dropdown list (defaults to undefined, with no header element)
11305 * @cfg {String/Roo.Template} tpl The template to use to render the output
11309 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11311 listWidth: undefined,
11313 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11314 * mode = 'remote' or 'text' if mode = 'local')
11316 displayField: undefined,
11319 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11320 * mode = 'remote' or 'value' if mode = 'local').
11321 * Note: use of a valueField requires the user make a selection
11322 * in order for a value to be mapped.
11324 valueField: undefined,
11328 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11329 * field's data value (defaults to the underlying DOM element's name)
11331 hiddenName: undefined,
11333 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11337 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11339 selectedClass: 'active',
11342 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11346 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11347 * anchor positions (defaults to 'tl-bl')
11349 listAlign: 'tl-bl?',
11351 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11355 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11356 * query specified by the allQuery config option (defaults to 'query')
11358 triggerAction: 'query',
11360 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11361 * (defaults to 4, does not apply if editable = false)
11365 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11366 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11370 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11371 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11375 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11376 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11380 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11381 * when editable = true (defaults to false)
11383 selectOnFocus:false,
11385 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11387 queryParam: 'query',
11389 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11390 * when mode = 'remote' (defaults to 'Loading...')
11392 loadingText: 'Loading...',
11394 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11398 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11402 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11403 * traditional select (defaults to true)
11407 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11411 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11415 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11416 * listWidth has a higher value)
11420 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11421 * allow the user to set arbitrary text into the field (defaults to false)
11423 forceSelection:false,
11425 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11426 * if typeAhead = true (defaults to 250)
11428 typeAheadDelay : 250,
11430 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11431 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11433 valueNotFoundText : undefined,
11435 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11437 blockFocus : false,
11440 * @cfg {Boolean} disableClear Disable showing of clear button.
11442 disableClear : false,
11444 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11446 alwaysQuery : false,
11449 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11454 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11456 invalidClass : "has-warning",
11459 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11461 validClass : "has-success",
11464 * @cfg {Boolean} specialFilter (true|false) special filter default false
11466 specialFilter : false,
11469 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11471 mobileTouchView : true,
11483 btnPosition : 'right',
11484 triggerList : true,
11485 showToggleBtn : true,
11487 emptyResultText: 'Empty',
11488 // element that contains real text value.. (when hidden is used..)
11490 getAutoCreate : function()
11498 if(Roo.isTouch && this.mobileTouchView){
11499 cfg = this.getAutoCreateTouchView();
11506 if(!this.tickable){
11507 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11512 * ComboBox with tickable selections
11515 var align = this.labelAlign || this.parentLabelAlign();
11518 cls : 'form-group roo-combobox-tickable' //input-group
11523 cls : 'tickable-buttons',
11528 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11535 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11542 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11549 buttons.cn.unshift({
11551 cls: 'select2-search-field-input'
11557 Roo.each(buttons.cn, function(c){
11559 c.cls += ' btn-' + _this.size;
11562 if (_this.disabled) {
11573 cls: 'form-hidden-field'
11577 cls: 'select2-choices',
11581 cls: 'select2-search-field',
11593 cls: 'select2-container input-group select2-container-multi',
11598 // cls: 'typeahead typeahead-long dropdown-menu',
11599 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11604 if(this.hasFeedback && !this.allowBlank){
11608 cls: 'glyphicon form-control-feedback'
11611 combobox.cn.push(feedback);
11614 if (align ==='left' && this.fieldLabel.length) {
11616 Roo.log("left and has label");
11622 cls : 'control-label col-sm-' + this.labelWidth,
11623 html : this.fieldLabel
11627 cls : "col-sm-" + (12 - this.labelWidth),
11634 } else if ( this.fieldLabel.length) {
11640 //cls : 'input-group-addon',
11641 html : this.fieldLabel
11651 Roo.log(" no label && no align");
11658 ['xs','sm','md','lg'].map(function(size){
11659 if (settings[size]) {
11660 cfg.cls += ' col-' + size + '-' + settings[size];
11668 _initEventsCalled : false,
11671 initEvents: function()
11674 if (this._initEventsCalled) { // as we call render... prevent looping...
11677 this._initEventsCalled = true;
11680 throw "can not find store for combo";
11683 this.store = Roo.factory(this.store, Roo.data);
11685 // if we are building from html. then this element is so complex, that we can not really
11686 // use the rendered HTML.
11687 // so we have to trash and replace the previous code.
11688 if (Roo.XComponent.build_from_html) {
11690 // remove this element....
11691 var e = this.el.dom, k=0;
11692 while (e ) { e = e.previousSibling; ++k;}
11697 this.rendered = false;
11699 this.render(this.parent().getChildContainer(true), k);
11710 if(Roo.isTouch && this.mobileTouchView){
11711 this.initTouchView();
11716 this.initTickableEvents();
11720 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11722 if(this.hiddenName){
11724 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11726 this.hiddenField.dom.value =
11727 this.hiddenValue !== undefined ? this.hiddenValue :
11728 this.value !== undefined ? this.value : '';
11730 // prevent input submission
11731 this.el.dom.removeAttribute('name');
11732 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11737 // this.el.dom.setAttribute('autocomplete', 'off');
11740 var cls = 'x-combo-list';
11742 //this.list = new Roo.Layer({
11743 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11749 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11750 _this.list.setWidth(lw);
11753 this.list.on('mouseover', this.onViewOver, this);
11754 this.list.on('mousemove', this.onViewMove, this);
11756 this.list.on('scroll', this.onViewScroll, this);
11759 this.list.swallowEvent('mousewheel');
11760 this.assetHeight = 0;
11763 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11764 this.assetHeight += this.header.getHeight();
11767 this.innerList = this.list.createChild({cls:cls+'-inner'});
11768 this.innerList.on('mouseover', this.onViewOver, this);
11769 this.innerList.on('mousemove', this.onViewMove, this);
11770 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11772 if(this.allowBlank && !this.pageSize && !this.disableClear){
11773 this.footer = this.list.createChild({cls:cls+'-ft'});
11774 this.pageTb = new Roo.Toolbar(this.footer);
11778 this.footer = this.list.createChild({cls:cls+'-ft'});
11779 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11780 {pageSize: this.pageSize});
11784 if (this.pageTb && this.allowBlank && !this.disableClear) {
11786 this.pageTb.add(new Roo.Toolbar.Fill(), {
11787 cls: 'x-btn-icon x-btn-clear',
11789 handler: function()
11792 _this.clearValue();
11793 _this.onSelect(false, -1);
11798 this.assetHeight += this.footer.getHeight();
11803 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11806 this.view = new Roo.View(this.list, this.tpl, {
11807 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11809 //this.view.wrapEl.setDisplayed(false);
11810 this.view.on('click', this.onViewClick, this);
11814 this.store.on('beforeload', this.onBeforeLoad, this);
11815 this.store.on('load', this.onLoad, this);
11816 this.store.on('loadexception', this.onLoadException, this);
11818 if(this.resizable){
11819 this.resizer = new Roo.Resizable(this.list, {
11820 pinned:true, handles:'se'
11822 this.resizer.on('resize', function(r, w, h){
11823 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11824 this.listWidth = w;
11825 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11826 this.restrictHeight();
11828 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11831 if(!this.editable){
11832 this.editable = true;
11833 this.setEditable(false);
11838 if (typeof(this.events.add.listeners) != 'undefined') {
11840 this.addicon = this.wrap.createChild(
11841 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11843 this.addicon.on('click', function(e) {
11844 this.fireEvent('add', this);
11847 if (typeof(this.events.edit.listeners) != 'undefined') {
11849 this.editicon = this.wrap.createChild(
11850 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11851 if (this.addicon) {
11852 this.editicon.setStyle('margin-left', '40px');
11854 this.editicon.on('click', function(e) {
11856 // we fire even if inothing is selected..
11857 this.fireEvent('edit', this, this.lastData );
11863 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11864 "up" : function(e){
11865 this.inKeyMode = true;
11869 "down" : function(e){
11870 if(!this.isExpanded()){
11871 this.onTriggerClick();
11873 this.inKeyMode = true;
11878 "enter" : function(e){
11879 // this.onViewClick();
11883 if(this.fireEvent("specialkey", this, e)){
11884 this.onViewClick(false);
11890 "esc" : function(e){
11894 "tab" : function(e){
11897 if(this.fireEvent("specialkey", this, e)){
11898 this.onViewClick(false);
11906 doRelay : function(foo, bar, hname){
11907 if(hname == 'down' || this.scope.isExpanded()){
11908 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11917 this.queryDelay = Math.max(this.queryDelay || 10,
11918 this.mode == 'local' ? 10 : 250);
11921 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11923 if(this.typeAhead){
11924 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11926 if(this.editable !== false){
11927 this.inputEl().on("keyup", this.onKeyUp, this);
11929 if(this.forceSelection){
11930 this.inputEl().on('blur', this.doForce, this);
11934 this.choices = this.el.select('ul.select2-choices', true).first();
11935 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11939 initTickableEvents: function()
11943 if(this.hiddenName){
11945 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11947 this.hiddenField.dom.value =
11948 this.hiddenValue !== undefined ? this.hiddenValue :
11949 this.value !== undefined ? this.value : '';
11951 // prevent input submission
11952 this.el.dom.removeAttribute('name');
11953 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11958 // this.list = this.el.select('ul.dropdown-menu',true).first();
11960 this.choices = this.el.select('ul.select2-choices', true).first();
11961 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11962 if(this.triggerList){
11963 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11966 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11967 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11969 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11970 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11972 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11973 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11975 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11976 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11977 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11980 this.cancelBtn.hide();
11985 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11986 _this.list.setWidth(lw);
11989 this.list.on('mouseover', this.onViewOver, this);
11990 this.list.on('mousemove', this.onViewMove, this);
11992 this.list.on('scroll', this.onViewScroll, this);
11995 this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11998 this.view = new Roo.View(this.list, this.tpl, {
11999 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12002 //this.view.wrapEl.setDisplayed(false);
12003 this.view.on('click', this.onViewClick, this);
12007 this.store.on('beforeload', this.onBeforeLoad, this);
12008 this.store.on('load', this.onLoad, this);
12009 this.store.on('loadexception', this.onLoadException, this);
12012 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12013 "up" : function(e){
12014 this.inKeyMode = true;
12018 "down" : function(e){
12019 this.inKeyMode = true;
12023 "enter" : function(e){
12024 if(this.fireEvent("specialkey", this, e)){
12025 this.onViewClick(false);
12031 "esc" : function(e){
12032 this.onTickableFooterButtonClick(e, false, false);
12035 "tab" : function(e){
12036 this.fireEvent("specialkey", this, e);
12038 this.onTickableFooterButtonClick(e, false, false);
12045 doRelay : function(e, fn, key){
12046 if(this.scope.isExpanded()){
12047 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12056 this.queryDelay = Math.max(this.queryDelay || 10,
12057 this.mode == 'local' ? 10 : 250);
12060 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12062 if(this.typeAhead){
12063 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12066 if(this.editable !== false){
12067 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12072 onDestroy : function(){
12074 this.view.setStore(null);
12075 this.view.el.removeAllListeners();
12076 this.view.el.remove();
12077 this.view.purgeListeners();
12080 this.list.dom.innerHTML = '';
12084 this.store.un('beforeload', this.onBeforeLoad, this);
12085 this.store.un('load', this.onLoad, this);
12086 this.store.un('loadexception', this.onLoadException, this);
12088 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12092 fireKey : function(e){
12093 if(e.isNavKeyPress() && !this.list.isVisible()){
12094 this.fireEvent("specialkey", this, e);
12099 onResize: function(w, h){
12100 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12102 // if(typeof w != 'number'){
12103 // // we do not handle it!?!?
12106 // var tw = this.trigger.getWidth();
12107 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12108 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12110 // this.inputEl().setWidth( this.adjustWidth('input', x));
12112 // //this.trigger.setStyle('left', x+'px');
12114 // if(this.list && this.listWidth === undefined){
12115 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12116 // this.list.setWidth(lw);
12117 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12125 * Allow or prevent the user from directly editing the field text. If false is passed,
12126 * the user will only be able to select from the items defined in the dropdown list. This method
12127 * is the runtime equivalent of setting the 'editable' config option at config time.
12128 * @param {Boolean} value True to allow the user to directly edit the field text
12130 setEditable : function(value){
12131 if(value == this.editable){
12134 this.editable = value;
12136 this.inputEl().dom.setAttribute('readOnly', true);
12137 this.inputEl().on('mousedown', this.onTriggerClick, this);
12138 this.inputEl().addClass('x-combo-noedit');
12140 this.inputEl().dom.setAttribute('readOnly', false);
12141 this.inputEl().un('mousedown', this.onTriggerClick, this);
12142 this.inputEl().removeClass('x-combo-noedit');
12148 onBeforeLoad : function(combo,opts){
12149 if(!this.hasFocus){
12153 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12155 this.restrictHeight();
12156 this.selectedIndex = -1;
12160 onLoad : function(){
12162 this.hasQuery = false;
12164 if(!this.hasFocus){
12168 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12169 this.loading.hide();
12172 if(this.store.getCount() > 0){
12174 this.restrictHeight();
12175 if(this.lastQuery == this.allQuery){
12176 if(this.editable && !this.tickable){
12177 this.inputEl().dom.select();
12181 !this.selectByValue(this.value, true) &&
12184 !this.store.lastOptions ||
12185 typeof(this.store.lastOptions.add) == 'undefined' ||
12186 this.store.lastOptions.add != true
12189 this.select(0, true);
12192 if(this.autoFocus){
12195 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12196 this.taTask.delay(this.typeAheadDelay);
12200 this.onEmptyResults();
12206 onLoadException : function()
12208 this.hasQuery = false;
12210 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12211 this.loading.hide();
12214 if(this.tickable && this.editable){
12220 Roo.log(this.store.reader.jsonData);
12221 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12223 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12229 onTypeAhead : function(){
12230 if(this.store.getCount() > 0){
12231 var r = this.store.getAt(0);
12232 var newValue = r.data[this.displayField];
12233 var len = newValue.length;
12234 var selStart = this.getRawValue().length;
12236 if(selStart != len){
12237 this.setRawValue(newValue);
12238 this.selectText(selStart, newValue.length);
12244 onSelect : function(record, index){
12246 if(this.fireEvent('beforeselect', this, record, index) !== false){
12248 this.setFromData(index > -1 ? record.data : false);
12251 this.fireEvent('select', this, record, index);
12256 * Returns the currently selected field value or empty string if no value is set.
12257 * @return {String} value The selected value
12259 getValue : function(){
12262 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12265 if(this.valueField){
12266 return typeof this.value != 'undefined' ? this.value : '';
12268 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12273 * Clears any text/value currently set in the field
12275 clearValue : function(){
12276 if(this.hiddenField){
12277 this.hiddenField.dom.value = '';
12280 this.setRawValue('');
12281 this.lastSelectionText = '';
12282 this.lastData = false;
12284 var close = this.closeTriggerEl();
12293 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12294 * will be displayed in the field. If the value does not match the data value of an existing item,
12295 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12296 * Otherwise the field will be blank (although the value will still be set).
12297 * @param {String} value The value to match
12299 setValue : function(v){
12306 if(this.valueField){
12307 var r = this.findRecord(this.valueField, v);
12309 text = r.data[this.displayField];
12310 }else if(this.valueNotFoundText !== undefined){
12311 text = this.valueNotFoundText;
12314 this.lastSelectionText = text;
12315 if(this.hiddenField){
12316 this.hiddenField.dom.value = v;
12318 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12321 var close = this.closeTriggerEl();
12324 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12328 * @property {Object} the last set data for the element
12333 * Sets the value of the field based on a object which is related to the record format for the store.
12334 * @param {Object} value the value to set as. or false on reset?
12336 setFromData : function(o){
12343 var dv = ''; // display value
12344 var vv = ''; // value value..
12346 if (this.displayField) {
12347 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12349 // this is an error condition!!!
12350 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12353 if(this.valueField){
12354 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12357 var close = this.closeTriggerEl();
12360 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12363 if(this.hiddenField){
12364 this.hiddenField.dom.value = vv;
12366 this.lastSelectionText = dv;
12367 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12371 // no hidden field.. - we store the value in 'value', but still display
12372 // display field!!!!
12373 this.lastSelectionText = dv;
12374 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12381 reset : function(){
12382 // overridden so that last data is reset..
12389 this.setValue(this.originalValue);
12390 this.clearInvalid();
12391 this.lastData = false;
12393 this.view.clearSelections();
12397 findRecord : function(prop, value){
12399 if(this.store.getCount() > 0){
12400 this.store.each(function(r){
12401 if(r.data[prop] == value){
12411 getName: function()
12413 // returns hidden if it's set..
12414 if (!this.rendered) {return ''};
12415 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12419 onViewMove : function(e, t){
12420 this.inKeyMode = false;
12424 onViewOver : function(e, t){
12425 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12428 var item = this.view.findItemFromChild(t);
12431 var index = this.view.indexOf(item);
12432 this.select(index, false);
12437 onViewClick : function(view, doFocus, el, e)
12439 var index = this.view.getSelectedIndexes()[0];
12441 var r = this.store.getAt(index);
12445 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12452 Roo.each(this.tickItems, function(v,k){
12454 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12455 _this.tickItems.splice(k, 1);
12457 if(typeof(e) == 'undefined' && view == false){
12458 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12470 this.tickItems.push(r.data);
12472 if(typeof(e) == 'undefined' && view == false){
12473 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12480 this.onSelect(r, index);
12482 if(doFocus !== false && !this.blockFocus){
12483 this.inputEl().focus();
12488 restrictHeight : function(){
12489 //this.innerList.dom.style.height = '';
12490 //var inner = this.innerList.dom;
12491 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12492 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12493 //this.list.beginUpdate();
12494 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12495 this.list.alignTo(this.inputEl(), this.listAlign);
12496 this.list.alignTo(this.inputEl(), this.listAlign);
12497 //this.list.endUpdate();
12501 onEmptyResults : function(){
12503 if(this.tickable && this.editable){
12504 this.restrictHeight();
12512 * Returns true if the dropdown list is expanded, else false.
12514 isExpanded : function(){
12515 return this.list.isVisible();
12519 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12520 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12521 * @param {String} value The data value of the item to select
12522 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12523 * selected item if it is not currently in view (defaults to true)
12524 * @return {Boolean} True if the value matched an item in the list, else false
12526 selectByValue : function(v, scrollIntoView){
12527 if(v !== undefined && v !== null){
12528 var r = this.findRecord(this.valueField || this.displayField, v);
12530 this.select(this.store.indexOf(r), scrollIntoView);
12538 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12539 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12540 * @param {Number} index The zero-based index of the list item to select
12541 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12542 * selected item if it is not currently in view (defaults to true)
12544 select : function(index, scrollIntoView){
12545 this.selectedIndex = index;
12546 this.view.select(index);
12547 if(scrollIntoView !== false){
12548 var el = this.view.getNode(index);
12550 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12553 this.list.scrollChildIntoView(el, false);
12559 selectNext : function(){
12560 var ct = this.store.getCount();
12562 if(this.selectedIndex == -1){
12564 }else if(this.selectedIndex < ct-1){
12565 this.select(this.selectedIndex+1);
12571 selectPrev : function(){
12572 var ct = this.store.getCount();
12574 if(this.selectedIndex == -1){
12576 }else if(this.selectedIndex != 0){
12577 this.select(this.selectedIndex-1);
12583 onKeyUp : function(e){
12584 if(this.editable !== false && !e.isSpecialKey()){
12585 this.lastKey = e.getKey();
12586 this.dqTask.delay(this.queryDelay);
12591 validateBlur : function(){
12592 return !this.list || !this.list.isVisible();
12596 initQuery : function(){
12598 var v = this.getRawValue();
12600 if(this.tickable && this.editable){
12601 v = this.tickableInputEl().getValue();
12608 doForce : function(){
12609 if(this.inputEl().dom.value.length > 0){
12610 this.inputEl().dom.value =
12611 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12617 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12618 * query allowing the query action to be canceled if needed.
12619 * @param {String} query The SQL query to execute
12620 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12621 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12622 * saved in the current store (defaults to false)
12624 doQuery : function(q, forceAll){
12626 if(q === undefined || q === null){
12631 forceAll: forceAll,
12635 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12640 forceAll = qe.forceAll;
12641 if(forceAll === true || (q.length >= this.minChars)){
12643 this.hasQuery = true;
12645 if(this.lastQuery != q || this.alwaysQuery){
12646 this.lastQuery = q;
12647 if(this.mode == 'local'){
12648 this.selectedIndex = -1;
12650 this.store.clearFilter();
12653 if(this.specialFilter){
12654 this.fireEvent('specialfilter', this);
12659 this.store.filter(this.displayField, q);
12662 this.store.fireEvent("datachanged", this.store);
12669 this.store.baseParams[this.queryParam] = q;
12671 var options = {params : this.getParams(q)};
12674 options.add = true;
12675 options.params.start = this.page * this.pageSize;
12678 this.store.load(options);
12681 * this code will make the page width larger, at the beginning, the list not align correctly,
12682 * we should expand the list on onLoad
12683 * so command out it
12688 this.selectedIndex = -1;
12693 this.loadNext = false;
12697 getParams : function(q){
12699 //p[this.queryParam] = q;
12703 p.limit = this.pageSize;
12709 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12711 collapse : function(){
12712 if(!this.isExpanded()){
12719 this.hasFocus = false;
12721 this.cancelBtn.hide();
12722 this.trigger.show();
12725 this.tickableInputEl().dom.value = '';
12726 this.tickableInputEl().blur();
12731 Roo.get(document).un('mousedown', this.collapseIf, this);
12732 Roo.get(document).un('mousewheel', this.collapseIf, this);
12733 if (!this.editable) {
12734 Roo.get(document).un('keydown', this.listKeyPress, this);
12736 this.fireEvent('collapse', this);
12740 collapseIf : function(e){
12741 var in_combo = e.within(this.el);
12742 var in_list = e.within(this.list);
12743 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12745 if (in_combo || in_list || is_list) {
12746 //e.stopPropagation();
12751 this.onTickableFooterButtonClick(e, false, false);
12759 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12761 expand : function(){
12763 if(this.isExpanded() || !this.hasFocus){
12767 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12768 this.list.setWidth(lw);
12775 this.restrictHeight();
12779 this.tickItems = Roo.apply([], this.item);
12782 this.cancelBtn.show();
12783 this.trigger.hide();
12786 this.tickableInputEl().focus();
12791 Roo.get(document).on('mousedown', this.collapseIf, this);
12792 Roo.get(document).on('mousewheel', this.collapseIf, this);
12793 if (!this.editable) {
12794 Roo.get(document).on('keydown', this.listKeyPress, this);
12797 this.fireEvent('expand', this);
12801 // Implements the default empty TriggerField.onTriggerClick function
12802 onTriggerClick : function(e)
12804 Roo.log('trigger click');
12806 if(this.disabled || !this.triggerList){
12811 this.loadNext = false;
12813 if(this.isExpanded()){
12815 if (!this.blockFocus) {
12816 this.inputEl().focus();
12820 this.hasFocus = true;
12821 if(this.triggerAction == 'all') {
12822 this.doQuery(this.allQuery, true);
12824 this.doQuery(this.getRawValue());
12826 if (!this.blockFocus) {
12827 this.inputEl().focus();
12832 onTickableTriggerClick : function(e)
12839 this.loadNext = false;
12840 this.hasFocus = true;
12842 if(this.triggerAction == 'all') {
12843 this.doQuery(this.allQuery, true);
12845 this.doQuery(this.getRawValue());
12849 onSearchFieldClick : function(e)
12851 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12852 this.onTickableFooterButtonClick(e, false, false);
12856 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12861 this.loadNext = false;
12862 this.hasFocus = true;
12864 if(this.triggerAction == 'all') {
12865 this.doQuery(this.allQuery, true);
12867 this.doQuery(this.getRawValue());
12871 listKeyPress : function(e)
12873 //Roo.log('listkeypress');
12874 // scroll to first matching element based on key pres..
12875 if (e.isSpecialKey()) {
12878 var k = String.fromCharCode(e.getKey()).toUpperCase();
12881 var csel = this.view.getSelectedNodes();
12882 var cselitem = false;
12884 var ix = this.view.indexOf(csel[0]);
12885 cselitem = this.store.getAt(ix);
12886 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12892 this.store.each(function(v) {
12894 // start at existing selection.
12895 if (cselitem.id == v.id) {
12901 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12902 match = this.store.indexOf(v);
12908 if (match === false) {
12909 return true; // no more action?
12912 this.view.select(match);
12913 var sn = Roo.get(this.view.getSelectedNodes()[0])
12914 sn.scrollIntoView(sn.dom.parentNode, false);
12917 onViewScroll : function(e, t){
12919 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
12923 this.hasQuery = true;
12925 this.loading = this.list.select('.loading', true).first();
12927 if(this.loading === null){
12928 this.list.createChild({
12930 cls: 'loading select2-more-results select2-active',
12931 html: 'Loading more results...'
12934 this.loading = this.list.select('.loading', true).first();
12936 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12938 this.loading.hide();
12941 this.loading.show();
12946 this.loadNext = true;
12948 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12953 addItem : function(o)
12955 var dv = ''; // display value
12957 if (this.displayField) {
12958 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12960 // this is an error condition!!!
12961 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12968 var choice = this.choices.createChild({
12970 cls: 'select2-search-choice',
12979 cls: 'select2-search-choice-close',
12984 }, this.searchField);
12986 var close = choice.select('a.select2-search-choice-close', true).first()
12988 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12996 this.inputEl().dom.value = '';
13001 onRemoveItem : function(e, _self, o)
13003 e.preventDefault();
13005 this.lastItem = Roo.apply([], this.item);
13007 var index = this.item.indexOf(o.data) * 1;
13010 Roo.log('not this item?!');
13014 this.item.splice(index, 1);
13019 this.fireEvent('remove', this, e);
13025 syncValue : function()
13027 if(!this.item.length){
13034 Roo.each(this.item, function(i){
13035 if(_this.valueField){
13036 value.push(i[_this.valueField]);
13043 this.value = value.join(',');
13045 if(this.hiddenField){
13046 this.hiddenField.dom.value = this.value;
13049 this.store.fireEvent("datachanged", this.store);
13052 clearItem : function()
13054 if(!this.multiple){
13060 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13068 if(this.tickable && !Roo.isTouch){
13069 this.view.refresh();
13073 inputEl: function ()
13075 if(Roo.isTouch && this.mobileTouchView){
13076 return this.el.select('input.form-control',true).first();
13080 return this.searchField;
13083 return this.el.select('input.form-control',true).first();
13087 onTickableFooterButtonClick : function(e, btn, el)
13089 e.preventDefault();
13091 this.lastItem = Roo.apply([], this.item);
13093 if(btn && btn.name == 'cancel'){
13094 this.tickItems = Roo.apply([], this.item);
13103 Roo.each(this.tickItems, function(o){
13111 validate : function()
13113 var v = this.getRawValue();
13116 v = this.getValue();
13119 if(this.disabled || this.allowBlank || v.length){
13124 this.markInvalid();
13128 tickableInputEl : function()
13130 if(!this.tickable || !this.editable){
13131 return this.inputEl();
13134 return this.inputEl().select('.select2-search-field-input', true).first();
13138 getAutoCreateTouchView : function()
13143 cls: 'form-group' //input-group
13149 type : this.inputType,
13150 cls : 'form-control x-combo-noedit',
13151 autocomplete: 'new-password',
13152 placeholder : this.placeholder || '',
13157 input.name = this.name;
13161 input.cls += ' input-' + this.size;
13164 if (this.disabled) {
13165 input.disabled = true;
13176 inputblock.cls += ' input-group';
13178 inputblock.cn.unshift({
13180 cls : 'input-group-addon',
13185 if(this.removable && !this.multiple){
13186 inputblock.cls += ' roo-removable';
13188 inputblock.cn.push({
13191 cls : 'roo-combo-removable-btn close'
13195 if(this.hasFeedback && !this.allowBlank){
13197 inputblock.cls += ' has-feedback';
13199 inputblock.cn.push({
13201 cls: 'glyphicon form-control-feedback'
13208 inputblock.cls += (this.before) ? '' : ' input-group';
13210 inputblock.cn.push({
13212 cls : 'input-group-addon',
13223 cls: 'form-hidden-field'
13237 cls: 'form-hidden-field'
13241 cls: 'select2-choices',
13245 cls: 'select2-search-field',
13258 cls: 'select2-container input-group',
13265 combobox.cls += ' select2-container-multi';
13268 var align = this.labelAlign || this.parentLabelAlign();
13272 if(this.fieldLabel.length){
13274 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13275 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13280 cls : 'control-label ' + lw,
13281 html : this.fieldLabel
13293 var settings = this;
13295 ['xs','sm','md','lg'].map(function(size){
13296 if (settings[size]) {
13297 cfg.cls += ' col-' + size + '-' + settings[size];
13304 initTouchView : function()
13306 this.renderTouchView();
13308 this.touchViewEl.on('scroll', function(){
13309 this.el.dom.scrollTop = 0;
13312 this.inputEl().on("click", this.showTouchView, this);
13313 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13314 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13316 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13318 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13319 this.store.on('load', this.onTouchViewLoad, this);
13320 this.store.on('loadexception', this.onTouchViewLoadException, this);
13322 if(this.hiddenName){
13324 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13326 this.hiddenField.dom.value =
13327 this.hiddenValue !== undefined ? this.hiddenValue :
13328 this.value !== undefined ? this.value : '';
13330 this.el.dom.removeAttribute('name');
13331 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13335 this.choices = this.el.select('ul.select2-choices', true).first();
13336 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13339 if(this.removable && !this.multiple){
13340 var close = this.closeTriggerEl();
13342 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13343 close.on('click', this.removeBtnClick, this, close);
13352 renderTouchView : function()
13354 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13355 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13357 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13358 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13360 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13361 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13362 this.touchViewBodyEl.setStyle('overflow', 'auto');
13364 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13365 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13367 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13368 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13372 showTouchView : function()
13374 this.touchViewHeaderEl.hide();
13376 if(this.fieldLabel.length){
13377 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13378 this.touchViewHeaderEl.show();
13381 this.touchViewEl.show();
13383 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13384 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13386 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13388 if(this.fieldLabel.length){
13389 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13392 this.touchViewBodyEl.setHeight(bodyHeight);
13396 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13398 this.touchViewEl.addClass('in');
13401 this.doTouchViewQuery();
13405 hideTouchView : function()
13407 this.touchViewEl.removeClass('in');
13411 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13413 this.touchViewEl.setStyle('display', 'none');
13418 setTouchViewValue : function()
13425 Roo.each(this.tickItems, function(o){
13430 this.hideTouchView();
13433 doTouchViewQuery : function()
13442 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13446 if(!this.alwaysQuery || this.mode == 'local'){
13447 this.onTouchViewLoad();
13454 onTouchViewBeforeLoad : function(combo,opts)
13460 onTouchViewLoad : function()
13462 if(this.store.getCount() < 1){
13463 this.onTouchViewEmptyResults();
13467 this.clearTouchView();
13469 var rawValue = this.getRawValue();
13471 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13473 this.tickItems = [];
13475 this.store.data.each(function(d, rowIndex){
13476 var row = this.touchViewListGroup.createChild(template);
13478 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13479 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13482 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13483 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13486 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13487 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13488 this.tickItems.push(d.data);
13491 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13495 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13497 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13499 if(this.fieldLabel.length){
13500 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13503 var listHeight = this.touchViewListGroup.getHeight();
13507 if(firstChecked && listHeight > bodyHeight){
13508 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13513 onTouchViewLoadException : function()
13515 this.hideTouchView();
13518 onTouchViewEmptyResults : function()
13520 this.clearTouchView();
13522 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13524 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13528 clearTouchView : function()
13530 this.touchViewListGroup.dom.innerHTML = '';
13533 onTouchViewClick : function(e, el, o)
13535 e.preventDefault();
13538 var rowIndex = o.rowIndex;
13540 var r = this.store.getAt(rowIndex);
13542 if(!this.multiple){
13543 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13544 c.dom.removeAttribute('checked');
13547 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13549 this.setFromData(r.data);
13551 var close = this.closeTriggerEl();
13557 this.hideTouchView();
13559 this.fireEvent('select', this, r, rowIndex);
13564 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13565 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13566 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13570 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13571 this.addItem(r.data);
13572 this.tickItems.push(r.data);
13578 * @cfg {Boolean} grow
13582 * @cfg {Number} growMin
13586 * @cfg {Number} growMax
13595 Roo.apply(Roo.bootstrap.ComboBox, {
13599 cls: 'modal-header',
13621 cls: 'list-group-item',
13625 cls: 'roo-combobox-list-group-item-value'
13629 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13643 listItemCheckbox : {
13645 cls: 'list-group-item',
13649 cls: 'roo-combobox-list-group-item-value'
13653 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13669 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13674 cls: 'modal-footer',
13682 cls: 'col-xs-6 text-left',
13685 cls: 'btn btn-danger roo-touch-view-cancel',
13691 cls: 'col-xs-6 text-right',
13694 cls: 'btn btn-success roo-touch-view-ok',
13705 Roo.apply(Roo.bootstrap.ComboBox, {
13707 touchViewTemplate : {
13709 cls: 'modal fade roo-combobox-touch-view',
13713 cls: 'modal-dialog',
13717 cls: 'modal-content',
13719 Roo.bootstrap.ComboBox.header,
13720 Roo.bootstrap.ComboBox.body,
13721 Roo.bootstrap.ComboBox.footer
13730 * Ext JS Library 1.1.1
13731 * Copyright(c) 2006-2007, Ext JS, LLC.
13733 * Originally Released Under LGPL - original licence link has changed is not relivant.
13736 * <script type="text/javascript">
13741 * @extends Roo.util.Observable
13742 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13743 * This class also supports single and multi selection modes. <br>
13744 * Create a data model bound view:
13746 var store = new Roo.data.Store(...);
13748 var view = new Roo.View({
13750 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13752 singleSelect: true,
13753 selectedClass: "ydataview-selected",
13757 // listen for node click?
13758 view.on("click", function(vw, index, node, e){
13759 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13763 dataModel.load("foobar.xml");
13765 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13767 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13768 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13770 * Note: old style constructor is still suported (container, template, config)
13773 * Create a new View
13774 * @param {Object} config The config object
13777 Roo.View = function(config, depreciated_tpl, depreciated_config){
13779 this.parent = false;
13781 if (typeof(depreciated_tpl) == 'undefined') {
13782 // new way.. - universal constructor.
13783 Roo.apply(this, config);
13784 this.el = Roo.get(this.el);
13787 this.el = Roo.get(config);
13788 this.tpl = depreciated_tpl;
13789 Roo.apply(this, depreciated_config);
13791 this.wrapEl = this.el.wrap().wrap();
13792 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13795 if(typeof(this.tpl) == "string"){
13796 this.tpl = new Roo.Template(this.tpl);
13798 // support xtype ctors..
13799 this.tpl = new Roo.factory(this.tpl, Roo);
13803 this.tpl.compile();
13808 * @event beforeclick
13809 * Fires before a click is processed. Returns false to cancel the default action.
13810 * @param {Roo.View} this
13811 * @param {Number} index The index of the target node
13812 * @param {HTMLElement} node The target node
13813 * @param {Roo.EventObject} e The raw event object
13815 "beforeclick" : true,
13818 * Fires when a template node is clicked.
13819 * @param {Roo.View} this
13820 * @param {Number} index The index of the target node
13821 * @param {HTMLElement} node The target node
13822 * @param {Roo.EventObject} e The raw event object
13827 * Fires when a template node is double clicked.
13828 * @param {Roo.View} this
13829 * @param {Number} index The index of the target node
13830 * @param {HTMLElement} node The target node
13831 * @param {Roo.EventObject} e The raw event object
13835 * @event contextmenu
13836 * Fires when a template node is right clicked.
13837 * @param {Roo.View} this
13838 * @param {Number} index The index of the target node
13839 * @param {HTMLElement} node The target node
13840 * @param {Roo.EventObject} e The raw event object
13842 "contextmenu" : true,
13844 * @event selectionchange
13845 * Fires when the selected nodes change.
13846 * @param {Roo.View} this
13847 * @param {Array} selections Array of the selected nodes
13849 "selectionchange" : true,
13852 * @event beforeselect
13853 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13854 * @param {Roo.View} this
13855 * @param {HTMLElement} node The node to be selected
13856 * @param {Array} selections Array of currently selected nodes
13858 "beforeselect" : true,
13860 * @event preparedata
13861 * Fires on every row to render, to allow you to change the data.
13862 * @param {Roo.View} this
13863 * @param {Object} data to be rendered (change this)
13865 "preparedata" : true
13873 "click": this.onClick,
13874 "dblclick": this.onDblClick,
13875 "contextmenu": this.onContextMenu,
13879 this.selections = [];
13881 this.cmp = new Roo.CompositeElementLite([]);
13883 this.store = Roo.factory(this.store, Roo.data);
13884 this.setStore(this.store, true);
13887 if ( this.footer && this.footer.xtype) {
13889 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13891 this.footer.dataSource = this.store
13892 this.footer.container = fctr;
13893 this.footer = Roo.factory(this.footer, Roo);
13894 fctr.insertFirst(this.el);
13896 // this is a bit insane - as the paging toolbar seems to detach the el..
13897 // dom.parentNode.parentNode.parentNode
13898 // they get detached?
13902 Roo.View.superclass.constructor.call(this);
13907 Roo.extend(Roo.View, Roo.util.Observable, {
13910 * @cfg {Roo.data.Store} store Data store to load data from.
13915 * @cfg {String|Roo.Element} el The container element.
13920 * @cfg {String|Roo.Template} tpl The template used by this View
13924 * @cfg {String} dataName the named area of the template to use as the data area
13925 * Works with domtemplates roo-name="name"
13929 * @cfg {String} selectedClass The css class to add to selected nodes
13931 selectedClass : "x-view-selected",
13933 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13938 * @cfg {String} text to display on mask (default Loading)
13942 * @cfg {Boolean} multiSelect Allow multiple selection
13944 multiSelect : false,
13946 * @cfg {Boolean} singleSelect Allow single selection
13948 singleSelect: false,
13951 * @cfg {Boolean} toggleSelect - selecting
13953 toggleSelect : false,
13956 * @cfg {Boolean} tickable - selecting
13961 * Returns the element this view is bound to.
13962 * @return {Roo.Element}
13964 getEl : function(){
13965 return this.wrapEl;
13971 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13973 refresh : function(){
13974 //Roo.log('refresh');
13977 // if we are using something like 'domtemplate', then
13978 // the what gets used is:
13979 // t.applySubtemplate(NAME, data, wrapping data..)
13980 // the outer template then get' applied with
13981 // the store 'extra data'
13982 // and the body get's added to the
13983 // roo-name="data" node?
13984 // <span class='roo-tpl-{name}'></span> ?????
13988 this.clearSelections();
13989 this.el.update("");
13991 var records = this.store.getRange();
13992 if(records.length < 1) {
13994 // is this valid?? = should it render a template??
13996 this.el.update(this.emptyText);
14000 if (this.dataName) {
14001 this.el.update(t.apply(this.store.meta)); //????
14002 el = this.el.child('.roo-tpl-' + this.dataName);
14005 for(var i = 0, len = records.length; i < len; i++){
14006 var data = this.prepareData(records[i].data, i, records[i]);
14007 this.fireEvent("preparedata", this, data, i, records[i]);
14009 var d = Roo.apply({}, data);
14012 Roo.apply(d, {'roo-id' : Roo.id()});
14016 Roo.each(this.parent.item, function(item){
14017 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14020 Roo.apply(d, {'roo-data-checked' : 'checked'});
14024 html[html.length] = Roo.util.Format.trim(
14026 t.applySubtemplate(this.dataName, d, this.store.meta) :
14033 el.update(html.join(""));
14034 this.nodes = el.dom.childNodes;
14035 this.updateIndexes(0);
14040 * Function to override to reformat the data that is sent to
14041 * the template for each node.
14042 * DEPRICATED - use the preparedata event handler.
14043 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14044 * a JSON object for an UpdateManager bound view).
14046 prepareData : function(data, index, record)
14048 this.fireEvent("preparedata", this, data, index, record);
14052 onUpdate : function(ds, record){
14053 // Roo.log('on update');
14054 this.clearSelections();
14055 var index = this.store.indexOf(record);
14056 var n = this.nodes[index];
14057 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14058 n.parentNode.removeChild(n);
14059 this.updateIndexes(index, index);
14065 onAdd : function(ds, records, index)
14067 //Roo.log(['on Add', ds, records, index] );
14068 this.clearSelections();
14069 if(this.nodes.length == 0){
14073 var n = this.nodes[index];
14074 for(var i = 0, len = records.length; i < len; i++){
14075 var d = this.prepareData(records[i].data, i, records[i]);
14077 this.tpl.insertBefore(n, d);
14080 this.tpl.append(this.el, d);
14083 this.updateIndexes(index);
14086 onRemove : function(ds, record, index){
14087 // Roo.log('onRemove');
14088 this.clearSelections();
14089 var el = this.dataName ?
14090 this.el.child('.roo-tpl-' + this.dataName) :
14093 el.dom.removeChild(this.nodes[index]);
14094 this.updateIndexes(index);
14098 * Refresh an individual node.
14099 * @param {Number} index
14101 refreshNode : function(index){
14102 this.onUpdate(this.store, this.store.getAt(index));
14105 updateIndexes : function(startIndex, endIndex){
14106 var ns = this.nodes;
14107 startIndex = startIndex || 0;
14108 endIndex = endIndex || ns.length - 1;
14109 for(var i = startIndex; i <= endIndex; i++){
14110 ns[i].nodeIndex = i;
14115 * Changes the data store this view uses and refresh the view.
14116 * @param {Store} store
14118 setStore : function(store, initial){
14119 if(!initial && this.store){
14120 this.store.un("datachanged", this.refresh);
14121 this.store.un("add", this.onAdd);
14122 this.store.un("remove", this.onRemove);
14123 this.store.un("update", this.onUpdate);
14124 this.store.un("clear", this.refresh);
14125 this.store.un("beforeload", this.onBeforeLoad);
14126 this.store.un("load", this.onLoad);
14127 this.store.un("loadexception", this.onLoad);
14131 store.on("datachanged", this.refresh, this);
14132 store.on("add", this.onAdd, this);
14133 store.on("remove", this.onRemove, this);
14134 store.on("update", this.onUpdate, this);
14135 store.on("clear", this.refresh, this);
14136 store.on("beforeload", this.onBeforeLoad, this);
14137 store.on("load", this.onLoad, this);
14138 store.on("loadexception", this.onLoad, this);
14146 * onbeforeLoad - masks the loading area.
14149 onBeforeLoad : function(store,opts)
14151 //Roo.log('onBeforeLoad');
14153 this.el.update("");
14155 this.el.mask(this.mask ? this.mask : "Loading" );
14157 onLoad : function ()
14164 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14165 * @param {HTMLElement} node
14166 * @return {HTMLElement} The template node
14168 findItemFromChild : function(node){
14169 var el = this.dataName ?
14170 this.el.child('.roo-tpl-' + this.dataName,true) :
14173 if(!node || node.parentNode == el){
14176 var p = node.parentNode;
14177 while(p && p != el){
14178 if(p.parentNode == el){
14187 onClick : function(e){
14188 var item = this.findItemFromChild(e.getTarget());
14190 var index = this.indexOf(item);
14191 if(this.onItemClick(item, index, e) !== false){
14192 this.fireEvent("click", this, index, item, e);
14195 this.clearSelections();
14200 onContextMenu : function(e){
14201 var item = this.findItemFromChild(e.getTarget());
14203 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14208 onDblClick : function(e){
14209 var item = this.findItemFromChild(e.getTarget());
14211 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14215 onItemClick : function(item, index, e)
14217 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14220 if (this.toggleSelect) {
14221 var m = this.isSelected(item) ? 'unselect' : 'select';
14224 _t[m](item, true, false);
14227 if(this.multiSelect || this.singleSelect){
14228 if(this.multiSelect && e.shiftKey && this.lastSelection){
14229 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14231 this.select(item, this.multiSelect && e.ctrlKey);
14232 this.lastSelection = item;
14235 if(!this.tickable){
14236 e.preventDefault();
14244 * Get the number of selected nodes.
14247 getSelectionCount : function(){
14248 return this.selections.length;
14252 * Get the currently selected nodes.
14253 * @return {Array} An array of HTMLElements
14255 getSelectedNodes : function(){
14256 return this.selections;
14260 * Get the indexes of the selected nodes.
14263 getSelectedIndexes : function(){
14264 var indexes = [], s = this.selections;
14265 for(var i = 0, len = s.length; i < len; i++){
14266 indexes.push(s[i].nodeIndex);
14272 * Clear all selections
14273 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14275 clearSelections : function(suppressEvent){
14276 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14277 this.cmp.elements = this.selections;
14278 this.cmp.removeClass(this.selectedClass);
14279 this.selections = [];
14280 if(!suppressEvent){
14281 this.fireEvent("selectionchange", this, this.selections);
14287 * Returns true if the passed node is selected
14288 * @param {HTMLElement/Number} node The node or node index
14289 * @return {Boolean}
14291 isSelected : function(node){
14292 var s = this.selections;
14296 node = this.getNode(node);
14297 return s.indexOf(node) !== -1;
14302 * @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
14303 * @param {Boolean} keepExisting (optional) true to keep existing selections
14304 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14306 select : function(nodeInfo, keepExisting, suppressEvent){
14307 if(nodeInfo instanceof Array){
14309 this.clearSelections(true);
14311 for(var i = 0, len = nodeInfo.length; i < len; i++){
14312 this.select(nodeInfo[i], true, true);
14316 var node = this.getNode(nodeInfo);
14317 if(!node || this.isSelected(node)){
14318 return; // already selected.
14321 this.clearSelections(true);
14324 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14325 Roo.fly(node).addClass(this.selectedClass);
14326 this.selections.push(node);
14327 if(!suppressEvent){
14328 this.fireEvent("selectionchange", this, this.selections);
14336 * @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
14337 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14338 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14340 unselect : function(nodeInfo, keepExisting, suppressEvent)
14342 if(nodeInfo instanceof Array){
14343 Roo.each(this.selections, function(s) {
14344 this.unselect(s, nodeInfo);
14348 var node = this.getNode(nodeInfo);
14349 if(!node || !this.isSelected(node)){
14350 //Roo.log("not selected");
14351 return; // not selected.
14355 Roo.each(this.selections, function(s) {
14357 Roo.fly(node).removeClass(this.selectedClass);
14364 this.selections= ns;
14365 this.fireEvent("selectionchange", this, this.selections);
14369 * Gets a template node.
14370 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14371 * @return {HTMLElement} The node or null if it wasn't found
14373 getNode : function(nodeInfo){
14374 if(typeof nodeInfo == "string"){
14375 return document.getElementById(nodeInfo);
14376 }else if(typeof nodeInfo == "number"){
14377 return this.nodes[nodeInfo];
14383 * Gets a range template nodes.
14384 * @param {Number} startIndex
14385 * @param {Number} endIndex
14386 * @return {Array} An array of nodes
14388 getNodes : function(start, end){
14389 var ns = this.nodes;
14390 start = start || 0;
14391 end = typeof end == "undefined" ? ns.length - 1 : end;
14394 for(var i = start; i <= end; i++){
14398 for(var i = start; i >= end; i--){
14406 * Finds the index of the passed node
14407 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14408 * @return {Number} The index of the node or -1
14410 indexOf : function(node){
14411 node = this.getNode(node);
14412 if(typeof node.nodeIndex == "number"){
14413 return node.nodeIndex;
14415 var ns = this.nodes;
14416 for(var i = 0, len = ns.length; i < len; i++){
14427 * based on jquery fullcalendar
14431 Roo.bootstrap = Roo.bootstrap || {};
14433 * @class Roo.bootstrap.Calendar
14434 * @extends Roo.bootstrap.Component
14435 * Bootstrap Calendar class
14436 * @cfg {Boolean} loadMask (true|false) default false
14437 * @cfg {Object} header generate the user specific header of the calendar, default false
14440 * Create a new Container
14441 * @param {Object} config The config object
14446 Roo.bootstrap.Calendar = function(config){
14447 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14451 * Fires when a date is selected
14452 * @param {DatePicker} this
14453 * @param {Date} date The selected date
14457 * @event monthchange
14458 * Fires when the displayed month changes
14459 * @param {DatePicker} this
14460 * @param {Date} date The selected month
14462 'monthchange': true,
14464 * @event evententer
14465 * Fires when mouse over an event
14466 * @param {Calendar} this
14467 * @param {event} Event
14469 'evententer': true,
14471 * @event eventleave
14472 * Fires when the mouse leaves an
14473 * @param {Calendar} this
14476 'eventleave': true,
14478 * @event eventclick
14479 * Fires when the mouse click an
14480 * @param {Calendar} this
14489 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14492 * @cfg {Number} startDay
14493 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14501 getAutoCreate : function(){
14504 var fc_button = function(name, corner, style, content ) {
14505 return Roo.apply({},{
14507 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14509 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14512 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14523 style : 'width:100%',
14530 cls : 'fc-header-left',
14532 fc_button('prev', 'left', 'arrow', '‹' ),
14533 fc_button('next', 'right', 'arrow', '›' ),
14534 { tag: 'span', cls: 'fc-header-space' },
14535 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14543 cls : 'fc-header-center',
14547 cls: 'fc-header-title',
14550 html : 'month / year'
14558 cls : 'fc-header-right',
14560 /* fc_button('month', 'left', '', 'month' ),
14561 fc_button('week', '', '', 'week' ),
14562 fc_button('day', 'right', '', 'day' )
14574 header = this.header;
14577 var cal_heads = function() {
14579 // fixme - handle this.
14581 for (var i =0; i < Date.dayNames.length; i++) {
14582 var d = Date.dayNames[i];
14585 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14586 html : d.substring(0,3)
14590 ret[0].cls += ' fc-first';
14591 ret[6].cls += ' fc-last';
14594 var cal_cell = function(n) {
14597 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14602 cls: 'fc-day-number',
14606 cls: 'fc-day-content',
14610 style: 'position: relative;' // height: 17px;
14622 var cal_rows = function() {
14625 for (var r = 0; r < 6; r++) {
14632 for (var i =0; i < Date.dayNames.length; i++) {
14633 var d = Date.dayNames[i];
14634 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14637 row.cn[0].cls+=' fc-first';
14638 row.cn[0].cn[0].style = 'min-height:90px';
14639 row.cn[6].cls+=' fc-last';
14643 ret[0].cls += ' fc-first';
14644 ret[4].cls += ' fc-prev-last';
14645 ret[5].cls += ' fc-last';
14652 cls: 'fc-border-separate',
14653 style : 'width:100%',
14661 cls : 'fc-first fc-last',
14679 cls : 'fc-content',
14680 style : "position: relative;",
14683 cls : 'fc-view fc-view-month fc-grid',
14684 style : 'position: relative',
14685 unselectable : 'on',
14688 cls : 'fc-event-container',
14689 style : 'position:absolute;z-index:8;top:0;left:0;'
14707 initEvents : function()
14710 throw "can not find store for calendar";
14716 style: "text-align:center",
14720 style: "background-color:white;width:50%;margin:250 auto",
14724 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14735 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14737 var size = this.el.select('.fc-content', true).first().getSize();
14738 this.maskEl.setSize(size.width, size.height);
14739 this.maskEl.enableDisplayMode("block");
14740 if(!this.loadMask){
14741 this.maskEl.hide();
14744 this.store = Roo.factory(this.store, Roo.data);
14745 this.store.on('load', this.onLoad, this);
14746 this.store.on('beforeload', this.onBeforeLoad, this);
14750 this.cells = this.el.select('.fc-day',true);
14751 //Roo.log(this.cells);
14752 this.textNodes = this.el.query('.fc-day-number');
14753 this.cells.addClassOnOver('fc-state-hover');
14755 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14756 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14757 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14758 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14760 this.on('monthchange', this.onMonthChange, this);
14762 this.update(new Date().clearTime());
14765 resize : function() {
14766 var sz = this.el.getSize();
14768 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14769 this.el.select('.fc-day-content div',true).setHeight(34);
14774 showPrevMonth : function(e){
14775 this.update(this.activeDate.add("mo", -1));
14777 showToday : function(e){
14778 this.update(new Date().clearTime());
14781 showNextMonth : function(e){
14782 this.update(this.activeDate.add("mo", 1));
14786 showPrevYear : function(){
14787 this.update(this.activeDate.add("y", -1));
14791 showNextYear : function(){
14792 this.update(this.activeDate.add("y", 1));
14797 update : function(date)
14799 var vd = this.activeDate;
14800 this.activeDate = date;
14801 // if(vd && this.el){
14802 // var t = date.getTime();
14803 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14804 // Roo.log('using add remove');
14806 // this.fireEvent('monthchange', this, date);
14808 // this.cells.removeClass("fc-state-highlight");
14809 // this.cells.each(function(c){
14810 // if(c.dateValue == t){
14811 // c.addClass("fc-state-highlight");
14812 // setTimeout(function(){
14813 // try{c.dom.firstChild.focus();}catch(e){}
14823 var days = date.getDaysInMonth();
14825 var firstOfMonth = date.getFirstDateOfMonth();
14826 var startingPos = firstOfMonth.getDay()-this.startDay;
14828 if(startingPos < this.startDay){
14832 var pm = date.add(Date.MONTH, -1);
14833 var prevStart = pm.getDaysInMonth()-startingPos;
14835 this.cells = this.el.select('.fc-day',true);
14836 this.textNodes = this.el.query('.fc-day-number');
14837 this.cells.addClassOnOver('fc-state-hover');
14839 var cells = this.cells.elements;
14840 var textEls = this.textNodes;
14842 Roo.each(cells, function(cell){
14843 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14846 days += startingPos;
14848 // convert everything to numbers so it's fast
14849 var day = 86400000;
14850 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14853 //Roo.log(prevStart);
14855 var today = new Date().clearTime().getTime();
14856 var sel = date.clearTime().getTime();
14857 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14858 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14859 var ddMatch = this.disabledDatesRE;
14860 var ddText = this.disabledDatesText;
14861 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14862 var ddaysText = this.disabledDaysText;
14863 var format = this.format;
14865 var setCellClass = function(cal, cell){
14869 //Roo.log('set Cell Class');
14871 var t = d.getTime();
14875 cell.dateValue = t;
14877 cell.className += " fc-today";
14878 cell.className += " fc-state-highlight";
14879 cell.title = cal.todayText;
14882 // disable highlight in other month..
14883 //cell.className += " fc-state-highlight";
14888 cell.className = " fc-state-disabled";
14889 cell.title = cal.minText;
14893 cell.className = " fc-state-disabled";
14894 cell.title = cal.maxText;
14898 if(ddays.indexOf(d.getDay()) != -1){
14899 cell.title = ddaysText;
14900 cell.className = " fc-state-disabled";
14903 if(ddMatch && format){
14904 var fvalue = d.dateFormat(format);
14905 if(ddMatch.test(fvalue)){
14906 cell.title = ddText.replace("%0", fvalue);
14907 cell.className = " fc-state-disabled";
14911 if (!cell.initialClassName) {
14912 cell.initialClassName = cell.dom.className;
14915 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14920 for(; i < startingPos; i++) {
14921 textEls[i].innerHTML = (++prevStart);
14922 d.setDate(d.getDate()+1);
14924 cells[i].className = "fc-past fc-other-month";
14925 setCellClass(this, cells[i]);
14930 for(; i < days; i++){
14931 intDay = i - startingPos + 1;
14932 textEls[i].innerHTML = (intDay);
14933 d.setDate(d.getDate()+1);
14935 cells[i].className = ''; // "x-date-active";
14936 setCellClass(this, cells[i]);
14940 for(; i < 42; i++) {
14941 textEls[i].innerHTML = (++extraDays);
14942 d.setDate(d.getDate()+1);
14944 cells[i].className = "fc-future fc-other-month";
14945 setCellClass(this, cells[i]);
14948 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14950 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14952 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14953 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14955 if(totalRows != 6){
14956 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14957 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14960 this.fireEvent('monthchange', this, date);
14964 if(!this.internalRender){
14965 var main = this.el.dom.firstChild;
14966 var w = main.offsetWidth;
14967 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14968 Roo.fly(main).setWidth(w);
14969 this.internalRender = true;
14970 // opera does not respect the auto grow header center column
14971 // then, after it gets a width opera refuses to recalculate
14972 // without a second pass
14973 if(Roo.isOpera && !this.secondPass){
14974 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14975 this.secondPass = true;
14976 this.update.defer(10, this, [date]);
14983 findCell : function(dt) {
14984 dt = dt.clearTime().getTime();
14986 this.cells.each(function(c){
14987 //Roo.log("check " +c.dateValue + '?=' + dt);
14988 if(c.dateValue == dt){
14998 findCells : function(ev) {
14999 var s = ev.start.clone().clearTime().getTime();
15001 var e= ev.end.clone().clearTime().getTime();
15004 this.cells.each(function(c){
15005 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15007 if(c.dateValue > e){
15010 if(c.dateValue < s){
15019 // findBestRow: function(cells)
15023 // for (var i =0 ; i < cells.length;i++) {
15024 // ret = Math.max(cells[i].rows || 0,ret);
15031 addItem : function(ev)
15033 // look for vertical location slot in
15034 var cells = this.findCells(ev);
15036 // ev.row = this.findBestRow(cells);
15038 // work out the location.
15042 for(var i =0; i < cells.length; i++) {
15044 cells[i].row = cells[0].row;
15047 cells[i].row = cells[i].row + 1;
15057 if (crow.start.getY() == cells[i].getY()) {
15059 crow.end = cells[i];
15076 cells[0].events.push(ev);
15078 this.calevents.push(ev);
15081 clearEvents: function() {
15083 if(!this.calevents){
15087 Roo.each(this.cells.elements, function(c){
15093 Roo.each(this.calevents, function(e) {
15094 Roo.each(e.els, function(el) {
15095 el.un('mouseenter' ,this.onEventEnter, this);
15096 el.un('mouseleave' ,this.onEventLeave, this);
15101 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15107 renderEvents: function()
15111 this.cells.each(function(c) {
15120 if(c.row != c.events.length){
15121 r = 4 - (4 - (c.row - c.events.length));
15124 c.events = ev.slice(0, r);
15125 c.more = ev.slice(r);
15127 if(c.more.length && c.more.length == 1){
15128 c.events.push(c.more.pop());
15131 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15135 this.cells.each(function(c) {
15137 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15140 for (var e = 0; e < c.events.length; e++){
15141 var ev = c.events[e];
15142 var rows = ev.rows;
15144 for(var i = 0; i < rows.length; i++) {
15146 // how many rows should it span..
15149 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15150 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15152 unselectable : "on",
15155 cls: 'fc-event-inner',
15159 // cls: 'fc-event-time',
15160 // html : cells.length > 1 ? '' : ev.time
15164 cls: 'fc-event-title',
15165 html : String.format('{0}', ev.title)
15172 cls: 'ui-resizable-handle ui-resizable-e',
15173 html : '  '
15180 cfg.cls += ' fc-event-start';
15182 if ((i+1) == rows.length) {
15183 cfg.cls += ' fc-event-end';
15186 var ctr = _this.el.select('.fc-event-container',true).first();
15187 var cg = ctr.createChild(cfg);
15189 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15190 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15192 var r = (c.more.length) ? 1 : 0;
15193 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15194 cg.setWidth(ebox.right - sbox.x -2);
15196 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15197 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15198 cg.on('click', _this.onEventClick, _this, ev);
15209 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15210 style : 'position: absolute',
15211 unselectable : "on",
15214 cls: 'fc-event-inner',
15218 cls: 'fc-event-title',
15226 cls: 'ui-resizable-handle ui-resizable-e',
15227 html : '  '
15233 var ctr = _this.el.select('.fc-event-container',true).first();
15234 var cg = ctr.createChild(cfg);
15236 var sbox = c.select('.fc-day-content',true).first().getBox();
15237 var ebox = c.select('.fc-day-content',true).first().getBox();
15239 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15240 cg.setWidth(ebox.right - sbox.x -2);
15242 cg.on('click', _this.onMoreEventClick, _this, c.more);
15252 onEventEnter: function (e, el,event,d) {
15253 this.fireEvent('evententer', this, el, event);
15256 onEventLeave: function (e, el,event,d) {
15257 this.fireEvent('eventleave', this, el, event);
15260 onEventClick: function (e, el,event,d) {
15261 this.fireEvent('eventclick', this, el, event);
15264 onMonthChange: function () {
15268 onMoreEventClick: function(e, el, more)
15272 this.calpopover.placement = 'right';
15273 this.calpopover.setTitle('More');
15275 this.calpopover.setContent('');
15277 var ctr = this.calpopover.el.select('.popover-content', true).first();
15279 Roo.each(more, function(m){
15281 cls : 'fc-event-hori fc-event-draggable',
15284 var cg = ctr.createChild(cfg);
15286 cg.on('click', _this.onEventClick, _this, m);
15289 this.calpopover.show(el);
15294 onLoad: function ()
15296 this.calevents = [];
15299 if(this.store.getCount() > 0){
15300 this.store.data.each(function(d){
15303 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15304 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15305 time : d.data.start_time,
15306 title : d.data.title,
15307 description : d.data.description,
15308 venue : d.data.venue
15313 this.renderEvents();
15315 if(this.calevents.length && this.loadMask){
15316 this.maskEl.hide();
15320 onBeforeLoad: function()
15322 this.clearEvents();
15324 this.maskEl.show();
15338 * @class Roo.bootstrap.Popover
15339 * @extends Roo.bootstrap.Component
15340 * Bootstrap Popover class
15341 * @cfg {String} html contents of the popover (or false to use children..)
15342 * @cfg {String} title of popover (or false to hide)
15343 * @cfg {String} placement how it is placed
15344 * @cfg {String} trigger click || hover (or false to trigger manually)
15345 * @cfg {String} over what (parent or false to trigger manually.)
15346 * @cfg {Number} delay - delay before showing
15349 * Create a new Popover
15350 * @param {Object} config The config object
15353 Roo.bootstrap.Popover = function(config){
15354 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15357 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15359 title: 'Fill in a title',
15362 placement : 'right',
15363 trigger : 'hover', // hover
15369 can_build_overlaid : false,
15371 getChildContainer : function()
15373 return this.el.select('.popover-content',true).first();
15376 getAutoCreate : function(){
15377 Roo.log('make popover?');
15379 cls : 'popover roo-dynamic',
15380 style: 'display:block',
15386 cls : 'popover-inner',
15390 cls: 'popover-title',
15394 cls : 'popover-content',
15405 setTitle: function(str)
15408 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15410 setContent: function(str)
15413 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15415 // as it get's added to the bottom of the page.
15416 onRender : function(ct, position)
15418 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15420 var cfg = Roo.apply({}, this.getAutoCreate());
15424 cfg.cls += ' ' + this.cls;
15427 cfg.style = this.style;
15429 Roo.log("adding to ")
15430 this.el = Roo.get(document.body).createChild(cfg, position);
15436 initEvents : function()
15438 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15439 this.el.enableDisplayMode('block');
15441 if (this.over === false) {
15444 if (this.triggers === false) {
15447 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15448 var triggers = this.trigger ? this.trigger.split(' ') : [];
15449 Roo.each(triggers, function(trigger) {
15451 if (trigger == 'click') {
15452 on_el.on('click', this.toggle, this);
15453 } else if (trigger != 'manual') {
15454 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15455 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15457 on_el.on(eventIn ,this.enter, this);
15458 on_el.on(eventOut, this.leave, this);
15469 toggle : function () {
15470 this.hoverState == 'in' ? this.leave() : this.enter();
15473 enter : function () {
15476 clearTimeout(this.timeout);
15478 this.hoverState = 'in';
15480 if (!this.delay || !this.delay.show) {
15485 this.timeout = setTimeout(function () {
15486 if (_t.hoverState == 'in') {
15489 }, this.delay.show)
15491 leave : function() {
15492 clearTimeout(this.timeout);
15494 this.hoverState = 'out';
15496 if (!this.delay || !this.delay.hide) {
15501 this.timeout = setTimeout(function () {
15502 if (_t.hoverState == 'out') {
15505 }, this.delay.hide)
15508 show : function (on_el)
15511 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15514 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15515 if (this.html !== false) {
15516 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15518 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15519 if (!this.title.length) {
15520 this.el.select('.popover-title',true).hide();
15523 var placement = typeof this.placement == 'function' ?
15524 this.placement.call(this, this.el, on_el) :
15527 var autoToken = /\s?auto?\s?/i;
15528 var autoPlace = autoToken.test(placement);
15530 placement = placement.replace(autoToken, '') || 'top';
15534 //this.el.setXY([0,0]);
15536 this.el.dom.style.display='block';
15537 this.el.addClass(placement);
15539 //this.el.appendTo(on_el);
15541 var p = this.getPosition();
15542 var box = this.el.getBox();
15547 var align = Roo.bootstrap.Popover.alignment[placement];
15548 this.el.alignTo(on_el, align[0],align[1]);
15549 //var arrow = this.el.select('.arrow',true).first();
15550 //arrow.set(align[2],
15552 this.el.addClass('in');
15555 if (this.el.hasClass('fade')) {
15562 this.el.setXY([0,0]);
15563 this.el.removeClass('in');
15565 this.hoverState = null;
15571 Roo.bootstrap.Popover.alignment = {
15572 'left' : ['r-l', [-10,0], 'right'],
15573 'right' : ['l-r', [10,0], 'left'],
15574 'bottom' : ['t-b', [0,10], 'top'],
15575 'top' : [ 'b-t', [0,-10], 'bottom']
15586 * @class Roo.bootstrap.Progress
15587 * @extends Roo.bootstrap.Component
15588 * Bootstrap Progress class
15589 * @cfg {Boolean} striped striped of the progress bar
15590 * @cfg {Boolean} active animated of the progress bar
15594 * Create a new Progress
15595 * @param {Object} config The config object
15598 Roo.bootstrap.Progress = function(config){
15599 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15602 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15607 getAutoCreate : function(){
15615 cfg.cls += ' progress-striped';
15619 cfg.cls += ' active';
15638 * @class Roo.bootstrap.ProgressBar
15639 * @extends Roo.bootstrap.Component
15640 * Bootstrap ProgressBar class
15641 * @cfg {Number} aria_valuenow aria-value now
15642 * @cfg {Number} aria_valuemin aria-value min
15643 * @cfg {Number} aria_valuemax aria-value max
15644 * @cfg {String} label label for the progress bar
15645 * @cfg {String} panel (success | info | warning | danger )
15646 * @cfg {String} role role of the progress bar
15647 * @cfg {String} sr_only text
15651 * Create a new ProgressBar
15652 * @param {Object} config The config object
15655 Roo.bootstrap.ProgressBar = function(config){
15656 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15659 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15663 aria_valuemax : 100,
15669 getAutoCreate : function()
15674 cls: 'progress-bar',
15675 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15687 cfg.role = this.role;
15690 if(this.aria_valuenow){
15691 cfg['aria-valuenow'] = this.aria_valuenow;
15694 if(this.aria_valuemin){
15695 cfg['aria-valuemin'] = this.aria_valuemin;
15698 if(this.aria_valuemax){
15699 cfg['aria-valuemax'] = this.aria_valuemax;
15702 if(this.label && !this.sr_only){
15703 cfg.html = this.label;
15707 cfg.cls += ' progress-bar-' + this.panel;
15713 update : function(aria_valuenow)
15715 this.aria_valuenow = aria_valuenow;
15717 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15732 * @class Roo.bootstrap.TabGroup
15733 * @extends Roo.bootstrap.Column
15734 * Bootstrap Column class
15735 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15736 * @cfg {Boolean} carousel true to make the group behave like a carousel
15737 * @cfg {Number} bullets show the panel pointer.. default 0
15738 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15739 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15740 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15743 * Create a new TabGroup
15744 * @param {Object} config The config object
15747 Roo.bootstrap.TabGroup = function(config){
15748 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15750 this.navId = Roo.id();
15753 Roo.bootstrap.TabGroup.register(this);
15757 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15760 transition : false,
15765 slideOnTouch : false,
15767 getAutoCreate : function()
15769 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15771 cfg.cls += ' tab-content';
15773 Roo.log('get auto create...............');
15775 if (this.carousel) {
15776 cfg.cls += ' carousel slide';
15779 cls : 'carousel-inner'
15782 if(this.bullets > 0 && !Roo.isTouch){
15785 cls : 'carousel-bullets',
15789 if(this.bullets_cls){
15790 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15793 for (var i = 0; i < this.bullets; i++){
15795 cls : 'bullet bullet-' + i
15803 cfg.cn[0].cn = bullets;
15810 initEvents: function()
15812 Roo.log('-------- init events on tab group ---------');
15814 if(this.bullets > 0 && !Roo.isTouch){
15820 if(Roo.isTouch && this.slideOnTouch){
15821 this.el.on("touchstart", this.onTouchStart, this);
15824 if(this.autoslide){
15827 this.slideFn = window.setInterval(function() {
15828 _this.showPanelNext();
15834 onTouchStart : function(e, el, o)
15836 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15840 this.showPanelNext();
15843 getChildContainer : function()
15845 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15849 * register a Navigation item
15850 * @param {Roo.bootstrap.NavItem} the navitem to add
15852 register : function(item)
15854 this.tabs.push( item);
15855 item.navId = this.navId; // not really needed..
15859 getActivePanel : function()
15862 Roo.each(this.tabs, function(t) {
15872 getPanelByName : function(n)
15875 Roo.each(this.tabs, function(t) {
15876 if (t.tabId == n) {
15884 indexOfPanel : function(p)
15887 Roo.each(this.tabs, function(t,i) {
15888 if (t.tabId == p.tabId) {
15897 * show a specific panel
15898 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15899 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15901 showPanel : function (pan)
15903 if(this.transition){
15904 Roo.log("waiting for the transitionend");
15908 if (typeof(pan) == 'number') {
15909 pan = this.tabs[pan];
15911 if (typeof(pan) == 'string') {
15912 pan = this.getPanelByName(pan);
15914 if (pan.tabId == this.getActivePanel().tabId) {
15917 var cur = this.getActivePanel();
15919 if (false === cur.fireEvent('beforedeactivate')) {
15923 if(this.bullets > 0 && !Roo.isTouch){
15924 this.setActiveBullet(this.indexOfPanel(pan));
15927 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15929 this.transition = true;
15930 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15931 var lr = dir == 'next' ? 'left' : 'right';
15932 pan.el.addClass(dir); // or prev
15933 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15934 cur.el.addClass(lr); // or right
15935 pan.el.addClass(lr);
15938 cur.el.on('transitionend', function() {
15939 Roo.log("trans end?");
15941 pan.el.removeClass([lr,dir]);
15942 pan.setActive(true);
15944 cur.el.removeClass([lr]);
15945 cur.setActive(false);
15947 _this.transition = false;
15949 }, this, { single: true } );
15954 cur.setActive(false);
15955 pan.setActive(true);
15960 showPanelNext : function()
15962 var i = this.indexOfPanel(this.getActivePanel());
15964 if (i >= this.tabs.length - 1 && !this.autoslide) {
15968 if (i >= this.tabs.length - 1 && this.autoslide) {
15972 this.showPanel(this.tabs[i+1]);
15975 showPanelPrev : function()
15977 var i = this.indexOfPanel(this.getActivePanel());
15979 if (i < 1 && !this.autoslide) {
15983 if (i < 1 && this.autoslide) {
15984 i = this.tabs.length;
15987 this.showPanel(this.tabs[i-1]);
15990 initBullet : function()
15998 for (var i = 0; i < this.bullets; i++){
15999 var bullet = this.el.select('.bullet-' + i, true).first();
16005 bullet.on('click', (function(e, el, o, ii, t){
16007 e.preventDefault();
16009 _this.showPanel(ii);
16011 if(_this.autoslide && _this.slideFn){
16012 clearInterval(_this.slideFn);
16013 _this.slideFn = window.setInterval(function() {
16014 _this.showPanelNext();
16018 }).createDelegate(this, [i, bullet], true));
16022 setActiveBullet : function(i)
16028 Roo.each(this.el.select('.bullet', true).elements, function(el){
16029 el.removeClass('selected');
16032 var bullet = this.el.select('.bullet-' + i, true).first();
16038 bullet.addClass('selected');
16049 Roo.apply(Roo.bootstrap.TabGroup, {
16053 * register a Navigation Group
16054 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16056 register : function(navgrp)
16058 this.groups[navgrp.navId] = navgrp;
16062 * fetch a Navigation Group based on the navigation ID
16063 * if one does not exist , it will get created.
16064 * @param {string} the navgroup to add
16065 * @returns {Roo.bootstrap.NavGroup} the navgroup
16067 get: function(navId) {
16068 if (typeof(this.groups[navId]) == 'undefined') {
16069 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16071 return this.groups[navId] ;
16086 * @class Roo.bootstrap.TabPanel
16087 * @extends Roo.bootstrap.Component
16088 * Bootstrap TabPanel class
16089 * @cfg {Boolean} active panel active
16090 * @cfg {String} html panel content
16091 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16092 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16096 * Create a new TabPanel
16097 * @param {Object} config The config object
16100 Roo.bootstrap.TabPanel = function(config){
16101 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16105 * Fires when the active status changes
16106 * @param {Roo.bootstrap.TabPanel} this
16107 * @param {Boolean} state the new state
16112 * @event beforedeactivate
16113 * Fires before a tab is de-activated - can be used to do validation on a form.
16114 * @param {Roo.bootstrap.TabPanel} this
16115 * @return {Boolean} false if there is an error
16118 'beforedeactivate': true
16121 this.tabId = this.tabId || Roo.id();
16125 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16132 getAutoCreate : function(){
16135 // item is needed for carousel - not sure if it has any effect otherwise
16136 cls: 'tab-pane item',
16137 html: this.html || ''
16141 cfg.cls += ' active';
16145 cfg.tabId = this.tabId;
16152 initEvents: function()
16154 Roo.log('-------- init events on tab panel ---------');
16156 var p = this.parent();
16157 this.navId = this.navId || p.navId;
16159 if (typeof(this.navId) != 'undefined') {
16160 // not really needed.. but just in case.. parent should be a NavGroup.
16161 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16162 Roo.log(['register', tg, this]);
16165 var i = tg.tabs.length - 1;
16167 if(this.active && tg.bullets > 0 && i < tg.bullets){
16168 tg.setActiveBullet(i);
16175 onRender : function(ct, position)
16177 // Roo.log("Call onRender: " + this.xtype);
16179 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16187 setActive: function(state)
16189 Roo.log("panel - set active " + this.tabId + "=" + state);
16191 this.active = state;
16193 this.el.removeClass('active');
16195 } else if (!this.el.hasClass('active')) {
16196 this.el.addClass('active');
16199 this.fireEvent('changed', this, state);
16216 * @class Roo.bootstrap.DateField
16217 * @extends Roo.bootstrap.Input
16218 * Bootstrap DateField class
16219 * @cfg {Number} weekStart default 0
16220 * @cfg {String} viewMode default empty, (months|years)
16221 * @cfg {String} minViewMode default empty, (months|years)
16222 * @cfg {Number} startDate default -Infinity
16223 * @cfg {Number} endDate default Infinity
16224 * @cfg {Boolean} todayHighlight default false
16225 * @cfg {Boolean} todayBtn default false
16226 * @cfg {Boolean} calendarWeeks default false
16227 * @cfg {Object} daysOfWeekDisabled default empty
16228 * @cfg {Boolean} singleMode default false (true | false)
16230 * @cfg {Boolean} keyboardNavigation default true
16231 * @cfg {String} language default en
16234 * Create a new DateField
16235 * @param {Object} config The config object
16238 Roo.bootstrap.DateField = function(config){
16239 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16243 * Fires when this field show.
16244 * @param {Roo.bootstrap.DateField} this
16245 * @param {Mixed} date The date value
16250 * Fires when this field hide.
16251 * @param {Roo.bootstrap.DateField} this
16252 * @param {Mixed} date The date value
16257 * Fires when select a date.
16258 * @param {Roo.bootstrap.DateField} this
16259 * @param {Mixed} date The date value
16265 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16268 * @cfg {String} format
16269 * The default date format string which can be overriden for localization support. The format must be
16270 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16274 * @cfg {String} altFormats
16275 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16276 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16278 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16286 todayHighlight : false,
16292 keyboardNavigation: true,
16294 calendarWeeks: false,
16296 startDate: -Infinity,
16300 daysOfWeekDisabled: [],
16304 singleMode : false,
16306 UTCDate: function()
16308 return new Date(Date.UTC.apply(Date, arguments));
16311 UTCToday: function()
16313 var today = new Date();
16314 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16317 getDate: function() {
16318 var d = this.getUTCDate();
16319 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16322 getUTCDate: function() {
16326 setDate: function(d) {
16327 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16330 setUTCDate: function(d) {
16332 this.setValue(this.formatDate(this.date));
16335 onRender: function(ct, position)
16338 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16340 this.language = this.language || 'en';
16341 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16342 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16344 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16345 this.format = this.format || 'm/d/y';
16346 this.isInline = false;
16347 this.isInput = true;
16348 this.component = this.el.select('.add-on', true).first() || false;
16349 this.component = (this.component && this.component.length === 0) ? false : this.component;
16350 this.hasInput = this.component && this.inputEL().length;
16352 if (typeof(this.minViewMode === 'string')) {
16353 switch (this.minViewMode) {
16355 this.minViewMode = 1;
16358 this.minViewMode = 2;
16361 this.minViewMode = 0;
16366 if (typeof(this.viewMode === 'string')) {
16367 switch (this.viewMode) {
16380 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16382 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16384 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16386 this.picker().on('mousedown', this.onMousedown, this);
16387 this.picker().on('click', this.onClick, this);
16389 this.picker().addClass('datepicker-dropdown');
16391 this.startViewMode = this.viewMode;
16393 if(this.singleMode){
16394 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16395 v.setVisibilityMode(Roo.Element.DISPLAY)
16399 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16400 v.setStyle('width', '189px');
16404 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16405 if(!this.calendarWeeks){
16410 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16411 v.attr('colspan', function(i, val){
16412 return parseInt(val) + 1;
16417 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16419 this.setStartDate(this.startDate);
16420 this.setEndDate(this.endDate);
16422 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16429 if(this.isInline) {
16434 picker : function()
16436 return this.pickerEl;
16437 // return this.el.select('.datepicker', true).first();
16440 fillDow: function()
16442 var dowCnt = this.weekStart;
16451 if(this.calendarWeeks){
16459 while (dowCnt < this.weekStart + 7) {
16463 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16467 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16470 fillMonths: function()
16473 var months = this.picker().select('>.datepicker-months td', true).first();
16475 months.dom.innerHTML = '';
16481 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16484 months.createChild(month);
16491 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16493 if (this.date < this.startDate) {
16494 this.viewDate = new Date(this.startDate);
16495 } else if (this.date > this.endDate) {
16496 this.viewDate = new Date(this.endDate);
16498 this.viewDate = new Date(this.date);
16506 var d = new Date(this.viewDate),
16507 year = d.getUTCFullYear(),
16508 month = d.getUTCMonth(),
16509 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16510 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16511 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16512 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16513 currentDate = this.date && this.date.valueOf(),
16514 today = this.UTCToday();
16516 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16518 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16520 // this.picker.select('>tfoot th.today').
16521 // .text(dates[this.language].today)
16522 // .toggle(this.todayBtn !== false);
16524 this.updateNavArrows();
16527 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16529 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16531 prevMonth.setUTCDate(day);
16533 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16535 var nextMonth = new Date(prevMonth);
16537 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16539 nextMonth = nextMonth.valueOf();
16541 var fillMonths = false;
16543 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16545 while(prevMonth.valueOf() < nextMonth) {
16548 if (prevMonth.getUTCDay() === this.weekStart) {
16550 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16558 if(this.calendarWeeks){
16559 // ISO 8601: First week contains first thursday.
16560 // ISO also states week starts on Monday, but we can be more abstract here.
16562 // Start of current week: based on weekstart/current date
16563 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16564 // Thursday of this week
16565 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16566 // First Thursday of year, year from thursday
16567 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16568 // Calendar week: ms between thursdays, div ms per day, div 7 days
16569 calWeek = (th - yth) / 864e5 / 7 + 1;
16571 fillMonths.cn.push({
16579 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16581 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16584 if (this.todayHighlight &&
16585 prevMonth.getUTCFullYear() == today.getFullYear() &&
16586 prevMonth.getUTCMonth() == today.getMonth() &&
16587 prevMonth.getUTCDate() == today.getDate()) {
16588 clsName += ' today';
16591 if (currentDate && prevMonth.valueOf() === currentDate) {
16592 clsName += ' active';
16595 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16596 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16597 clsName += ' disabled';
16600 fillMonths.cn.push({
16602 cls: 'day ' + clsName,
16603 html: prevMonth.getDate()
16606 prevMonth.setDate(prevMonth.getDate()+1);
16609 var currentYear = this.date && this.date.getUTCFullYear();
16610 var currentMonth = this.date && this.date.getUTCMonth();
16612 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16614 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16615 v.removeClass('active');
16617 if(currentYear === year && k === currentMonth){
16618 v.addClass('active');
16621 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16622 v.addClass('disabled');
16628 year = parseInt(year/10, 10) * 10;
16630 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16632 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16635 for (var i = -1; i < 11; i++) {
16636 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16638 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16646 showMode: function(dir)
16649 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16652 Roo.each(this.picker().select('>div',true).elements, function(v){
16653 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16656 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16661 if(this.isInline) return;
16663 this.picker().removeClass(['bottom', 'top']);
16665 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16667 * place to the top of element!
16671 this.picker().addClass('top');
16672 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16677 this.picker().addClass('bottom');
16679 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16682 parseDate : function(value)
16684 if(!value || value instanceof Date){
16687 var v = Date.parseDate(value, this.format);
16688 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16689 v = Date.parseDate(value, 'Y-m-d');
16691 if(!v && this.altFormats){
16692 if(!this.altFormatsArray){
16693 this.altFormatsArray = this.altFormats.split("|");
16695 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16696 v = Date.parseDate(value, this.altFormatsArray[i]);
16702 formatDate : function(date, fmt)
16704 return (!date || !(date instanceof Date)) ?
16705 date : date.dateFormat(fmt || this.format);
16708 onFocus : function()
16710 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16714 onBlur : function()
16716 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16718 var d = this.inputEl().getValue();
16727 this.picker().show();
16731 this.fireEvent('show', this, this.date);
16736 if(this.isInline) return;
16737 this.picker().hide();
16738 this.viewMode = this.startViewMode;
16741 this.fireEvent('hide', this, this.date);
16745 onMousedown: function(e)
16747 e.stopPropagation();
16748 e.preventDefault();
16753 Roo.bootstrap.DateField.superclass.keyup.call(this);
16757 setValue: function(v)
16760 // v can be a string or a date..
16763 var d = new Date(this.parseDate(v) ).clearTime();
16765 if(isNaN(d.getTime())){
16766 this.date = this.viewDate = '';
16767 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16771 v = this.formatDate(d);
16773 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16775 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16779 this.fireEvent('select', this, this.date);
16783 getValue: function()
16785 return this.formatDate(this.date);
16788 fireKey: function(e)
16790 if (!this.picker().isVisible()){
16791 if (e.keyCode == 27) // allow escape to hide and re-show picker
16796 var dateChanged = false,
16798 newDate, newViewDate;
16803 e.preventDefault();
16807 if (!this.keyboardNavigation) break;
16808 dir = e.keyCode == 37 ? -1 : 1;
16811 newDate = this.moveYear(this.date, dir);
16812 newViewDate = this.moveYear(this.viewDate, dir);
16813 } else if (e.shiftKey){
16814 newDate = this.moveMonth(this.date, dir);
16815 newViewDate = this.moveMonth(this.viewDate, dir);
16817 newDate = new Date(this.date);
16818 newDate.setUTCDate(this.date.getUTCDate() + dir);
16819 newViewDate = new Date(this.viewDate);
16820 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16822 if (this.dateWithinRange(newDate)){
16823 this.date = newDate;
16824 this.viewDate = newViewDate;
16825 this.setValue(this.formatDate(this.date));
16827 e.preventDefault();
16828 dateChanged = true;
16833 if (!this.keyboardNavigation) break;
16834 dir = e.keyCode == 38 ? -1 : 1;
16836 newDate = this.moveYear(this.date, dir);
16837 newViewDate = this.moveYear(this.viewDate, dir);
16838 } else if (e.shiftKey){
16839 newDate = this.moveMonth(this.date, dir);
16840 newViewDate = this.moveMonth(this.viewDate, dir);
16842 newDate = new Date(this.date);
16843 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16844 newViewDate = new Date(this.viewDate);
16845 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16847 if (this.dateWithinRange(newDate)){
16848 this.date = newDate;
16849 this.viewDate = newViewDate;
16850 this.setValue(this.formatDate(this.date));
16852 e.preventDefault();
16853 dateChanged = true;
16857 this.setValue(this.formatDate(this.date));
16859 e.preventDefault();
16862 this.setValue(this.formatDate(this.date));
16876 onClick: function(e)
16878 e.stopPropagation();
16879 e.preventDefault();
16881 var target = e.getTarget();
16883 if(target.nodeName.toLowerCase() === 'i'){
16884 target = Roo.get(target).dom.parentNode;
16887 var nodeName = target.nodeName;
16888 var className = target.className;
16889 var html = target.innerHTML;
16890 //Roo.log(nodeName);
16892 switch(nodeName.toLowerCase()) {
16894 switch(className) {
16900 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16901 switch(this.viewMode){
16903 this.viewDate = this.moveMonth(this.viewDate, dir);
16907 this.viewDate = this.moveYear(this.viewDate, dir);
16913 var date = new Date();
16914 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16916 this.setValue(this.formatDate(this.date));
16923 if (className.indexOf('disabled') < 0) {
16924 this.viewDate.setUTCDate(1);
16925 if (className.indexOf('month') > -1) {
16926 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16928 var year = parseInt(html, 10) || 0;
16929 this.viewDate.setUTCFullYear(year);
16933 if(this.singleMode){
16934 this.setValue(this.formatDate(this.viewDate));
16945 //Roo.log(className);
16946 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16947 var day = parseInt(html, 10) || 1;
16948 var year = this.viewDate.getUTCFullYear(),
16949 month = this.viewDate.getUTCMonth();
16951 if (className.indexOf('old') > -1) {
16958 } else if (className.indexOf('new') > -1) {
16966 //Roo.log([year,month,day]);
16967 this.date = this.UTCDate(year, month, day,0,0,0,0);
16968 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16970 //Roo.log(this.formatDate(this.date));
16971 this.setValue(this.formatDate(this.date));
16978 setStartDate: function(startDate)
16980 this.startDate = startDate || -Infinity;
16981 if (this.startDate !== -Infinity) {
16982 this.startDate = this.parseDate(this.startDate);
16985 this.updateNavArrows();
16988 setEndDate: function(endDate)
16990 this.endDate = endDate || Infinity;
16991 if (this.endDate !== Infinity) {
16992 this.endDate = this.parseDate(this.endDate);
16995 this.updateNavArrows();
16998 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17000 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17001 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17002 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17004 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17005 return parseInt(d, 10);
17008 this.updateNavArrows();
17011 updateNavArrows: function()
17013 if(this.singleMode){
17017 var d = new Date(this.viewDate),
17018 year = d.getUTCFullYear(),
17019 month = d.getUTCMonth();
17021 Roo.each(this.picker().select('.prev', true).elements, function(v){
17023 switch (this.viewMode) {
17026 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17032 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17039 Roo.each(this.picker().select('.next', true).elements, function(v){
17041 switch (this.viewMode) {
17044 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17050 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17058 moveMonth: function(date, dir)
17060 if (!dir) return date;
17061 var new_date = new Date(date.valueOf()),
17062 day = new_date.getUTCDate(),
17063 month = new_date.getUTCMonth(),
17064 mag = Math.abs(dir),
17066 dir = dir > 0 ? 1 : -1;
17069 // If going back one month, make sure month is not current month
17070 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17072 return new_date.getUTCMonth() == month;
17074 // If going forward one month, make sure month is as expected
17075 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17077 return new_date.getUTCMonth() != new_month;
17079 new_month = month + dir;
17080 new_date.setUTCMonth(new_month);
17081 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17082 if (new_month < 0 || new_month > 11)
17083 new_month = (new_month + 12) % 12;
17085 // For magnitudes >1, move one month at a time...
17086 for (var i=0; i<mag; i++)
17087 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17088 new_date = this.moveMonth(new_date, dir);
17089 // ...then reset the day, keeping it in the new month
17090 new_month = new_date.getUTCMonth();
17091 new_date.setUTCDate(day);
17093 return new_month != new_date.getUTCMonth();
17096 // Common date-resetting loop -- if date is beyond end of month, make it
17099 new_date.setUTCDate(--day);
17100 new_date.setUTCMonth(new_month);
17105 moveYear: function(date, dir)
17107 return this.moveMonth(date, dir*12);
17110 dateWithinRange: function(date)
17112 return date >= this.startDate && date <= this.endDate;
17118 this.picker().remove();
17123 Roo.apply(Roo.bootstrap.DateField, {
17134 html: '<i class="fa fa-arrow-left"/>'
17144 html: '<i class="fa fa-arrow-right"/>'
17186 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17187 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17188 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17189 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17190 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17203 navFnc: 'FullYear',
17208 navFnc: 'FullYear',
17213 Roo.apply(Roo.bootstrap.DateField, {
17217 cls: 'datepicker dropdown-menu roo-dynamic',
17221 cls: 'datepicker-days',
17225 cls: 'table-condensed',
17227 Roo.bootstrap.DateField.head,
17231 Roo.bootstrap.DateField.footer
17238 cls: 'datepicker-months',
17242 cls: 'table-condensed',
17244 Roo.bootstrap.DateField.head,
17245 Roo.bootstrap.DateField.content,
17246 Roo.bootstrap.DateField.footer
17253 cls: 'datepicker-years',
17257 cls: 'table-condensed',
17259 Roo.bootstrap.DateField.head,
17260 Roo.bootstrap.DateField.content,
17261 Roo.bootstrap.DateField.footer
17280 * @class Roo.bootstrap.TimeField
17281 * @extends Roo.bootstrap.Input
17282 * Bootstrap DateField class
17286 * Create a new TimeField
17287 * @param {Object} config The config object
17290 Roo.bootstrap.TimeField = function(config){
17291 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17295 * Fires when this field show.
17296 * @param {Roo.bootstrap.DateField} thisthis
17297 * @param {Mixed} date The date value
17302 * Fires when this field hide.
17303 * @param {Roo.bootstrap.DateField} this
17304 * @param {Mixed} date The date value
17309 * Fires when select a date.
17310 * @param {Roo.bootstrap.DateField} this
17311 * @param {Mixed} date The date value
17317 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17320 * @cfg {String} format
17321 * The default time format string which can be overriden for localization support. The format must be
17322 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17326 onRender: function(ct, position)
17329 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17331 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17333 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17335 this.pop = this.picker().select('>.datepicker-time',true).first();
17336 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17338 this.picker().on('mousedown', this.onMousedown, this);
17339 this.picker().on('click', this.onClick, this);
17341 this.picker().addClass('datepicker-dropdown');
17346 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17347 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17348 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17349 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17350 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17351 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17355 fireKey: function(e){
17356 if (!this.picker().isVisible()){
17357 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17363 e.preventDefault();
17371 this.onTogglePeriod();
17374 this.onIncrementMinutes();
17377 this.onDecrementMinutes();
17386 onClick: function(e) {
17387 e.stopPropagation();
17388 e.preventDefault();
17391 picker : function()
17393 return this.el.select('.datepicker', true).first();
17396 fillTime: function()
17398 var time = this.pop.select('tbody', true).first();
17400 time.dom.innerHTML = '';
17415 cls: 'hours-up glyphicon glyphicon-chevron-up'
17435 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17456 cls: 'timepicker-hour',
17471 cls: 'timepicker-minute',
17486 cls: 'btn btn-primary period',
17508 cls: 'hours-down glyphicon glyphicon-chevron-down'
17528 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17546 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17553 var hours = this.time.getHours();
17554 var minutes = this.time.getMinutes();
17567 hours = hours - 12;
17571 hours = '0' + hours;
17575 minutes = '0' + minutes;
17578 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17579 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17580 this.pop.select('button', true).first().dom.innerHTML = period;
17586 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17588 var cls = ['bottom'];
17590 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17597 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17602 this.picker().addClass(cls.join('-'));
17606 Roo.each(cls, function(c){
17608 _this.picker().setTop(_this.inputEl().getHeight());
17612 _this.picker().setTop(0 - _this.picker().getHeight());
17617 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17621 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17628 onFocus : function()
17630 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17634 onBlur : function()
17636 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17642 this.picker().show();
17647 this.fireEvent('show', this, this.date);
17652 this.picker().hide();
17655 this.fireEvent('hide', this, this.date);
17658 setTime : function()
17661 this.setValue(this.time.format(this.format));
17663 this.fireEvent('select', this, this.date);
17668 onMousedown: function(e){
17669 e.stopPropagation();
17670 e.preventDefault();
17673 onIncrementHours: function()
17675 Roo.log('onIncrementHours');
17676 this.time = this.time.add(Date.HOUR, 1);
17681 onDecrementHours: function()
17683 Roo.log('onDecrementHours');
17684 this.time = this.time.add(Date.HOUR, -1);
17688 onIncrementMinutes: function()
17690 Roo.log('onIncrementMinutes');
17691 this.time = this.time.add(Date.MINUTE, 1);
17695 onDecrementMinutes: function()
17697 Roo.log('onDecrementMinutes');
17698 this.time = this.time.add(Date.MINUTE, -1);
17702 onTogglePeriod: function()
17704 Roo.log('onTogglePeriod');
17705 this.time = this.time.add(Date.HOUR, 12);
17712 Roo.apply(Roo.bootstrap.TimeField, {
17742 cls: 'btn btn-info ok',
17754 Roo.apply(Roo.bootstrap.TimeField, {
17758 cls: 'datepicker dropdown-menu',
17762 cls: 'datepicker-time',
17766 cls: 'table-condensed',
17768 Roo.bootstrap.TimeField.content,
17769 Roo.bootstrap.TimeField.footer
17788 * @class Roo.bootstrap.MonthField
17789 * @extends Roo.bootstrap.Input
17790 * Bootstrap MonthField class
17792 * @cfg {String} language default en
17795 * Create a new MonthField
17796 * @param {Object} config The config object
17799 Roo.bootstrap.MonthField = function(config){
17800 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17805 * Fires when this field show.
17806 * @param {Roo.bootstrap.MonthField} this
17807 * @param {Mixed} date The date value
17812 * Fires when this field hide.
17813 * @param {Roo.bootstrap.MonthField} this
17814 * @param {Mixed} date The date value
17819 * Fires when select a date.
17820 * @param {Roo.bootstrap.MonthField} this
17821 * @param {String} oldvalue The old value
17822 * @param {String} newvalue The new value
17828 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17830 onRender: function(ct, position)
17833 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17835 this.language = this.language || 'en';
17836 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17837 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17839 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17840 this.isInline = false;
17841 this.isInput = true;
17842 this.component = this.el.select('.add-on', true).first() || false;
17843 this.component = (this.component && this.component.length === 0) ? false : this.component;
17844 this.hasInput = this.component && this.inputEL().length;
17846 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17848 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17850 this.picker().on('mousedown', this.onMousedown, this);
17851 this.picker().on('click', this.onClick, this);
17853 this.picker().addClass('datepicker-dropdown');
17855 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17856 v.setStyle('width', '189px');
17863 if(this.isInline) {
17869 setValue: function(v, suppressEvent)
17871 var o = this.getValue();
17873 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17877 if(suppressEvent !== true){
17878 this.fireEvent('select', this, o, v);
17883 getValue: function()
17888 onClick: function(e)
17890 e.stopPropagation();
17891 e.preventDefault();
17893 var target = e.getTarget();
17895 if(target.nodeName.toLowerCase() === 'i'){
17896 target = Roo.get(target).dom.parentNode;
17899 var nodeName = target.nodeName;
17900 var className = target.className;
17901 var html = target.innerHTML;
17903 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17907 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17909 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17915 picker : function()
17917 return this.pickerEl;
17920 fillMonths: function()
17923 var months = this.picker().select('>.datepicker-months td', true).first();
17925 months.dom.innerHTML = '';
17931 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17934 months.createChild(month);
17943 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17944 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17947 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17948 e.removeClass('active');
17950 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17951 e.addClass('active');
17958 if(this.isInline) return;
17960 this.picker().removeClass(['bottom', 'top']);
17962 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17964 * place to the top of element!
17968 this.picker().addClass('top');
17969 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17974 this.picker().addClass('bottom');
17976 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17979 onFocus : function()
17981 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17985 onBlur : function()
17987 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17989 var d = this.inputEl().getValue();
17998 this.picker().show();
17999 this.picker().select('>.datepicker-months', true).first().show();
18003 this.fireEvent('show', this, this.date);
18008 if(this.isInline) return;
18009 this.picker().hide();
18010 this.fireEvent('hide', this, this.date);
18014 onMousedown: function(e)
18016 e.stopPropagation();
18017 e.preventDefault();
18022 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18026 fireKey: function(e)
18028 if (!this.picker().isVisible()){
18029 if (e.keyCode == 27) // allow escape to hide and re-show picker
18039 e.preventDefault();
18043 dir = e.keyCode == 37 ? -1 : 1;
18045 this.vIndex = this.vIndex + dir;
18047 if(this.vIndex < 0){
18051 if(this.vIndex > 11){
18055 if(isNaN(this.vIndex)){
18059 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18065 dir = e.keyCode == 38 ? -1 : 1;
18067 this.vIndex = this.vIndex + dir * 4;
18069 if(this.vIndex < 0){
18073 if(this.vIndex > 11){
18077 if(isNaN(this.vIndex)){
18081 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18086 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18087 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18091 e.preventDefault();
18094 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18095 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18111 this.picker().remove();
18116 Roo.apply(Roo.bootstrap.MonthField, {
18135 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18136 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18141 Roo.apply(Roo.bootstrap.MonthField, {
18145 cls: 'datepicker dropdown-menu roo-dynamic',
18149 cls: 'datepicker-months',
18153 cls: 'table-condensed',
18155 Roo.bootstrap.DateField.content
18175 * @class Roo.bootstrap.CheckBox
18176 * @extends Roo.bootstrap.Input
18177 * Bootstrap CheckBox class
18179 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18180 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18181 * @cfg {String} boxLabel The text that appears beside the checkbox
18182 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18183 * @cfg {Boolean} checked initnal the element
18184 * @cfg {Boolean} inline inline the element (default false)
18185 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18188 * Create a new CheckBox
18189 * @param {Object} config The config object
18192 Roo.bootstrap.CheckBox = function(config){
18193 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18198 * Fires when the element is checked or unchecked.
18199 * @param {Roo.bootstrap.CheckBox} this This input
18200 * @param {Boolean} checked The new checked value
18207 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18209 inputType: 'checkbox',
18217 getAutoCreate : function()
18219 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18225 cfg.cls = 'form-group ' + this.inputType; //input-group
18228 cfg.cls += ' ' + this.inputType + '-inline';
18234 type : this.inputType,
18235 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18236 cls : 'roo-' + this.inputType, //'form-box',
18237 placeholder : this.placeholder || ''
18241 if (this.weight) { // Validity check?
18242 cfg.cls += " " + this.inputType + "-" + this.weight;
18245 if (this.disabled) {
18246 input.disabled=true;
18250 input.checked = this.checked;
18254 input.name = this.name;
18258 input.cls += ' input-' + this.size;
18263 ['xs','sm','md','lg'].map(function(size){
18264 if (settings[size]) {
18265 cfg.cls += ' col-' + size + '-' + settings[size];
18269 var inputblock = input;
18271 if (this.before || this.after) {
18274 cls : 'input-group',
18279 inputblock.cn.push({
18281 cls : 'input-group-addon',
18286 inputblock.cn.push(input);
18289 inputblock.cn.push({
18291 cls : 'input-group-addon',
18298 if (align ==='left' && this.fieldLabel.length) {
18299 Roo.log("left and has label");
18305 cls : 'control-label col-md-' + this.labelWidth,
18306 html : this.fieldLabel
18310 cls : "col-md-" + (12 - this.labelWidth),
18317 } else if ( this.fieldLabel.length) {
18322 tag: this.boxLabel ? 'span' : 'label',
18324 cls: 'control-label box-input-label',
18325 //cls : 'input-group-addon',
18326 html : this.fieldLabel
18336 Roo.log(" no label && no align");
18337 cfg.cn = [ inputblock ] ;
18342 var boxLabelCfg = {
18344 //'for': id, // box label is handled by onclick - so no for...
18346 html: this.boxLabel
18350 boxLabelCfg.tooltip = this.tooltip;
18353 cfg.cn.push(boxLabelCfg);
18363 * return the real input element.
18365 inputEl: function ()
18367 return this.el.select('input.roo-' + this.inputType,true).first();
18370 labelEl: function()
18372 return this.el.select('label.control-label',true).first();
18374 /* depricated... */
18378 return this.labelEl();
18381 initEvents : function()
18383 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18385 this.inputEl().on('click', this.onClick, this);
18387 if (this.boxLabel) {
18388 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18391 this.startValue = this.getValue();
18394 Roo.bootstrap.CheckBox.register(this);
18398 onClick : function()
18400 this.setChecked(!this.checked);
18403 setChecked : function(state,suppressEvent)
18405 this.startValue = this.getValue();
18407 if(this.inputType == 'radio'){
18409 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18410 e.dom.checked = false;
18413 this.inputEl().dom.checked = true;
18415 this.inputEl().dom.value = this.inputValue;
18417 if(suppressEvent !== true){
18418 this.fireEvent('check', this, true);
18426 this.checked = state;
18428 this.inputEl().dom.checked = state;
18430 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18432 if(suppressEvent !== true){
18433 this.fireEvent('check', this, state);
18439 getValue : function()
18441 if(this.inputType == 'radio'){
18442 return this.getGroupValue();
18445 return this.inputEl().getValue();
18449 getGroupValue : function()
18451 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18455 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18458 setValue : function(v,suppressEvent)
18460 if(this.inputType == 'radio'){
18461 this.setGroupValue(v, suppressEvent);
18465 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18470 setGroupValue : function(v, suppressEvent)
18472 this.startValue = this.getValue();
18474 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18475 e.dom.checked = false;
18477 if(e.dom.value == v){
18478 e.dom.checked = true;
18482 if(suppressEvent !== true){
18483 this.fireEvent('check', this, true);
18491 validate : function()
18495 (this.inputType == 'radio' && this.validateRadio()) ||
18496 (this.inputType == 'checkbox' && this.validateCheckbox())
18502 this.markInvalid();
18506 validateRadio : function()
18510 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18511 if(!e.dom.checked){
18523 validateCheckbox : function()
18526 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18529 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18537 for(var i in group){
18542 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18549 * Mark this field as valid
18551 markValid : function()
18553 if(this.allowBlank){
18559 this.fireEvent('valid', this);
18561 if(this.inputType == 'radio'){
18562 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18563 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18564 e.findParent('.form-group', false, true).addClass(_this.validClass);
18571 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18572 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18576 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18582 for(var i in group){
18583 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18584 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18589 * Mark this field as invalid
18590 * @param {String} msg The validation message
18592 markInvalid : function(msg)
18594 if(this.allowBlank){
18600 this.fireEvent('invalid', this, msg);
18602 if(this.inputType == 'radio'){
18603 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18604 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18605 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18612 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18613 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18617 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18623 for(var i in group){
18624 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18625 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18632 Roo.apply(Roo.bootstrap.CheckBox, {
18637 * register a CheckBox Group
18638 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18640 register : function(checkbox)
18642 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18643 this.groups[checkbox.groupId] = {};
18646 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18650 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18654 * fetch a CheckBox Group based on the group ID
18655 * @param {string} the group ID
18656 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18658 get: function(groupId) {
18659 if (typeof(this.groups[groupId]) == 'undefined') {
18663 return this.groups[groupId] ;
18675 *<div class="radio">
18677 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18678 Option one is this and that—be sure to include why it's great
18685 *<label class="radio-inline">fieldLabel</label>
18686 *<label class="radio-inline">
18687 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18695 * @class Roo.bootstrap.Radio
18696 * @extends Roo.bootstrap.CheckBox
18697 * Bootstrap Radio class
18700 * Create a new Radio
18701 * @param {Object} config The config object
18704 Roo.bootstrap.Radio = function(config){
18705 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18709 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18711 inputType: 'radio',
18715 getAutoCreate : function()
18717 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18718 align = align || 'left'; // default...
18725 tag : this.inline ? 'span' : 'div',
18730 var inline = this.inline ? ' radio-inline' : '';
18734 // does not need for, as we wrap the input with it..
18736 cls : 'control-label box-label' + inline,
18739 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18743 //cls : 'control-label' + inline,
18744 html : this.fieldLabel,
18745 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18754 type : this.inputType,
18755 //value : (!this.checked) ? this.valueOff : this.inputValue,
18756 value : this.inputValue,
18758 placeholder : this.placeholder || '' // ?? needed????
18761 if (this.weight) { // Validity check?
18762 input.cls += " radio-" + this.weight;
18764 if (this.disabled) {
18765 input.disabled=true;
18769 input.checked = this.checked;
18773 input.name = this.name;
18777 input.cls += ' input-' + this.size;
18780 //?? can span's inline have a width??
18783 ['xs','sm','md','lg'].map(function(size){
18784 if (settings[size]) {
18785 cfg.cls += ' col-' + size + '-' + settings[size];
18789 var inputblock = input;
18791 if (this.before || this.after) {
18794 cls : 'input-group',
18799 inputblock.cn.push({
18801 cls : 'input-group-addon',
18805 inputblock.cn.push(input);
18807 inputblock.cn.push({
18809 cls : 'input-group-addon',
18817 if (this.fieldLabel && this.fieldLabel.length) {
18818 cfg.cn.push(fieldLabel);
18821 // normal bootstrap puts the input inside the label.
18822 // however with our styled version - it has to go after the input.
18824 //lbl.cn.push(inputblock);
18828 cls: 'radio' + inline,
18835 cfg.cn.push( lblwrap);
18840 html: this.boxLabel
18849 initEvents : function()
18851 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18853 this.inputEl().on('click', this.onClick, this);
18854 if (this.boxLabel) {
18855 Roo.log('find label')
18856 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18861 inputEl: function ()
18863 return this.el.select('input.roo-radio',true).first();
18865 onClick : function()
18868 this.setChecked(true);
18871 setChecked : function(state,suppressEvent)
18874 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18875 v.dom.checked = false;
18878 Roo.log(this.inputEl().dom);
18879 this.checked = state;
18880 this.inputEl().dom.checked = state;
18882 if(suppressEvent !== true){
18883 this.fireEvent('check', this, state);
18886 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18890 getGroupValue : function()
18893 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18894 if(v.dom.checked == true){
18895 value = v.dom.value;
18903 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18904 * @return {Mixed} value The field value
18906 getValue : function(){
18907 return this.getGroupValue();
18913 //<script type="text/javascript">
18916 * Based Ext JS Library 1.1.1
18917 * Copyright(c) 2006-2007, Ext JS, LLC.
18923 * @class Roo.HtmlEditorCore
18924 * @extends Roo.Component
18925 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18927 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18930 Roo.HtmlEditorCore = function(config){
18933 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18938 * @event initialize
18939 * Fires when the editor is fully initialized (including the iframe)
18940 * @param {Roo.HtmlEditorCore} this
18945 * Fires when the editor is first receives the focus. Any insertion must wait
18946 * until after this event.
18947 * @param {Roo.HtmlEditorCore} this
18951 * @event beforesync
18952 * Fires before the textarea is updated with content from the editor iframe. Return false
18953 * to cancel the sync.
18954 * @param {Roo.HtmlEditorCore} this
18955 * @param {String} html
18959 * @event beforepush
18960 * Fires before the iframe editor is updated with content from the textarea. Return false
18961 * to cancel the push.
18962 * @param {Roo.HtmlEditorCore} this
18963 * @param {String} html
18968 * Fires when the textarea is updated with content from the editor iframe.
18969 * @param {Roo.HtmlEditorCore} this
18970 * @param {String} html
18975 * Fires when the iframe editor is updated with content from the textarea.
18976 * @param {Roo.HtmlEditorCore} this
18977 * @param {String} html
18982 * @event editorevent
18983 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18984 * @param {Roo.HtmlEditorCore} this
18990 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18992 // defaults : white / black...
18993 this.applyBlacklists();
19000 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19004 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19010 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19015 * @cfg {Number} height (in pixels)
19019 * @cfg {Number} width (in pixels)
19024 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19027 stylesheets: false,
19032 // private properties
19033 validationEvent : false,
19035 initialized : false,
19037 sourceEditMode : false,
19038 onFocus : Roo.emptyFn,
19040 hideMode:'offsets',
19044 // blacklist + whitelisted elements..
19051 * Protected method that will not generally be called directly. It
19052 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19053 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19055 getDocMarkup : function(){
19059 // inherit styels from page...??
19060 if (this.stylesheets === false) {
19062 Roo.get(document.head).select('style').each(function(node) {
19063 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19066 Roo.get(document.head).select('link').each(function(node) {
19067 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19070 } else if (!this.stylesheets.length) {
19072 st = '<style type="text/css">' +
19073 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19079 st += '<style type="text/css">' +
19080 'IMG { cursor: pointer } ' +
19084 return '<html><head>' + st +
19085 //<style type="text/css">' +
19086 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19088 ' </head><body class="roo-htmleditor-body"></body></html>';
19092 onRender : function(ct, position)
19095 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19096 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19099 this.el.dom.style.border = '0 none';
19100 this.el.dom.setAttribute('tabIndex', -1);
19101 this.el.addClass('x-hidden hide');
19105 if(Roo.isIE){ // fix IE 1px bogus margin
19106 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19110 this.frameId = Roo.id();
19114 var iframe = this.owner.wrap.createChild({
19116 cls: 'form-control', // bootstrap..
19118 name: this.frameId,
19119 frameBorder : 'no',
19120 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19125 this.iframe = iframe.dom;
19127 this.assignDocWin();
19129 this.doc.designMode = 'on';
19132 this.doc.write(this.getDocMarkup());
19136 var task = { // must defer to wait for browser to be ready
19138 //console.log("run task?" + this.doc.readyState);
19139 this.assignDocWin();
19140 if(this.doc.body || this.doc.readyState == 'complete'){
19142 this.doc.designMode="on";
19146 Roo.TaskMgr.stop(task);
19147 this.initEditor.defer(10, this);
19154 Roo.TaskMgr.start(task);
19159 onResize : function(w, h)
19161 Roo.log('resize: ' +w + ',' + h );
19162 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19166 if(typeof w == 'number'){
19168 this.iframe.style.width = w + 'px';
19170 if(typeof h == 'number'){
19172 this.iframe.style.height = h + 'px';
19174 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19181 * Toggles the editor between standard and source edit mode.
19182 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19184 toggleSourceEdit : function(sourceEditMode){
19186 this.sourceEditMode = sourceEditMode === true;
19188 if(this.sourceEditMode){
19190 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19193 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19194 //this.iframe.className = '';
19197 //this.setSize(this.owner.wrap.getSize());
19198 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19205 * Protected method that will not generally be called directly. If you need/want
19206 * custom HTML cleanup, this is the method you should override.
19207 * @param {String} html The HTML to be cleaned
19208 * return {String} The cleaned HTML
19210 cleanHtml : function(html){
19211 html = String(html);
19212 if(html.length > 5){
19213 if(Roo.isSafari){ // strip safari nonsense
19214 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19217 if(html == ' '){
19224 * HTML Editor -> Textarea
19225 * Protected method that will not generally be called directly. Syncs the contents
19226 * of the editor iframe with the textarea.
19228 syncValue : function(){
19229 if(this.initialized){
19230 var bd = (this.doc.body || this.doc.documentElement);
19231 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19232 var html = bd.innerHTML;
19234 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19235 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19237 html = '<div style="'+m[0]+'">' + html + '</div>';
19240 html = this.cleanHtml(html);
19241 // fix up the special chars.. normaly like back quotes in word...
19242 // however we do not want to do this with chinese..
19243 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19244 var cc = b.charCodeAt();
19246 (cc >= 0x4E00 && cc < 0xA000 ) ||
19247 (cc >= 0x3400 && cc < 0x4E00 ) ||
19248 (cc >= 0xf900 && cc < 0xfb00 )
19254 if(this.owner.fireEvent('beforesync', this, html) !== false){
19255 this.el.dom.value = html;
19256 this.owner.fireEvent('sync', this, html);
19262 * Protected method that will not generally be called directly. Pushes the value of the textarea
19263 * into the iframe editor.
19265 pushValue : function(){
19266 if(this.initialized){
19267 var v = this.el.dom.value.trim();
19269 // if(v.length < 1){
19273 if(this.owner.fireEvent('beforepush', this, v) !== false){
19274 var d = (this.doc.body || this.doc.documentElement);
19276 this.cleanUpPaste();
19277 this.el.dom.value = d.innerHTML;
19278 this.owner.fireEvent('push', this, v);
19284 deferFocus : function(){
19285 this.focus.defer(10, this);
19289 focus : function(){
19290 if(this.win && !this.sourceEditMode){
19297 assignDocWin: function()
19299 var iframe = this.iframe;
19302 this.doc = iframe.contentWindow.document;
19303 this.win = iframe.contentWindow;
19305 // if (!Roo.get(this.frameId)) {
19308 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19309 // this.win = Roo.get(this.frameId).dom.contentWindow;
19311 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19315 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19316 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19321 initEditor : function(){
19322 //console.log("INIT EDITOR");
19323 this.assignDocWin();
19327 this.doc.designMode="on";
19329 this.doc.write(this.getDocMarkup());
19332 var dbody = (this.doc.body || this.doc.documentElement);
19333 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19334 // this copies styles from the containing element into thsi one..
19335 // not sure why we need all of this..
19336 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19338 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19339 //ss['background-attachment'] = 'fixed'; // w3c
19340 dbody.bgProperties = 'fixed'; // ie
19341 //Roo.DomHelper.applyStyles(dbody, ss);
19342 Roo.EventManager.on(this.doc, {
19343 //'mousedown': this.onEditorEvent,
19344 'mouseup': this.onEditorEvent,
19345 'dblclick': this.onEditorEvent,
19346 'click': this.onEditorEvent,
19347 'keyup': this.onEditorEvent,
19352 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19354 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19355 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19357 this.initialized = true;
19359 this.owner.fireEvent('initialize', this);
19364 onDestroy : function(){
19370 //for (var i =0; i < this.toolbars.length;i++) {
19371 // // fixme - ask toolbars for heights?
19372 // this.toolbars[i].onDestroy();
19375 //this.wrap.dom.innerHTML = '';
19376 //this.wrap.remove();
19381 onFirstFocus : function(){
19383 this.assignDocWin();
19386 this.activated = true;
19389 if(Roo.isGecko){ // prevent silly gecko errors
19391 var s = this.win.getSelection();
19392 if(!s.focusNode || s.focusNode.nodeType != 3){
19393 var r = s.getRangeAt(0);
19394 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19399 this.execCmd('useCSS', true);
19400 this.execCmd('styleWithCSS', false);
19403 this.owner.fireEvent('activate', this);
19407 adjustFont: function(btn){
19408 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19409 //if(Roo.isSafari){ // safari
19412 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19413 if(Roo.isSafari){ // safari
19414 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19415 v = (v < 10) ? 10 : v;
19416 v = (v > 48) ? 48 : v;
19417 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19422 v = Math.max(1, v+adjust);
19424 this.execCmd('FontSize', v );
19427 onEditorEvent : function(e)
19429 this.owner.fireEvent('editorevent', this, e);
19430 // this.updateToolbar();
19431 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19434 insertTag : function(tg)
19436 // could be a bit smarter... -> wrap the current selected tRoo..
19437 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19439 range = this.createRange(this.getSelection());
19440 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19441 wrappingNode.appendChild(range.extractContents());
19442 range.insertNode(wrappingNode);
19449 this.execCmd("formatblock", tg);
19453 insertText : function(txt)
19457 var range = this.createRange();
19458 range.deleteContents();
19459 //alert(Sender.getAttribute('label'));
19461 range.insertNode(this.doc.createTextNode(txt));
19467 * Executes a Midas editor command on the editor document and performs necessary focus and
19468 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19469 * @param {String} cmd The Midas command
19470 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19472 relayCmd : function(cmd, value){
19474 this.execCmd(cmd, value);
19475 this.owner.fireEvent('editorevent', this);
19476 //this.updateToolbar();
19477 this.owner.deferFocus();
19481 * Executes a Midas editor command directly on the editor document.
19482 * For visual commands, you should use {@link #relayCmd} instead.
19483 * <b>This should only be called after the editor is initialized.</b>
19484 * @param {String} cmd The Midas command
19485 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19487 execCmd : function(cmd, value){
19488 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19495 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19497 * @param {String} text | dom node..
19499 insertAtCursor : function(text)
19504 if(!this.activated){
19510 var r = this.doc.selection.createRange();
19521 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19525 // from jquery ui (MIT licenced)
19527 var win = this.win;
19529 if (win.getSelection && win.getSelection().getRangeAt) {
19530 range = win.getSelection().getRangeAt(0);
19531 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19532 range.insertNode(node);
19533 } else if (win.document.selection && win.document.selection.createRange) {
19534 // no firefox support
19535 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19536 win.document.selection.createRange().pasteHTML(txt);
19538 // no firefox support
19539 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19540 this.execCmd('InsertHTML', txt);
19549 mozKeyPress : function(e){
19551 var c = e.getCharCode(), cmd;
19554 c = String.fromCharCode(c).toLowerCase();
19568 this.cleanUpPaste.defer(100, this);
19576 e.preventDefault();
19584 fixKeys : function(){ // load time branching for fastest keydown performance
19586 return function(e){
19587 var k = e.getKey(), r;
19590 r = this.doc.selection.createRange();
19593 r.pasteHTML('    ');
19600 r = this.doc.selection.createRange();
19602 var target = r.parentElement();
19603 if(!target || target.tagName.toLowerCase() != 'li'){
19605 r.pasteHTML('<br />');
19611 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19612 this.cleanUpPaste.defer(100, this);
19618 }else if(Roo.isOpera){
19619 return function(e){
19620 var k = e.getKey();
19624 this.execCmd('InsertHTML','    ');
19627 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19628 this.cleanUpPaste.defer(100, this);
19633 }else if(Roo.isSafari){
19634 return function(e){
19635 var k = e.getKey();
19639 this.execCmd('InsertText','\t');
19643 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19644 this.cleanUpPaste.defer(100, this);
19652 getAllAncestors: function()
19654 var p = this.getSelectedNode();
19657 a.push(p); // push blank onto stack..
19658 p = this.getParentElement();
19662 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19666 a.push(this.doc.body);
19670 lastSelNode : false,
19673 getSelection : function()
19675 this.assignDocWin();
19676 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19679 getSelectedNode: function()
19681 // this may only work on Gecko!!!
19683 // should we cache this!!!!
19688 var range = this.createRange(this.getSelection()).cloneRange();
19691 var parent = range.parentElement();
19693 var testRange = range.duplicate();
19694 testRange.moveToElementText(parent);
19695 if (testRange.inRange(range)) {
19698 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19701 parent = parent.parentElement;
19706 // is ancestor a text element.
19707 var ac = range.commonAncestorContainer;
19708 if (ac.nodeType == 3) {
19709 ac = ac.parentNode;
19712 var ar = ac.childNodes;
19715 var other_nodes = [];
19716 var has_other_nodes = false;
19717 for (var i=0;i<ar.length;i++) {
19718 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19721 // fullly contained node.
19723 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19728 // probably selected..
19729 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19730 other_nodes.push(ar[i]);
19734 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19739 has_other_nodes = true;
19741 if (!nodes.length && other_nodes.length) {
19742 nodes= other_nodes;
19744 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19750 createRange: function(sel)
19752 // this has strange effects when using with
19753 // top toolbar - not sure if it's a great idea.
19754 //this.editor.contentWindow.focus();
19755 if (typeof sel != "undefined") {
19757 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19759 return this.doc.createRange();
19762 return this.doc.createRange();
19765 getParentElement: function()
19768 this.assignDocWin();
19769 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19771 var range = this.createRange(sel);
19774 var p = range.commonAncestorContainer;
19775 while (p.nodeType == 3) { // text node
19786 * Range intersection.. the hard stuff...
19790 * [ -- selected range --- ]
19794 * if end is before start or hits it. fail.
19795 * if start is after end or hits it fail.
19797 * if either hits (but other is outside. - then it's not
19803 // @see http://www.thismuchiknow.co.uk/?p=64.
19804 rangeIntersectsNode : function(range, node)
19806 var nodeRange = node.ownerDocument.createRange();
19808 nodeRange.selectNode(node);
19810 nodeRange.selectNodeContents(node);
19813 var rangeStartRange = range.cloneRange();
19814 rangeStartRange.collapse(true);
19816 var rangeEndRange = range.cloneRange();
19817 rangeEndRange.collapse(false);
19819 var nodeStartRange = nodeRange.cloneRange();
19820 nodeStartRange.collapse(true);
19822 var nodeEndRange = nodeRange.cloneRange();
19823 nodeEndRange.collapse(false);
19825 return rangeStartRange.compareBoundaryPoints(
19826 Range.START_TO_START, nodeEndRange) == -1 &&
19827 rangeEndRange.compareBoundaryPoints(
19828 Range.START_TO_START, nodeStartRange) == 1;
19832 rangeCompareNode : function(range, node)
19834 var nodeRange = node.ownerDocument.createRange();
19836 nodeRange.selectNode(node);
19838 nodeRange.selectNodeContents(node);
19842 range.collapse(true);
19844 nodeRange.collapse(true);
19846 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19847 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19849 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19851 var nodeIsBefore = ss == 1;
19852 var nodeIsAfter = ee == -1;
19854 if (nodeIsBefore && nodeIsAfter)
19856 if (!nodeIsBefore && nodeIsAfter)
19857 return 1; //right trailed.
19859 if (nodeIsBefore && !nodeIsAfter)
19860 return 2; // left trailed.
19865 // private? - in a new class?
19866 cleanUpPaste : function()
19868 // cleans up the whole document..
19869 Roo.log('cleanuppaste');
19871 this.cleanUpChildren(this.doc.body);
19872 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19873 if (clean != this.doc.body.innerHTML) {
19874 this.doc.body.innerHTML = clean;
19879 cleanWordChars : function(input) {// change the chars to hex code
19880 var he = Roo.HtmlEditorCore;
19882 var output = input;
19883 Roo.each(he.swapCodes, function(sw) {
19884 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19886 output = output.replace(swapper, sw[1]);
19893 cleanUpChildren : function (n)
19895 if (!n.childNodes.length) {
19898 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19899 this.cleanUpChild(n.childNodes[i]);
19906 cleanUpChild : function (node)
19909 //console.log(node);
19910 if (node.nodeName == "#text") {
19911 // clean up silly Windows -- stuff?
19914 if (node.nodeName == "#comment") {
19915 node.parentNode.removeChild(node);
19916 // clean up silly Windows -- stuff?
19919 var lcname = node.tagName.toLowerCase();
19920 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19921 // whitelist of tags..
19923 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19925 node.parentNode.removeChild(node);
19930 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19932 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19933 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19935 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19936 // remove_keep_children = true;
19939 if (remove_keep_children) {
19940 this.cleanUpChildren(node);
19941 // inserts everything just before this node...
19942 while (node.childNodes.length) {
19943 var cn = node.childNodes[0];
19944 node.removeChild(cn);
19945 node.parentNode.insertBefore(cn, node);
19947 node.parentNode.removeChild(node);
19951 if (!node.attributes || !node.attributes.length) {
19952 this.cleanUpChildren(node);
19956 function cleanAttr(n,v)
19959 if (v.match(/^\./) || v.match(/^\//)) {
19962 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19965 if (v.match(/^#/)) {
19968 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19969 node.removeAttribute(n);
19973 var cwhite = this.cwhite;
19974 var cblack = this.cblack;
19976 function cleanStyle(n,v)
19978 if (v.match(/expression/)) { //XSS?? should we even bother..
19979 node.removeAttribute(n);
19983 var parts = v.split(/;/);
19986 Roo.each(parts, function(p) {
19987 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19991 var l = p.split(':').shift().replace(/\s+/g,'');
19992 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19994 if ( cwhite.length && cblack.indexOf(l) > -1) {
19995 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19996 //node.removeAttribute(n);
20000 // only allow 'c whitelisted system attributes'
20001 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20002 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20003 //node.removeAttribute(n);
20013 if (clean.length) {
20014 node.setAttribute(n, clean.join(';'));
20016 node.removeAttribute(n);
20022 for (var i = node.attributes.length-1; i > -1 ; i--) {
20023 var a = node.attributes[i];
20026 if (a.name.toLowerCase().substr(0,2)=='on') {
20027 node.removeAttribute(a.name);
20030 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20031 node.removeAttribute(a.name);
20034 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20035 cleanAttr(a.name,a.value); // fixme..
20038 if (a.name == 'style') {
20039 cleanStyle(a.name,a.value);
20042 /// clean up MS crap..
20043 // tecnically this should be a list of valid class'es..
20046 if (a.name == 'class') {
20047 if (a.value.match(/^Mso/)) {
20048 node.className = '';
20051 if (a.value.match(/body/)) {
20052 node.className = '';
20063 this.cleanUpChildren(node);
20069 * Clean up MS wordisms...
20071 cleanWord : function(node)
20076 this.cleanWord(this.doc.body);
20079 if (node.nodeName == "#text") {
20080 // clean up silly Windows -- stuff?
20083 if (node.nodeName == "#comment") {
20084 node.parentNode.removeChild(node);
20085 // clean up silly Windows -- stuff?
20089 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20090 node.parentNode.removeChild(node);
20094 // remove - but keep children..
20095 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20096 while (node.childNodes.length) {
20097 var cn = node.childNodes[0];
20098 node.removeChild(cn);
20099 node.parentNode.insertBefore(cn, node);
20101 node.parentNode.removeChild(node);
20102 this.iterateChildren(node, this.cleanWord);
20106 if (node.className.length) {
20108 var cn = node.className.split(/\W+/);
20110 Roo.each(cn, function(cls) {
20111 if (cls.match(/Mso[a-zA-Z]+/)) {
20116 node.className = cna.length ? cna.join(' ') : '';
20118 node.removeAttribute("class");
20122 if (node.hasAttribute("lang")) {
20123 node.removeAttribute("lang");
20126 if (node.hasAttribute("style")) {
20128 var styles = node.getAttribute("style").split(";");
20130 Roo.each(styles, function(s) {
20131 if (!s.match(/:/)) {
20134 var kv = s.split(":");
20135 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20138 // what ever is left... we allow.
20141 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20142 if (!nstyle.length) {
20143 node.removeAttribute('style');
20146 this.iterateChildren(node, this.cleanWord);
20152 * iterateChildren of a Node, calling fn each time, using this as the scole..
20153 * @param {DomNode} node node to iterate children of.
20154 * @param {Function} fn method of this class to call on each item.
20156 iterateChildren : function(node, fn)
20158 if (!node.childNodes.length) {
20161 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20162 fn.call(this, node.childNodes[i])
20168 * cleanTableWidths.
20170 * Quite often pasting from word etc.. results in tables with column and widths.
20171 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20174 cleanTableWidths : function(node)
20179 this.cleanTableWidths(this.doc.body);
20184 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20187 Roo.log(node.tagName);
20188 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20189 this.iterateChildren(node, this.cleanTableWidths);
20192 if (node.hasAttribute('width')) {
20193 node.removeAttribute('width');
20197 if (node.hasAttribute("style")) {
20200 var styles = node.getAttribute("style").split(";");
20202 Roo.each(styles, function(s) {
20203 if (!s.match(/:/)) {
20206 var kv = s.split(":");
20207 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20210 // what ever is left... we allow.
20213 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20214 if (!nstyle.length) {
20215 node.removeAttribute('style');
20219 this.iterateChildren(node, this.cleanTableWidths);
20227 domToHTML : function(currentElement, depth, nopadtext) {
20229 depth = depth || 0;
20230 nopadtext = nopadtext || false;
20232 if (!currentElement) {
20233 return this.domToHTML(this.doc.body);
20236 //Roo.log(currentElement);
20238 var allText = false;
20239 var nodeName = currentElement.nodeName;
20240 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20242 if (nodeName == '#text') {
20244 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20249 if (nodeName != 'BODY') {
20252 // Prints the node tagName, such as <A>, <IMG>, etc
20255 for(i = 0; i < currentElement.attributes.length;i++) {
20257 var aname = currentElement.attributes.item(i).name;
20258 if (!currentElement.attributes.item(i).value.length) {
20261 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20264 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20273 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20276 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20281 // Traverse the tree
20283 var currentElementChild = currentElement.childNodes.item(i);
20284 var allText = true;
20285 var innerHTML = '';
20287 while (currentElementChild) {
20288 // Formatting code (indent the tree so it looks nice on the screen)
20289 var nopad = nopadtext;
20290 if (lastnode == 'SPAN') {
20294 if (currentElementChild.nodeName == '#text') {
20295 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20296 toadd = nopadtext ? toadd : toadd.trim();
20297 if (!nopad && toadd.length > 80) {
20298 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20300 innerHTML += toadd;
20303 currentElementChild = currentElement.childNodes.item(i);
20309 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20311 // Recursively traverse the tree structure of the child node
20312 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20313 lastnode = currentElementChild.nodeName;
20315 currentElementChild=currentElement.childNodes.item(i);
20321 // The remaining code is mostly for formatting the tree
20322 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20327 ret+= "</"+tagName+">";
20333 applyBlacklists : function()
20335 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20336 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20340 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20341 if (b.indexOf(tag) > -1) {
20344 this.white.push(tag);
20348 Roo.each(w, function(tag) {
20349 if (b.indexOf(tag) > -1) {
20352 if (this.white.indexOf(tag) > -1) {
20355 this.white.push(tag);
20360 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20361 if (w.indexOf(tag) > -1) {
20364 this.black.push(tag);
20368 Roo.each(b, function(tag) {
20369 if (w.indexOf(tag) > -1) {
20372 if (this.black.indexOf(tag) > -1) {
20375 this.black.push(tag);
20380 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20381 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20385 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20386 if (b.indexOf(tag) > -1) {
20389 this.cwhite.push(tag);
20393 Roo.each(w, function(tag) {
20394 if (b.indexOf(tag) > -1) {
20397 if (this.cwhite.indexOf(tag) > -1) {
20400 this.cwhite.push(tag);
20405 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20406 if (w.indexOf(tag) > -1) {
20409 this.cblack.push(tag);
20413 Roo.each(b, function(tag) {
20414 if (w.indexOf(tag) > -1) {
20417 if (this.cblack.indexOf(tag) > -1) {
20420 this.cblack.push(tag);
20425 setStylesheets : function(stylesheets)
20427 if(typeof(stylesheets) == 'string'){
20428 Roo.get(this.iframe.contentDocument.head).createChild({
20430 rel : 'stylesheet',
20439 Roo.each(stylesheets, function(s) {
20444 Roo.get(_this.iframe.contentDocument.head).createChild({
20446 rel : 'stylesheet',
20455 removeStylesheets : function()
20459 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20464 // hide stuff that is not compatible
20478 * @event specialkey
20482 * @cfg {String} fieldClass @hide
20485 * @cfg {String} focusClass @hide
20488 * @cfg {String} autoCreate @hide
20491 * @cfg {String} inputType @hide
20494 * @cfg {String} invalidClass @hide
20497 * @cfg {String} invalidText @hide
20500 * @cfg {String} msgFx @hide
20503 * @cfg {String} validateOnBlur @hide
20507 Roo.HtmlEditorCore.white = [
20508 'area', 'br', 'img', 'input', 'hr', 'wbr',
20510 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20511 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20512 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20513 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20514 'table', 'ul', 'xmp',
20516 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20519 'dir', 'menu', 'ol', 'ul', 'dl',
20525 Roo.HtmlEditorCore.black = [
20526 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20528 'base', 'basefont', 'bgsound', 'blink', 'body',
20529 'frame', 'frameset', 'head', 'html', 'ilayer',
20530 'iframe', 'layer', 'link', 'meta', 'object',
20531 'script', 'style' ,'title', 'xml' // clean later..
20533 Roo.HtmlEditorCore.clean = [
20534 'script', 'style', 'title', 'xml'
20536 Roo.HtmlEditorCore.remove = [
20541 Roo.HtmlEditorCore.ablack = [
20545 Roo.HtmlEditorCore.aclean = [
20546 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20550 Roo.HtmlEditorCore.pwhite= [
20551 'http', 'https', 'mailto'
20554 // white listed style attributes.
20555 Roo.HtmlEditorCore.cwhite= [
20556 // 'text-align', /// default is to allow most things..
20562 // black listed style attributes.
20563 Roo.HtmlEditorCore.cblack= [
20564 // 'font-size' -- this can be set by the project
20568 Roo.HtmlEditorCore.swapCodes =[
20587 * @class Roo.bootstrap.HtmlEditor
20588 * @extends Roo.bootstrap.TextArea
20589 * Bootstrap HtmlEditor class
20592 * Create a new HtmlEditor
20593 * @param {Object} config The config object
20596 Roo.bootstrap.HtmlEditor = function(config){
20597 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20598 if (!this.toolbars) {
20599 this.toolbars = [];
20601 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20604 * @event initialize
20605 * Fires when the editor is fully initialized (including the iframe)
20606 * @param {HtmlEditor} this
20611 * Fires when the editor is first receives the focus. Any insertion must wait
20612 * until after this event.
20613 * @param {HtmlEditor} this
20617 * @event beforesync
20618 * Fires before the textarea is updated with content from the editor iframe. Return false
20619 * to cancel the sync.
20620 * @param {HtmlEditor} this
20621 * @param {String} html
20625 * @event beforepush
20626 * Fires before the iframe editor is updated with content from the textarea. Return false
20627 * to cancel the push.
20628 * @param {HtmlEditor} this
20629 * @param {String} html
20634 * Fires when the textarea is updated with content from the editor iframe.
20635 * @param {HtmlEditor} this
20636 * @param {String} html
20641 * Fires when the iframe editor is updated with content from the textarea.
20642 * @param {HtmlEditor} this
20643 * @param {String} html
20647 * @event editmodechange
20648 * Fires when the editor switches edit modes
20649 * @param {HtmlEditor} this
20650 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20652 editmodechange: true,
20654 * @event editorevent
20655 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20656 * @param {HtmlEditor} this
20660 * @event firstfocus
20661 * Fires when on first focus - needed by toolbars..
20662 * @param {HtmlEditor} this
20667 * Auto save the htmlEditor value as a file into Events
20668 * @param {HtmlEditor} this
20672 * @event savedpreview
20673 * preview the saved version of htmlEditor
20674 * @param {HtmlEditor} this
20681 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20685 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20690 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20695 * @cfg {Number} height (in pixels)
20699 * @cfg {Number} width (in pixels)
20704 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20707 stylesheets: false,
20712 // private properties
20713 validationEvent : false,
20715 initialized : false,
20718 onFocus : Roo.emptyFn,
20720 hideMode:'offsets',
20723 tbContainer : false,
20725 toolbarContainer :function() {
20726 return this.wrap.select('.x-html-editor-tb',true).first();
20730 * Protected method that will not generally be called directly. It
20731 * is called when the editor creates its toolbar. Override this method if you need to
20732 * add custom toolbar buttons.
20733 * @param {HtmlEditor} editor
20735 createToolbar : function(){
20737 Roo.log("create toolbars");
20739 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20740 this.toolbars[0].render(this.toolbarContainer());
20744 // if (!editor.toolbars || !editor.toolbars.length) {
20745 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20748 // for (var i =0 ; i < editor.toolbars.length;i++) {
20749 // editor.toolbars[i] = Roo.factory(
20750 // typeof(editor.toolbars[i]) == 'string' ?
20751 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20752 // Roo.bootstrap.HtmlEditor);
20753 // editor.toolbars[i].init(editor);
20759 onRender : function(ct, position)
20761 // Roo.log("Call onRender: " + this.xtype);
20763 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20765 this.wrap = this.inputEl().wrap({
20766 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20769 this.editorcore.onRender(ct, position);
20771 if (this.resizable) {
20772 this.resizeEl = new Roo.Resizable(this.wrap, {
20776 minHeight : this.height,
20777 height: this.height,
20778 handles : this.resizable,
20781 resize : function(r, w, h) {
20782 _t.onResize(w,h); // -something
20788 this.createToolbar(this);
20791 if(!this.width && this.resizable){
20792 this.setSize(this.wrap.getSize());
20794 if (this.resizeEl) {
20795 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20796 // should trigger onReize..
20802 onResize : function(w, h)
20804 Roo.log('resize: ' +w + ',' + h );
20805 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20809 if(this.inputEl() ){
20810 if(typeof w == 'number'){
20811 var aw = w - this.wrap.getFrameWidth('lr');
20812 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20815 if(typeof h == 'number'){
20816 var tbh = -11; // fixme it needs to tool bar size!
20817 for (var i =0; i < this.toolbars.length;i++) {
20818 // fixme - ask toolbars for heights?
20819 tbh += this.toolbars[i].el.getHeight();
20820 //if (this.toolbars[i].footer) {
20821 // tbh += this.toolbars[i].footer.el.getHeight();
20829 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20830 ah -= 5; // knock a few pixes off for look..
20831 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20835 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20836 this.editorcore.onResize(ew,eh);
20841 * Toggles the editor between standard and source edit mode.
20842 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20844 toggleSourceEdit : function(sourceEditMode)
20846 this.editorcore.toggleSourceEdit(sourceEditMode);
20848 if(this.editorcore.sourceEditMode){
20849 Roo.log('editor - showing textarea');
20852 // Roo.log(this.syncValue());
20854 this.inputEl().removeClass(['hide', 'x-hidden']);
20855 this.inputEl().dom.removeAttribute('tabIndex');
20856 this.inputEl().focus();
20858 Roo.log('editor - hiding textarea');
20860 // Roo.log(this.pushValue());
20863 this.inputEl().addClass(['hide', 'x-hidden']);
20864 this.inputEl().dom.setAttribute('tabIndex', -1);
20865 //this.deferFocus();
20868 if(this.resizable){
20869 this.setSize(this.wrap.getSize());
20872 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20875 // private (for BoxComponent)
20876 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20878 // private (for BoxComponent)
20879 getResizeEl : function(){
20883 // private (for BoxComponent)
20884 getPositionEl : function(){
20889 initEvents : function(){
20890 this.originalValue = this.getValue();
20894 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20897 // markInvalid : Roo.emptyFn,
20899 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20902 // clearInvalid : Roo.emptyFn,
20904 setValue : function(v){
20905 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20906 this.editorcore.pushValue();
20911 deferFocus : function(){
20912 this.focus.defer(10, this);
20916 focus : function(){
20917 this.editorcore.focus();
20923 onDestroy : function(){
20929 for (var i =0; i < this.toolbars.length;i++) {
20930 // fixme - ask toolbars for heights?
20931 this.toolbars[i].onDestroy();
20934 this.wrap.dom.innerHTML = '';
20935 this.wrap.remove();
20940 onFirstFocus : function(){
20941 //Roo.log("onFirstFocus");
20942 this.editorcore.onFirstFocus();
20943 for (var i =0; i < this.toolbars.length;i++) {
20944 this.toolbars[i].onFirstFocus();
20950 syncValue : function()
20952 this.editorcore.syncValue();
20955 pushValue : function()
20957 this.editorcore.pushValue();
20961 // hide stuff that is not compatible
20975 * @event specialkey
20979 * @cfg {String} fieldClass @hide
20982 * @cfg {String} focusClass @hide
20985 * @cfg {String} autoCreate @hide
20988 * @cfg {String} inputType @hide
20991 * @cfg {String} invalidClass @hide
20994 * @cfg {String} invalidText @hide
20997 * @cfg {String} msgFx @hide
21000 * @cfg {String} validateOnBlur @hide
21009 Roo.namespace('Roo.bootstrap.htmleditor');
21011 * @class Roo.bootstrap.HtmlEditorToolbar1
21016 new Roo.bootstrap.HtmlEditor({
21019 new Roo.bootstrap.HtmlEditorToolbar1({
21020 disable : { fonts: 1 , format: 1, ..., ... , ...],
21026 * @cfg {Object} disable List of elements to disable..
21027 * @cfg {Array} btns List of additional buttons.
21031 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21034 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21037 Roo.apply(this, config);
21039 // default disabled, based on 'good practice'..
21040 this.disable = this.disable || {};
21041 Roo.applyIf(this.disable, {
21044 specialElements : true
21046 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21048 this.editor = config.editor;
21049 this.editorcore = config.editor.editorcore;
21051 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21053 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21054 // dont call parent... till later.
21056 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21061 editorcore : false,
21066 "h1","h2","h3","h4","h5","h6",
21068 "abbr", "acronym", "address", "cite", "samp", "var",
21072 onRender : function(ct, position)
21074 // Roo.log("Call onRender: " + this.xtype);
21076 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21078 this.el.dom.style.marginBottom = '0';
21080 var editorcore = this.editorcore;
21081 var editor= this.editor;
21084 var btn = function(id,cmd , toggle, handler){
21086 var event = toggle ? 'toggle' : 'click';
21091 xns: Roo.bootstrap,
21094 enableToggle:toggle !== false,
21096 pressed : toggle ? false : null,
21099 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21100 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21109 xns: Roo.bootstrap,
21110 glyphicon : 'font',
21114 xns: Roo.bootstrap,
21118 Roo.each(this.formats, function(f) {
21119 style.menu.items.push({
21121 xns: Roo.bootstrap,
21122 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21127 editorcore.insertTag(this.tagname);
21134 children.push(style);
21137 btn('bold',false,true);
21138 btn('italic',false,true);
21139 btn('align-left', 'justifyleft',true);
21140 btn('align-center', 'justifycenter',true);
21141 btn('align-right' , 'justifyright',true);
21142 btn('link', false, false, function(btn) {
21143 //Roo.log("create link?");
21144 var url = prompt(this.createLinkText, this.defaultLinkValue);
21145 if(url && url != 'http:/'+'/'){
21146 this.editorcore.relayCmd('createlink', url);
21149 btn('list','insertunorderedlist',true);
21150 btn('pencil', false,true, function(btn){
21153 this.toggleSourceEdit(btn.pressed);
21159 xns: Roo.bootstrap,
21164 xns: Roo.bootstrap,
21169 cog.menu.items.push({
21171 xns: Roo.bootstrap,
21172 html : Clean styles,
21177 editorcore.insertTag(this.tagname);
21186 this.xtype = 'NavSimplebar';
21188 for(var i=0;i< children.length;i++) {
21190 this.buttons.add(this.addxtypeChild(children[i]));
21194 editor.on('editorevent', this.updateToolbar, this);
21196 onBtnClick : function(id)
21198 this.editorcore.relayCmd(id);
21199 this.editorcore.focus();
21203 * Protected method that will not generally be called directly. It triggers
21204 * a toolbar update by reading the markup state of the current selection in the editor.
21206 updateToolbar: function(){
21208 if(!this.editorcore.activated){
21209 this.editor.onFirstFocus(); // is this neeed?
21213 var btns = this.buttons;
21214 var doc = this.editorcore.doc;
21215 btns.get('bold').setActive(doc.queryCommandState('bold'));
21216 btns.get('italic').setActive(doc.queryCommandState('italic'));
21217 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21219 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21220 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21221 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21223 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21224 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21227 var ans = this.editorcore.getAllAncestors();
21228 if (this.formatCombo) {
21231 var store = this.formatCombo.store;
21232 this.formatCombo.setValue("");
21233 for (var i =0; i < ans.length;i++) {
21234 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21236 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21244 // hides menus... - so this cant be on a menu...
21245 Roo.bootstrap.MenuMgr.hideAll();
21247 Roo.bootstrap.MenuMgr.hideAll();
21248 //this.editorsyncValue();
21250 onFirstFocus: function() {
21251 this.buttons.each(function(item){
21255 toggleSourceEdit : function(sourceEditMode){
21258 if(sourceEditMode){
21259 Roo.log("disabling buttons");
21260 this.buttons.each( function(item){
21261 if(item.cmd != 'pencil'){
21267 Roo.log("enabling buttons");
21268 if(this.editorcore.initialized){
21269 this.buttons.each( function(item){
21275 Roo.log("calling toggole on editor");
21276 // tell the editor that it's been pressed..
21277 this.editor.toggleSourceEdit(sourceEditMode);
21287 * @class Roo.bootstrap.Table.AbstractSelectionModel
21288 * @extends Roo.util.Observable
21289 * Abstract base class for grid SelectionModels. It provides the interface that should be
21290 * implemented by descendant classes. This class should not be directly instantiated.
21293 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21294 this.locked = false;
21295 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21299 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21300 /** @ignore Called by the grid automatically. Do not call directly. */
21301 init : function(grid){
21307 * Locks the selections.
21310 this.locked = true;
21314 * Unlocks the selections.
21316 unlock : function(){
21317 this.locked = false;
21321 * Returns true if the selections are locked.
21322 * @return {Boolean}
21324 isLocked : function(){
21325 return this.locked;
21329 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21330 * @class Roo.bootstrap.Table.RowSelectionModel
21331 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21332 * It supports multiple selections and keyboard selection/navigation.
21334 * @param {Object} config
21337 Roo.bootstrap.Table.RowSelectionModel = function(config){
21338 Roo.apply(this, config);
21339 this.selections = new Roo.util.MixedCollection(false, function(o){
21344 this.lastActive = false;
21348 * @event selectionchange
21349 * Fires when the selection changes
21350 * @param {SelectionModel} this
21352 "selectionchange" : true,
21354 * @event afterselectionchange
21355 * Fires after the selection changes (eg. by key press or clicking)
21356 * @param {SelectionModel} this
21358 "afterselectionchange" : true,
21360 * @event beforerowselect
21361 * Fires when a row is selected being selected, return false to cancel.
21362 * @param {SelectionModel} this
21363 * @param {Number} rowIndex The selected index
21364 * @param {Boolean} keepExisting False if other selections will be cleared
21366 "beforerowselect" : true,
21369 * Fires when a row is selected.
21370 * @param {SelectionModel} this
21371 * @param {Number} rowIndex The selected index
21372 * @param {Roo.data.Record} r The record
21374 "rowselect" : true,
21376 * @event rowdeselect
21377 * Fires when a row is deselected.
21378 * @param {SelectionModel} this
21379 * @param {Number} rowIndex The selected index
21381 "rowdeselect" : true
21383 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21384 this.locked = false;
21387 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21389 * @cfg {Boolean} singleSelect
21390 * True to allow selection of only one row at a time (defaults to false)
21392 singleSelect : false,
21395 initEvents : function(){
21397 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21398 this.grid.on("mousedown", this.handleMouseDown, this);
21399 }else{ // allow click to work like normal
21400 this.grid.on("rowclick", this.handleDragableRowClick, this);
21403 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21404 "up" : function(e){
21406 this.selectPrevious(e.shiftKey);
21407 }else if(this.last !== false && this.lastActive !== false){
21408 var last = this.last;
21409 this.selectRange(this.last, this.lastActive-1);
21410 this.grid.getView().focusRow(this.lastActive);
21411 if(last !== false){
21415 this.selectFirstRow();
21417 this.fireEvent("afterselectionchange", this);
21419 "down" : function(e){
21421 this.selectNext(e.shiftKey);
21422 }else if(this.last !== false && this.lastActive !== false){
21423 var last = this.last;
21424 this.selectRange(this.last, this.lastActive+1);
21425 this.grid.getView().focusRow(this.lastActive);
21426 if(last !== false){
21430 this.selectFirstRow();
21432 this.fireEvent("afterselectionchange", this);
21437 var view = this.grid.view;
21438 view.on("refresh", this.onRefresh, this);
21439 view.on("rowupdated", this.onRowUpdated, this);
21440 view.on("rowremoved", this.onRemove, this);
21444 onRefresh : function(){
21445 var ds = this.grid.dataSource, i, v = this.grid.view;
21446 var s = this.selections;
21447 s.each(function(r){
21448 if((i = ds.indexOfId(r.id)) != -1){
21457 onRemove : function(v, index, r){
21458 this.selections.remove(r);
21462 onRowUpdated : function(v, index, r){
21463 if(this.isSelected(r)){
21464 v.onRowSelect(index);
21470 * @param {Array} records The records to select
21471 * @param {Boolean} keepExisting (optional) True to keep existing selections
21473 selectRecords : function(records, keepExisting){
21475 this.clearSelections();
21477 var ds = this.grid.dataSource;
21478 for(var i = 0, len = records.length; i < len; i++){
21479 this.selectRow(ds.indexOf(records[i]), true);
21484 * Gets the number of selected rows.
21487 getCount : function(){
21488 return this.selections.length;
21492 * Selects the first row in the grid.
21494 selectFirstRow : function(){
21499 * Select the last row.
21500 * @param {Boolean} keepExisting (optional) True to keep existing selections
21502 selectLastRow : function(keepExisting){
21503 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21507 * Selects the row immediately following the last selected row.
21508 * @param {Boolean} keepExisting (optional) True to keep existing selections
21510 selectNext : function(keepExisting){
21511 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21512 this.selectRow(this.last+1, keepExisting);
21513 this.grid.getView().focusRow(this.last);
21518 * Selects the row that precedes the last selected row.
21519 * @param {Boolean} keepExisting (optional) True to keep existing selections
21521 selectPrevious : function(keepExisting){
21523 this.selectRow(this.last-1, keepExisting);
21524 this.grid.getView().focusRow(this.last);
21529 * Returns the selected records
21530 * @return {Array} Array of selected records
21532 getSelections : function(){
21533 return [].concat(this.selections.items);
21537 * Returns the first selected record.
21540 getSelected : function(){
21541 return this.selections.itemAt(0);
21546 * Clears all selections.
21548 clearSelections : function(fast){
21549 if(this.locked) return;
21551 var ds = this.grid.dataSource;
21552 var s = this.selections;
21553 s.each(function(r){
21554 this.deselectRow(ds.indexOfId(r.id));
21558 this.selections.clear();
21565 * Selects all rows.
21567 selectAll : function(){
21568 if(this.locked) return;
21569 this.selections.clear();
21570 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21571 this.selectRow(i, true);
21576 * Returns True if there is a selection.
21577 * @return {Boolean}
21579 hasSelection : function(){
21580 return this.selections.length > 0;
21584 * Returns True if the specified row is selected.
21585 * @param {Number/Record} record The record or index of the record to check
21586 * @return {Boolean}
21588 isSelected : function(index){
21589 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21590 return (r && this.selections.key(r.id) ? true : false);
21594 * Returns True if the specified record id is selected.
21595 * @param {String} id The id of record to check
21596 * @return {Boolean}
21598 isIdSelected : function(id){
21599 return (this.selections.key(id) ? true : false);
21603 handleMouseDown : function(e, t){
21604 var view = this.grid.getView(), rowIndex;
21605 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21608 if(e.shiftKey && this.last !== false){
21609 var last = this.last;
21610 this.selectRange(last, rowIndex, e.ctrlKey);
21611 this.last = last; // reset the last
21612 view.focusRow(rowIndex);
21614 var isSelected = this.isSelected(rowIndex);
21615 if(e.button !== 0 && isSelected){
21616 view.focusRow(rowIndex);
21617 }else if(e.ctrlKey && isSelected){
21618 this.deselectRow(rowIndex);
21619 }else if(!isSelected){
21620 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21621 view.focusRow(rowIndex);
21624 this.fireEvent("afterselectionchange", this);
21627 handleDragableRowClick : function(grid, rowIndex, e)
21629 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21630 this.selectRow(rowIndex, false);
21631 grid.view.focusRow(rowIndex);
21632 this.fireEvent("afterselectionchange", this);
21637 * Selects multiple rows.
21638 * @param {Array} rows Array of the indexes of the row to select
21639 * @param {Boolean} keepExisting (optional) True to keep existing selections
21641 selectRows : function(rows, keepExisting){
21643 this.clearSelections();
21645 for(var i = 0, len = rows.length; i < len; i++){
21646 this.selectRow(rows[i], true);
21651 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21652 * @param {Number} startRow The index of the first row in the range
21653 * @param {Number} endRow The index of the last row in the range
21654 * @param {Boolean} keepExisting (optional) True to retain existing selections
21656 selectRange : function(startRow, endRow, keepExisting){
21657 if(this.locked) return;
21659 this.clearSelections();
21661 if(startRow <= endRow){
21662 for(var i = startRow; i <= endRow; i++){
21663 this.selectRow(i, true);
21666 for(var i = startRow; i >= endRow; i--){
21667 this.selectRow(i, true);
21673 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21674 * @param {Number} startRow The index of the first row in the range
21675 * @param {Number} endRow The index of the last row in the range
21677 deselectRange : function(startRow, endRow, preventViewNotify){
21678 if(this.locked) return;
21679 for(var i = startRow; i <= endRow; i++){
21680 this.deselectRow(i, preventViewNotify);
21686 * @param {Number} row The index of the row to select
21687 * @param {Boolean} keepExisting (optional) True to keep existing selections
21689 selectRow : function(index, keepExisting, preventViewNotify){
21690 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21691 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21692 if(!keepExisting || this.singleSelect){
21693 this.clearSelections();
21695 var r = this.grid.dataSource.getAt(index);
21696 this.selections.add(r);
21697 this.last = this.lastActive = index;
21698 if(!preventViewNotify){
21699 this.grid.getView().onRowSelect(index);
21701 this.fireEvent("rowselect", this, index, r);
21702 this.fireEvent("selectionchange", this);
21708 * @param {Number} row The index of the row to deselect
21710 deselectRow : function(index, preventViewNotify){
21711 if(this.locked) return;
21712 if(this.last == index){
21715 if(this.lastActive == index){
21716 this.lastActive = false;
21718 var r = this.grid.dataSource.getAt(index);
21719 this.selections.remove(r);
21720 if(!preventViewNotify){
21721 this.grid.getView().onRowDeselect(index);
21723 this.fireEvent("rowdeselect", this, index);
21724 this.fireEvent("selectionchange", this);
21728 restoreLast : function(){
21730 this.last = this._last;
21735 acceptsNav : function(row, col, cm){
21736 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21740 onEditorKey : function(field, e){
21741 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21746 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21748 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21750 }else if(k == e.ENTER && !e.ctrlKey){
21754 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21756 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21758 }else if(k == e.ESC){
21762 g.startEditing(newCell[0], newCell[1]);
21767 * Ext JS Library 1.1.1
21768 * Copyright(c) 2006-2007, Ext JS, LLC.
21770 * Originally Released Under LGPL - original licence link has changed is not relivant.
21773 * <script type="text/javascript">
21777 * @class Roo.bootstrap.PagingToolbar
21778 * @extends Roo.bootstrap.NavSimplebar
21779 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21781 * Create a new PagingToolbar
21782 * @param {Object} config The config object
21783 * @param {Roo.data.Store} store
21785 Roo.bootstrap.PagingToolbar = function(config)
21787 // old args format still supported... - xtype is prefered..
21788 // created from xtype...
21790 this.ds = config.dataSource;
21792 if (config.store && !this.ds) {
21793 this.store= Roo.factory(config.store, Roo.data);
21794 this.ds = this.store;
21795 this.ds.xmodule = this.xmodule || false;
21798 this.toolbarItems = [];
21799 if (config.items) {
21800 this.toolbarItems = config.items;
21803 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21808 this.bind(this.ds);
21811 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21815 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21817 * @cfg {Roo.data.Store} dataSource
21818 * The underlying data store providing the paged data
21821 * @cfg {String/HTMLElement/Element} container
21822 * container The id or element that will contain the toolbar
21825 * @cfg {Boolean} displayInfo
21826 * True to display the displayMsg (defaults to false)
21829 * @cfg {Number} pageSize
21830 * The number of records to display per page (defaults to 20)
21834 * @cfg {String} displayMsg
21835 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21837 displayMsg : 'Displaying {0} - {1} of {2}',
21839 * @cfg {String} emptyMsg
21840 * The message to display when no records are found (defaults to "No data to display")
21842 emptyMsg : 'No data to display',
21844 * Customizable piece of the default paging text (defaults to "Page")
21847 beforePageText : "Page",
21849 * Customizable piece of the default paging text (defaults to "of %0")
21852 afterPageText : "of {0}",
21854 * Customizable piece of the default paging text (defaults to "First Page")
21857 firstText : "First Page",
21859 * Customizable piece of the default paging text (defaults to "Previous Page")
21862 prevText : "Previous Page",
21864 * Customizable piece of the default paging text (defaults to "Next Page")
21867 nextText : "Next Page",
21869 * Customizable piece of the default paging text (defaults to "Last Page")
21872 lastText : "Last Page",
21874 * Customizable piece of the default paging text (defaults to "Refresh")
21877 refreshText : "Refresh",
21881 onRender : function(ct, position)
21883 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21884 this.navgroup.parentId = this.id;
21885 this.navgroup.onRender(this.el, null);
21886 // add the buttons to the navgroup
21888 if(this.displayInfo){
21889 Roo.log(this.el.select('ul.navbar-nav',true).first());
21890 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21891 this.displayEl = this.el.select('.x-paging-info', true).first();
21892 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21893 // this.displayEl = navel.el.select('span',true).first();
21899 Roo.each(_this.buttons, function(e){ // this might need to use render????
21900 Roo.factory(e).onRender(_this.el, null);
21904 Roo.each(_this.toolbarItems, function(e) {
21905 _this.navgroup.addItem(e);
21909 this.first = this.navgroup.addItem({
21910 tooltip: this.firstText,
21912 icon : 'fa fa-backward',
21914 preventDefault: true,
21915 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21918 this.prev = this.navgroup.addItem({
21919 tooltip: this.prevText,
21921 icon : 'fa fa-step-backward',
21923 preventDefault: true,
21924 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21926 //this.addSeparator();
21929 var field = this.navgroup.addItem( {
21931 cls : 'x-paging-position',
21933 html : this.beforePageText +
21934 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21935 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21938 this.field = field.el.select('input', true).first();
21939 this.field.on("keydown", this.onPagingKeydown, this);
21940 this.field.on("focus", function(){this.dom.select();});
21943 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21944 //this.field.setHeight(18);
21945 //this.addSeparator();
21946 this.next = this.navgroup.addItem({
21947 tooltip: this.nextText,
21949 html : ' <i class="fa fa-step-forward">',
21951 preventDefault: true,
21952 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21954 this.last = this.navgroup.addItem({
21955 tooltip: this.lastText,
21956 icon : 'fa fa-forward',
21959 preventDefault: true,
21960 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21962 //this.addSeparator();
21963 this.loading = this.navgroup.addItem({
21964 tooltip: this.refreshText,
21965 icon: 'fa fa-refresh',
21966 preventDefault: true,
21967 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21973 updateInfo : function(){
21974 if(this.displayEl){
21975 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21976 var msg = count == 0 ?
21980 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21982 this.displayEl.update(msg);
21987 onLoad : function(ds, r, o){
21988 this.cursor = o.params ? o.params.start : 0;
21989 var d = this.getPageData(),
21993 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21994 this.field.dom.value = ap;
21995 this.first.setDisabled(ap == 1);
21996 this.prev.setDisabled(ap == 1);
21997 this.next.setDisabled(ap == ps);
21998 this.last.setDisabled(ap == ps);
21999 this.loading.enable();
22004 getPageData : function(){
22005 var total = this.ds.getTotalCount();
22008 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22009 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22014 onLoadError : function(){
22015 this.loading.enable();
22019 onPagingKeydown : function(e){
22020 var k = e.getKey();
22021 var d = this.getPageData();
22023 var v = this.field.dom.value, pageNum;
22024 if(!v || isNaN(pageNum = parseInt(v, 10))){
22025 this.field.dom.value = d.activePage;
22028 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22029 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22032 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
22034 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22035 this.field.dom.value = pageNum;
22036 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22039 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22041 var v = this.field.dom.value, pageNum;
22042 var increment = (e.shiftKey) ? 10 : 1;
22043 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22045 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22046 this.field.dom.value = d.activePage;
22049 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22051 this.field.dom.value = parseInt(v, 10) + increment;
22052 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22053 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22060 beforeLoad : function(){
22062 this.loading.disable();
22067 onClick : function(which){
22076 ds.load({params:{start: 0, limit: this.pageSize}});
22079 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22082 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22085 var total = ds.getTotalCount();
22086 var extra = total % this.pageSize;
22087 var lastStart = extra ? (total - extra) : total-this.pageSize;
22088 ds.load({params:{start: lastStart, limit: this.pageSize}});
22091 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22097 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22098 * @param {Roo.data.Store} store The data store to unbind
22100 unbind : function(ds){
22101 ds.un("beforeload", this.beforeLoad, this);
22102 ds.un("load", this.onLoad, this);
22103 ds.un("loadexception", this.onLoadError, this);
22104 ds.un("remove", this.updateInfo, this);
22105 ds.un("add", this.updateInfo, this);
22106 this.ds = undefined;
22110 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22111 * @param {Roo.data.Store} store The data store to bind
22113 bind : function(ds){
22114 ds.on("beforeload", this.beforeLoad, this);
22115 ds.on("load", this.onLoad, this);
22116 ds.on("loadexception", this.onLoadError, this);
22117 ds.on("remove", this.updateInfo, this);
22118 ds.on("add", this.updateInfo, this);
22129 * @class Roo.bootstrap.MessageBar
22130 * @extends Roo.bootstrap.Component
22131 * Bootstrap MessageBar class
22132 * @cfg {String} html contents of the MessageBar
22133 * @cfg {String} weight (info | success | warning | danger) default info
22134 * @cfg {String} beforeClass insert the bar before the given class
22135 * @cfg {Boolean} closable (true | false) default false
22136 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22139 * Create a new Element
22140 * @param {Object} config The config object
22143 Roo.bootstrap.MessageBar = function(config){
22144 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22147 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22153 beforeClass: 'bootstrap-sticky-wrap',
22155 getAutoCreate : function(){
22159 cls: 'alert alert-dismissable alert-' + this.weight,
22164 html: this.html || ''
22170 cfg.cls += ' alert-messages-fixed';
22184 onRender : function(ct, position)
22186 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22189 var cfg = Roo.apply({}, this.getAutoCreate());
22193 cfg.cls += ' ' + this.cls;
22196 cfg.style = this.style;
22198 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22200 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22203 this.el.select('>button.close').on('click', this.hide, this);
22209 if (!this.rendered) {
22215 this.fireEvent('show', this);
22221 if (!this.rendered) {
22227 this.fireEvent('hide', this);
22230 update : function()
22232 // var e = this.el.dom.firstChild;
22234 // if(this.closable){
22235 // e = e.nextSibling;
22238 // e.data = this.html || '';
22240 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22256 * @class Roo.bootstrap.Graph
22257 * @extends Roo.bootstrap.Component
22258 * Bootstrap Graph class
22262 @cfg {String} graphtype bar | vbar | pie
22263 @cfg {number} g_x coodinator | centre x (pie)
22264 @cfg {number} g_y coodinator | centre y (pie)
22265 @cfg {number} g_r radius (pie)
22266 @cfg {number} g_height height of the chart (respected by all elements in the set)
22267 @cfg {number} g_width width of the chart (respected by all elements in the set)
22268 @cfg {Object} title The title of the chart
22271 -opts (object) options for the chart
22273 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22274 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22276 o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
22277 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22279 o stretch (boolean)
22281 -opts (object) options for the pie
22284 o startAngle (number)
22285 o endAngle (number)
22289 * Create a new Input
22290 * @param {Object} config The config object
22293 Roo.bootstrap.Graph = function(config){
22294 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22300 * The img click event for the img.
22301 * @param {Roo.EventObject} e
22307 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22318 //g_colors: this.colors,
22325 getAutoCreate : function(){
22336 onRender : function(ct,position){
22337 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22338 this.raphael = Raphael(this.el.dom);
22340 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22341 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22342 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22343 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22345 r.text(160, 10, "Single Series Chart").attr(txtattr);
22346 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22347 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22348 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22350 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22351 r.barchart(330, 10, 300, 220, data1);
22352 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22353 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22356 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22357 // r.barchart(30, 30, 560, 250, xdata, {
22358 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22359 // axis : "0 0 1 1",
22360 // axisxlabels : xdata
22361 // //yvalues : cols,
22364 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22366 // this.load(null,xdata,{
22367 // axis : "0 0 1 1",
22368 // axisxlabels : xdata
22373 load : function(graphtype,xdata,opts){
22374 this.raphael.clear();
22376 graphtype = this.graphtype;
22381 var r = this.raphael,
22382 fin = function () {
22383 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22385 fout = function () {
22386 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22388 pfin = function() {
22389 this.sector.stop();
22390 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22393 this.label[0].stop();
22394 this.label[0].attr({ r: 7.5 });
22395 this.label[1].attr({ "font-weight": 800 });
22398 pfout = function() {
22399 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22402 this.label[0].animate({ r: 5 }, 500, "bounce");
22403 this.label[1].attr({ "font-weight": 400 });
22409 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22412 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22415 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22416 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22418 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22425 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22430 setTitle: function(o)
22435 initEvents: function() {
22438 this.el.on('click', this.onClick, this);
22442 onClick : function(e)
22444 Roo.log('img onclick');
22445 this.fireEvent('click', this, e);
22457 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22460 * @class Roo.bootstrap.dash.NumberBox
22461 * @extends Roo.bootstrap.Component
22462 * Bootstrap NumberBox class
22463 * @cfg {String} headline Box headline
22464 * @cfg {String} content Box content
22465 * @cfg {String} icon Box icon
22466 * @cfg {String} footer Footer text
22467 * @cfg {String} fhref Footer href
22470 * Create a new NumberBox
22471 * @param {Object} config The config object
22475 Roo.bootstrap.dash.NumberBox = function(config){
22476 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22480 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22489 getAutoCreate : function(){
22493 cls : 'small-box ',
22501 cls : 'roo-headline',
22502 html : this.headline
22506 cls : 'roo-content',
22507 html : this.content
22521 cls : 'ion ' + this.icon
22530 cls : 'small-box-footer',
22531 href : this.fhref || '#',
22535 cfg.cn.push(footer);
22542 onRender : function(ct,position){
22543 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22550 setHeadline: function (value)
22552 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22555 setFooter: function (value, href)
22557 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22560 this.el.select('a.small-box-footer',true).first().attr('href', href);
22565 setContent: function (value)
22567 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22570 initEvents: function()
22584 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22587 * @class Roo.bootstrap.dash.TabBox
22588 * @extends Roo.bootstrap.Component
22589 * Bootstrap TabBox class
22590 * @cfg {String} title Title of the TabBox
22591 * @cfg {String} icon Icon of the TabBox
22592 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22593 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22596 * Create a new TabBox
22597 * @param {Object} config The config object
22601 Roo.bootstrap.dash.TabBox = function(config){
22602 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22607 * When a pane is added
22608 * @param {Roo.bootstrap.dash.TabPane} pane
22612 * @event activatepane
22613 * When a pane is activated
22614 * @param {Roo.bootstrap.dash.TabPane} pane
22616 "activatepane" : true
22624 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22629 tabScrollable : false,
22631 getChildContainer : function()
22633 return this.el.select('.tab-content', true).first();
22636 getAutoCreate : function(){
22640 cls: 'pull-left header',
22648 cls: 'fa ' + this.icon
22654 cls: 'nav nav-tabs pull-right',
22660 if(this.tabScrollable){
22667 cls: 'nav nav-tabs pull-right',
22678 cls: 'nav-tabs-custom',
22683 cls: 'tab-content no-padding',
22691 initEvents : function()
22693 //Roo.log('add add pane handler');
22694 this.on('addpane', this.onAddPane, this);
22697 * Updates the box title
22698 * @param {String} html to set the title to.
22700 setTitle : function(value)
22702 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22704 onAddPane : function(pane)
22706 this.panes.push(pane);
22707 //Roo.log('addpane');
22709 // tabs are rendere left to right..
22710 if(!this.showtabs){
22714 var ctr = this.el.select('.nav-tabs', true).first();
22717 var existing = ctr.select('.nav-tab',true);
22718 var qty = existing.getCount();;
22721 var tab = ctr.createChild({
22723 cls : 'nav-tab' + (qty ? '' : ' active'),
22731 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22734 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22736 pane.el.addClass('active');
22741 onTabClick : function(ev,un,ob,pane)
22743 //Roo.log('tab - prev default');
22744 ev.preventDefault();
22747 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22748 pane.tab.addClass('active');
22749 //Roo.log(pane.title);
22750 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22751 // technically we should have a deactivate event.. but maybe add later.
22752 // and it should not de-activate the selected tab...
22753 this.fireEvent('activatepane', pane);
22754 pane.el.addClass('active');
22755 pane.fireEvent('activate');
22760 getActivePane : function()
22763 Roo.each(this.panes, function(p) {
22764 if(p.el.hasClass('active')){
22785 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22787 * @class Roo.bootstrap.TabPane
22788 * @extends Roo.bootstrap.Component
22789 * Bootstrap TabPane class
22790 * @cfg {Boolean} active (false | true) Default false
22791 * @cfg {String} title title of panel
22795 * Create a new TabPane
22796 * @param {Object} config The config object
22799 Roo.bootstrap.dash.TabPane = function(config){
22800 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22806 * When a pane is activated
22807 * @param {Roo.bootstrap.dash.TabPane} pane
22814 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22819 // the tabBox that this is attached to.
22822 getAutoCreate : function()
22830 cfg.cls += ' active';
22835 initEvents : function()
22837 //Roo.log('trigger add pane handler');
22838 this.parent().fireEvent('addpane', this)
22842 * Updates the tab title
22843 * @param {String} html to set the title to.
22845 setTitle: function(str)
22851 this.tab.select('a', true).first().dom.innerHTML = str;
22868 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22871 * @class Roo.bootstrap.menu.Menu
22872 * @extends Roo.bootstrap.Component
22873 * Bootstrap Menu class - container for Menu
22874 * @cfg {String} html Text of the menu
22875 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22876 * @cfg {String} icon Font awesome icon
22877 * @cfg {String} pos Menu align to (top | bottom) default bottom
22881 * Create a new Menu
22882 * @param {Object} config The config object
22886 Roo.bootstrap.menu.Menu = function(config){
22887 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22891 * @event beforeshow
22892 * Fires before this menu is displayed
22893 * @param {Roo.bootstrap.menu.Menu} this
22897 * @event beforehide
22898 * Fires before this menu is hidden
22899 * @param {Roo.bootstrap.menu.Menu} this
22904 * Fires after this menu is displayed
22905 * @param {Roo.bootstrap.menu.Menu} this
22910 * Fires after this menu is hidden
22911 * @param {Roo.bootstrap.menu.Menu} this
22916 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22917 * @param {Roo.bootstrap.menu.Menu} this
22918 * @param {Roo.EventObject} e
22925 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22929 weight : 'default',
22934 getChildContainer : function() {
22935 if(this.isSubMenu){
22939 return this.el.select('ul.dropdown-menu', true).first();
22942 getAutoCreate : function()
22947 cls : 'roo-menu-text',
22955 cls : 'fa ' + this.icon
22966 cls : 'dropdown-button btn btn-' + this.weight,
22971 cls : 'dropdown-toggle btn btn-' + this.weight,
22981 cls : 'dropdown-menu'
22987 if(this.pos == 'top'){
22988 cfg.cls += ' dropup';
22991 if(this.isSubMenu){
22994 cls : 'dropdown-menu'
23001 onRender : function(ct, position)
23003 this.isSubMenu = ct.hasClass('dropdown-submenu');
23005 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23008 initEvents : function()
23010 if(this.isSubMenu){
23014 this.hidden = true;
23016 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23017 this.triggerEl.on('click', this.onTriggerPress, this);
23019 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23020 this.buttonEl.on('click', this.onClick, this);
23026 if(this.isSubMenu){
23030 return this.el.select('ul.dropdown-menu', true).first();
23033 onClick : function(e)
23035 this.fireEvent("click", this, e);
23038 onTriggerPress : function(e)
23040 if (this.isVisible()) {
23047 isVisible : function(){
23048 return !this.hidden;
23053 this.fireEvent("beforeshow", this);
23055 this.hidden = false;
23056 this.el.addClass('open');
23058 Roo.get(document).on("mouseup", this.onMouseUp, this);
23060 this.fireEvent("show", this);
23067 this.fireEvent("beforehide", this);
23069 this.hidden = true;
23070 this.el.removeClass('open');
23072 Roo.get(document).un("mouseup", this.onMouseUp);
23074 this.fireEvent("hide", this);
23077 onMouseUp : function()
23091 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23094 * @class Roo.bootstrap.menu.Item
23095 * @extends Roo.bootstrap.Component
23096 * Bootstrap MenuItem class
23097 * @cfg {Boolean} submenu (true | false) default false
23098 * @cfg {String} html text of the item
23099 * @cfg {String} href the link
23100 * @cfg {Boolean} disable (true | false) default false
23101 * @cfg {Boolean} preventDefault (true | false) default true
23102 * @cfg {String} icon Font awesome icon
23103 * @cfg {String} pos Submenu align to (left | right) default right
23107 * Create a new Item
23108 * @param {Object} config The config object
23112 Roo.bootstrap.menu.Item = function(config){
23113 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23117 * Fires when the mouse is hovering over this menu
23118 * @param {Roo.bootstrap.menu.Item} this
23119 * @param {Roo.EventObject} e
23124 * Fires when the mouse exits this menu
23125 * @param {Roo.bootstrap.menu.Item} this
23126 * @param {Roo.EventObject} e
23132 * The raw click event for the entire grid.
23133 * @param {Roo.EventObject} e
23139 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23144 preventDefault: true,
23149 getAutoCreate : function()
23154 cls : 'roo-menu-item-text',
23162 cls : 'fa ' + this.icon
23171 href : this.href || '#',
23178 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23182 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23184 if(this.pos == 'left'){
23185 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23192 initEvents : function()
23194 this.el.on('mouseover', this.onMouseOver, this);
23195 this.el.on('mouseout', this.onMouseOut, this);
23197 this.el.select('a', true).first().on('click', this.onClick, this);
23201 onClick : function(e)
23203 if(this.preventDefault){
23204 e.preventDefault();
23207 this.fireEvent("click", this, e);
23210 onMouseOver : function(e)
23212 if(this.submenu && this.pos == 'left'){
23213 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23216 this.fireEvent("mouseover", this, e);
23219 onMouseOut : function(e)
23221 this.fireEvent("mouseout", this, e);
23233 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23236 * @class Roo.bootstrap.menu.Separator
23237 * @extends Roo.bootstrap.Component
23238 * Bootstrap Separator class
23241 * Create a new Separator
23242 * @param {Object} config The config object
23246 Roo.bootstrap.menu.Separator = function(config){
23247 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23250 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23252 getAutoCreate : function(){
23273 * @class Roo.bootstrap.Tooltip
23274 * Bootstrap Tooltip class
23275 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23276 * to determine which dom element triggers the tooltip.
23278 * It needs to add support for additional attributes like tooltip-position
23281 * Create a new Toolti
23282 * @param {Object} config The config object
23285 Roo.bootstrap.Tooltip = function(config){
23286 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23289 Roo.apply(Roo.bootstrap.Tooltip, {
23291 * @function init initialize tooltip monitoring.
23295 currentTip : false,
23296 currentRegion : false,
23302 Roo.get(document).on('mouseover', this.enter ,this);
23303 Roo.get(document).on('mouseout', this.leave, this);
23306 this.currentTip = new Roo.bootstrap.Tooltip();
23309 enter : function(ev)
23311 var dom = ev.getTarget();
23313 //Roo.log(['enter',dom]);
23314 var el = Roo.fly(dom);
23315 if (this.currentEl) {
23317 //Roo.log(this.currentEl);
23318 //Roo.log(this.currentEl.contains(dom));
23319 if (this.currentEl == el) {
23322 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23328 if (this.currentTip.el) {
23329 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23334 // you can not look for children, as if el is the body.. then everythign is the child..
23335 if (!el.attr('tooltip')) { //
23336 if (!el.select("[tooltip]").elements.length) {
23339 // is the mouse over this child...?
23340 bindEl = el.select("[tooltip]").first();
23341 var xy = ev.getXY();
23342 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23343 //Roo.log("not in region.");
23346 //Roo.log("child element over..");
23349 this.currentEl = bindEl;
23350 this.currentTip.bind(bindEl);
23351 this.currentRegion = Roo.lib.Region.getRegion(dom);
23352 this.currentTip.enter();
23355 leave : function(ev)
23357 var dom = ev.getTarget();
23358 //Roo.log(['leave',dom]);
23359 if (!this.currentEl) {
23364 if (dom != this.currentEl.dom) {
23367 var xy = ev.getXY();
23368 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23371 // only activate leave if mouse cursor is outside... bounding box..
23376 if (this.currentTip) {
23377 this.currentTip.leave();
23379 //Roo.log('clear currentEl');
23380 this.currentEl = false;
23385 'left' : ['r-l', [-2,0], 'right'],
23386 'right' : ['l-r', [2,0], 'left'],
23387 'bottom' : ['t-b', [0,2], 'top'],
23388 'top' : [ 'b-t', [0,-2], 'bottom']
23394 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23399 delay : null, // can be { show : 300 , hide: 500}
23403 hoverState : null, //???
23405 placement : 'bottom',
23407 getAutoCreate : function(){
23414 cls : 'tooltip-arrow'
23417 cls : 'tooltip-inner'
23424 bind : function(el)
23430 enter : function () {
23432 if (this.timeout != null) {
23433 clearTimeout(this.timeout);
23436 this.hoverState = 'in';
23437 //Roo.log("enter - show");
23438 if (!this.delay || !this.delay.show) {
23443 this.timeout = setTimeout(function () {
23444 if (_t.hoverState == 'in') {
23447 }, this.delay.show);
23451 clearTimeout(this.timeout);
23453 this.hoverState = 'out';
23454 if (!this.delay || !this.delay.hide) {
23460 this.timeout = setTimeout(function () {
23461 //Roo.log("leave - timeout");
23463 if (_t.hoverState == 'out') {
23465 Roo.bootstrap.Tooltip.currentEl = false;
23473 this.render(document.body);
23476 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23478 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23480 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23482 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23484 var placement = typeof this.placement == 'function' ?
23485 this.placement.call(this, this.el, on_el) :
23488 var autoToken = /\s?auto?\s?/i;
23489 var autoPlace = autoToken.test(placement);
23491 placement = placement.replace(autoToken, '') || 'top';
23495 //this.el.setXY([0,0]);
23497 //this.el.dom.style.display='block';
23499 //this.el.appendTo(on_el);
23501 var p = this.getPosition();
23502 var box = this.el.getBox();
23508 var align = Roo.bootstrap.Tooltip.alignment[placement];
23510 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23512 if(placement == 'top' || placement == 'bottom'){
23514 placement = 'right';
23517 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23518 placement = 'left';
23522 align = Roo.bootstrap.Tooltip.alignment[placement];
23524 this.el.alignTo(this.bindEl, align[0],align[1]);
23525 //var arrow = this.el.select('.arrow',true).first();
23526 //arrow.set(align[2],
23528 this.el.addClass(placement);
23530 this.el.addClass('in fade');
23532 this.hoverState = null;
23534 if (this.el.hasClass('fade')) {
23545 //this.el.setXY([0,0]);
23546 this.el.removeClass('in');
23562 * @class Roo.bootstrap.LocationPicker
23563 * @extends Roo.bootstrap.Component
23564 * Bootstrap LocationPicker class
23565 * @cfg {Number} latitude Position when init default 0
23566 * @cfg {Number} longitude Position when init default 0
23567 * @cfg {Number} zoom default 15
23568 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23569 * @cfg {Boolean} mapTypeControl default false
23570 * @cfg {Boolean} disableDoubleClickZoom default false
23571 * @cfg {Boolean} scrollwheel default true
23572 * @cfg {Boolean} streetViewControl default false
23573 * @cfg {Number} radius default 0
23574 * @cfg {String} locationName
23575 * @cfg {Boolean} draggable default true
23576 * @cfg {Boolean} enableAutocomplete default false
23577 * @cfg {Boolean} enableReverseGeocode default true
23578 * @cfg {String} markerTitle
23581 * Create a new LocationPicker
23582 * @param {Object} config The config object
23586 Roo.bootstrap.LocationPicker = function(config){
23588 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23593 * Fires when the picker initialized.
23594 * @param {Roo.bootstrap.LocationPicker} this
23595 * @param {Google Location} location
23599 * @event positionchanged
23600 * Fires when the picker position changed.
23601 * @param {Roo.bootstrap.LocationPicker} this
23602 * @param {Google Location} location
23604 positionchanged : true,
23607 * Fires when the map resize.
23608 * @param {Roo.bootstrap.LocationPicker} this
23613 * Fires when the map show.
23614 * @param {Roo.bootstrap.LocationPicker} this
23619 * Fires when the map hide.
23620 * @param {Roo.bootstrap.LocationPicker} this
23625 * Fires when click the map.
23626 * @param {Roo.bootstrap.LocationPicker} this
23627 * @param {Map event} e
23631 * @event mapRightClick
23632 * Fires when right click the map.
23633 * @param {Roo.bootstrap.LocationPicker} this
23634 * @param {Map event} e
23636 mapRightClick : true,
23638 * @event markerClick
23639 * Fires when click the marker.
23640 * @param {Roo.bootstrap.LocationPicker} this
23641 * @param {Map event} e
23643 markerClick : true,
23645 * @event markerRightClick
23646 * Fires when right click the marker.
23647 * @param {Roo.bootstrap.LocationPicker} this
23648 * @param {Map event} e
23650 markerRightClick : true,
23652 * @event OverlayViewDraw
23653 * Fires when OverlayView Draw
23654 * @param {Roo.bootstrap.LocationPicker} this
23656 OverlayViewDraw : true,
23658 * @event OverlayViewOnAdd
23659 * Fires when OverlayView Draw
23660 * @param {Roo.bootstrap.LocationPicker} this
23662 OverlayViewOnAdd : true,
23664 * @event OverlayViewOnRemove
23665 * Fires when OverlayView Draw
23666 * @param {Roo.bootstrap.LocationPicker} this
23668 OverlayViewOnRemove : true,
23670 * @event OverlayViewShow
23671 * Fires when OverlayView Draw
23672 * @param {Roo.bootstrap.LocationPicker} this
23673 * @param {Pixel} cpx
23675 OverlayViewShow : true,
23677 * @event OverlayViewHide
23678 * Fires when OverlayView Draw
23679 * @param {Roo.bootstrap.LocationPicker} this
23681 OverlayViewHide : true
23686 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23688 gMapContext: false,
23694 mapTypeControl: false,
23695 disableDoubleClickZoom: false,
23697 streetViewControl: false,
23701 enableAutocomplete: false,
23702 enableReverseGeocode: true,
23705 getAutoCreate: function()
23710 cls: 'roo-location-picker'
23716 initEvents: function(ct, position)
23718 if(!this.el.getWidth() || this.isApplied()){
23722 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23727 initial: function()
23729 if(!this.mapTypeId){
23730 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23733 this.gMapContext = this.GMapContext();
23735 this.initOverlayView();
23737 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23741 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23742 _this.setPosition(_this.gMapContext.marker.position);
23745 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23746 _this.fireEvent('mapClick', this, event);
23750 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23751 _this.fireEvent('mapRightClick', this, event);
23755 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23756 _this.fireEvent('markerClick', this, event);
23760 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23761 _this.fireEvent('markerRightClick', this, event);
23765 this.setPosition(this.gMapContext.location);
23767 this.fireEvent('initial', this, this.gMapContext.location);
23770 initOverlayView: function()
23774 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23778 _this.fireEvent('OverlayViewDraw', _this);
23783 _this.fireEvent('OverlayViewOnAdd', _this);
23786 onRemove: function()
23788 _this.fireEvent('OverlayViewOnRemove', _this);
23791 show: function(cpx)
23793 _this.fireEvent('OverlayViewShow', _this, cpx);
23798 _this.fireEvent('OverlayViewHide', _this);
23804 fromLatLngToContainerPixel: function(event)
23806 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23809 isApplied: function()
23811 return this.getGmapContext() == false ? false : true;
23814 getGmapContext: function()
23816 return this.gMapContext
23819 GMapContext: function()
23821 var position = new google.maps.LatLng(this.latitude, this.longitude);
23823 var _map = new google.maps.Map(this.el.dom, {
23826 mapTypeId: this.mapTypeId,
23827 mapTypeControl: this.mapTypeControl,
23828 disableDoubleClickZoom: this.disableDoubleClickZoom,
23829 scrollwheel: this.scrollwheel,
23830 streetViewControl: this.streetViewControl,
23831 locationName: this.locationName,
23832 draggable: this.draggable,
23833 enableAutocomplete: this.enableAutocomplete,
23834 enableReverseGeocode: this.enableReverseGeocode
23837 var _marker = new google.maps.Marker({
23838 position: position,
23840 title: this.markerTitle,
23841 draggable: this.draggable
23848 location: position,
23849 radius: this.radius,
23850 locationName: this.locationName,
23851 addressComponents: {
23852 formatted_address: null,
23853 addressLine1: null,
23854 addressLine2: null,
23856 streetNumber: null,
23860 stateOrProvince: null
23863 domContainer: this.el.dom,
23864 geodecoder: new google.maps.Geocoder()
23868 drawCircle: function(center, radius, options)
23870 if (this.gMapContext.circle != null) {
23871 this.gMapContext.circle.setMap(null);
23875 options = Roo.apply({}, options, {
23876 strokeColor: "#0000FF",
23877 strokeOpacity: .35,
23879 fillColor: "#0000FF",
23883 options.map = this.gMapContext.map;
23884 options.radius = radius;
23885 options.center = center;
23886 this.gMapContext.circle = new google.maps.Circle(options);
23887 return this.gMapContext.circle;
23893 setPosition: function(location)
23895 this.gMapContext.location = location;
23896 this.gMapContext.marker.setPosition(location);
23897 this.gMapContext.map.panTo(location);
23898 this.drawCircle(location, this.gMapContext.radius, {});
23902 if (this.gMapContext.settings.enableReverseGeocode) {
23903 this.gMapContext.geodecoder.geocode({
23904 latLng: this.gMapContext.location
23905 }, function(results, status) {
23907 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23908 _this.gMapContext.locationName = results[0].formatted_address;
23909 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23911 _this.fireEvent('positionchanged', this, location);
23918 this.fireEvent('positionchanged', this, location);
23923 google.maps.event.trigger(this.gMapContext.map, "resize");
23925 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23927 this.fireEvent('resize', this);
23930 setPositionByLatLng: function(latitude, longitude)
23932 this.setPosition(new google.maps.LatLng(latitude, longitude));
23935 getCurrentPosition: function()
23938 latitude: this.gMapContext.location.lat(),
23939 longitude: this.gMapContext.location.lng()
23943 getAddressName: function()
23945 return this.gMapContext.locationName;
23948 getAddressComponents: function()
23950 return this.gMapContext.addressComponents;
23953 address_component_from_google_geocode: function(address_components)
23957 for (var i = 0; i < address_components.length; i++) {
23958 var component = address_components[i];
23959 if (component.types.indexOf("postal_code") >= 0) {
23960 result.postalCode = component.short_name;
23961 } else if (component.types.indexOf("street_number") >= 0) {
23962 result.streetNumber = component.short_name;
23963 } else if (component.types.indexOf("route") >= 0) {
23964 result.streetName = component.short_name;
23965 } else if (component.types.indexOf("neighborhood") >= 0) {
23966 result.city = component.short_name;
23967 } else if (component.types.indexOf("locality") >= 0) {
23968 result.city = component.short_name;
23969 } else if (component.types.indexOf("sublocality") >= 0) {
23970 result.district = component.short_name;
23971 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23972 result.stateOrProvince = component.short_name;
23973 } else if (component.types.indexOf("country") >= 0) {
23974 result.country = component.short_name;
23978 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23979 result.addressLine2 = "";
23983 setZoomLevel: function(zoom)
23985 this.gMapContext.map.setZoom(zoom);
23998 this.fireEvent('show', this);
24009 this.fireEvent('hide', this);
24014 Roo.apply(Roo.bootstrap.LocationPicker, {
24016 OverlayView : function(map, options)
24018 options = options || {};
24032 * @class Roo.bootstrap.Alert
24033 * @extends Roo.bootstrap.Component
24034 * Bootstrap Alert class
24035 * @cfg {String} title The title of alert
24036 * @cfg {String} html The content of alert
24037 * @cfg {String} weight ( success | info | warning | danger )
24038 * @cfg {String} faicon font-awesomeicon
24041 * Create a new alert
24042 * @param {Object} config The config object
24046 Roo.bootstrap.Alert = function(config){
24047 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24051 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24058 getAutoCreate : function()
24067 cls : 'roo-alert-icon'
24072 cls : 'roo-alert-title',
24077 cls : 'roo-alert-text',
24084 cfg.cn[0].cls += ' fa ' + this.faicon;
24088 cfg.cls += ' alert-' + this.weight;
24094 initEvents: function()
24096 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24099 setTitle : function(str)
24101 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24104 setText : function(str)
24106 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24109 setWeight : function(weight)
24112 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24115 this.weight = weight;
24117 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24120 setIcon : function(icon)
24123 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24128 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24149 * @class Roo.bootstrap.UploadCropbox
24150 * @extends Roo.bootstrap.Component
24151 * Bootstrap UploadCropbox class
24152 * @cfg {String} emptyText show when image has been loaded
24153 * @cfg {String} rotateNotify show when image too small to rotate
24154 * @cfg {Number} errorTimeout default 3000
24155 * @cfg {Number} minWidth default 300
24156 * @cfg {Number} minHeight default 300
24157 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24158 * @cfg {Boolean} isDocument (true|false) default false
24159 * @cfg {String} url action url
24160 * @cfg {String} paramName default 'imageUpload'
24161 * @cfg {String} method default POST
24164 * Create a new UploadCropbox
24165 * @param {Object} config The config object
24168 Roo.bootstrap.UploadCropbox = function(config){
24169 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24173 * @event beforeselectfile
24174 * Fire before select file
24175 * @param {Roo.bootstrap.UploadCropbox} this
24177 "beforeselectfile" : true,
24180 * Fire after initEvent
24181 * @param {Roo.bootstrap.UploadCropbox} this
24186 * Fire after initEvent
24187 * @param {Roo.bootstrap.UploadCropbox} this
24188 * @param {String} data
24193 * Fire when preparing the file data
24194 * @param {Roo.bootstrap.UploadCropbox} this
24195 * @param {Object} file
24200 * Fire when get exception
24201 * @param {Roo.bootstrap.UploadCropbox} this
24202 * @param {XMLHttpRequest} xhr
24204 "exception" : true,
24206 * @event beforeloadcanvas
24207 * Fire before load the canvas
24208 * @param {Roo.bootstrap.UploadCropbox} this
24209 * @param {String} src
24211 "beforeloadcanvas" : true,
24214 * Fire when trash image
24215 * @param {Roo.bootstrap.UploadCropbox} this
24220 * Fire when download the image
24221 * @param {Roo.bootstrap.UploadCropbox} this
24225 * @event footerbuttonclick
24226 * Fire when footerbuttonclick
24227 * @param {Roo.bootstrap.UploadCropbox} this
24228 * @param {String} type
24230 "footerbuttonclick" : true,
24234 * @param {Roo.bootstrap.UploadCropbox} this
24239 * Fire when rotate the image
24240 * @param {Roo.bootstrap.UploadCropbox} this
24241 * @param {String} pos
24246 * Fire when inspect the file
24247 * @param {Roo.bootstrap.UploadCropbox} this
24248 * @param {Object} file
24253 * Fire when xhr upload the file
24254 * @param {Roo.bootstrap.UploadCropbox} this
24255 * @param {Object} data
24260 * Fire when arrange the file data
24261 * @param {Roo.bootstrap.UploadCropbox} this
24262 * @param {Object} formData
24267 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24270 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24272 emptyText : 'Click to upload image',
24273 rotateNotify : 'Image is too small to rotate',
24274 errorTimeout : 3000,
24288 cropType : 'image/jpeg',
24290 canvasLoaded : false,
24291 isDocument : false,
24293 paramName : 'imageUpload',
24295 getAutoCreate : function()
24299 cls : 'roo-upload-cropbox',
24303 cls : 'roo-upload-cropbox-selector',
24308 cls : 'roo-upload-cropbox-body',
24309 style : 'cursor:pointer',
24313 cls : 'roo-upload-cropbox-preview'
24317 cls : 'roo-upload-cropbox-thumb'
24321 cls : 'roo-upload-cropbox-empty-notify',
24322 html : this.emptyText
24326 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24327 html : this.rotateNotify
24333 cls : 'roo-upload-cropbox-footer',
24336 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24346 onRender : function(ct, position)
24348 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24350 if (this.buttons.length) {
24352 Roo.each(this.buttons, function(bb) {
24354 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24356 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24362 initEvents : function()
24364 this.urlAPI = (window.createObjectURL && window) ||
24365 (window.URL && URL.revokeObjectURL && URL) ||
24366 (window.webkitURL && webkitURL);
24368 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24369 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24371 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24372 this.selectorEl.hide();
24374 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24375 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24377 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24378 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24379 this.thumbEl.hide();
24381 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24382 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24384 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24385 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24386 this.errorEl.hide();
24388 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24389 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24390 this.footerEl.hide();
24392 this.setThumbBoxSize();
24398 this.fireEvent('initial', this);
24405 window.addEventListener("resize", function() { _this.resize(); } );
24407 this.bodyEl.on('click', this.beforeSelectFile, this);
24410 this.bodyEl.on('touchstart', this.onTouchStart, this);
24411 this.bodyEl.on('touchmove', this.onTouchMove, this);
24412 this.bodyEl.on('touchend', this.onTouchEnd, this);
24416 this.bodyEl.on('mousedown', this.onMouseDown, this);
24417 this.bodyEl.on('mousemove', this.onMouseMove, this);
24418 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24419 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24420 Roo.get(document).on('mouseup', this.onMouseUp, this);
24423 this.selectorEl.on('change', this.onFileSelected, this);
24429 this.baseScale = 1;
24431 this.baseRotate = 1;
24432 this.dragable = false;
24433 this.pinching = false;
24436 this.cropData = false;
24437 this.notifyEl.dom.innerHTML = this.emptyText;
24439 this.selectorEl.dom.value = '';
24443 resize : function()
24445 if(this.fireEvent('resize', this) != false){
24446 this.setThumbBoxPosition();
24447 this.setCanvasPosition();
24451 onFooterButtonClick : function(e, el, o, type)
24454 case 'rotate-left' :
24455 this.onRotateLeft(e);
24457 case 'rotate-right' :
24458 this.onRotateRight(e);
24461 this.beforeSelectFile(e);
24476 this.fireEvent('footerbuttonclick', this, type);
24479 beforeSelectFile : function(e)
24481 e.preventDefault();
24483 if(this.fireEvent('beforeselectfile', this) != false){
24484 this.selectorEl.dom.click();
24488 onFileSelected : function(e)
24490 e.preventDefault();
24492 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24496 var file = this.selectorEl.dom.files[0];
24498 if(this.fireEvent('inspect', this, file) != false){
24499 this.prepare(file);
24504 trash : function(e)
24506 this.fireEvent('trash', this);
24509 download : function(e)
24511 this.fireEvent('download', this);
24514 loadCanvas : function(src)
24516 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24520 this.imageEl = document.createElement('img');
24524 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24526 this.imageEl.src = src;
24530 onLoadCanvas : function()
24532 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24533 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24535 this.bodyEl.un('click', this.beforeSelectFile, this);
24537 this.notifyEl.hide();
24538 this.thumbEl.show();
24539 this.footerEl.show();
24541 this.baseRotateLevel();
24543 if(this.isDocument){
24544 this.setThumbBoxSize();
24547 this.setThumbBoxPosition();
24549 this.baseScaleLevel();
24555 this.canvasLoaded = true;
24559 setCanvasPosition : function()
24561 if(!this.canvasEl){
24565 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24566 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24568 this.previewEl.setLeft(pw);
24569 this.previewEl.setTop(ph);
24573 onMouseDown : function(e)
24577 this.dragable = true;
24578 this.pinching = false;
24580 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24581 this.dragable = false;
24585 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24586 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24590 onMouseMove : function(e)
24594 if(!this.canvasLoaded){
24598 if (!this.dragable){
24602 var minX = Math.ceil(this.thumbEl.getLeft(true));
24603 var minY = Math.ceil(this.thumbEl.getTop(true));
24605 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24606 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24608 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24609 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24611 x = x - this.mouseX;
24612 y = y - this.mouseY;
24614 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24615 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24617 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24618 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24620 this.previewEl.setLeft(bgX);
24621 this.previewEl.setTop(bgY);
24623 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24624 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24627 onMouseUp : function(e)
24631 this.dragable = false;
24634 onMouseWheel : function(e)
24638 this.startScale = this.scale;
24640 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24642 if(!this.zoomable()){
24643 this.scale = this.startScale;
24652 zoomable : function()
24654 var minScale = this.thumbEl.getWidth() / this.minWidth;
24656 if(this.minWidth < this.minHeight){
24657 minScale = this.thumbEl.getHeight() / this.minHeight;
24660 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24661 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24665 (this.rotate == 0 || this.rotate == 180) &&
24667 width > this.imageEl.OriginWidth ||
24668 height > this.imageEl.OriginHeight ||
24669 (width < this.minWidth && height < this.minHeight)
24677 (this.rotate == 90 || this.rotate == 270) &&
24679 width > this.imageEl.OriginWidth ||
24680 height > this.imageEl.OriginHeight ||
24681 (width < this.minHeight && height < this.minWidth)
24688 !this.isDocument &&
24689 (this.rotate == 0 || this.rotate == 180) &&
24691 width < this.minWidth ||
24692 width > this.imageEl.OriginWidth ||
24693 height < this.minHeight ||
24694 height > this.imageEl.OriginHeight
24701 !this.isDocument &&
24702 (this.rotate == 90 || this.rotate == 270) &&
24704 width < this.minHeight ||
24705 width > this.imageEl.OriginWidth ||
24706 height < this.minWidth ||
24707 height > this.imageEl.OriginHeight
24717 onRotateLeft : function(e)
24719 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24721 var minScale = this.thumbEl.getWidth() / this.minWidth;
24723 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24724 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24726 this.startScale = this.scale;
24728 while (this.getScaleLevel() < minScale){
24730 this.scale = this.scale + 1;
24732 if(!this.zoomable()){
24737 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24738 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24743 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24750 this.scale = this.startScale;
24752 this.onRotateFail();
24757 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24759 if(this.isDocument){
24760 this.setThumbBoxSize();
24761 this.setThumbBoxPosition();
24762 this.setCanvasPosition();
24767 this.fireEvent('rotate', this, 'left');
24771 onRotateRight : function(e)
24773 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24775 var minScale = this.thumbEl.getWidth() / this.minWidth;
24777 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24778 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24780 this.startScale = this.scale;
24782 while (this.getScaleLevel() < minScale){
24784 this.scale = this.scale + 1;
24786 if(!this.zoomable()){
24791 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24792 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24797 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24804 this.scale = this.startScale;
24806 this.onRotateFail();
24811 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24813 if(this.isDocument){
24814 this.setThumbBoxSize();
24815 this.setThumbBoxPosition();
24816 this.setCanvasPosition();
24821 this.fireEvent('rotate', this, 'right');
24824 onRotateFail : function()
24826 this.errorEl.show(true);
24830 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24835 this.previewEl.dom.innerHTML = '';
24837 var canvasEl = document.createElement("canvas");
24839 var contextEl = canvasEl.getContext("2d");
24841 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24842 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24843 var center = this.imageEl.OriginWidth / 2;
24845 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24846 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24847 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24848 center = this.imageEl.OriginHeight / 2;
24851 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24853 contextEl.translate(center, center);
24854 contextEl.rotate(this.rotate * Math.PI / 180);
24856 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24858 this.canvasEl = document.createElement("canvas");
24860 this.contextEl = this.canvasEl.getContext("2d");
24862 switch (this.rotate) {
24865 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24866 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24868 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24873 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24874 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24876 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24877 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24881 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24886 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24887 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24889 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24890 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24894 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24899 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24900 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24902 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24903 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24907 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24914 this.previewEl.appendChild(this.canvasEl);
24916 this.setCanvasPosition();
24921 if(!this.canvasLoaded){
24925 var imageCanvas = document.createElement("canvas");
24927 var imageContext = imageCanvas.getContext("2d");
24929 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24930 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24932 var center = imageCanvas.width / 2;
24934 imageContext.translate(center, center);
24936 imageContext.rotate(this.rotate * Math.PI / 180);
24938 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24940 var canvas = document.createElement("canvas");
24942 var context = canvas.getContext("2d");
24944 canvas.width = this.minWidth;
24945 canvas.height = this.minHeight;
24947 switch (this.rotate) {
24950 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24951 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24953 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24954 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24956 var targetWidth = this.minWidth - 2 * x;
24957 var targetHeight = this.minHeight - 2 * y;
24961 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24962 scale = targetWidth / width;
24965 if(x > 0 && y == 0){
24966 scale = targetHeight / height;
24969 if(x > 0 && y > 0){
24970 scale = targetWidth / width;
24972 if(width < height){
24973 scale = targetHeight / height;
24977 context.scale(scale, scale);
24979 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24980 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24982 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24983 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24985 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24990 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24991 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24993 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24994 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24996 var targetWidth = this.minWidth - 2 * x;
24997 var targetHeight = this.minHeight - 2 * y;
25001 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25002 scale = targetWidth / width;
25005 if(x > 0 && y == 0){
25006 scale = targetHeight / height;
25009 if(x > 0 && y > 0){
25010 scale = targetWidth / width;
25012 if(width < height){
25013 scale = targetHeight / height;
25017 context.scale(scale, scale);
25019 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25020 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25022 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25023 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25025 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25027 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25032 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25033 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25035 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25036 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25038 var targetWidth = this.minWidth - 2 * x;
25039 var targetHeight = this.minHeight - 2 * y;
25043 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25044 scale = targetWidth / width;
25047 if(x > 0 && y == 0){
25048 scale = targetHeight / height;
25051 if(x > 0 && y > 0){
25052 scale = targetWidth / width;
25054 if(width < height){
25055 scale = targetHeight / height;
25059 context.scale(scale, scale);
25061 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25062 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25064 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25065 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25067 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25068 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25070 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25075 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25076 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25078 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25079 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25081 var targetWidth = this.minWidth - 2 * x;
25082 var targetHeight = this.minHeight - 2 * y;
25086 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25087 scale = targetWidth / width;
25090 if(x > 0 && y == 0){
25091 scale = targetHeight / height;
25094 if(x > 0 && y > 0){
25095 scale = targetWidth / width;
25097 if(width < height){
25098 scale = targetHeight / height;
25102 context.scale(scale, scale);
25104 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25105 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25107 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25108 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25110 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25112 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25119 this.cropData = canvas.toDataURL(this.cropType);
25121 if(this.fireEvent('crop', this, this.cropData) !== false){
25122 this.process(this.file, this.cropData);
25129 setThumbBoxSize : function()
25133 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25134 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25135 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25137 this.minWidth = width;
25138 this.minHeight = height;
25140 if(this.rotate == 90 || this.rotate == 270){
25141 this.minWidth = height;
25142 this.minHeight = width;
25147 width = Math.ceil(this.minWidth * height / this.minHeight);
25149 if(this.minWidth > this.minHeight){
25151 height = Math.ceil(this.minHeight * width / this.minWidth);
25154 this.thumbEl.setStyle({
25155 width : width + 'px',
25156 height : height + 'px'
25163 setThumbBoxPosition : function()
25165 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25166 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25168 this.thumbEl.setLeft(x);
25169 this.thumbEl.setTop(y);
25173 baseRotateLevel : function()
25175 this.baseRotate = 1;
25178 typeof(this.exif) != 'undefined' &&
25179 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25180 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25182 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25185 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25189 baseScaleLevel : function()
25193 if(this.isDocument){
25195 if(this.baseRotate == 6 || this.baseRotate == 8){
25197 height = this.thumbEl.getHeight();
25198 this.baseScale = height / this.imageEl.OriginWidth;
25200 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25201 width = this.thumbEl.getWidth();
25202 this.baseScale = width / this.imageEl.OriginHeight;
25208 height = this.thumbEl.getHeight();
25209 this.baseScale = height / this.imageEl.OriginHeight;
25211 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25212 width = this.thumbEl.getWidth();
25213 this.baseScale = width / this.imageEl.OriginWidth;
25219 if(this.baseRotate == 6 || this.baseRotate == 8){
25221 width = this.thumbEl.getHeight();
25222 this.baseScale = width / this.imageEl.OriginHeight;
25224 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25225 height = this.thumbEl.getWidth();
25226 this.baseScale = height / this.imageEl.OriginHeight;
25229 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25230 height = this.thumbEl.getWidth();
25231 this.baseScale = height / this.imageEl.OriginHeight;
25233 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25234 width = this.thumbEl.getHeight();
25235 this.baseScale = width / this.imageEl.OriginWidth;
25242 width = this.thumbEl.getWidth();
25243 this.baseScale = width / this.imageEl.OriginWidth;
25245 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25246 height = this.thumbEl.getHeight();
25247 this.baseScale = height / this.imageEl.OriginHeight;
25250 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25252 height = this.thumbEl.getHeight();
25253 this.baseScale = height / this.imageEl.OriginHeight;
25255 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25256 width = this.thumbEl.getWidth();
25257 this.baseScale = width / this.imageEl.OriginWidth;
25265 getScaleLevel : function()
25267 return this.baseScale * Math.pow(1.1, this.scale);
25270 onTouchStart : function(e)
25272 if(!this.canvasLoaded){
25273 this.beforeSelectFile(e);
25277 var touches = e.browserEvent.touches;
25283 if(touches.length == 1){
25284 this.onMouseDown(e);
25288 if(touches.length != 2){
25294 for(var i = 0, finger; finger = touches[i]; i++){
25295 coords.push(finger.pageX, finger.pageY);
25298 var x = Math.pow(coords[0] - coords[2], 2);
25299 var y = Math.pow(coords[1] - coords[3], 2);
25301 this.startDistance = Math.sqrt(x + y);
25303 this.startScale = this.scale;
25305 this.pinching = true;
25306 this.dragable = false;
25310 onTouchMove : function(e)
25312 if(!this.pinching && !this.dragable){
25316 var touches = e.browserEvent.touches;
25323 this.onMouseMove(e);
25329 for(var i = 0, finger; finger = touches[i]; i++){
25330 coords.push(finger.pageX, finger.pageY);
25333 var x = Math.pow(coords[0] - coords[2], 2);
25334 var y = Math.pow(coords[1] - coords[3], 2);
25336 this.endDistance = Math.sqrt(x + y);
25338 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25340 if(!this.zoomable()){
25341 this.scale = this.startScale;
25349 onTouchEnd : function(e)
25351 this.pinching = false;
25352 this.dragable = false;
25356 process : function(file, crop)
25358 this.xhr = new XMLHttpRequest();
25360 file.xhr = this.xhr;
25362 this.xhr.open(this.method, this.url, true);
25365 "Accept": "application/json",
25366 "Cache-Control": "no-cache",
25367 "X-Requested-With": "XMLHttpRequest"
25370 for (var headerName in headers) {
25371 var headerValue = headers[headerName];
25373 this.xhr.setRequestHeader(headerName, headerValue);
25379 this.xhr.onload = function()
25381 _this.xhrOnLoad(_this.xhr);
25384 this.xhr.onerror = function()
25386 _this.xhrOnError(_this.xhr);
25389 var formData = new FormData();
25391 formData.append('returnHTML', 'NO');
25394 formData.append('crop', crop);
25397 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25398 formData.append(this.paramName, file, file.name);
25401 if(typeof(file.filename) != 'undefined'){
25402 formData.append('filename', file.filename);
25405 if(typeof(file.mimetype) != 'undefined'){
25406 formData.append('mimetype', file.mimetype);
25409 if(this.fireEvent('arrange', this, formData) != false){
25410 this.xhr.send(formData);
25414 xhrOnLoad : function(xhr)
25416 if (xhr.readyState !== 4) {
25417 this.fireEvent('exception', this, xhr);
25421 var response = Roo.decode(xhr.responseText);
25423 if(!response.success){
25424 this.fireEvent('exception', this, xhr);
25428 var response = Roo.decode(xhr.responseText);
25430 this.fireEvent('upload', this, response);
25434 xhrOnError : function()
25436 Roo.log('xhr on error');
25438 var response = Roo.decode(xhr.responseText);
25444 prepare : function(file)
25449 if(typeof(file) === 'string'){
25450 this.loadCanvas(file);
25454 if(!file || !this.urlAPI){
25459 this.cropType = file.type;
25463 if(this.fireEvent('prepare', this, this.file) != false){
25465 var reader = new FileReader();
25467 reader.onload = function (e) {
25468 if (e.target.error) {
25469 Roo.log(e.target.error);
25473 var buffer = e.target.result,
25474 dataView = new DataView(buffer),
25476 maxOffset = dataView.byteLength - 4,
25480 if (dataView.getUint16(0) === 0xffd8) {
25481 while (offset < maxOffset) {
25482 markerBytes = dataView.getUint16(offset);
25484 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25485 markerLength = dataView.getUint16(offset + 2) + 2;
25486 if (offset + markerLength > dataView.byteLength) {
25487 Roo.log('Invalid meta data: Invalid segment size.');
25491 if(markerBytes == 0xffe1){
25492 _this.parseExifData(
25499 offset += markerLength;
25509 var url = _this.urlAPI.createObjectURL(_this.file);
25511 _this.loadCanvas(url);
25516 reader.readAsArrayBuffer(this.file);
25522 parseExifData : function(dataView, offset, length)
25524 var tiffOffset = offset + 10,
25528 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25529 // No Exif data, might be XMP data instead
25533 // Check for the ASCII code for "Exif" (0x45786966):
25534 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25535 // No Exif data, might be XMP data instead
25538 if (tiffOffset + 8 > dataView.byteLength) {
25539 Roo.log('Invalid Exif data: Invalid segment size.');
25542 // Check for the two null bytes:
25543 if (dataView.getUint16(offset + 8) !== 0x0000) {
25544 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25547 // Check the byte alignment:
25548 switch (dataView.getUint16(tiffOffset)) {
25550 littleEndian = true;
25553 littleEndian = false;
25556 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25559 // Check for the TIFF tag marker (0x002A):
25560 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25561 Roo.log('Invalid Exif data: Missing TIFF marker.');
25564 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25565 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25567 this.parseExifTags(
25570 tiffOffset + dirOffset,
25575 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25580 if (dirOffset + 6 > dataView.byteLength) {
25581 Roo.log('Invalid Exif data: Invalid directory offset.');
25584 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25585 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25586 if (dirEndOffset + 4 > dataView.byteLength) {
25587 Roo.log('Invalid Exif data: Invalid directory size.');
25590 for (i = 0; i < tagsNumber; i += 1) {
25594 dirOffset + 2 + 12 * i, // tag offset
25598 // Return the offset to the next directory:
25599 return dataView.getUint32(dirEndOffset, littleEndian);
25602 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25604 var tag = dataView.getUint16(offset, littleEndian);
25606 this.exif[tag] = this.getExifValue(
25610 dataView.getUint16(offset + 2, littleEndian), // tag type
25611 dataView.getUint32(offset + 4, littleEndian), // tag length
25616 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25618 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25627 Roo.log('Invalid Exif data: Invalid tag type.');
25631 tagSize = tagType.size * length;
25632 // Determine if the value is contained in the dataOffset bytes,
25633 // or if the value at the dataOffset is a pointer to the actual data:
25634 dataOffset = tagSize > 4 ?
25635 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25636 if (dataOffset + tagSize > dataView.byteLength) {
25637 Roo.log('Invalid Exif data: Invalid data offset.');
25640 if (length === 1) {
25641 return tagType.getValue(dataView, dataOffset, littleEndian);
25644 for (i = 0; i < length; i += 1) {
25645 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25648 if (tagType.ascii) {
25650 // Concatenate the chars:
25651 for (i = 0; i < values.length; i += 1) {
25653 // Ignore the terminating NULL byte(s):
25654 if (c === '\u0000') {
25666 Roo.apply(Roo.bootstrap.UploadCropbox, {
25668 'Orientation': 0x0112
25672 1: 0, //'top-left',
25674 3: 180, //'bottom-right',
25675 // 4: 'bottom-left',
25677 6: 90, //'right-top',
25678 // 7: 'right-bottom',
25679 8: 270 //'left-bottom'
25683 // byte, 8-bit unsigned int:
25685 getValue: function (dataView, dataOffset) {
25686 return dataView.getUint8(dataOffset);
25690 // ascii, 8-bit byte:
25692 getValue: function (dataView, dataOffset) {
25693 return String.fromCharCode(dataView.getUint8(dataOffset));
25698 // short, 16 bit int:
25700 getValue: function (dataView, dataOffset, littleEndian) {
25701 return dataView.getUint16(dataOffset, littleEndian);
25705 // long, 32 bit int:
25707 getValue: function (dataView, dataOffset, littleEndian) {
25708 return dataView.getUint32(dataOffset, littleEndian);
25712 // rational = two long values, first is numerator, second is denominator:
25714 getValue: function (dataView, dataOffset, littleEndian) {
25715 return dataView.getUint32(dataOffset, littleEndian) /
25716 dataView.getUint32(dataOffset + 4, littleEndian);
25720 // slong, 32 bit signed int:
25722 getValue: function (dataView, dataOffset, littleEndian) {
25723 return dataView.getInt32(dataOffset, littleEndian);
25727 // srational, two slongs, first is numerator, second is denominator:
25729 getValue: function (dataView, dataOffset, littleEndian) {
25730 return dataView.getInt32(dataOffset, littleEndian) /
25731 dataView.getInt32(dataOffset + 4, littleEndian);
25741 cls : 'btn-group roo-upload-cropbox-rotate-left',
25742 action : 'rotate-left',
25746 cls : 'btn btn-default',
25747 html : '<i class="fa fa-undo"></i>'
25753 cls : 'btn-group roo-upload-cropbox-picture',
25754 action : 'picture',
25758 cls : 'btn btn-default',
25759 html : '<i class="fa fa-picture-o"></i>'
25765 cls : 'btn-group roo-upload-cropbox-rotate-right',
25766 action : 'rotate-right',
25770 cls : 'btn btn-default',
25771 html : '<i class="fa fa-repeat"></i>'
25779 cls : 'btn-group roo-upload-cropbox-rotate-left',
25780 action : 'rotate-left',
25784 cls : 'btn btn-default',
25785 html : '<i class="fa fa-undo"></i>'
25791 cls : 'btn-group roo-upload-cropbox-download',
25792 action : 'download',
25796 cls : 'btn btn-default',
25797 html : '<i class="fa fa-download"></i>'
25803 cls : 'btn-group roo-upload-cropbox-crop',
25808 cls : 'btn btn-default',
25809 html : '<i class="fa fa-crop"></i>'
25815 cls : 'btn-group roo-upload-cropbox-trash',
25820 cls : 'btn btn-default',
25821 html : '<i class="fa fa-trash"></i>'
25827 cls : 'btn-group roo-upload-cropbox-rotate-right',
25828 action : 'rotate-right',
25832 cls : 'btn btn-default',
25833 html : '<i class="fa fa-repeat"></i>'
25841 cls : 'btn-group roo-upload-cropbox-rotate-left',
25842 action : 'rotate-left',
25846 cls : 'btn btn-default',
25847 html : '<i class="fa fa-undo"></i>'
25853 cls : 'btn-group roo-upload-cropbox-rotate-right',
25854 action : 'rotate-right',
25858 cls : 'btn btn-default',
25859 html : '<i class="fa fa-repeat"></i>'
25872 * @class Roo.bootstrap.DocumentManager
25873 * @extends Roo.bootstrap.Component
25874 * Bootstrap DocumentManager class
25875 * @cfg {String} paramName default 'imageUpload'
25876 * @cfg {String} method default POST
25877 * @cfg {String} url action url
25878 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25879 * @cfg {Boolean} multiple multiple upload default true
25880 * @cfg {Number} thumbSize default 300
25881 * @cfg {String} fieldLabel
25882 * @cfg {Number} labelWidth default 4
25883 * @cfg {String} labelAlign (left|top) default left
25884 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25887 * Create a new DocumentManager
25888 * @param {Object} config The config object
25891 Roo.bootstrap.DocumentManager = function(config){
25892 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25897 * Fire when initial the DocumentManager
25898 * @param {Roo.bootstrap.DocumentManager} this
25903 * inspect selected file
25904 * @param {Roo.bootstrap.DocumentManager} this
25905 * @param {File} file
25910 * Fire when xhr load exception
25911 * @param {Roo.bootstrap.DocumentManager} this
25912 * @param {XMLHttpRequest} xhr
25914 "exception" : true,
25917 * prepare the form data
25918 * @param {Roo.bootstrap.DocumentManager} this
25919 * @param {Object} formData
25924 * Fire when remove the file
25925 * @param {Roo.bootstrap.DocumentManager} this
25926 * @param {Object} file
25931 * Fire after refresh the file
25932 * @param {Roo.bootstrap.DocumentManager} this
25937 * Fire after click the image
25938 * @param {Roo.bootstrap.DocumentManager} this
25939 * @param {Object} file
25944 * Fire when upload a image and editable set to true
25945 * @param {Roo.bootstrap.DocumentManager} this
25946 * @param {Object} file
25950 * @event beforeselectfile
25951 * Fire before select file
25952 * @param {Roo.bootstrap.DocumentManager} this
25954 "beforeselectfile" : true,
25957 * Fire before process file
25958 * @param {Roo.bootstrap.DocumentManager} this
25959 * @param {Object} file
25966 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25975 paramName : 'imageUpload',
25978 labelAlign : 'left',
25982 getAutoCreate : function()
25984 var managerWidget = {
25986 cls : 'roo-document-manager',
25990 cls : 'roo-document-manager-selector',
25995 cls : 'roo-document-manager-uploader',
25999 cls : 'roo-document-manager-upload-btn',
26000 html : '<i class="fa fa-plus"></i>'
26011 cls : 'column col-md-12',
26016 if(this.fieldLabel.length){
26021 cls : 'column col-md-12',
26022 html : this.fieldLabel
26026 cls : 'column col-md-12',
26031 if(this.labelAlign == 'left'){
26035 cls : 'column col-md-' + this.labelWidth,
26036 html : this.fieldLabel
26040 cls : 'column col-md-' + (12 - this.labelWidth),
26050 cls : 'row clearfix',
26058 initEvents : function()
26060 this.managerEl = this.el.select('.roo-document-manager', true).first();
26061 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26063 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26064 this.selectorEl.hide();
26067 this.selectorEl.attr('multiple', 'multiple');
26070 this.selectorEl.on('change', this.onFileSelected, this);
26072 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26073 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26075 this.uploader.on('click', this.onUploaderClick, this);
26077 this.renderProgressDialog();
26081 window.addEventListener("resize", function() { _this.refresh(); } );
26083 this.fireEvent('initial', this);
26086 renderProgressDialog : function()
26090 this.progressDialog = new Roo.bootstrap.Modal({
26091 cls : 'roo-document-manager-progress-dialog',
26092 allow_close : false,
26102 btnclick : function() {
26103 _this.uploadCancel();
26109 this.progressDialog.render(Roo.get(document.body));
26111 this.progress = new Roo.bootstrap.Progress({
26112 cls : 'roo-document-manager-progress',
26117 this.progress.render(this.progressDialog.getChildContainer());
26119 this.progressBar = new Roo.bootstrap.ProgressBar({
26120 cls : 'roo-document-manager-progress-bar',
26123 aria_valuemax : 12,
26127 this.progressBar.render(this.progress.getChildContainer());
26130 onUploaderClick : function(e)
26132 e.preventDefault();
26134 if(this.fireEvent('beforeselectfile', this) != false){
26135 this.selectorEl.dom.click();
26140 onFileSelected : function(e)
26142 e.preventDefault();
26144 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26148 Roo.each(this.selectorEl.dom.files, function(file){
26149 if(this.fireEvent('inspect', this, file) != false){
26150 this.files.push(file);
26160 this.selectorEl.dom.value = '';
26162 if(!this.files.length){
26166 if(this.boxes > 0 && this.files.length > this.boxes){
26167 this.files = this.files.slice(0, this.boxes);
26170 this.uploader.show();
26172 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26173 this.uploader.hide();
26182 Roo.each(this.files, function(file){
26184 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26185 var f = this.renderPreview(file);
26190 if(file.type.indexOf('image') != -1){
26191 this.delegates.push(
26193 _this.process(file);
26194 }).createDelegate(this)
26202 _this.process(file);
26203 }).createDelegate(this)
26208 this.files = files;
26210 this.delegates = this.delegates.concat(docs);
26212 if(!this.delegates.length){
26217 this.progressBar.aria_valuemax = this.delegates.length;
26224 arrange : function()
26226 if(!this.delegates.length){
26227 this.progressDialog.hide();
26232 var delegate = this.delegates.shift();
26234 this.progressDialog.show();
26236 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26238 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26243 refresh : function()
26245 this.uploader.show();
26247 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26248 this.uploader.hide();
26251 Roo.isTouch ? this.closable(false) : this.closable(true);
26253 this.fireEvent('refresh', this);
26256 onRemove : function(e, el, o)
26258 e.preventDefault();
26260 this.fireEvent('remove', this, o);
26264 remove : function(o)
26268 Roo.each(this.files, function(file){
26269 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26278 this.files = files;
26285 Roo.each(this.files, function(file){
26290 file.target.remove();
26299 onClick : function(e, el, o)
26301 e.preventDefault();
26303 this.fireEvent('click', this, o);
26307 closable : function(closable)
26309 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26311 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26323 xhrOnLoad : function(xhr)
26325 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26329 if (xhr.readyState !== 4) {
26331 this.fireEvent('exception', this, xhr);
26335 var response = Roo.decode(xhr.responseText);
26337 if(!response.success){
26339 this.fireEvent('exception', this, xhr);
26343 var file = this.renderPreview(response.data);
26345 this.files.push(file);
26351 xhrOnError : function()
26353 Roo.log('xhr on error');
26355 var response = Roo.decode(xhr.responseText);
26362 process : function(file)
26364 if(this.fireEvent('process', this, file) !== false){
26365 if(this.editable && file.type.indexOf('image') != -1){
26366 this.fireEvent('edit', this, file);
26370 this.uploadStart(file, false);
26377 uploadStart : function(file, crop)
26379 this.xhr = new XMLHttpRequest();
26381 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26386 file.xhr = this.xhr;
26388 this.managerEl.createChild({
26390 cls : 'roo-document-manager-loading',
26394 tooltip : file.name,
26395 cls : 'roo-document-manager-thumb',
26396 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26402 this.xhr.open(this.method, this.url, true);
26405 "Accept": "application/json",
26406 "Cache-Control": "no-cache",
26407 "X-Requested-With": "XMLHttpRequest"
26410 for (var headerName in headers) {
26411 var headerValue = headers[headerName];
26413 this.xhr.setRequestHeader(headerName, headerValue);
26419 this.xhr.onload = function()
26421 _this.xhrOnLoad(_this.xhr);
26424 this.xhr.onerror = function()
26426 _this.xhrOnError(_this.xhr);
26429 var formData = new FormData();
26431 formData.append('returnHTML', 'NO');
26434 formData.append('crop', crop);
26437 formData.append(this.paramName, file, file.name);
26439 if(this.fireEvent('prepare', this, formData) != false){
26440 this.xhr.send(formData);
26444 uploadCancel : function()
26448 this.delegates = [];
26450 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26457 renderPreview : function(file)
26459 if(typeof(file.target) != 'undefined' && file.target){
26463 var previewEl = this.managerEl.createChild({
26465 cls : 'roo-document-manager-preview',
26469 tooltip : file.filename,
26470 cls : 'roo-document-manager-thumb',
26471 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26476 html : '<i class="fa fa-times-circle"></i>'
26481 var close = previewEl.select('button.close', true).first();
26483 close.on('click', this.onRemove, this, file);
26485 file.target = previewEl;
26487 var image = previewEl.select('img', true).first();
26491 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26493 image.on('click', this.onClick, this, file);
26499 onPreviewLoad : function(file, image)
26501 if(typeof(file.target) == 'undefined' || !file.target){
26505 var width = image.dom.naturalWidth || image.dom.width;
26506 var height = image.dom.naturalHeight || image.dom.height;
26508 if(width > height){
26509 file.target.addClass('wide');
26513 file.target.addClass('tall');
26518 uploadFromSource : function(file, crop)
26520 this.xhr = new XMLHttpRequest();
26522 this.managerEl.createChild({
26524 cls : 'roo-document-manager-loading',
26528 tooltip : file.name,
26529 cls : 'roo-document-manager-thumb',
26530 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26536 this.xhr.open(this.method, this.url, true);
26539 "Accept": "application/json",
26540 "Cache-Control": "no-cache",
26541 "X-Requested-With": "XMLHttpRequest"
26544 for (var headerName in headers) {
26545 var headerValue = headers[headerName];
26547 this.xhr.setRequestHeader(headerName, headerValue);
26553 this.xhr.onload = function()
26555 _this.xhrOnLoad(_this.xhr);
26558 this.xhr.onerror = function()
26560 _this.xhrOnError(_this.xhr);
26563 var formData = new FormData();
26565 formData.append('returnHTML', 'NO');
26567 formData.append('crop', crop);
26569 if(typeof(file.filename) != 'undefined'){
26570 formData.append('filename', file.filename);
26573 if(typeof(file.mimetype) != 'undefined'){
26574 formData.append('mimetype', file.mimetype);
26577 if(this.fireEvent('prepare', this, formData) != false){
26578 this.xhr.send(formData);
26588 * @class Roo.bootstrap.DocumentViewer
26589 * @extends Roo.bootstrap.Component
26590 * Bootstrap DocumentViewer class
26593 * Create a new DocumentViewer
26594 * @param {Object} config The config object
26597 Roo.bootstrap.DocumentViewer = function(config){
26598 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26603 * Fire after initEvent
26604 * @param {Roo.bootstrap.DocumentViewer} this
26610 * @param {Roo.bootstrap.DocumentViewer} this
26615 * Fire after trash button
26616 * @param {Roo.bootstrap.DocumentViewer} this
26623 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26625 getAutoCreate : function()
26629 cls : 'roo-document-viewer',
26633 cls : 'roo-document-viewer-body',
26637 cls : 'roo-document-viewer-thumb',
26641 cls : 'roo-document-viewer-image'
26649 cls : 'roo-document-viewer-footer',
26652 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26660 cls : 'btn btn-default roo-document-viewer-trash',
26661 html : '<i class="fa fa-trash"></i>'
26674 initEvents : function()
26677 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26678 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26680 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26681 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26683 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26684 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26686 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26687 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26689 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26690 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26692 this.bodyEl.on('click', this.onClick, this);
26694 this.trashBtn.on('click', this.onTrash, this);
26698 initial : function()
26700 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26703 this.fireEvent('initial', this);
26707 onClick : function(e)
26709 e.preventDefault();
26711 this.fireEvent('click', this);
26714 onTrash : function(e)
26716 e.preventDefault();
26718 this.fireEvent('trash', this);
26730 * @class Roo.bootstrap.NavProgressBar
26731 * @extends Roo.bootstrap.Component
26732 * Bootstrap NavProgressBar class
26735 * Create a new nav progress bar
26736 * @param {Object} config The config object
26739 Roo.bootstrap.NavProgressBar = function(config){
26740 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26742 this.bullets = this.bullets || [];
26744 // Roo.bootstrap.NavProgressBar.register(this);
26748 * Fires when the active item changes
26749 * @param {Roo.bootstrap.NavProgressBar} this
26750 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26751 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26758 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26763 getAutoCreate : function()
26765 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26769 cls : 'roo-navigation-bar-group',
26773 cls : 'roo-navigation-top-bar'
26777 cls : 'roo-navigation-bullets-bar',
26781 cls : 'roo-navigation-bar'
26788 cls : 'roo-navigation-bottom-bar'
26798 initEvents: function()
26803 onRender : function(ct, position)
26805 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26807 if(this.bullets.length){
26808 Roo.each(this.bullets, function(b){
26817 addItem : function(cfg)
26819 var item = new Roo.bootstrap.NavProgressItem(cfg);
26821 item.parentId = this.id;
26822 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26825 var top = new Roo.bootstrap.Element({
26827 cls : 'roo-navigation-bar-text'
26830 var bottom = new Roo.bootstrap.Element({
26832 cls : 'roo-navigation-bar-text'
26835 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26836 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26838 var topText = new Roo.bootstrap.Element({
26840 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26843 var bottomText = new Roo.bootstrap.Element({
26845 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26848 topText.onRender(top.el, null);
26849 bottomText.onRender(bottom.el, null);
26852 item.bottomEl = bottom;
26855 this.barItems.push(item);
26860 getActive : function()
26862 var active = false;
26864 Roo.each(this.barItems, function(v){
26866 if (!v.isActive()) {
26878 setActiveItem : function(item)
26882 Roo.each(this.barItems, function(v){
26883 if (v.rid == item.rid) {
26887 if (v.isActive()) {
26888 v.setActive(false);
26893 item.setActive(true);
26895 this.fireEvent('changed', this, item, prev);
26898 getBarItem: function(rid)
26902 Roo.each(this.barItems, function(e) {
26903 if (e.rid != rid) {
26914 indexOfItem : function(item)
26918 Roo.each(this.barItems, function(v, i){
26920 if (v.rid != item.rid) {
26931 setActiveNext : function()
26933 var i = this.indexOfItem(this.getActive());
26935 if (i > this.barItems.length) {
26939 this.setActiveItem(this.barItems[i+1]);
26942 setActivePrev : function()
26944 var i = this.indexOfItem(this.getActive());
26950 this.setActiveItem(this.barItems[i-1]);
26953 format : function()
26955 if(!this.barItems.length){
26959 var width = 100 / this.barItems.length;
26961 Roo.each(this.barItems, function(i){
26962 i.el.setStyle('width', width + '%');
26963 i.topEl.el.setStyle('width', width + '%');
26964 i.bottomEl.el.setStyle('width', width + '%');
26973 * Nav Progress Item
26978 * @class Roo.bootstrap.NavProgressItem
26979 * @extends Roo.bootstrap.Component
26980 * Bootstrap NavProgressItem class
26981 * @cfg {String} rid the reference id
26982 * @cfg {Boolean} active (true|false) Is item active default false
26983 * @cfg {Boolean} disabled (true|false) Is item active default false
26984 * @cfg {String} html
26985 * @cfg {String} position (top|bottom) text position default bottom
26986 * @cfg {String} icon show icon instead of number
26989 * Create a new NavProgressItem
26990 * @param {Object} config The config object
26992 Roo.bootstrap.NavProgressItem = function(config){
26993 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
26998 * The raw click event for the entire grid.
26999 * @param {Roo.bootstrap.NavProgressItem} this
27000 * @param {Roo.EventObject} e
27007 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27013 position : 'bottom',
27016 getAutoCreate : function()
27018 var iconCls = 'roo-navigation-bar-item-icon';
27020 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27024 cls: 'roo-navigation-bar-item',
27034 cfg.cls += ' active';
27037 cfg.cls += ' disabled';
27043 disable : function()
27045 this.setDisabled(true);
27048 enable : function()
27050 this.setDisabled(false);
27053 initEvents: function()
27055 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27057 this.iconEl.on('click', this.onClick, this);
27060 onClick : function(e)
27062 e.preventDefault();
27068 if(this.fireEvent('click', this, e) === false){
27072 this.parent().setActiveItem(this);
27075 isActive: function ()
27077 return this.active;
27080 setActive : function(state)
27082 if(this.active == state){
27086 this.active = state;
27089 this.el.addClass('active');
27093 this.el.removeClass('active');
27098 setDisabled : function(state)
27100 if(this.disabled == state){
27104 this.disabled = state;
27107 this.el.addClass('disabled');
27111 this.el.removeClass('disabled');
27114 tooltipEl : function()
27116 return this.el.select('.roo-navigation-bar-item-icon', true).first();;