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
4313 * Create a new Navbar Button
4314 * @param {Object} config The config object
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4322 * The raw click event for the entire grid.
4323 * @param {Roo.EventObject} e
4328 * Fires when the active item active state changes
4329 * @param {Roo.bootstrap.NavSidebarItem} this
4330 * @param {boolean} state the new state
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4341 getAutoCreate : function(){
4346 href : this.href || '#',
4358 html : this.html || ''
4363 cfg.cls += ' active';
4367 if (this.glyphicon || this.icon) {
4368 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4369 a.cn.push({ tag : 'i', cls : c }) ;
4374 if (this.badge !== '') {
4375 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
4379 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380 a.cls += 'dropdown-toggle treeview' ;
4404 * @class Roo.bootstrap.Row
4405 * @extends Roo.bootstrap.Component
4406 * Bootstrap Row class (contains columns...)
4410 * @param {Object} config The config object
4413 Roo.bootstrap.Row = function(config){
4414 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4419 getAutoCreate : function(){
4438 * @class Roo.bootstrap.Element
4439 * @extends Roo.bootstrap.Component
4440 * Bootstrap Element class
4441 * @cfg {String} html contents of the element
4442 * @cfg {String} tag tag of the element
4443 * @cfg {String} cls class of the element
4444 * @cfg {Boolean} preventDefault (true|false) default false
4445 * @cfg {Boolean} clickable (true|false) default false
4448 * Create a new Element
4449 * @param {Object} config The config object
4452 Roo.bootstrap.Element = function(config){
4453 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4459 * When a element is chick
4460 * @param {Roo.bootstrap.Element} this
4461 * @param {Roo.EventObject} e
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4472 preventDefault: false,
4475 getAutoCreate : function(){
4486 initEvents: function()
4488 Roo.bootstrap.Element.superclass.initEvents.call(this);
4491 this.el.on('click', this.onClick, this);
4496 onClick : function(e)
4498 if(this.preventDefault){
4502 this.fireEvent('click', this, e);
4505 getValue : function()
4507 return this.el.dom.innerHTML;
4510 setValue : function(value)
4512 this.el.dom.innerHTML = value;
4527 * @class Roo.bootstrap.Pagination
4528 * @extends Roo.bootstrap.Component
4529 * Bootstrap Pagination class
4530 * @cfg {String} size xs | sm | md | lg
4531 * @cfg {Boolean} inverse false | true
4534 * Create a new Pagination
4535 * @param {Object} config The config object
4538 Roo.bootstrap.Pagination = function(config){
4539 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4548 getAutoCreate : function(){
4554 cfg.cls += ' inverse';
4560 cfg.cls += " " + this.cls;
4578 * @class Roo.bootstrap.PaginationItem
4579 * @extends Roo.bootstrap.Component
4580 * Bootstrap PaginationItem class
4581 * @cfg {String} html text
4582 * @cfg {String} href the link
4583 * @cfg {Boolean} preventDefault (true | false) default true
4584 * @cfg {Boolean} active (true | false) default false
4585 * @cfg {Boolean} disabled default false
4589 * Create a new PaginationItem
4590 * @param {Object} config The config object
4594 Roo.bootstrap.PaginationItem = function(config){
4595 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4600 * The raw click event for the entire grid.
4601 * @param {Roo.EventObject} e
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4611 preventDefault: true,
4616 getAutoCreate : function(){
4622 href : this.href ? this.href : '#',
4623 html : this.html ? this.html : ''
4633 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4637 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4643 initEvents: function() {
4645 this.el.on('click', this.onClick, this);
4648 onClick : function(e)
4650 Roo.log('PaginationItem on click ');
4651 if(this.preventDefault){
4659 this.fireEvent('click', this, e);
4675 * @class Roo.bootstrap.Slider
4676 * @extends Roo.bootstrap.Component
4677 * Bootstrap Slider class
4680 * Create a new Slider
4681 * @param {Object} config The config object
4684 Roo.bootstrap.Slider = function(config){
4685 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4690 getAutoCreate : function(){
4694 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4698 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4710 * Ext JS Library 1.1.1
4711 * Copyright(c) 2006-2007, Ext JS, LLC.
4713 * Originally Released Under LGPL - original licence link has changed is not relivant.
4716 * <script type="text/javascript">
4721 * @class Roo.grid.ColumnModel
4722 * @extends Roo.util.Observable
4723 * This is the default implementation of a ColumnModel used by the Grid. It defines
4724 * the columns in the grid.
4727 var colModel = new Roo.grid.ColumnModel([
4728 {header: "Ticker", width: 60, sortable: true, locked: true},
4729 {header: "Company Name", width: 150, sortable: true},
4730 {header: "Market Cap.", width: 100, sortable: true},
4731 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732 {header: "Employees", width: 100, sortable: true, resizable: false}
4737 * The config options listed for this class are options which may appear in each
4738 * individual column definition.
4739 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4741 * @param {Object} config An Array of column config objects. See this class's
4742 * config objects for details.
4744 Roo.grid.ColumnModel = function(config){
4746 * The config passed into the constructor
4748 this.config = config;
4751 // if no id, create one
4752 // if the column does not have a dataIndex mapping,
4753 // map it to the order it is in the config
4754 for(var i = 0, len = config.length; i < len; i++){
4756 if(typeof c.dataIndex == "undefined"){
4759 if(typeof c.renderer == "string"){
4760 c.renderer = Roo.util.Format[c.renderer];
4762 if(typeof c.id == "undefined"){
4765 if(c.editor && c.editor.xtype){
4766 c.editor = Roo.factory(c.editor, Roo.grid);
4768 if(c.editor && c.editor.isFormField){
4769 c.editor = new Roo.grid.GridEditor(c.editor);
4771 this.lookup[c.id] = c;
4775 * The width of columns which have no width specified (defaults to 100)
4778 this.defaultWidth = 100;
4781 * Default sortable of columns which have no sortable specified (defaults to false)
4784 this.defaultSortable = false;
4788 * @event widthchange
4789 * Fires when the width of a column changes.
4790 * @param {ColumnModel} this
4791 * @param {Number} columnIndex The column index
4792 * @param {Number} newWidth The new width
4794 "widthchange": true,
4796 * @event headerchange
4797 * Fires when the text of a header changes.
4798 * @param {ColumnModel} this
4799 * @param {Number} columnIndex The column index
4800 * @param {Number} newText The new header text
4802 "headerchange": true,
4804 * @event hiddenchange
4805 * Fires when a column is hidden or "unhidden".
4806 * @param {ColumnModel} this
4807 * @param {Number} columnIndex The column index
4808 * @param {Boolean} hidden true if hidden, false otherwise
4810 "hiddenchange": true,
4812 * @event columnmoved
4813 * Fires when a column is moved.
4814 * @param {ColumnModel} this
4815 * @param {Number} oldIndex
4816 * @param {Number} newIndex
4818 "columnmoved" : true,
4820 * @event columlockchange
4821 * Fires when a column's locked state is changed
4822 * @param {ColumnModel} this
4823 * @param {Number} colIndex
4824 * @param {Boolean} locked true if locked
4826 "columnlockchange" : true
4828 Roo.grid.ColumnModel.superclass.constructor.call(this);
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4832 * @cfg {String} header The header text to display in the Grid view.
4835 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837 * specified, the column's index is used as an index into the Record's data Array.
4840 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4844 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845 * Defaults to the value of the {@link #defaultSortable} property.
4846 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4849 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4852 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4855 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4858 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4861 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4867 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4870 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4873 * @cfg {String} cursor (Optional)
4876 * @cfg {String} tooltip (Optional)
4879 * Returns the id of the column at the specified index.
4880 * @param {Number} index The column index
4881 * @return {String} the id
4883 getColumnId : function(index){
4884 return this.config[index].id;
4888 * Returns the column for a specified id.
4889 * @param {String} id The column id
4890 * @return {Object} the column
4892 getColumnById : function(id){
4893 return this.lookup[id];
4898 * Returns the column for a specified dataIndex.
4899 * @param {String} dataIndex The column dataIndex
4900 * @return {Object|Boolean} the column or false if not found
4902 getColumnByDataIndex: function(dataIndex){
4903 var index = this.findColumnIndex(dataIndex);
4904 return index > -1 ? this.config[index] : false;
4908 * Returns the index for a specified column id.
4909 * @param {String} id The column id
4910 * @return {Number} the index, or -1 if not found
4912 getIndexById : function(id){
4913 for(var i = 0, len = this.config.length; i < len; i++){
4914 if(this.config[i].id == id){
4922 * Returns the index for a specified column dataIndex.
4923 * @param {String} dataIndex The column dataIndex
4924 * @return {Number} the index, or -1 if not found
4927 findColumnIndex : function(dataIndex){
4928 for(var i = 0, len = this.config.length; i < len; i++){
4929 if(this.config[i].dataIndex == dataIndex){
4937 moveColumn : function(oldIndex, newIndex){
4938 var c = this.config[oldIndex];
4939 this.config.splice(oldIndex, 1);
4940 this.config.splice(newIndex, 0, c);
4941 this.dataMap = null;
4942 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4945 isLocked : function(colIndex){
4946 return this.config[colIndex].locked === true;
4949 setLocked : function(colIndex, value, suppressEvent){
4950 if(this.isLocked(colIndex) == value){
4953 this.config[colIndex].locked = value;
4955 this.fireEvent("columnlockchange", this, colIndex, value);
4959 getTotalLockedWidth : function(){
4961 for(var i = 0; i < this.config.length; i++){
4962 if(this.isLocked(i) && !this.isHidden(i)){
4963 this.totalWidth += this.getColumnWidth(i);
4969 getLockedCount : function(){
4970 for(var i = 0, len = this.config.length; i < len; i++){
4971 if(!this.isLocked(i)){
4978 * Returns the number of columns.
4981 getColumnCount : function(visibleOnly){
4982 if(visibleOnly === true){
4984 for(var i = 0, len = this.config.length; i < len; i++){
4985 if(!this.isHidden(i)){
4991 return this.config.length;
4995 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996 * @param {Function} fn
4997 * @param {Object} scope (optional)
4998 * @return {Array} result
5000 getColumnsBy : function(fn, scope){
5002 for(var i = 0, len = this.config.length; i < len; i++){
5003 var c = this.config[i];
5004 if(fn.call(scope||this, c, i) === true){
5012 * Returns true if the specified column is sortable.
5013 * @param {Number} col The column index
5016 isSortable : function(col){
5017 if(typeof this.config[col].sortable == "undefined"){
5018 return this.defaultSortable;
5020 return this.config[col].sortable;
5024 * Returns the rendering (formatting) function defined for the column.
5025 * @param {Number} col The column index.
5026 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5028 getRenderer : function(col){
5029 if(!this.config[col].renderer){
5030 return Roo.grid.ColumnModel.defaultRenderer;
5032 return this.config[col].renderer;
5036 * Sets the rendering (formatting) function for a column.
5037 * @param {Number} col The column index
5038 * @param {Function} fn The function to use to process the cell's raw data
5039 * to return HTML markup for the grid view. The render function is called with
5040 * the following parameters:<ul>
5041 * <li>Data value.</li>
5042 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043 * <li>css A CSS style string to apply to the table cell.</li>
5044 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046 * <li>Row index</li>
5047 * <li>Column index</li>
5048 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5050 setRenderer : function(col, fn){
5051 this.config[col].renderer = fn;
5055 * Returns the width for the specified column.
5056 * @param {Number} col The column index
5059 getColumnWidth : function(col){
5060 return this.config[col].width * 1 || this.defaultWidth;
5064 * Sets the width for a column.
5065 * @param {Number} col The column index
5066 * @param {Number} width The new width
5068 setColumnWidth : function(col, width, suppressEvent){
5069 this.config[col].width = width;
5070 this.totalWidth = null;
5072 this.fireEvent("widthchange", this, col, width);
5077 * Returns the total width of all columns.
5078 * @param {Boolean} includeHidden True to include hidden column widths
5081 getTotalWidth : function(includeHidden){
5082 if(!this.totalWidth){
5083 this.totalWidth = 0;
5084 for(var i = 0, len = this.config.length; i < len; i++){
5085 if(includeHidden || !this.isHidden(i)){
5086 this.totalWidth += this.getColumnWidth(i);
5090 return this.totalWidth;
5094 * Returns the header for the specified column.
5095 * @param {Number} col The column index
5098 getColumnHeader : function(col){
5099 return this.config[col].header;
5103 * Sets the header for a column.
5104 * @param {Number} col The column index
5105 * @param {String} header The new header
5107 setColumnHeader : function(col, header){
5108 this.config[col].header = header;
5109 this.fireEvent("headerchange", this, col, header);
5113 * Returns the tooltip for the specified column.
5114 * @param {Number} col The column index
5117 getColumnTooltip : function(col){
5118 return this.config[col].tooltip;
5121 * Sets the tooltip for a column.
5122 * @param {Number} col The column index
5123 * @param {String} tooltip The new tooltip
5125 setColumnTooltip : function(col, tooltip){
5126 this.config[col].tooltip = tooltip;
5130 * Returns the dataIndex for the specified column.
5131 * @param {Number} col The column index
5134 getDataIndex : function(col){
5135 return this.config[col].dataIndex;
5139 * Sets the dataIndex for a column.
5140 * @param {Number} col The column index
5141 * @param {Number} dataIndex The new dataIndex
5143 setDataIndex : function(col, dataIndex){
5144 this.config[col].dataIndex = dataIndex;
5150 * Returns true if the cell is editable.
5151 * @param {Number} colIndex The column index
5152 * @param {Number} rowIndex The row index
5155 isCellEditable : function(colIndex, rowIndex){
5156 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5160 * Returns the editor defined for the cell/column.
5161 * return false or null to disable editing.
5162 * @param {Number} colIndex The column index
5163 * @param {Number} rowIndex The row index
5166 getCellEditor : function(colIndex, rowIndex){
5167 return this.config[colIndex].editor;
5171 * Sets if a column is editable.
5172 * @param {Number} col The column index
5173 * @param {Boolean} editable True if the column is editable
5175 setEditable : function(col, editable){
5176 this.config[col].editable = editable;
5181 * Returns true if the column is hidden.
5182 * @param {Number} colIndex The column index
5185 isHidden : function(colIndex){
5186 return this.config[colIndex].hidden;
5191 * Returns true if the column width cannot be changed
5193 isFixed : function(colIndex){
5194 return this.config[colIndex].fixed;
5198 * Returns true if the column can be resized
5201 isResizable : function(colIndex){
5202 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5205 * Sets if a column is hidden.
5206 * @param {Number} colIndex The column index
5207 * @param {Boolean} hidden True if the column is hidden
5209 setHidden : function(colIndex, hidden){
5210 this.config[colIndex].hidden = hidden;
5211 this.totalWidth = null;
5212 this.fireEvent("hiddenchange", this, colIndex, hidden);
5216 * Sets the editor for a column.
5217 * @param {Number} col The column index
5218 * @param {Object} editor The editor object
5220 setEditor : function(col, editor){
5221 this.config[col].editor = editor;
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226 if(typeof value == "string" && value.length < 1){
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5236 * Ext JS Library 1.1.1
5237 * Copyright(c) 2006-2007, Ext JS, LLC.
5239 * Originally Released Under LGPL - original licence link has changed is not relivant.
5242 * <script type="text/javascript">
5246 * @class Roo.LoadMask
5247 * A simple utility class for generically masking elements while loading data. If the element being masked has
5248 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5250 * element's UpdateManager load indicator and will be destroyed after the initial load.
5252 * Create a new LoadMask
5253 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254 * @param {Object} config The config object
5256 Roo.LoadMask = function(el, config){
5257 this.el = Roo.get(el);
5258 Roo.apply(this, config);
5260 this.store.on('beforeload', this.onBeforeLoad, this);
5261 this.store.on('load', this.onLoad, this);
5262 this.store.on('loadexception', this.onLoadException, this);
5263 this.removeMask = false;
5265 var um = this.el.getUpdateManager();
5266 um.showLoadIndicator = false; // disable the default indicator
5267 um.on('beforeupdate', this.onBeforeLoad, this);
5268 um.on('update', this.onLoad, this);
5269 um.on('failure', this.onLoad, this);
5270 this.removeMask = true;
5274 Roo.LoadMask.prototype = {
5276 * @cfg {Boolean} removeMask
5277 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5282 * The text to display in a centered loading message box (defaults to 'Loading...')
5286 * @cfg {String} msgCls
5287 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5289 msgCls : 'x-mask-loading',
5292 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5298 * Disables the mask to prevent it from being displayed
5300 disable : function(){
5301 this.disabled = true;
5305 * Enables the mask so that it can be displayed
5307 enable : function(){
5308 this.disabled = false;
5311 onLoadException : function()
5315 if (typeof(arguments[3]) != 'undefined') {
5316 Roo.MessageBox.alert("Error loading",arguments[3]);
5320 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5330 this.el.unmask(this.removeMask);
5335 this.el.unmask(this.removeMask);
5339 onBeforeLoad : function(){
5341 this.el.mask(this.msg, this.msgCls);
5346 destroy : function(){
5348 this.store.un('beforeload', this.onBeforeLoad, this);
5349 this.store.un('load', this.onLoad, this);
5350 this.store.un('loadexception', this.onLoadException, this);
5352 var um = this.el.getUpdateManager();
5353 um.un('beforeupdate', this.onBeforeLoad, this);
5354 um.un('update', this.onLoad, this);
5355 um.un('failure', this.onLoad, this);
5366 * @class Roo.bootstrap.Table
5367 * @extends Roo.bootstrap.Component
5368 * Bootstrap Table class
5369 * @cfg {String} cls table class
5370 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371 * @cfg {String} bgcolor Specifies the background color for a table
5372 * @cfg {Number} border Specifies whether the table cells should have borders or not
5373 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374 * @cfg {Number} cellspacing Specifies the space between cells
5375 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377 * @cfg {String} sortable Specifies that the table should be sortable
5378 * @cfg {String} summary Specifies a summary of the content of a table
5379 * @cfg {Number} width Specifies the width of a table
5380 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5382 * @cfg {boolean} striped Should the rows be alternative striped
5383 * @cfg {boolean} bordered Add borders to the table
5384 * @cfg {boolean} hover Add hover highlighting
5385 * @cfg {boolean} condensed Format condensed
5386 * @cfg {boolean} responsive Format condensed
5387 * @cfg {Boolean} loadMask (true|false) default false
5388 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5389 * @cfg {Boolean} thead (true|false) generate thead, default true
5390 * @cfg {Boolean} RowSelection (true|false) default false
5391 * @cfg {Boolean} CellSelection (true|false) default false
5392 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5396 * Create a new Table
5397 * @param {Object} config The config object
5400 Roo.bootstrap.Table = function(config){
5401 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5404 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5405 this.sm = this.selModel;
5406 this.sm.xmodule = this.xmodule || false;
5408 if (this.cm && typeof(this.cm.config) == 'undefined') {
5409 this.colModel = new Roo.grid.ColumnModel(this.cm);
5410 this.cm = this.colModel;
5411 this.cm.xmodule = this.xmodule || false;
5414 this.store= Roo.factory(this.store, Roo.data);
5415 this.ds = this.store;
5416 this.ds.xmodule = this.xmodule || false;
5419 if (this.footer && this.store) {
5420 this.footer.dataSource = this.ds;
5421 this.footer = Roo.factory(this.footer);
5428 * Fires when a cell is clicked
5429 * @param {Roo.bootstrap.Table} this
5430 * @param {Roo.Element} el
5431 * @param {Number} rowIndex
5432 * @param {Number} columnIndex
5433 * @param {Roo.EventObject} e
5437 * @event celldblclick
5438 * Fires when a cell is double clicked
5439 * @param {Roo.bootstrap.Table} this
5440 * @param {Roo.Element} el
5441 * @param {Number} rowIndex
5442 * @param {Number} columnIndex
5443 * @param {Roo.EventObject} e
5445 "celldblclick" : true,
5448 * Fires when a row is clicked
5449 * @param {Roo.bootstrap.Table} this
5450 * @param {Roo.Element} el
5451 * @param {Number} rowIndex
5452 * @param {Roo.EventObject} e
5456 * @event rowdblclick
5457 * Fires when a row is double clicked
5458 * @param {Roo.bootstrap.Table} this
5459 * @param {Roo.Element} el
5460 * @param {Number} rowIndex
5461 * @param {Roo.EventObject} e
5463 "rowdblclick" : true,
5466 * Fires when a mouseover occur
5467 * @param {Roo.bootstrap.Table} this
5468 * @param {Roo.Element} el
5469 * @param {Number} rowIndex
5470 * @param {Number} columnIndex
5471 * @param {Roo.EventObject} e
5476 * Fires when a mouseout occur
5477 * @param {Roo.bootstrap.Table} this
5478 * @param {Roo.Element} el
5479 * @param {Number} rowIndex
5480 * @param {Number} columnIndex
5481 * @param {Roo.EventObject} e
5486 * Fires when a row is rendered, so you can change add a style to it.
5487 * @param {Roo.bootstrap.Table} this
5488 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5492 * @event rowsrendered
5493 * Fires when all the rows have been rendered
5494 * @param {Roo.bootstrap.Table} this
5496 'rowsrendered' : true
5501 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5525 RowSelection : false,
5526 CellSelection : false,
5529 // Roo.Element - the tbody
5532 getAutoCreate : function(){
5533 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5542 cfg.cls += ' table-striped';
5546 cfg.cls += ' table-hover';
5548 if (this.bordered) {
5549 cfg.cls += ' table-bordered';
5551 if (this.condensed) {
5552 cfg.cls += ' table-condensed';
5554 if (this.responsive) {
5555 cfg.cls += ' table-responsive';
5559 cfg.cls+= ' ' +this.cls;
5562 // this lot should be simplifed...
5565 cfg.align=this.align;
5568 cfg.bgcolor=this.bgcolor;
5571 cfg.border=this.border;
5573 if (this.cellpadding) {
5574 cfg.cellpadding=this.cellpadding;
5576 if (this.cellspacing) {
5577 cfg.cellspacing=this.cellspacing;
5580 cfg.frame=this.frame;
5583 cfg.rules=this.rules;
5585 if (this.sortable) {
5586 cfg.sortable=this.sortable;
5589 cfg.summary=this.summary;
5592 cfg.width=this.width;
5595 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5598 if(this.store || this.cm){
5600 cfg.cn.push(this.renderHeader());
5603 cfg.cn.push(this.renderBody());
5606 cfg.cn.push(this.renderFooter());
5609 cfg.cls+= ' TableGrid';
5612 return { cn : [ cfg ] };
5615 initEvents : function()
5617 if(!this.store || !this.cm){
5621 //Roo.log('initEvents with ds!!!!');
5623 this.mainBody = this.el.select('tbody', true).first();
5628 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5629 e.on('click', _this.sort, _this);
5632 this.el.on("click", this.onClick, this);
5633 this.el.on("dblclick", this.onDblClick, this);
5635 // why is this done????? = it breaks dialogs??
5636 //this.parent().el.setStyle('position', 'relative');
5640 this.footer.parentId = this.id;
5641 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5644 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5646 this.store.on('load', this.onLoad, this);
5647 this.store.on('beforeload', this.onBeforeLoad, this);
5648 this.store.on('update', this.onUpdate, this);
5649 this.store.on('add', this.onAdd, this);
5653 onMouseover : function(e, el)
5655 var cell = Roo.get(el);
5661 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5662 cell = cell.findParent('td', false, true);
5665 var row = cell.findParent('tr', false, true);
5666 var cellIndex = cell.dom.cellIndex;
5667 var rowIndex = row.dom.rowIndex - 1; // start from 0
5669 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5673 onMouseout : function(e, el)
5675 var cell = Roo.get(el);
5681 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5682 cell = cell.findParent('td', false, true);
5685 var row = cell.findParent('tr', false, true);
5686 var cellIndex = cell.dom.cellIndex;
5687 var rowIndex = row.dom.rowIndex - 1; // start from 0
5689 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5693 onClick : function(e, el)
5695 var cell = Roo.get(el);
5697 if(!cell || (!this.CellSelection && !this.RowSelection)){
5701 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5702 cell = cell.findParent('td', false, true);
5705 if(!cell || typeof(cell) == 'undefined'){
5709 var row = cell.findParent('tr', false, true);
5711 if(!row || typeof(row) == 'undefined'){
5715 var cellIndex = cell.dom.cellIndex;
5716 var rowIndex = this.getRowIndex(row);
5718 if(this.CellSelection){
5719 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5722 if(this.RowSelection){
5723 this.fireEvent('rowclick', this, row, rowIndex, e);
5729 onDblClick : function(e,el)
5731 var cell = Roo.get(el);
5733 if(!cell || (!this.CellSelection && !this.RowSelection)){
5737 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5738 cell = cell.findParent('td', false, true);
5741 if(!cell || typeof(cell) == 'undefined'){
5745 var row = cell.findParent('tr', false, true);
5747 if(!row || typeof(row) == 'undefined'){
5751 var cellIndex = cell.dom.cellIndex;
5752 var rowIndex = this.getRowIndex(row);
5754 if(this.CellSelection){
5755 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5758 if(this.RowSelection){
5759 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5763 sort : function(e,el)
5765 var col = Roo.get(el);
5767 if(!col.hasClass('sortable')){
5771 var sort = col.attr('sort');
5774 if(col.hasClass('glyphicon-arrow-up')){
5778 this.store.sortInfo = {field : sort, direction : dir};
5781 Roo.log("calling footer first");
5782 this.footer.onClick('first');
5785 this.store.load({ params : { start : 0 } });
5789 renderHeader : function()
5798 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5800 var config = cm.config[i];
5805 html: cm.getColumnHeader(i)
5808 if(typeof(config.tooltip) != 'undefined'){
5809 c.tooltip = config.tooltip;
5812 if(typeof(config.colspan) != 'undefined'){
5813 c.colspan = config.colspan;
5816 if(typeof(config.hidden) != 'undefined' && config.hidden){
5817 c.style += ' display:none;';
5820 if(typeof(config.dataIndex) != 'undefined'){
5821 c.sort = config.dataIndex;
5824 if(typeof(config.sortable) != 'undefined' && config.sortable){
5828 if(typeof(config.align) != 'undefined' && config.align.length){
5829 c.style += ' text-align:' + config.align + ';';
5832 if(typeof(config.width) != 'undefined'){
5833 c.style += ' width:' + config.width + 'px;';
5836 if(typeof(config.cls) != 'undefined'){
5837 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5846 renderBody : function()
5856 colspan : this.cm.getColumnCount()
5866 renderFooter : function()
5876 colspan : this.cm.getColumnCount()
5890 Roo.log('ds onload');
5895 var ds = this.store;
5897 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5898 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5900 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5901 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5904 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5905 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5909 var tbody = this.mainBody;
5911 if(ds.getCount() > 0){
5912 ds.data.each(function(d,rowIndex){
5913 var row = this.renderRow(cm, ds, rowIndex);
5915 tbody.createChild(row);
5919 if(row.cellObjects.length){
5920 Roo.each(row.cellObjects, function(r){
5921 _this.renderCellObject(r);
5928 Roo.each(this.el.select('tbody td', true).elements, function(e){
5929 e.on('mouseover', _this.onMouseover, _this);
5932 Roo.each(this.el.select('tbody td', true).elements, function(e){
5933 e.on('mouseout', _this.onMouseout, _this);
5935 this.fireEvent('rowsrendered', this);
5936 //if(this.loadMask){
5937 // this.maskEl.hide();
5942 onUpdate : function(ds,record)
5944 this.refreshRow(record);
5947 onRemove : function(ds, record, index, isUpdate){
5948 if(isUpdate !== true){
5949 this.fireEvent("beforerowremoved", this, index, record);
5951 var bt = this.mainBody.dom;
5953 var rows = this.el.select('tbody > tr', true).elements;
5955 if(typeof(rows[index]) != 'undefined'){
5956 bt.removeChild(rows[index].dom);
5959 // if(bt.rows[index]){
5960 // bt.removeChild(bt.rows[index]);
5963 if(isUpdate !== true){
5964 //this.stripeRows(index);
5965 //this.syncRowHeights(index, index);
5967 this.fireEvent("rowremoved", this, index, record);
5971 onAdd : function(ds, records, rowIndex)
5973 //Roo.log('on Add called');
5974 // - note this does not handle multiple adding very well..
5975 var bt = this.mainBody.dom;
5976 for (var i =0 ; i < records.length;i++) {
5977 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5978 //Roo.log(records[i]);
5979 //Roo.log(this.store.getAt(rowIndex+i));
5980 this.insertRow(this.store, rowIndex + i, false);
5987 refreshRow : function(record){
5988 var ds = this.store, index;
5989 if(typeof record == 'number'){
5991 record = ds.getAt(index);
5993 index = ds.indexOf(record);
5995 this.insertRow(ds, index, true);
5996 this.onRemove(ds, record, index+1, true);
5997 //this.syncRowHeights(index, index);
5999 this.fireEvent("rowupdated", this, index, record);
6002 insertRow : function(dm, rowIndex, isUpdate){
6005 this.fireEvent("beforerowsinserted", this, rowIndex);
6007 //var s = this.getScrollState();
6008 var row = this.renderRow(this.cm, this.store, rowIndex);
6009 // insert before rowIndex..
6010 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6014 if(row.cellObjects.length){
6015 Roo.each(row.cellObjects, function(r){
6016 _this.renderCellObject(r);
6021 this.fireEvent("rowsinserted", this, rowIndex);
6022 //this.syncRowHeights(firstRow, lastRow);
6023 //this.stripeRows(firstRow);
6030 getRowDom : function(rowIndex)
6032 var rows = this.el.select('tbody > tr', true).elements;
6034 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6037 // returns the object tree for a tr..
6040 renderRow : function(cm, ds, rowIndex)
6043 var d = ds.getAt(rowIndex);
6050 var cellObjects = [];
6052 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6053 var config = cm.config[i];
6055 var renderer = cm.getRenderer(i);
6059 if(typeof(renderer) !== 'undefined'){
6060 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6062 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6063 // and are rendered into the cells after the row is rendered - using the id for the element.
6065 if(typeof(value) === 'object'){
6075 rowIndex : rowIndex,
6080 this.fireEvent('rowclass', this, rowcfg);
6084 cls : rowcfg.rowClass,
6086 html: (typeof(value) === 'object') ? '' : value
6093 if(typeof(config.colspan) != 'undefined'){
6094 td.colspan = config.colspan;
6097 if(typeof(config.hidden) != 'undefined' && config.hidden){
6098 td.style += ' display:none;';
6101 if(typeof(config.align) != 'undefined' && config.align.length){
6102 td.style += ' text-align:' + config.align + ';';
6105 if(typeof(config.width) != 'undefined'){
6106 td.style += ' width:' + config.width + 'px;';
6109 if(typeof(config.cursor) != 'undefined'){
6110 td.style += ' cursor:' + config.cursor + ';';
6113 if(typeof(config.cls) != 'undefined'){
6114 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6121 row.cellObjects = cellObjects;
6129 onBeforeLoad : function()
6131 //Roo.log('ds onBeforeLoad');
6135 //if(this.loadMask){
6136 // this.maskEl.show();
6144 this.el.select('tbody', true).first().dom.innerHTML = '';
6147 * Show or hide a row.
6148 * @param {Number} rowIndex to show or hide
6149 * @param {Boolean} state hide
6151 setRowVisibility : function(rowIndex, state)
6153 var bt = this.mainBody.dom;
6155 var rows = this.el.select('tbody > tr', true).elements;
6157 if(typeof(rows[rowIndex]) == 'undefined'){
6160 rows[rowIndex].dom.style.display = state ? '' : 'none';
6164 getSelectionModel : function(){
6166 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6168 return this.selModel;
6171 * Render the Roo.bootstrap object from renderder
6173 renderCellObject : function(r)
6177 var t = r.cfg.render(r.container);
6180 Roo.each(r.cfg.cn, function(c){
6182 container: t.getChildContainer(),
6185 _this.renderCellObject(child);
6190 getRowIndex : function(row)
6194 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6217 * @class Roo.bootstrap.TableCell
6218 * @extends Roo.bootstrap.Component
6219 * Bootstrap TableCell class
6220 * @cfg {String} html cell contain text
6221 * @cfg {String} cls cell class
6222 * @cfg {String} tag cell tag (td|th) default td
6223 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6224 * @cfg {String} align Aligns the content in a cell
6225 * @cfg {String} axis Categorizes cells
6226 * @cfg {String} bgcolor Specifies the background color of a cell
6227 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6228 * @cfg {Number} colspan Specifies the number of columns a cell should span
6229 * @cfg {String} headers Specifies one or more header cells a cell is related to
6230 * @cfg {Number} height Sets the height of a cell
6231 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6232 * @cfg {Number} rowspan Sets the number of rows a cell should span
6233 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6234 * @cfg {String} valign Vertical aligns the content in a cell
6235 * @cfg {Number} width Specifies the width of a cell
6238 * Create a new TableCell
6239 * @param {Object} config The config object
6242 Roo.bootstrap.TableCell = function(config){
6243 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6266 getAutoCreate : function(){
6267 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6287 cfg.align=this.align
6293 cfg.bgcolor=this.bgcolor
6296 cfg.charoff=this.charoff
6299 cfg.colspan=this.colspan
6302 cfg.headers=this.headers
6305 cfg.height=this.height
6308 cfg.nowrap=this.nowrap
6311 cfg.rowspan=this.rowspan
6314 cfg.scope=this.scope
6317 cfg.valign=this.valign
6320 cfg.width=this.width
6339 * @class Roo.bootstrap.TableRow
6340 * @extends Roo.bootstrap.Component
6341 * Bootstrap TableRow class
6342 * @cfg {String} cls row class
6343 * @cfg {String} align Aligns the content in a table row
6344 * @cfg {String} bgcolor Specifies a background color for a table row
6345 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6346 * @cfg {String} valign Vertical aligns the content in a table row
6349 * Create a new TableRow
6350 * @param {Object} config The config object
6353 Roo.bootstrap.TableRow = function(config){
6354 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6365 getAutoCreate : function(){
6366 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6376 cfg.align = this.align;
6379 cfg.bgcolor = this.bgcolor;
6382 cfg.charoff = this.charoff;
6385 cfg.valign = this.valign;
6403 * @class Roo.bootstrap.TableBody
6404 * @extends Roo.bootstrap.Component
6405 * Bootstrap TableBody class
6406 * @cfg {String} cls element class
6407 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6408 * @cfg {String} align Aligns the content inside the element
6409 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6410 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6413 * Create a new TableBody
6414 * @param {Object} config The config object
6417 Roo.bootstrap.TableBody = function(config){
6418 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6429 getAutoCreate : function(){
6430 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6444 cfg.align = this.align;
6447 cfg.charoff = this.charoff;
6450 cfg.valign = this.valign;
6457 // initEvents : function()
6464 // this.store = Roo.factory(this.store, Roo.data);
6465 // this.store.on('load', this.onLoad, this);
6467 // this.store.load();
6471 // onLoad: function ()
6473 // this.fireEvent('load', this);
6483 * Ext JS Library 1.1.1
6484 * Copyright(c) 2006-2007, Ext JS, LLC.
6486 * Originally Released Under LGPL - original licence link has changed is not relivant.
6489 * <script type="text/javascript">
6492 // as we use this in bootstrap.
6493 Roo.namespace('Roo.form');
6495 * @class Roo.form.Action
6496 * Internal Class used to handle form actions
6498 * @param {Roo.form.BasicForm} el The form element or its id
6499 * @param {Object} config Configuration options
6504 // define the action interface
6505 Roo.form.Action = function(form, options){
6507 this.options = options || {};
6510 * Client Validation Failed
6513 Roo.form.Action.CLIENT_INVALID = 'client';
6515 * Server Validation Failed
6518 Roo.form.Action.SERVER_INVALID = 'server';
6520 * Connect to Server Failed
6523 Roo.form.Action.CONNECT_FAILURE = 'connect';
6525 * Reading Data from Server Failed
6528 Roo.form.Action.LOAD_FAILURE = 'load';
6530 Roo.form.Action.prototype = {
6532 failureType : undefined,
6533 response : undefined,
6537 run : function(options){
6542 success : function(response){
6547 handleResponse : function(response){
6551 // default connection failure
6552 failure : function(response){
6554 this.response = response;
6555 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6556 this.form.afterAction(this, false);
6559 processResponse : function(response){
6560 this.response = response;
6561 if(!response.responseText){
6564 this.result = this.handleResponse(response);
6568 // utility functions used internally
6569 getUrl : function(appendParams){
6570 var url = this.options.url || this.form.url || this.form.el.dom.action;
6572 var p = this.getParams();
6574 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6580 getMethod : function(){
6581 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6584 getParams : function(){
6585 var bp = this.form.baseParams;
6586 var p = this.options.params;
6588 if(typeof p == "object"){
6589 p = Roo.urlEncode(Roo.applyIf(p, bp));
6590 }else if(typeof p == 'string' && bp){
6591 p += '&' + Roo.urlEncode(bp);
6594 p = Roo.urlEncode(bp);
6599 createCallback : function(){
6601 success: this.success,
6602 failure: this.failure,
6604 timeout: (this.form.timeout*1000),
6605 upload: this.form.fileUpload ? this.success : undefined
6610 Roo.form.Action.Submit = function(form, options){
6611 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6617 haveProgress : false,
6618 uploadComplete : false,
6620 // uploadProgress indicator.
6621 uploadProgress : function()
6623 if (!this.form.progressUrl) {
6627 if (!this.haveProgress) {
6628 Roo.MessageBox.progress("Uploading", "Uploading");
6630 if (this.uploadComplete) {
6631 Roo.MessageBox.hide();
6635 this.haveProgress = true;
6637 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6639 var c = new Roo.data.Connection();
6641 url : this.form.progressUrl,
6646 success : function(req){
6647 //console.log(data);
6651 rdata = Roo.decode(req.responseText)
6653 Roo.log("Invalid data from server..");
6657 if (!rdata || !rdata.success) {
6659 Roo.MessageBox.alert(Roo.encode(rdata));
6662 var data = rdata.data;
6664 if (this.uploadComplete) {
6665 Roo.MessageBox.hide();
6670 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6671 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6674 this.uploadProgress.defer(2000,this);
6677 failure: function(data) {
6678 Roo.log('progress url failed ');
6689 // run get Values on the form, so it syncs any secondary forms.
6690 this.form.getValues();
6692 var o = this.options;
6693 var method = this.getMethod();
6694 var isPost = method == 'POST';
6695 if(o.clientValidation === false || this.form.isValid()){
6697 if (this.form.progressUrl) {
6698 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6699 (new Date() * 1) + '' + Math.random());
6704 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6705 form:this.form.el.dom,
6706 url:this.getUrl(!isPost),
6708 params:isPost ? this.getParams() : null,
6709 isUpload: this.form.fileUpload
6712 this.uploadProgress();
6714 }else if (o.clientValidation !== false){ // client validation failed
6715 this.failureType = Roo.form.Action.CLIENT_INVALID;
6716 this.form.afterAction(this, false);
6720 success : function(response)
6722 this.uploadComplete= true;
6723 if (this.haveProgress) {
6724 Roo.MessageBox.hide();
6728 var result = this.processResponse(response);
6729 if(result === true || result.success){
6730 this.form.afterAction(this, true);
6734 this.form.markInvalid(result.errors);
6735 this.failureType = Roo.form.Action.SERVER_INVALID;
6737 this.form.afterAction(this, false);
6739 failure : function(response)
6741 this.uploadComplete= true;
6742 if (this.haveProgress) {
6743 Roo.MessageBox.hide();
6746 this.response = response;
6747 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6748 this.form.afterAction(this, false);
6751 handleResponse : function(response){
6752 if(this.form.errorReader){
6753 var rs = this.form.errorReader.read(response);
6756 for(var i = 0, len = rs.records.length; i < len; i++) {
6757 var r = rs.records[i];
6761 if(errors.length < 1){
6765 success : rs.success,
6771 ret = Roo.decode(response.responseText);
6775 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6785 Roo.form.Action.Load = function(form, options){
6786 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6787 this.reader = this.form.reader;
6790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6795 Roo.Ajax.request(Roo.apply(
6796 this.createCallback(), {
6797 method:this.getMethod(),
6798 url:this.getUrl(false),
6799 params:this.getParams()
6803 success : function(response){
6805 var result = this.processResponse(response);
6806 if(result === true || !result.success || !result.data){
6807 this.failureType = Roo.form.Action.LOAD_FAILURE;
6808 this.form.afterAction(this, false);
6811 this.form.clearInvalid();
6812 this.form.setValues(result.data);
6813 this.form.afterAction(this, true);
6816 handleResponse : function(response){
6817 if(this.form.reader){
6818 var rs = this.form.reader.read(response);
6819 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6821 success : rs.success,
6825 return Roo.decode(response.responseText);
6829 Roo.form.Action.ACTION_TYPES = {
6830 'load' : Roo.form.Action.Load,
6831 'submit' : Roo.form.Action.Submit
6840 * @class Roo.bootstrap.Form
6841 * @extends Roo.bootstrap.Component
6842 * Bootstrap Form class
6843 * @cfg {String} method GET | POST (default POST)
6844 * @cfg {String} labelAlign top | left (default top)
6845 * @cfg {String} align left | right - for navbars
6846 * @cfg {Boolean} loadMask load mask when submit (default true)
6851 * @param {Object} config The config object
6855 Roo.bootstrap.Form = function(config){
6856 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6859 * @event clientvalidation
6860 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6861 * @param {Form} this
6862 * @param {Boolean} valid true if the form has passed client-side validation
6864 clientvalidation: true,
6866 * @event beforeaction
6867 * Fires before any action is performed. Return false to cancel the action.
6868 * @param {Form} this
6869 * @param {Action} action The action to be performed
6873 * @event actionfailed
6874 * Fires when an action fails.
6875 * @param {Form} this
6876 * @param {Action} action The action that failed
6878 actionfailed : true,
6880 * @event actioncomplete
6881 * Fires when an action is completed.
6882 * @param {Form} this
6883 * @param {Action} action The action that completed
6885 actioncomplete : true
6890 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6893 * @cfg {String} method
6894 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6899 * The URL to use for form actions if one isn't supplied in the action options.
6902 * @cfg {Boolean} fileUpload
6903 * Set to true if this form is a file upload.
6907 * @cfg {Object} baseParams
6908 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6912 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6916 * @cfg {Sting} align (left|right) for navbar forms
6921 activeAction : null,
6924 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6925 * element by passing it or its id or mask the form itself by passing in true.
6928 waitMsgTarget : false,
6932 getAutoCreate : function(){
6936 method : this.method || 'POST',
6937 id : this.id || Roo.id(),
6940 if (this.parent().xtype.match(/^Nav/)) {
6941 cfg.cls = 'navbar-form navbar-' + this.align;
6945 if (this.labelAlign == 'left' ) {
6946 cfg.cls += ' form-horizontal';
6952 initEvents : function()
6954 this.el.on('submit', this.onSubmit, this);
6955 // this was added as random key presses on the form where triggering form submit.
6956 this.el.on('keypress', function(e) {
6957 if (e.getCharCode() != 13) {
6960 // we might need to allow it for textareas.. and some other items.
6961 // check e.getTarget().
6963 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6967 Roo.log("keypress blocked");
6975 onSubmit : function(e){
6980 * Returns true if client-side validation on the form is successful.
6983 isValid : function(){
6984 var items = this.getItems();
6986 items.each(function(f){
6995 * Returns true if any fields in this form have changed since their original load.
6998 isDirty : function(){
7000 var items = this.getItems();
7001 items.each(function(f){
7011 * Performs a predefined action (submit or load) or custom actions you define on this form.
7012 * @param {String} actionName The name of the action type
7013 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7014 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7015 * accept other config options):
7017 Property Type Description
7018 ---------------- --------------- ----------------------------------------------------------------------------------
7019 url String The url for the action (defaults to the form's url)
7020 method String The form method to use (defaults to the form's method, or POST if not defined)
7021 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7022 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7023 validate the form on the client (defaults to false)
7025 * @return {BasicForm} this
7027 doAction : function(action, options){
7028 if(typeof action == 'string'){
7029 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7031 if(this.fireEvent('beforeaction', this, action) !== false){
7032 this.beforeAction(action);
7033 action.run.defer(100, action);
7039 beforeAction : function(action){
7040 var o = action.options;
7043 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7045 // not really supported yet.. ??
7047 //if(this.waitMsgTarget === true){
7048 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7049 //}else if(this.waitMsgTarget){
7050 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7051 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7053 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7059 afterAction : function(action, success){
7060 this.activeAction = null;
7061 var o = action.options;
7063 //if(this.waitMsgTarget === true){
7065 //}else if(this.waitMsgTarget){
7066 // this.waitMsgTarget.unmask();
7068 // Roo.MessageBox.updateProgress(1);
7069 // Roo.MessageBox.hide();
7076 Roo.callback(o.success, o.scope, [this, action]);
7077 this.fireEvent('actioncomplete', this, action);
7081 // failure condition..
7082 // we have a scenario where updates need confirming.
7083 // eg. if a locking scenario exists..
7084 // we look for { errors : { needs_confirm : true }} in the response.
7086 (typeof(action.result) != 'undefined') &&
7087 (typeof(action.result.errors) != 'undefined') &&
7088 (typeof(action.result.errors.needs_confirm) != 'undefined')
7091 Roo.log("not supported yet");
7094 Roo.MessageBox.confirm(
7095 "Change requires confirmation",
7096 action.result.errorMsg,
7101 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7111 Roo.callback(o.failure, o.scope, [this, action]);
7112 // show an error message if no failed handler is set..
7113 if (!this.hasListener('actionfailed')) {
7114 Roo.log("need to add dialog support");
7116 Roo.MessageBox.alert("Error",
7117 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7118 action.result.errorMsg :
7119 "Saving Failed, please check your entries or try again"
7124 this.fireEvent('actionfailed', this, action);
7129 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7130 * @param {String} id The value to search for
7133 findField : function(id){
7134 var items = this.getItems();
7135 var field = items.get(id);
7137 items.each(function(f){
7138 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7145 return field || null;
7148 * Mark fields in this form invalid in bulk.
7149 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7150 * @return {BasicForm} this
7152 markInvalid : function(errors){
7153 if(errors instanceof Array){
7154 for(var i = 0, len = errors.length; i < len; i++){
7155 var fieldError = errors[i];
7156 var f = this.findField(fieldError.id);
7158 f.markInvalid(fieldError.msg);
7164 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7165 field.markInvalid(errors[id]);
7169 //Roo.each(this.childForms || [], function (f) {
7170 // f.markInvalid(errors);
7177 * Set values for fields in this form in bulk.
7178 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7179 * @return {BasicForm} this
7181 setValues : function(values){
7182 if(values instanceof Array){ // array of objects
7183 for(var i = 0, len = values.length; i < len; i++){
7185 var f = this.findField(v.id);
7187 f.setValue(v.value);
7188 if(this.trackResetOnLoad){
7189 f.originalValue = f.getValue();
7193 }else{ // object hash
7196 if(typeof values[id] != 'function' && (field = this.findField(id))){
7198 if (field.setFromData &&
7200 field.displayField &&
7201 // combos' with local stores can
7202 // be queried via setValue()
7203 // to set their value..
7204 (field.store && !field.store.isLocal)
7208 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7209 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7210 field.setFromData(sd);
7213 field.setValue(values[id]);
7217 if(this.trackResetOnLoad){
7218 field.originalValue = field.getValue();
7224 //Roo.each(this.childForms || [], function (f) {
7225 // f.setValues(values);
7232 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7233 * they are returned as an array.
7234 * @param {Boolean} asString
7237 getValues : function(asString){
7238 //if (this.childForms) {
7239 // copy values from the child forms
7240 // Roo.each(this.childForms, function (f) {
7241 // this.setValues(f.getValues());
7247 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7248 if(asString === true){
7251 return Roo.urlDecode(fs);
7255 * Returns the fields in this form as an object with key/value pairs.
7256 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7259 getFieldValues : function(with_hidden)
7261 var items = this.getItems();
7263 items.each(function(f){
7267 var v = f.getValue();
7268 if (f.inputType =='radio') {
7269 if (typeof(ret[f.getName()]) == 'undefined') {
7270 ret[f.getName()] = ''; // empty..
7273 if (!f.el.dom.checked) {
7281 // not sure if this supported any more..
7282 if ((typeof(v) == 'object') && f.getRawValue) {
7283 v = f.getRawValue() ; // dates..
7285 // combo boxes where name != hiddenName...
7286 if (f.name != f.getName()) {
7287 ret[f.name] = f.getRawValue();
7289 ret[f.getName()] = v;
7296 * Clears all invalid messages in this form.
7297 * @return {BasicForm} this
7299 clearInvalid : function(){
7300 var items = this.getItems();
7302 items.each(function(f){
7313 * @return {BasicForm} this
7316 var items = this.getItems();
7317 items.each(function(f){
7321 Roo.each(this.childForms || [], function (f) {
7328 getItems : function()
7330 var r=new Roo.util.MixedCollection(false, function(o){
7331 return o.id || (o.id = Roo.id());
7333 var iter = function(el) {
7340 Roo.each(el.items,function(e) {
7360 * Ext JS Library 1.1.1
7361 * Copyright(c) 2006-2007, Ext JS, LLC.
7363 * Originally Released Under LGPL - original licence link has changed is not relivant.
7366 * <script type="text/javascript">
7369 * @class Roo.form.VTypes
7370 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7373 Roo.form.VTypes = function(){
7374 // closure these in so they are only created once.
7375 var alpha = /^[a-zA-Z_]+$/;
7376 var alphanum = /^[a-zA-Z0-9_]+$/;
7377 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7378 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7380 // All these messages and functions are configurable
7383 * The function used to validate email addresses
7384 * @param {String} value The email address
7386 'email' : function(v){
7387 return email.test(v);
7390 * The error text to display when the email validation function returns false
7393 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7395 * The keystroke filter mask to be applied on email input
7398 'emailMask' : /[a-z0-9_\.\-@]/i,
7401 * The function used to validate URLs
7402 * @param {String} value The URL
7404 'url' : function(v){
7408 * The error text to display when the url validation function returns false
7411 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7414 * The function used to validate alpha values
7415 * @param {String} value The value
7417 'alpha' : function(v){
7418 return alpha.test(v);
7421 * The error text to display when the alpha validation function returns false
7424 'alphaText' : 'This field should only contain letters and _',
7426 * The keystroke filter mask to be applied on alpha input
7429 'alphaMask' : /[a-z_]/i,
7432 * The function used to validate alphanumeric values
7433 * @param {String} value The value
7435 'alphanum' : function(v){
7436 return alphanum.test(v);
7439 * The error text to display when the alphanumeric validation function returns false
7442 'alphanumText' : 'This field should only contain letters, numbers and _',
7444 * The keystroke filter mask to be applied on alphanumeric input
7447 'alphanumMask' : /[a-z0-9_]/i
7457 * @class Roo.bootstrap.Input
7458 * @extends Roo.bootstrap.Component
7459 * Bootstrap Input class
7460 * @cfg {Boolean} disabled is it disabled
7461 * @cfg {String} fieldLabel - the label associated
7462 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7463 * @cfg {String} name name of the input
7464 * @cfg {string} fieldLabel - the label associated
7465 * @cfg {string} inputType - input / file submit ...
7466 * @cfg {string} placeholder - placeholder to put in text.
7467 * @cfg {string} before - input group add on before
7468 * @cfg {string} after - input group add on after
7469 * @cfg {string} size - (lg|sm) or leave empty..
7470 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7471 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7472 * @cfg {Number} md colspan out of 12 for computer-sized screens
7473 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7474 * @cfg {string} value default value of the input
7475 * @cfg {Number} labelWidth set the width of label (0-12)
7476 * @cfg {String} labelAlign (top|left)
7477 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7478 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7480 * @cfg {String} align (left|center|right) Default left
7481 * @cfg {Boolean} forceFeedback (true|false) Default false
7487 * Create a new Input
7488 * @param {Object} config The config object
7491 Roo.bootstrap.Input = function(config){
7492 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7497 * Fires when this field receives input focus.
7498 * @param {Roo.form.Field} this
7503 * Fires when this field loses input focus.
7504 * @param {Roo.form.Field} this
7509 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7510 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7511 * @param {Roo.form.Field} this
7512 * @param {Roo.EventObject} e The event object
7517 * Fires just before the field blurs if the field value has changed.
7518 * @param {Roo.form.Field} this
7519 * @param {Mixed} newValue The new value
7520 * @param {Mixed} oldValue The original value
7525 * Fires after the field has been marked as invalid.
7526 * @param {Roo.form.Field} this
7527 * @param {String} msg The validation message
7532 * Fires after the field has been validated with no errors.
7533 * @param {Roo.form.Field} this
7538 * Fires after the key up
7539 * @param {Roo.form.Field} this
7540 * @param {Roo.EventObject} e The event Object
7546 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7548 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7549 automatic validation (defaults to "keyup").
7551 validationEvent : "keyup",
7553 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7555 validateOnBlur : true,
7557 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7559 validationDelay : 250,
7561 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7563 focusClass : "x-form-focus", // not needed???
7567 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7569 invalidClass : "has-warning",
7572 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7574 validClass : "has-success",
7577 * @cfg {Boolean} hasFeedback (true|false) default true
7582 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7584 invalidFeedbackClass : "glyphicon-warning-sign",
7587 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7589 validFeedbackClass : "glyphicon-ok",
7592 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7594 selectOnFocus : false,
7597 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7601 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7606 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7608 disableKeyFilter : false,
7611 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7615 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7619 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7621 blankText : "This field is required",
7624 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7628 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7630 maxLength : Number.MAX_VALUE,
7632 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7634 minLengthText : "The minimum length for this field is {0}",
7636 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7638 maxLengthText : "The maximum length for this field is {0}",
7642 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7643 * If available, this function will be called only after the basic validators all return true, and will be passed the
7644 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7648 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7649 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7650 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7654 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7658 autocomplete: false,
7677 formatedValue : false,
7678 forceFeedback : false,
7680 parentLabelAlign : function()
7683 while (parent.parent()) {
7684 parent = parent.parent();
7685 if (typeof(parent.labelAlign) !='undefined') {
7686 return parent.labelAlign;
7693 getAutoCreate : function(){
7695 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7701 if(this.inputType != 'hidden'){
7702 cfg.cls = 'form-group' //input-group
7708 type : this.inputType,
7710 cls : 'form-control',
7711 placeholder : this.placeholder || '',
7712 autocomplete : this.autocomplete || 'new-password'
7717 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7720 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7721 input.maxLength = this.maxLength;
7724 if (this.disabled) {
7725 input.disabled=true;
7728 if (this.readOnly) {
7729 input.readonly=true;
7733 input.name = this.name;
7736 input.cls += ' input-' + this.size;
7739 ['xs','sm','md','lg'].map(function(size){
7740 if (settings[size]) {
7741 cfg.cls += ' col-' + size + '-' + settings[size];
7745 var inputblock = input;
7749 cls: 'glyphicon form-control-feedback'
7752 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7755 cls : 'has-feedback',
7763 if (this.before || this.after) {
7766 cls : 'input-group',
7770 if (this.before && typeof(this.before) == 'string') {
7772 inputblock.cn.push({
7774 cls : 'roo-input-before input-group-addon',
7778 if (this.before && typeof(this.before) == 'object') {
7779 this.before = Roo.factory(this.before);
7780 Roo.log(this.before);
7781 inputblock.cn.push({
7783 cls : 'roo-input-before input-group-' +
7784 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7788 inputblock.cn.push(input);
7790 if (this.after && typeof(this.after) == 'string') {
7791 inputblock.cn.push({
7793 cls : 'roo-input-after input-group-addon',
7797 if (this.after && typeof(this.after) == 'object') {
7798 this.after = Roo.factory(this.after);
7799 Roo.log(this.after);
7800 inputblock.cn.push({
7802 cls : 'roo-input-after input-group-' +
7803 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7807 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7808 inputblock.cls += ' has-feedback';
7809 inputblock.cn.push(feedback);
7813 if (align ==='left' && this.fieldLabel.length) {
7814 Roo.log("left and has label");
7820 cls : 'control-label col-sm-' + this.labelWidth,
7821 html : this.fieldLabel
7825 cls : "col-sm-" + (12 - this.labelWidth),
7832 } else if ( this.fieldLabel.length) {
7838 //cls : 'input-group-addon',
7839 html : this.fieldLabel
7849 Roo.log(" no label && no align");
7858 Roo.log('input-parentType: ' + this.parentType);
7860 if (this.parentType === 'Navbar' && this.parent().bar) {
7861 cfg.cls += ' navbar-form';
7869 * return the real input element.
7871 inputEl: function ()
7873 return this.el.select('input.form-control',true).first();
7876 tooltipEl : function()
7878 return this.inputEl();
7881 setDisabled : function(v)
7883 var i = this.inputEl().dom;
7885 i.removeAttribute('disabled');
7889 i.setAttribute('disabled','true');
7891 initEvents : function()
7894 this.inputEl().on("keydown" , this.fireKey, this);
7895 this.inputEl().on("focus", this.onFocus, this);
7896 this.inputEl().on("blur", this.onBlur, this);
7898 this.inputEl().relayEvent('keyup', this);
7900 // reference to original value for reset
7901 this.originalValue = this.getValue();
7902 //Roo.form.TextField.superclass.initEvents.call(this);
7903 if(this.validationEvent == 'keyup'){
7904 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7905 this.inputEl().on('keyup', this.filterValidation, this);
7907 else if(this.validationEvent !== false){
7908 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7911 if(this.selectOnFocus){
7912 this.on("focus", this.preFocus, this);
7915 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7916 this.inputEl().on("keypress", this.filterKeys, this);
7919 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7920 this.el.on("click", this.autoSize, this);
7923 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7924 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7927 if (typeof(this.before) == 'object') {
7928 this.before.render(this.el.select('.roo-input-before',true).first());
7930 if (typeof(this.after) == 'object') {
7931 this.after.render(this.el.select('.roo-input-after',true).first());
7936 filterValidation : function(e){
7937 if(!e.isNavKeyPress()){
7938 this.validationTask.delay(this.validationDelay);
7942 * Validates the field value
7943 * @return {Boolean} True if the value is valid, else false
7945 validate : function(){
7946 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7947 if(this.disabled || this.validateValue(this.getRawValue())){
7958 * Validates a value according to the field's validation rules and marks the field as invalid
7959 * if the validation fails
7960 * @param {Mixed} value The value to validate
7961 * @return {Boolean} True if the value is valid, else false
7963 validateValue : function(value){
7964 if(value.length < 1) { // if it's blank
7965 if(this.allowBlank){
7971 if(value.length < this.minLength){
7974 if(value.length > this.maxLength){
7978 var vt = Roo.form.VTypes;
7979 if(!vt[this.vtype](value, this)){
7983 if(typeof this.validator == "function"){
7984 var msg = this.validator(value);
7990 if(this.regex && !this.regex.test(value)){
8000 fireKey : function(e){
8001 //Roo.log('field ' + e.getKey());
8002 if(e.isNavKeyPress()){
8003 this.fireEvent("specialkey", this, e);
8006 focus : function (selectText){
8008 this.inputEl().focus();
8009 if(selectText === true){
8010 this.inputEl().dom.select();
8016 onFocus : function(){
8017 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8018 // this.el.addClass(this.focusClass);
8021 this.hasFocus = true;
8022 this.startValue = this.getValue();
8023 this.fireEvent("focus", this);
8027 beforeBlur : Roo.emptyFn,
8031 onBlur : function(){
8033 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8034 //this.el.removeClass(this.focusClass);
8036 this.hasFocus = false;
8037 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8040 var v = this.getValue();
8041 if(String(v) !== String(this.startValue)){
8042 this.fireEvent('change', this, v, this.startValue);
8044 this.fireEvent("blur", this);
8048 * Resets the current field value to the originally loaded value and clears any validation messages
8051 this.setValue(this.originalValue);
8055 * Returns the name of the field
8056 * @return {Mixed} name The name field
8058 getName: function(){
8062 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8063 * @return {Mixed} value The field value
8065 getValue : function(){
8067 var v = this.inputEl().getValue();
8072 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8073 * @return {Mixed} value The field value
8075 getRawValue : function(){
8076 var v = this.inputEl().getValue();
8082 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8083 * @param {Mixed} value The value to set
8085 setRawValue : function(v){
8086 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8089 selectText : function(start, end){
8090 var v = this.getRawValue();
8092 start = start === undefined ? 0 : start;
8093 end = end === undefined ? v.length : end;
8094 var d = this.inputEl().dom;
8095 if(d.setSelectionRange){
8096 d.setSelectionRange(start, end);
8097 }else if(d.createTextRange){
8098 var range = d.createTextRange();
8099 range.moveStart("character", start);
8100 range.moveEnd("character", v.length-end);
8107 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8108 * @param {Mixed} value The value to set
8110 setValue : function(v){
8113 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8119 processValue : function(value){
8120 if(this.stripCharsRe){
8121 var newValue = value.replace(this.stripCharsRe, '');
8122 if(newValue !== value){
8123 this.setRawValue(newValue);
8130 preFocus : function(){
8132 if(this.selectOnFocus){
8133 this.inputEl().dom.select();
8136 filterKeys : function(e){
8138 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8141 var c = e.getCharCode(), cc = String.fromCharCode(c);
8142 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8145 if(!this.maskRe.test(cc)){
8150 * Clear any invalid styles/messages for this field
8152 clearInvalid : function(){
8154 if(!this.el || this.preventMark){ // not rendered
8157 this.el.removeClass(this.invalidClass);
8159 this.fireEvent('valid', this);
8163 * Mark this field as valid
8165 markValid : function(){
8166 if(!this.el || this.preventMark){ // not rendered
8170 this.el.removeClass([this.invalidClass, this.validClass]);
8172 var feedback = this.el.select('.form-control-feedback', true).first();
8175 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8178 if(this.disabled || this.allowBlank){
8182 this.el.addClass(this.validClass);
8184 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8186 var feedback = this.el.select('.form-control-feedback', true).first();
8189 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8190 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8195 this.fireEvent('valid', this);
8199 * Mark this field as invalid
8200 * @param {String} msg The validation message
8202 markInvalid : function(msg){
8203 if(!this.el || this.preventMark){ // not rendered
8207 this.el.removeClass([this.invalidClass, this.validClass]);
8209 var feedback = this.el.select('.form-control-feedback', true).first();
8212 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8215 if(this.disabled || this.allowBlank){
8219 this.el.addClass(this.invalidClass);
8221 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8223 var feedback = this.el.select('.form-control-feedback', true).first();
8226 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8228 if(this.getValue().length || this.forceFeedback){
8229 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8236 this.fireEvent('invalid', this, msg);
8239 SafariOnKeyDown : function(event)
8241 // this is a workaround for a password hang bug on chrome/ webkit.
8243 var isSelectAll = false;
8245 if(this.inputEl().dom.selectionEnd > 0){
8246 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8248 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8249 event.preventDefault();
8254 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8256 event.preventDefault();
8257 // this is very hacky as keydown always get's upper case.
8259 var cc = String.fromCharCode(event.getCharCode());
8260 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8264 adjustWidth : function(tag, w){
8265 tag = tag.toLowerCase();
8266 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8267 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8271 if(tag == 'textarea'){
8274 }else if(Roo.isOpera){
8278 if(tag == 'textarea'){
8297 * @class Roo.bootstrap.TextArea
8298 * @extends Roo.bootstrap.Input
8299 * Bootstrap TextArea class
8300 * @cfg {Number} cols Specifies the visible width of a text area
8301 * @cfg {Number} rows Specifies the visible number of lines in a text area
8302 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8303 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8304 * @cfg {string} html text
8307 * Create a new TextArea
8308 * @param {Object} config The config object
8311 Roo.bootstrap.TextArea = function(config){
8312 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8316 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8326 getAutoCreate : function(){
8328 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8339 value : this.value || '',
8340 html: this.html || '',
8341 cls : 'form-control',
8342 placeholder : this.placeholder || ''
8346 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8347 input.maxLength = this.maxLength;
8351 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8355 input.cols = this.cols;
8358 if (this.readOnly) {
8359 input.readonly = true;
8363 input.name = this.name;
8367 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8371 ['xs','sm','md','lg'].map(function(size){
8372 if (settings[size]) {
8373 cfg.cls += ' col-' + size + '-' + settings[size];
8377 var inputblock = input;
8379 if(this.hasFeedback && !this.allowBlank){
8383 cls: 'glyphicon form-control-feedback'
8387 cls : 'has-feedback',
8396 if (this.before || this.after) {
8399 cls : 'input-group',
8403 inputblock.cn.push({
8405 cls : 'input-group-addon',
8410 inputblock.cn.push(input);
8412 if(this.hasFeedback && !this.allowBlank){
8413 inputblock.cls += ' has-feedback';
8414 inputblock.cn.push(feedback);
8418 inputblock.cn.push({
8420 cls : 'input-group-addon',
8427 if (align ==='left' && this.fieldLabel.length) {
8428 Roo.log("left and has label");
8434 cls : 'control-label col-sm-' + this.labelWidth,
8435 html : this.fieldLabel
8439 cls : "col-sm-" + (12 - this.labelWidth),
8446 } else if ( this.fieldLabel.length) {
8452 //cls : 'input-group-addon',
8453 html : this.fieldLabel
8463 Roo.log(" no label && no align");
8473 if (this.disabled) {
8474 input.disabled=true;
8481 * return the real textarea element.
8483 inputEl: function ()
8485 return this.el.select('textarea.form-control',true).first();
8493 * trigger field - base class for combo..
8498 * @class Roo.bootstrap.TriggerField
8499 * @extends Roo.bootstrap.Input
8500 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8501 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8502 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8503 * for which you can provide a custom implementation. For example:
8505 var trigger = new Roo.bootstrap.TriggerField();
8506 trigger.onTriggerClick = myTriggerFn;
8507 trigger.applyTo('my-field');
8510 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8511 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8512 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8513 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8514 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8517 * Create a new TriggerField.
8518 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8519 * to the base TextField)
8521 Roo.bootstrap.TriggerField = function(config){
8522 this.mimicing = false;
8523 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8526 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8528 * @cfg {String} triggerClass A CSS class to apply to the trigger
8531 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8536 * @cfg {Boolean} removable (true|false) special filter default false
8540 /** @cfg {Boolean} grow @hide */
8541 /** @cfg {Number} growMin @hide */
8542 /** @cfg {Number} growMax @hide */
8548 autoSize: Roo.emptyFn,
8555 actionMode : 'wrap',
8560 getAutoCreate : function(){
8562 var align = this.labelAlign || this.parentLabelAlign();
8567 cls: 'form-group' //input-group
8574 type : this.inputType,
8575 cls : 'form-control',
8576 autocomplete: 'new-password',
8577 placeholder : this.placeholder || ''
8581 input.name = this.name;
8584 input.cls += ' input-' + this.size;
8587 if (this.disabled) {
8588 input.disabled=true;
8591 var inputblock = input;
8593 if(this.hasFeedback && !this.allowBlank){
8597 cls: 'glyphicon form-control-feedback'
8600 if(this.removable && !this.editable && !this.tickable){
8602 cls : 'has-feedback',
8608 cls : 'roo-combo-removable-btn close'
8615 cls : 'has-feedback',
8624 if(this.removable && !this.editable && !this.tickable){
8626 cls : 'roo-removable',
8632 cls : 'roo-combo-removable-btn close'
8639 if (this.before || this.after) {
8642 cls : 'input-group',
8646 inputblock.cn.push({
8648 cls : 'input-group-addon',
8653 inputblock.cn.push(input);
8655 if(this.hasFeedback && !this.allowBlank){
8656 inputblock.cls += ' has-feedback';
8657 inputblock.cn.push(feedback);
8661 inputblock.cn.push({
8663 cls : 'input-group-addon',
8676 cls: 'form-hidden-field'
8684 Roo.log('multiple');
8692 cls: 'form-hidden-field'
8696 cls: 'select2-choices',
8700 cls: 'select2-search-field',
8713 cls: 'select2-container input-group',
8718 // cls: 'typeahead typeahead-long dropdown-menu',
8719 // style: 'display:none'
8724 if(!this.multiple && this.showToggleBtn){
8730 if (this.caret != false) {
8733 cls: 'fa fa-' + this.caret
8740 cls : 'input-group-addon btn dropdown-toggle',
8745 cls: 'combobox-clear',
8759 combobox.cls += ' select2-container-multi';
8762 if (align ==='left' && this.fieldLabel.length) {
8764 Roo.log("left and has label");
8770 cls : 'control-label col-sm-' + this.labelWidth,
8771 html : this.fieldLabel
8775 cls : "col-sm-" + (12 - this.labelWidth),
8782 } else if ( this.fieldLabel.length) {
8788 //cls : 'input-group-addon',
8789 html : this.fieldLabel
8799 Roo.log(" no label && no align");
8806 ['xs','sm','md','lg'].map(function(size){
8807 if (settings[size]) {
8808 cfg.cls += ' col-' + size + '-' + settings[size];
8819 onResize : function(w, h){
8820 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8821 // if(typeof w == 'number'){
8822 // var x = w - this.trigger.getWidth();
8823 // this.inputEl().setWidth(this.adjustWidth('input', x));
8824 // this.trigger.setStyle('left', x+'px');
8829 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8832 getResizeEl : function(){
8833 return this.inputEl();
8837 getPositionEl : function(){
8838 return this.inputEl();
8842 alignErrorIcon : function(){
8843 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8847 initEvents : function(){
8851 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8852 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8853 if(!this.multiple && this.showToggleBtn){
8854 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8855 if(this.hideTrigger){
8856 this.trigger.setDisplayed(false);
8858 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8862 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8865 if(this.removable && !this.editable && !this.tickable){
8866 var close = this.closeTriggerEl();
8869 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8870 close.on('click', this.removeBtnClick, this, close);
8874 //this.trigger.addClassOnOver('x-form-trigger-over');
8875 //this.trigger.addClassOnClick('x-form-trigger-click');
8878 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8882 closeTriggerEl : function()
8884 var close = this.el.select('.roo-combo-removable-btn', true).first();
8885 return close ? close : false;
8888 removeBtnClick : function(e, h, el)
8892 if(this.fireEvent("remove", this) !== false){
8897 createList : function()
8899 this.list = Roo.get(document.body).createChild({
8901 cls: 'typeahead typeahead-long dropdown-menu',
8902 style: 'display:none'
8905 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8910 initTrigger : function(){
8915 onDestroy : function(){
8917 this.trigger.removeAllListeners();
8918 // this.trigger.remove();
8921 // this.wrap.remove();
8923 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8927 onFocus : function(){
8928 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8931 this.wrap.addClass('x-trigger-wrap-focus');
8932 this.mimicing = true;
8933 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8934 if(this.monitorTab){
8935 this.el.on("keydown", this.checkTab, this);
8942 checkTab : function(e){
8943 if(e.getKey() == e.TAB){
8949 onBlur : function(){
8954 mimicBlur : function(e, t){
8956 if(!this.wrap.contains(t) && this.validateBlur()){
8963 triggerBlur : function(){
8964 this.mimicing = false;
8965 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8966 if(this.monitorTab){
8967 this.el.un("keydown", this.checkTab, this);
8969 //this.wrap.removeClass('x-trigger-wrap-focus');
8970 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8974 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8975 validateBlur : function(e, t){
8980 onDisable : function(){
8981 this.inputEl().dom.disabled = true;
8982 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8984 // this.wrap.addClass('x-item-disabled');
8989 onEnable : function(){
8990 this.inputEl().dom.disabled = false;
8991 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8993 // this.el.removeClass('x-item-disabled');
8998 onShow : function(){
8999 var ae = this.getActionEl();
9002 ae.dom.style.display = '';
9003 ae.dom.style.visibility = 'visible';
9009 onHide : function(){
9010 var ae = this.getActionEl();
9011 ae.dom.style.display = 'none';
9015 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9016 * by an implementing function.
9018 * @param {EventObject} e
9020 onTriggerClick : Roo.emptyFn
9024 * Ext JS Library 1.1.1
9025 * Copyright(c) 2006-2007, Ext JS, LLC.
9027 * Originally Released Under LGPL - original licence link has changed is not relivant.
9030 * <script type="text/javascript">
9035 * @class Roo.data.SortTypes
9037 * Defines the default sorting (casting?) comparison functions used when sorting data.
9039 Roo.data.SortTypes = {
9041 * Default sort that does nothing
9042 * @param {Mixed} s The value being converted
9043 * @return {Mixed} The comparison value
9050 * The regular expression used to strip tags
9054 stripTagsRE : /<\/?[^>]+>/gi,
9057 * Strips all HTML tags to sort on text only
9058 * @param {Mixed} s The value being converted
9059 * @return {String} The comparison value
9061 asText : function(s){
9062 return String(s).replace(this.stripTagsRE, "");
9066 * Strips all HTML tags to sort on text only - Case insensitive
9067 * @param {Mixed} s The value being converted
9068 * @return {String} The comparison value
9070 asUCText : function(s){
9071 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9075 * Case insensitive string
9076 * @param {Mixed} s The value being converted
9077 * @return {String} The comparison value
9079 asUCString : function(s) {
9080 return String(s).toUpperCase();
9085 * @param {Mixed} s The value being converted
9086 * @return {Number} The comparison value
9088 asDate : function(s) {
9092 if(s instanceof Date){
9095 return Date.parse(String(s));
9100 * @param {Mixed} s The value being converted
9101 * @return {Float} The comparison value
9103 asFloat : function(s) {
9104 var val = parseFloat(String(s).replace(/,/g, ""));
9105 if(isNaN(val)) val = 0;
9111 * @param {Mixed} s The value being converted
9112 * @return {Number} The comparison value
9114 asInt : function(s) {
9115 var val = parseInt(String(s).replace(/,/g, ""));
9116 if(isNaN(val)) val = 0;
9121 * Ext JS Library 1.1.1
9122 * Copyright(c) 2006-2007, Ext JS, LLC.
9124 * Originally Released Under LGPL - original licence link has changed is not relivant.
9127 * <script type="text/javascript">
9131 * @class Roo.data.Record
9132 * Instances of this class encapsulate both record <em>definition</em> information, and record
9133 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9134 * to access Records cached in an {@link Roo.data.Store} object.<br>
9136 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9137 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9140 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9142 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9143 * {@link #create}. The parameters are the same.
9144 * @param {Array} data An associative Array of data values keyed by the field name.
9145 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9146 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9147 * not specified an integer id is generated.
9149 Roo.data.Record = function(data, id){
9150 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9155 * Generate a constructor for a specific record layout.
9156 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9157 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9158 * Each field definition object may contain the following properties: <ul>
9159 * <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,
9160 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9161 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9162 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9163 * is being used, then this is a string containing the javascript expression to reference the data relative to
9164 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9165 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9166 * this may be omitted.</p></li>
9167 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9168 * <ul><li>auto (Default, implies no conversion)</li>
9173 * <li>date</li></ul></p></li>
9174 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9175 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9176 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9177 * by the Reader into an object that will be stored in the Record. It is passed the
9178 * following parameters:<ul>
9179 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9181 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9183 * <br>usage:<br><pre><code>
9184 var TopicRecord = Roo.data.Record.create(
9185 {name: 'title', mapping: 'topic_title'},
9186 {name: 'author', mapping: 'username'},
9187 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9188 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9189 {name: 'lastPoster', mapping: 'user2'},
9190 {name: 'excerpt', mapping: 'post_text'}
9193 var myNewRecord = new TopicRecord({
9194 title: 'Do my job please',
9197 lastPost: new Date(),
9198 lastPoster: 'Animal',
9199 excerpt: 'No way dude!'
9201 myStore.add(myNewRecord);
9206 Roo.data.Record.create = function(o){
9208 f.superclass.constructor.apply(this, arguments);
9210 Roo.extend(f, Roo.data.Record);
9211 var p = f.prototype;
9212 p.fields = new Roo.util.MixedCollection(false, function(field){
9215 for(var i = 0, len = o.length; i < len; i++){
9216 p.fields.add(new Roo.data.Field(o[i]));
9218 f.getField = function(name){
9219 return p.fields.get(name);
9224 Roo.data.Record.AUTO_ID = 1000;
9225 Roo.data.Record.EDIT = 'edit';
9226 Roo.data.Record.REJECT = 'reject';
9227 Roo.data.Record.COMMIT = 'commit';
9229 Roo.data.Record.prototype = {
9231 * Readonly flag - true if this record has been modified.
9240 join : function(store){
9245 * Set the named field to the specified value.
9246 * @param {String} name The name of the field to set.
9247 * @param {Object} value The value to set the field to.
9249 set : function(name, value){
9250 if(this.data[name] == value){
9257 if(typeof this.modified[name] == 'undefined'){
9258 this.modified[name] = this.data[name];
9260 this.data[name] = value;
9261 if(!this.editing && this.store){
9262 this.store.afterEdit(this);
9267 * Get the value of the named field.
9268 * @param {String} name The name of the field to get the value of.
9269 * @return {Object} The value of the field.
9271 get : function(name){
9272 return this.data[name];
9276 beginEdit : function(){
9277 this.editing = true;
9282 cancelEdit : function(){
9283 this.editing = false;
9284 delete this.modified;
9288 endEdit : function(){
9289 this.editing = false;
9290 if(this.dirty && this.store){
9291 this.store.afterEdit(this);
9296 * Usually called by the {@link Roo.data.Store} which owns the Record.
9297 * Rejects all changes made to the Record since either creation, or the last commit operation.
9298 * Modified fields are reverted to their original values.
9300 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9301 * of reject operations.
9303 reject : function(){
9304 var m = this.modified;
9306 if(typeof m[n] != "function"){
9307 this.data[n] = m[n];
9311 delete this.modified;
9312 this.editing = false;
9314 this.store.afterReject(this);
9319 * Usually called by the {@link Roo.data.Store} which owns the Record.
9320 * Commits all changes made to the Record since either creation, or the last commit operation.
9322 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9323 * of commit operations.
9325 commit : function(){
9327 delete this.modified;
9328 this.editing = false;
9330 this.store.afterCommit(this);
9335 hasError : function(){
9336 return this.error != null;
9340 clearError : function(){
9345 * Creates a copy of this record.
9346 * @param {String} id (optional) A new record id if you don't want to use this record's id
9349 copy : function(newId) {
9350 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9354 * Ext JS Library 1.1.1
9355 * Copyright(c) 2006-2007, Ext JS, LLC.
9357 * Originally Released Under LGPL - original licence link has changed is not relivant.
9360 * <script type="text/javascript">
9366 * @class Roo.data.Store
9367 * @extends Roo.util.Observable
9368 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9369 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9371 * 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
9372 * has no knowledge of the format of the data returned by the Proxy.<br>
9374 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9375 * instances from the data object. These records are cached and made available through accessor functions.
9377 * Creates a new Store.
9378 * @param {Object} config A config object containing the objects needed for the Store to access data,
9379 * and read the data into Records.
9381 Roo.data.Store = function(config){
9382 this.data = new Roo.util.MixedCollection(false);
9383 this.data.getKey = function(o){
9386 this.baseParams = {};
9393 "multisort" : "_multisort"
9396 if(config && config.data){
9397 this.inlineData = config.data;
9401 Roo.apply(this, config);
9403 if(this.reader){ // reader passed
9404 this.reader = Roo.factory(this.reader, Roo.data);
9405 this.reader.xmodule = this.xmodule || false;
9406 if(!this.recordType){
9407 this.recordType = this.reader.recordType;
9409 if(this.reader.onMetaChange){
9410 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9414 if(this.recordType){
9415 this.fields = this.recordType.prototype.fields;
9421 * @event datachanged
9422 * Fires when the data cache has changed, and a widget which is using this Store
9423 * as a Record cache should refresh its view.
9424 * @param {Store} this
9429 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9430 * @param {Store} this
9431 * @param {Object} meta The JSON metadata
9436 * Fires when Records have been added to the Store
9437 * @param {Store} this
9438 * @param {Roo.data.Record[]} records The array of Records added
9439 * @param {Number} index The index at which the record(s) were added
9444 * Fires when a Record has been removed from the Store
9445 * @param {Store} this
9446 * @param {Roo.data.Record} record The Record that was removed
9447 * @param {Number} index The index at which the record was removed
9452 * Fires when a Record has been updated
9453 * @param {Store} this
9454 * @param {Roo.data.Record} record The Record that was updated
9455 * @param {String} operation The update operation being performed. Value may be one of:
9457 Roo.data.Record.EDIT
9458 Roo.data.Record.REJECT
9459 Roo.data.Record.COMMIT
9465 * Fires when the data cache has been cleared.
9466 * @param {Store} this
9471 * Fires before a request is made for a new data object. If the beforeload handler returns false
9472 * the load action will be canceled.
9473 * @param {Store} this
9474 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9478 * @event beforeloadadd
9479 * Fires after a new set of Records has been loaded.
9480 * @param {Store} this
9481 * @param {Roo.data.Record[]} records The Records that were loaded
9482 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9484 beforeloadadd : true,
9487 * Fires after a new set of Records has been loaded, before they are added to the store.
9488 * @param {Store} this
9489 * @param {Roo.data.Record[]} records The Records that were loaded
9490 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9491 * @params {Object} return from reader
9495 * @event loadexception
9496 * Fires if an exception occurs in the Proxy during loading.
9497 * Called with the signature of the Proxy's "loadexception" event.
9498 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9501 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9502 * @param {Object} load options
9503 * @param {Object} jsonData from your request (normally this contains the Exception)
9505 loadexception : true
9509 this.proxy = Roo.factory(this.proxy, Roo.data);
9510 this.proxy.xmodule = this.xmodule || false;
9511 this.relayEvents(this.proxy, ["loadexception"]);
9513 this.sortToggle = {};
9514 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9516 Roo.data.Store.superclass.constructor.call(this);
9518 if(this.inlineData){
9519 this.loadData(this.inlineData);
9520 delete this.inlineData;
9524 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9526 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9527 * without a remote query - used by combo/forms at present.
9531 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9534 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9537 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9538 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9541 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9542 * on any HTTP request
9545 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9548 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9552 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9553 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9558 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9559 * loaded or when a record is removed. (defaults to false).
9561 pruneModifiedRecords : false,
9567 * Add Records to the Store and fires the add event.
9568 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9570 add : function(records){
9571 records = [].concat(records);
9572 for(var i = 0, len = records.length; i < len; i++){
9573 records[i].join(this);
9575 var index = this.data.length;
9576 this.data.addAll(records);
9577 this.fireEvent("add", this, records, index);
9581 * Remove a Record from the Store and fires the remove event.
9582 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9584 remove : function(record){
9585 var index = this.data.indexOf(record);
9586 this.data.removeAt(index);
9587 if(this.pruneModifiedRecords){
9588 this.modified.remove(record);
9590 this.fireEvent("remove", this, record, index);
9594 * Remove all Records from the Store and fires the clear event.
9596 removeAll : function(){
9598 if(this.pruneModifiedRecords){
9601 this.fireEvent("clear", this);
9605 * Inserts Records to the Store at the given index and fires the add event.
9606 * @param {Number} index The start index at which to insert the passed Records.
9607 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9609 insert : function(index, records){
9610 records = [].concat(records);
9611 for(var i = 0, len = records.length; i < len; i++){
9612 this.data.insert(index, records[i]);
9613 records[i].join(this);
9615 this.fireEvent("add", this, records, index);
9619 * Get the index within the cache of the passed Record.
9620 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9621 * @return {Number} The index of the passed Record. Returns -1 if not found.
9623 indexOf : function(record){
9624 return this.data.indexOf(record);
9628 * Get the index within the cache of the Record with the passed id.
9629 * @param {String} id The id of the Record to find.
9630 * @return {Number} The index of the Record. Returns -1 if not found.
9632 indexOfId : function(id){
9633 return this.data.indexOfKey(id);
9637 * Get the Record with the specified id.
9638 * @param {String} id The id of the Record to find.
9639 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9641 getById : function(id){
9642 return this.data.key(id);
9646 * Get the Record at the specified index.
9647 * @param {Number} index The index of the Record to find.
9648 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9650 getAt : function(index){
9651 return this.data.itemAt(index);
9655 * Returns a range of Records between specified indices.
9656 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9657 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9658 * @return {Roo.data.Record[]} An array of Records
9660 getRange : function(start, end){
9661 return this.data.getRange(start, end);
9665 storeOptions : function(o){
9666 o = Roo.apply({}, o);
9669 this.lastOptions = o;
9673 * Loads the Record cache from the configured Proxy using the configured Reader.
9675 * If using remote paging, then the first load call must specify the <em>start</em>
9676 * and <em>limit</em> properties in the options.params property to establish the initial
9677 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9679 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9680 * and this call will return before the new data has been loaded. Perform any post-processing
9681 * in a callback function, or in a "load" event handler.</strong>
9683 * @param {Object} options An object containing properties which control loading options:<ul>
9684 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9685 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9686 * passed the following arguments:<ul>
9687 * <li>r : Roo.data.Record[]</li>
9688 * <li>options: Options object from the load call</li>
9689 * <li>success: Boolean success indicator</li></ul></li>
9690 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9691 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9694 load : function(options){
9695 options = options || {};
9696 if(this.fireEvent("beforeload", this, options) !== false){
9697 this.storeOptions(options);
9698 var p = Roo.apply(options.params || {}, this.baseParams);
9699 // if meta was not loaded from remote source.. try requesting it.
9700 if (!this.reader.metaFromRemote) {
9703 if(this.sortInfo && this.remoteSort){
9704 var pn = this.paramNames;
9705 p[pn["sort"]] = this.sortInfo.field;
9706 p[pn["dir"]] = this.sortInfo.direction;
9708 if (this.multiSort) {
9709 var pn = this.paramNames;
9710 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9713 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9718 * Reloads the Record cache from the configured Proxy using the configured Reader and
9719 * the options from the last load operation performed.
9720 * @param {Object} options (optional) An object containing properties which may override the options
9721 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9722 * the most recently used options are reused).
9724 reload : function(options){
9725 this.load(Roo.applyIf(options||{}, this.lastOptions));
9729 // Called as a callback by the Reader during a load operation.
9730 loadRecords : function(o, options, success){
9731 if(!o || success === false){
9732 if(success !== false){
9733 this.fireEvent("load", this, [], options, o);
9735 if(options.callback){
9736 options.callback.call(options.scope || this, [], options, false);
9740 // if data returned failure - throw an exception.
9741 if (o.success === false) {
9742 // show a message if no listener is registered.
9743 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9744 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9746 // loadmask wil be hooked into this..
9747 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9750 var r = o.records, t = o.totalRecords || r.length;
9752 this.fireEvent("beforeloadadd", this, r, options, o);
9754 if(!options || options.add !== true){
9755 if(this.pruneModifiedRecords){
9758 for(var i = 0, len = r.length; i < len; i++){
9762 this.data = this.snapshot;
9763 delete this.snapshot;
9766 this.data.addAll(r);
9767 this.totalLength = t;
9769 this.fireEvent("datachanged", this);
9771 this.totalLength = Math.max(t, this.data.length+r.length);
9774 this.fireEvent("load", this, r, options, o);
9775 if(options.callback){
9776 options.callback.call(options.scope || this, r, options, true);
9782 * Loads data from a passed data block. A Reader which understands the format of the data
9783 * must have been configured in the constructor.
9784 * @param {Object} data The data block from which to read the Records. The format of the data expected
9785 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9786 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9788 loadData : function(o, append){
9789 var r = this.reader.readRecords(o);
9790 this.loadRecords(r, {add: append}, true);
9794 * Gets the number of cached records.
9796 * <em>If using paging, this may not be the total size of the dataset. If the data object
9797 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9798 * the data set size</em>
9800 getCount : function(){
9801 return this.data.length || 0;
9805 * Gets the total number of records in the dataset as returned by the server.
9807 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9808 * the dataset size</em>
9810 getTotalCount : function(){
9811 return this.totalLength || 0;
9815 * Returns the sort state of the Store as an object with two properties:
9817 field {String} The name of the field by which the Records are sorted
9818 direction {String} The sort order, "ASC" or "DESC"
9821 getSortState : function(){
9822 return this.sortInfo;
9826 applySort : function(){
9827 if(this.sortInfo && !this.remoteSort){
9828 var s = this.sortInfo, f = s.field;
9829 var st = this.fields.get(f).sortType;
9830 var fn = function(r1, r2){
9831 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9832 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9834 this.data.sort(s.direction, fn);
9835 if(this.snapshot && this.snapshot != this.data){
9836 this.snapshot.sort(s.direction, fn);
9842 * Sets the default sort column and order to be used by the next load operation.
9843 * @param {String} fieldName The name of the field to sort by.
9844 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9846 setDefaultSort : function(field, dir){
9847 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9852 * If remote sorting is used, the sort is performed on the server, and the cache is
9853 * reloaded. If local sorting is used, the cache is sorted internally.
9854 * @param {String} fieldName The name of the field to sort by.
9855 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9857 sort : function(fieldName, dir){
9858 var f = this.fields.get(fieldName);
9860 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9862 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9863 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9868 this.sortToggle[f.name] = dir;
9869 this.sortInfo = {field: f.name, direction: dir};
9870 if(!this.remoteSort){
9872 this.fireEvent("datachanged", this);
9874 this.load(this.lastOptions);
9879 * Calls the specified function for each of the Records in the cache.
9880 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9881 * Returning <em>false</em> aborts and exits the iteration.
9882 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9884 each : function(fn, scope){
9885 this.data.each(fn, scope);
9889 * Gets all records modified since the last commit. Modified records are persisted across load operations
9890 * (e.g., during paging).
9891 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9893 getModifiedRecords : function(){
9894 return this.modified;
9898 createFilterFn : function(property, value, anyMatch){
9899 if(!value.exec){ // not a regex
9900 value = String(value);
9901 if(value.length == 0){
9904 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9907 return value.test(r.data[property]);
9912 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9913 * @param {String} property A field on your records
9914 * @param {Number} start The record index to start at (defaults to 0)
9915 * @param {Number} end The last record index to include (defaults to length - 1)
9916 * @return {Number} The sum
9918 sum : function(property, start, end){
9919 var rs = this.data.items, v = 0;
9921 end = (end || end === 0) ? end : rs.length-1;
9923 for(var i = start; i <= end; i++){
9924 v += (rs[i].data[property] || 0);
9930 * Filter the records by a specified property.
9931 * @param {String} field A field on your records
9932 * @param {String/RegExp} value Either a string that the field
9933 * should start with or a RegExp to test against the field
9934 * @param {Boolean} anyMatch True to match any part not just the beginning
9936 filter : function(property, value, anyMatch){
9937 var fn = this.createFilterFn(property, value, anyMatch);
9938 return fn ? this.filterBy(fn) : this.clearFilter();
9942 * Filter by a function. The specified function will be called with each
9943 * record in this data source. If the function returns true the record is included,
9944 * otherwise it is filtered.
9945 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9946 * @param {Object} scope (optional) The scope of the function (defaults to this)
9948 filterBy : function(fn, scope){
9949 this.snapshot = this.snapshot || this.data;
9950 this.data = this.queryBy(fn, scope||this);
9951 this.fireEvent("datachanged", this);
9955 * Query the records by a specified property.
9956 * @param {String} field A field on your records
9957 * @param {String/RegExp} value Either a string that the field
9958 * should start with or a RegExp to test against the field
9959 * @param {Boolean} anyMatch True to match any part not just the beginning
9960 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9962 query : function(property, value, anyMatch){
9963 var fn = this.createFilterFn(property, value, anyMatch);
9964 return fn ? this.queryBy(fn) : this.data.clone();
9968 * Query by a function. The specified function will be called with each
9969 * record in this data source. If the function returns true the record is included
9971 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9972 * @param {Object} scope (optional) The scope of the function (defaults to this)
9973 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9975 queryBy : function(fn, scope){
9976 var data = this.snapshot || this.data;
9977 return data.filterBy(fn, scope||this);
9981 * Collects unique values for a particular dataIndex from this store.
9982 * @param {String} dataIndex The property to collect
9983 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9984 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9985 * @return {Array} An array of the unique values
9987 collect : function(dataIndex, allowNull, bypassFilter){
9988 var d = (bypassFilter === true && this.snapshot) ?
9989 this.snapshot.items : this.data.items;
9990 var v, sv, r = [], l = {};
9991 for(var i = 0, len = d.length; i < len; i++){
9992 v = d[i].data[dataIndex];
9994 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10003 * Revert to a view of the Record cache with no filtering applied.
10004 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10006 clearFilter : function(suppressEvent){
10007 if(this.snapshot && this.snapshot != this.data){
10008 this.data = this.snapshot;
10009 delete this.snapshot;
10010 if(suppressEvent !== true){
10011 this.fireEvent("datachanged", this);
10017 afterEdit : function(record){
10018 if(this.modified.indexOf(record) == -1){
10019 this.modified.push(record);
10021 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10025 afterReject : function(record){
10026 this.modified.remove(record);
10027 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10031 afterCommit : function(record){
10032 this.modified.remove(record);
10033 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10037 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10038 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10040 commitChanges : function(){
10041 var m = this.modified.slice(0);
10042 this.modified = [];
10043 for(var i = 0, len = m.length; i < len; i++){
10049 * Cancel outstanding changes on all changed records.
10051 rejectChanges : function(){
10052 var m = this.modified.slice(0);
10053 this.modified = [];
10054 for(var i = 0, len = m.length; i < len; i++){
10059 onMetaChange : function(meta, rtype, o){
10060 this.recordType = rtype;
10061 this.fields = rtype.prototype.fields;
10062 delete this.snapshot;
10063 this.sortInfo = meta.sortInfo || this.sortInfo;
10064 this.modified = [];
10065 this.fireEvent('metachange', this, this.reader.meta);
10068 moveIndex : function(data, type)
10070 var index = this.indexOf(data);
10072 var newIndex = index + type;
10076 this.insert(newIndex, data);
10081 * Ext JS Library 1.1.1
10082 * Copyright(c) 2006-2007, Ext JS, LLC.
10084 * Originally Released Under LGPL - original licence link has changed is not relivant.
10087 * <script type="text/javascript">
10091 * @class Roo.data.SimpleStore
10092 * @extends Roo.data.Store
10093 * Small helper class to make creating Stores from Array data easier.
10094 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10095 * @cfg {Array} fields An array of field definition objects, or field name strings.
10096 * @cfg {Array} data The multi-dimensional array of data
10098 * @param {Object} config
10100 Roo.data.SimpleStore = function(config){
10101 Roo.data.SimpleStore.superclass.constructor.call(this, {
10103 reader: new Roo.data.ArrayReader({
10106 Roo.data.Record.create(config.fields)
10108 proxy : new Roo.data.MemoryProxy(config.data)
10112 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10114 * Ext JS Library 1.1.1
10115 * Copyright(c) 2006-2007, Ext JS, LLC.
10117 * Originally Released Under LGPL - original licence link has changed is not relivant.
10120 * <script type="text/javascript">
10125 * @extends Roo.data.Store
10126 * @class Roo.data.JsonStore
10127 * Small helper class to make creating Stores for JSON data easier. <br/>
10129 var store = new Roo.data.JsonStore({
10130 url: 'get-images.php',
10132 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10135 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10136 * JsonReader and HttpProxy (unless inline data is provided).</b>
10137 * @cfg {Array} fields An array of field definition objects, or field name strings.
10139 * @param {Object} config
10141 Roo.data.JsonStore = function(c){
10142 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10143 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10144 reader: new Roo.data.JsonReader(c, c.fields)
10147 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10149 * Ext JS Library 1.1.1
10150 * Copyright(c) 2006-2007, Ext JS, LLC.
10152 * Originally Released Under LGPL - original licence link has changed is not relivant.
10155 * <script type="text/javascript">
10159 Roo.data.Field = function(config){
10160 if(typeof config == "string"){
10161 config = {name: config};
10163 Roo.apply(this, config);
10166 this.type = "auto";
10169 var st = Roo.data.SortTypes;
10170 // named sortTypes are supported, here we look them up
10171 if(typeof this.sortType == "string"){
10172 this.sortType = st[this.sortType];
10175 // set default sortType for strings and dates
10176 if(!this.sortType){
10179 this.sortType = st.asUCString;
10182 this.sortType = st.asDate;
10185 this.sortType = st.none;
10190 var stripRe = /[\$,%]/g;
10192 // prebuilt conversion function for this field, instead of
10193 // switching every time we're reading a value
10195 var cv, dateFormat = this.dateFormat;
10200 cv = function(v){ return v; };
10203 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10207 return v !== undefined && v !== null && v !== '' ?
10208 parseInt(String(v).replace(stripRe, ""), 10) : '';
10213 return v !== undefined && v !== null && v !== '' ?
10214 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10219 cv = function(v){ return v === true || v === "true" || v == 1; };
10226 if(v instanceof Date){
10230 if(dateFormat == "timestamp"){
10231 return new Date(v*1000);
10233 return Date.parseDate(v, dateFormat);
10235 var parsed = Date.parse(v);
10236 return parsed ? new Date(parsed) : null;
10245 Roo.data.Field.prototype = {
10253 * Ext JS Library 1.1.1
10254 * Copyright(c) 2006-2007, Ext JS, LLC.
10256 * Originally Released Under LGPL - original licence link has changed is not relivant.
10259 * <script type="text/javascript">
10262 // Base class for reading structured data from a data source. This class is intended to be
10263 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10266 * @class Roo.data.DataReader
10267 * Base class for reading structured data from a data source. This class is intended to be
10268 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10271 Roo.data.DataReader = function(meta, recordType){
10275 this.recordType = recordType instanceof Array ?
10276 Roo.data.Record.create(recordType) : recordType;
10279 Roo.data.DataReader.prototype = {
10281 * Create an empty record
10282 * @param {Object} data (optional) - overlay some values
10283 * @return {Roo.data.Record} record created.
10285 newRow : function(d) {
10287 this.recordType.prototype.fields.each(function(c) {
10289 case 'int' : da[c.name] = 0; break;
10290 case 'date' : da[c.name] = new Date(); break;
10291 case 'float' : da[c.name] = 0.0; break;
10292 case 'boolean' : da[c.name] = false; break;
10293 default : da[c.name] = ""; break;
10297 return new this.recordType(Roo.apply(da, d));
10302 * Ext JS Library 1.1.1
10303 * Copyright(c) 2006-2007, Ext JS, LLC.
10305 * Originally Released Under LGPL - original licence link has changed is not relivant.
10308 * <script type="text/javascript">
10312 * @class Roo.data.DataProxy
10313 * @extends Roo.data.Observable
10314 * This class is an abstract base class for implementations which provide retrieval of
10315 * unformatted data objects.<br>
10317 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10318 * (of the appropriate type which knows how to parse the data object) to provide a block of
10319 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10321 * Custom implementations must implement the load method as described in
10322 * {@link Roo.data.HttpProxy#load}.
10324 Roo.data.DataProxy = function(){
10327 * @event beforeload
10328 * Fires before a network request is made to retrieve a data object.
10329 * @param {Object} This DataProxy object.
10330 * @param {Object} params The params parameter to the load function.
10335 * Fires before the load method's callback is called.
10336 * @param {Object} This DataProxy object.
10337 * @param {Object} o The data object.
10338 * @param {Object} arg The callback argument object passed to the load function.
10342 * @event loadexception
10343 * Fires if an Exception occurs during data retrieval.
10344 * @param {Object} This DataProxy object.
10345 * @param {Object} o The data object.
10346 * @param {Object} arg The callback argument object passed to the load function.
10347 * @param {Object} e The Exception.
10349 loadexception : true
10351 Roo.data.DataProxy.superclass.constructor.call(this);
10354 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10357 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10361 * Ext JS Library 1.1.1
10362 * Copyright(c) 2006-2007, Ext JS, LLC.
10364 * Originally Released Under LGPL - original licence link has changed is not relivant.
10367 * <script type="text/javascript">
10370 * @class Roo.data.MemoryProxy
10371 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10372 * to the Reader when its load method is called.
10374 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10376 Roo.data.MemoryProxy = function(data){
10380 Roo.data.MemoryProxy.superclass.constructor.call(this);
10384 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10386 * Load data from the requested source (in this case an in-memory
10387 * data object passed to the constructor), read the data object into
10388 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10389 * process that block using the passed callback.
10390 * @param {Object} params This parameter is not used by the MemoryProxy class.
10391 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10392 * object into a block of Roo.data.Records.
10393 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10394 * The function must be passed <ul>
10395 * <li>The Record block object</li>
10396 * <li>The "arg" argument from the load function</li>
10397 * <li>A boolean success indicator</li>
10399 * @param {Object} scope The scope in which to call the callback
10400 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10402 load : function(params, reader, callback, scope, arg){
10403 params = params || {};
10406 result = reader.readRecords(this.data);
10408 this.fireEvent("loadexception", this, arg, null, e);
10409 callback.call(scope, null, arg, false);
10412 callback.call(scope, result, arg, true);
10416 update : function(params, records){
10421 * Ext JS Library 1.1.1
10422 * Copyright(c) 2006-2007, Ext JS, LLC.
10424 * Originally Released Under LGPL - original licence link has changed is not relivant.
10427 * <script type="text/javascript">
10430 * @class Roo.data.HttpProxy
10431 * @extends Roo.data.DataProxy
10432 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10433 * configured to reference a certain URL.<br><br>
10435 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10436 * from which the running page was served.<br><br>
10438 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10440 * Be aware that to enable the browser to parse an XML document, the server must set
10441 * the Content-Type header in the HTTP response to "text/xml".
10443 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10444 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10445 * will be used to make the request.
10447 Roo.data.HttpProxy = function(conn){
10448 Roo.data.HttpProxy.superclass.constructor.call(this);
10449 // is conn a conn config or a real conn?
10451 this.useAjax = !conn || !conn.events;
10455 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10456 // thse are take from connection...
10459 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10462 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10463 * extra parameters to each request made by this object. (defaults to undefined)
10466 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10467 * to each request made by this object. (defaults to undefined)
10470 * @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)
10473 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10476 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10482 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10486 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10487 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10488 * a finer-grained basis than the DataProxy events.
10490 getConnection : function(){
10491 return this.useAjax ? Roo.Ajax : this.conn;
10495 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10496 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10497 * process that block using the passed callback.
10498 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10499 * for the request to the remote server.
10500 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10501 * object into a block of Roo.data.Records.
10502 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10503 * The function must be passed <ul>
10504 * <li>The Record block object</li>
10505 * <li>The "arg" argument from the load function</li>
10506 * <li>A boolean success indicator</li>
10508 * @param {Object} scope The scope in which to call the callback
10509 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10511 load : function(params, reader, callback, scope, arg){
10512 if(this.fireEvent("beforeload", this, params) !== false){
10514 params : params || {},
10516 callback : callback,
10521 callback : this.loadResponse,
10525 Roo.applyIf(o, this.conn);
10526 if(this.activeRequest){
10527 Roo.Ajax.abort(this.activeRequest);
10529 this.activeRequest = Roo.Ajax.request(o);
10531 this.conn.request(o);
10534 callback.call(scope||this, null, arg, false);
10539 loadResponse : function(o, success, response){
10540 delete this.activeRequest;
10542 this.fireEvent("loadexception", this, o, response);
10543 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10548 result = o.reader.read(response);
10550 this.fireEvent("loadexception", this, o, response, e);
10551 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10555 this.fireEvent("load", this, o, o.request.arg);
10556 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10560 update : function(dataSet){
10565 updateResponse : function(dataSet){
10570 * Ext JS Library 1.1.1
10571 * Copyright(c) 2006-2007, Ext JS, LLC.
10573 * Originally Released Under LGPL - original licence link has changed is not relivant.
10576 * <script type="text/javascript">
10580 * @class Roo.data.ScriptTagProxy
10581 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10582 * other than the originating domain of the running page.<br><br>
10584 * <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
10585 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10587 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10588 * source code that is used as the source inside a <script> tag.<br><br>
10590 * In order for the browser to process the returned data, the server must wrap the data object
10591 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10592 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10593 * depending on whether the callback name was passed:
10596 boolean scriptTag = false;
10597 String cb = request.getParameter("callback");
10600 response.setContentType("text/javascript");
10602 response.setContentType("application/x-json");
10604 Writer out = response.getWriter();
10606 out.write(cb + "(");
10608 out.print(dataBlock.toJsonString());
10615 * @param {Object} config A configuration object.
10617 Roo.data.ScriptTagProxy = function(config){
10618 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10619 Roo.apply(this, config);
10620 this.head = document.getElementsByTagName("head")[0];
10623 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10625 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10627 * @cfg {String} url The URL from which to request the data object.
10630 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10634 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10635 * the server the name of the callback function set up by the load call to process the returned data object.
10636 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10637 * javascript output which calls this named function passing the data object as its only parameter.
10639 callbackParam : "callback",
10641 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10642 * name to the request.
10647 * Load data from the configured URL, read the data object into
10648 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10649 * process that block using the passed callback.
10650 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10651 * for the request to the remote server.
10652 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10653 * object into a block of Roo.data.Records.
10654 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10655 * The function must be passed <ul>
10656 * <li>The Record block object</li>
10657 * <li>The "arg" argument from the load function</li>
10658 * <li>A boolean success indicator</li>
10660 * @param {Object} scope The scope in which to call the callback
10661 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10663 load : function(params, reader, callback, scope, arg){
10664 if(this.fireEvent("beforeload", this, params) !== false){
10666 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10668 var url = this.url;
10669 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10671 url += "&_dc=" + (new Date().getTime());
10673 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10676 cb : "stcCallback"+transId,
10677 scriptId : "stcScript"+transId,
10681 callback : callback,
10687 window[trans.cb] = function(o){
10688 conn.handleResponse(o, trans);
10691 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10693 if(this.autoAbort !== false){
10697 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10699 var script = document.createElement("script");
10700 script.setAttribute("src", url);
10701 script.setAttribute("type", "text/javascript");
10702 script.setAttribute("id", trans.scriptId);
10703 this.head.appendChild(script);
10705 this.trans = trans;
10707 callback.call(scope||this, null, arg, false);
10712 isLoading : function(){
10713 return this.trans ? true : false;
10717 * Abort the current server request.
10719 abort : function(){
10720 if(this.isLoading()){
10721 this.destroyTrans(this.trans);
10726 destroyTrans : function(trans, isLoaded){
10727 this.head.removeChild(document.getElementById(trans.scriptId));
10728 clearTimeout(trans.timeoutId);
10730 window[trans.cb] = undefined;
10732 delete window[trans.cb];
10735 // if hasn't been loaded, wait for load to remove it to prevent script error
10736 window[trans.cb] = function(){
10737 window[trans.cb] = undefined;
10739 delete window[trans.cb];
10746 handleResponse : function(o, trans){
10747 this.trans = false;
10748 this.destroyTrans(trans, true);
10751 result = trans.reader.readRecords(o);
10753 this.fireEvent("loadexception", this, o, trans.arg, e);
10754 trans.callback.call(trans.scope||window, null, trans.arg, false);
10757 this.fireEvent("load", this, o, trans.arg);
10758 trans.callback.call(trans.scope||window, result, trans.arg, true);
10762 handleFailure : function(trans){
10763 this.trans = false;
10764 this.destroyTrans(trans, false);
10765 this.fireEvent("loadexception", this, null, trans.arg);
10766 trans.callback.call(trans.scope||window, null, trans.arg, false);
10770 * Ext JS Library 1.1.1
10771 * Copyright(c) 2006-2007, Ext JS, LLC.
10773 * Originally Released Under LGPL - original licence link has changed is not relivant.
10776 * <script type="text/javascript">
10780 * @class Roo.data.JsonReader
10781 * @extends Roo.data.DataReader
10782 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10783 * based on mappings in a provided Roo.data.Record constructor.
10785 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10786 * in the reply previously.
10791 var RecordDef = Roo.data.Record.create([
10792 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10793 {name: 'occupation'} // This field will use "occupation" as the mapping.
10795 var myReader = new Roo.data.JsonReader({
10796 totalProperty: "results", // The property which contains the total dataset size (optional)
10797 root: "rows", // The property which contains an Array of row objects
10798 id: "id" // The property within each row object that provides an ID for the record (optional)
10802 * This would consume a JSON file like this:
10804 { 'results': 2, 'rows': [
10805 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10806 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10809 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10810 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10811 * paged from the remote server.
10812 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10813 * @cfg {String} root name of the property which contains the Array of row objects.
10814 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10815 * @cfg {Array} fields Array of field definition objects
10817 * Create a new JsonReader
10818 * @param {Object} meta Metadata configuration options
10819 * @param {Object} recordType Either an Array of field definition objects,
10820 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10822 Roo.data.JsonReader = function(meta, recordType){
10825 // set some defaults:
10826 Roo.applyIf(meta, {
10827 totalProperty: 'total',
10828 successProperty : 'success',
10833 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10835 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10838 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10839 * Used by Store query builder to append _requestMeta to params.
10842 metaFromRemote : false,
10844 * This method is only used by a DataProxy which has retrieved data from a remote server.
10845 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10846 * @return {Object} data A data block which is used by an Roo.data.Store object as
10847 * a cache of Roo.data.Records.
10849 read : function(response){
10850 var json = response.responseText;
10852 var o = /* eval:var:o */ eval("("+json+")");
10854 throw {message: "JsonReader.read: Json object not found"};
10860 this.metaFromRemote = true;
10861 this.meta = o.metaData;
10862 this.recordType = Roo.data.Record.create(o.metaData.fields);
10863 this.onMetaChange(this.meta, this.recordType, o);
10865 return this.readRecords(o);
10868 // private function a store will implement
10869 onMetaChange : function(meta, recordType, o){
10876 simpleAccess: function(obj, subsc) {
10883 getJsonAccessor: function(){
10885 return function(expr) {
10887 return(re.test(expr))
10888 ? new Function("obj", "return obj." + expr)
10893 return Roo.emptyFn;
10898 * Create a data block containing Roo.data.Records from an XML document.
10899 * @param {Object} o An object which contains an Array of row objects in the property specified
10900 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10901 * which contains the total size of the dataset.
10902 * @return {Object} data A data block which is used by an Roo.data.Store object as
10903 * a cache of Roo.data.Records.
10905 readRecords : function(o){
10907 * After any data loads, the raw JSON data is available for further custom processing.
10911 var s = this.meta, Record = this.recordType,
10912 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10914 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10916 if(s.totalProperty) {
10917 this.getTotal = this.getJsonAccessor(s.totalProperty);
10919 if(s.successProperty) {
10920 this.getSuccess = this.getJsonAccessor(s.successProperty);
10922 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10924 var g = this.getJsonAccessor(s.id);
10925 this.getId = function(rec) {
10927 return (r === undefined || r === "") ? null : r;
10930 this.getId = function(){return null;};
10933 for(var jj = 0; jj < fl; jj++){
10935 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10936 this.ef[jj] = this.getJsonAccessor(map);
10940 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10941 if(s.totalProperty){
10942 var vt = parseInt(this.getTotal(o), 10);
10947 if(s.successProperty){
10948 var vs = this.getSuccess(o);
10949 if(vs === false || vs === 'false'){
10954 for(var i = 0; i < c; i++){
10957 var id = this.getId(n);
10958 for(var j = 0; j < fl; j++){
10960 var v = this.ef[j](n);
10962 Roo.log('missing convert for ' + f.name);
10966 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10968 var record = new Record(values, id);
10970 records[i] = record;
10976 totalRecords : totalRecords
10981 * Ext JS Library 1.1.1
10982 * Copyright(c) 2006-2007, Ext JS, LLC.
10984 * Originally Released Under LGPL - original licence link has changed is not relivant.
10987 * <script type="text/javascript">
10991 * @class Roo.data.ArrayReader
10992 * @extends Roo.data.DataReader
10993 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10994 * Each element of that Array represents a row of data fields. The
10995 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10996 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11000 var RecordDef = Roo.data.Record.create([
11001 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11002 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11004 var myReader = new Roo.data.ArrayReader({
11005 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11009 * This would consume an Array like this:
11011 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11013 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11015 * Create a new JsonReader
11016 * @param {Object} meta Metadata configuration options.
11017 * @param {Object} recordType Either an Array of field definition objects
11018 * as specified to {@link Roo.data.Record#create},
11019 * or an {@link Roo.data.Record} object
11020 * created using {@link Roo.data.Record#create}.
11022 Roo.data.ArrayReader = function(meta, recordType){
11023 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11026 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11028 * Create a data block containing Roo.data.Records from an XML document.
11029 * @param {Object} o An Array of row objects which represents the dataset.
11030 * @return {Object} data A data block which is used by an Roo.data.Store object as
11031 * a cache of Roo.data.Records.
11033 readRecords : function(o){
11034 var sid = this.meta ? this.meta.id : null;
11035 var recordType = this.recordType, fields = recordType.prototype.fields;
11038 for(var i = 0; i < root.length; i++){
11041 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11042 for(var j = 0, jlen = fields.length; j < jlen; j++){
11043 var f = fields.items[j];
11044 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11045 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11047 values[f.name] = v;
11049 var record = new recordType(values, id);
11051 records[records.length] = record;
11055 totalRecords : records.length
11064 * @class Roo.bootstrap.ComboBox
11065 * @extends Roo.bootstrap.TriggerField
11066 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11067 * @cfg {Boolean} append (true|false) default false
11068 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11069 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11070 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11071 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11072 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11073 * @cfg {Boolean} animate default true
11074 * @cfg {Boolean} emptyResultText only for touch device
11076 * Create a new ComboBox.
11077 * @param {Object} config Configuration options
11079 Roo.bootstrap.ComboBox = function(config){
11080 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11084 * Fires when the dropdown list is expanded
11085 * @param {Roo.bootstrap.ComboBox} combo This combo box
11090 * Fires when the dropdown list is collapsed
11091 * @param {Roo.bootstrap.ComboBox} combo This combo box
11095 * @event beforeselect
11096 * Fires before a list item is selected. Return false to cancel the selection.
11097 * @param {Roo.bootstrap.ComboBox} combo This combo box
11098 * @param {Roo.data.Record} record The data record returned from the underlying store
11099 * @param {Number} index The index of the selected item in the dropdown list
11101 'beforeselect' : true,
11104 * Fires when a list item is selected
11105 * @param {Roo.bootstrap.ComboBox} combo This combo box
11106 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11107 * @param {Number} index The index of the selected item in the dropdown list
11111 * @event beforequery
11112 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11113 * The event object passed has these properties:
11114 * @param {Roo.bootstrap.ComboBox} combo This combo box
11115 * @param {String} query The query
11116 * @param {Boolean} forceAll true to force "all" query
11117 * @param {Boolean} cancel true to cancel the query
11118 * @param {Object} e The query event object
11120 'beforequery': true,
11123 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11124 * @param {Roo.bootstrap.ComboBox} combo This combo box
11129 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11130 * @param {Roo.bootstrap.ComboBox} combo This combo box
11131 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11136 * Fires when the remove value from the combobox array
11137 * @param {Roo.bootstrap.ComboBox} combo This combo box
11141 * @event specialfilter
11142 * Fires when specialfilter
11143 * @param {Roo.bootstrap.ComboBox} combo This combo box
11145 'specialfilter' : true
11150 this.tickItems = [];
11152 this.selectedIndex = -1;
11153 if(this.mode == 'local'){
11154 if(config.queryDelay === undefined){
11155 this.queryDelay = 10;
11157 if(config.minChars === undefined){
11163 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11166 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11167 * rendering into an Roo.Editor, defaults to false)
11170 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11171 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11174 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11177 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11178 * the dropdown list (defaults to undefined, with no header element)
11182 * @cfg {String/Roo.Template} tpl The template to use to render the output
11186 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11188 listWidth: undefined,
11190 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11191 * mode = 'remote' or 'text' if mode = 'local')
11193 displayField: undefined,
11196 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11197 * mode = 'remote' or 'value' if mode = 'local').
11198 * Note: use of a valueField requires the user make a selection
11199 * in order for a value to be mapped.
11201 valueField: undefined,
11205 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11206 * field's data value (defaults to the underlying DOM element's name)
11208 hiddenName: undefined,
11210 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11214 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11216 selectedClass: 'active',
11219 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11223 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11224 * anchor positions (defaults to 'tl-bl')
11226 listAlign: 'tl-bl?',
11228 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11232 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11233 * query specified by the allQuery config option (defaults to 'query')
11235 triggerAction: 'query',
11237 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11238 * (defaults to 4, does not apply if editable = false)
11242 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11243 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11247 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11248 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11252 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11253 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11257 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11258 * when editable = true (defaults to false)
11260 selectOnFocus:false,
11262 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11264 queryParam: 'query',
11266 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11267 * when mode = 'remote' (defaults to 'Loading...')
11269 loadingText: 'Loading...',
11271 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11275 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11279 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11280 * traditional select (defaults to true)
11284 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11288 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11292 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11293 * listWidth has a higher value)
11297 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11298 * allow the user to set arbitrary text into the field (defaults to false)
11300 forceSelection:false,
11302 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11303 * if typeAhead = true (defaults to 250)
11305 typeAheadDelay : 250,
11307 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11308 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11310 valueNotFoundText : undefined,
11312 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11314 blockFocus : false,
11317 * @cfg {Boolean} disableClear Disable showing of clear button.
11319 disableClear : false,
11321 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11323 alwaysQuery : false,
11326 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11331 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11333 invalidClass : "has-warning",
11336 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11338 validClass : "has-success",
11341 * @cfg {Boolean} specialFilter (true|false) special filter default false
11343 specialFilter : false,
11346 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11348 mobileTouchView : true,
11360 btnPosition : 'right',
11361 triggerList : true,
11362 showToggleBtn : true,
11364 emptyResultText: 'Empty',
11365 // element that contains real text value.. (when hidden is used..)
11367 getAutoCreate : function()
11375 if(Roo.isTouch && this.mobileTouchView){
11376 cfg = this.getAutoCreateTouchView();
11383 if(!this.tickable){
11384 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11389 * ComboBox with tickable selections
11392 var align = this.labelAlign || this.parentLabelAlign();
11395 cls : 'form-group roo-combobox-tickable' //input-group
11400 cls : 'tickable-buttons',
11405 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11412 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11419 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11426 buttons.cn.unshift({
11428 cls: 'select2-search-field-input'
11434 Roo.each(buttons.cn, function(c){
11436 c.cls += ' btn-' + _this.size;
11439 if (_this.disabled) {
11450 cls: 'form-hidden-field'
11454 cls: 'select2-choices',
11458 cls: 'select2-search-field',
11470 cls: 'select2-container input-group select2-container-multi',
11475 // cls: 'typeahead typeahead-long dropdown-menu',
11476 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11481 if(this.hasFeedback && !this.allowBlank){
11485 cls: 'glyphicon form-control-feedback'
11488 combobox.cn.push(feedback);
11491 if (align ==='left' && this.fieldLabel.length) {
11493 Roo.log("left and has label");
11499 cls : 'control-label col-sm-' + this.labelWidth,
11500 html : this.fieldLabel
11504 cls : "col-sm-" + (12 - this.labelWidth),
11511 } else if ( this.fieldLabel.length) {
11517 //cls : 'input-group-addon',
11518 html : this.fieldLabel
11528 Roo.log(" no label && no align");
11535 ['xs','sm','md','lg'].map(function(size){
11536 if (settings[size]) {
11537 cfg.cls += ' col-' + size + '-' + settings[size];
11546 initEvents: function()
11550 throw "can not find store for combo";
11553 this.store = Roo.factory(this.store, Roo.data);
11559 if(Roo.isTouch && this.mobileTouchView){
11560 this.initTouchView();
11565 this.initTickableEvents();
11569 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11571 if(this.hiddenName){
11573 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11575 this.hiddenField.dom.value =
11576 this.hiddenValue !== undefined ? this.hiddenValue :
11577 this.value !== undefined ? this.value : '';
11579 // prevent input submission
11580 this.el.dom.removeAttribute('name');
11581 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11586 // this.el.dom.setAttribute('autocomplete', 'off');
11589 var cls = 'x-combo-list';
11591 //this.list = new Roo.Layer({
11592 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11598 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11599 _this.list.setWidth(lw);
11602 this.list.on('mouseover', this.onViewOver, this);
11603 this.list.on('mousemove', this.onViewMove, this);
11605 this.list.on('scroll', this.onViewScroll, this);
11608 this.list.swallowEvent('mousewheel');
11609 this.assetHeight = 0;
11612 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11613 this.assetHeight += this.header.getHeight();
11616 this.innerList = this.list.createChild({cls:cls+'-inner'});
11617 this.innerList.on('mouseover', this.onViewOver, this);
11618 this.innerList.on('mousemove', this.onViewMove, this);
11619 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11621 if(this.allowBlank && !this.pageSize && !this.disableClear){
11622 this.footer = this.list.createChild({cls:cls+'-ft'});
11623 this.pageTb = new Roo.Toolbar(this.footer);
11627 this.footer = this.list.createChild({cls:cls+'-ft'});
11628 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11629 {pageSize: this.pageSize});
11633 if (this.pageTb && this.allowBlank && !this.disableClear) {
11635 this.pageTb.add(new Roo.Toolbar.Fill(), {
11636 cls: 'x-btn-icon x-btn-clear',
11638 handler: function()
11641 _this.clearValue();
11642 _this.onSelect(false, -1);
11647 this.assetHeight += this.footer.getHeight();
11652 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11655 this.view = new Roo.View(this.list, this.tpl, {
11656 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11658 //this.view.wrapEl.setDisplayed(false);
11659 this.view.on('click', this.onViewClick, this);
11663 this.store.on('beforeload', this.onBeforeLoad, this);
11664 this.store.on('load', this.onLoad, this);
11665 this.store.on('loadexception', this.onLoadException, this);
11667 if(this.resizable){
11668 this.resizer = new Roo.Resizable(this.list, {
11669 pinned:true, handles:'se'
11671 this.resizer.on('resize', function(r, w, h){
11672 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11673 this.listWidth = w;
11674 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11675 this.restrictHeight();
11677 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11680 if(!this.editable){
11681 this.editable = true;
11682 this.setEditable(false);
11687 if (typeof(this.events.add.listeners) != 'undefined') {
11689 this.addicon = this.wrap.createChild(
11690 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11692 this.addicon.on('click', function(e) {
11693 this.fireEvent('add', this);
11696 if (typeof(this.events.edit.listeners) != 'undefined') {
11698 this.editicon = this.wrap.createChild(
11699 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11700 if (this.addicon) {
11701 this.editicon.setStyle('margin-left', '40px');
11703 this.editicon.on('click', function(e) {
11705 // we fire even if inothing is selected..
11706 this.fireEvent('edit', this, this.lastData );
11712 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11713 "up" : function(e){
11714 this.inKeyMode = true;
11718 "down" : function(e){
11719 if(!this.isExpanded()){
11720 this.onTriggerClick();
11722 this.inKeyMode = true;
11727 "enter" : function(e){
11728 // this.onViewClick();
11732 if(this.fireEvent("specialkey", this, e)){
11733 this.onViewClick(false);
11739 "esc" : function(e){
11743 "tab" : function(e){
11746 if(this.fireEvent("specialkey", this, e)){
11747 this.onViewClick(false);
11755 doRelay : function(foo, bar, hname){
11756 if(hname == 'down' || this.scope.isExpanded()){
11757 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11766 this.queryDelay = Math.max(this.queryDelay || 10,
11767 this.mode == 'local' ? 10 : 250);
11770 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11772 if(this.typeAhead){
11773 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11775 if(this.editable !== false){
11776 this.inputEl().on("keyup", this.onKeyUp, this);
11778 if(this.forceSelection){
11779 this.inputEl().on('blur', this.doForce, this);
11783 this.choices = this.el.select('ul.select2-choices', true).first();
11784 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11788 initTickableEvents: function()
11792 if(this.hiddenName){
11794 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11796 this.hiddenField.dom.value =
11797 this.hiddenValue !== undefined ? this.hiddenValue :
11798 this.value !== undefined ? this.value : '';
11800 // prevent input submission
11801 this.el.dom.removeAttribute('name');
11802 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11807 // this.list = this.el.select('ul.dropdown-menu',true).first();
11809 this.choices = this.el.select('ul.select2-choices', true).first();
11810 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11811 if(this.triggerList){
11812 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11815 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11816 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11818 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11819 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11821 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11822 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11824 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11825 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11826 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11829 this.cancelBtn.hide();
11834 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11835 _this.list.setWidth(lw);
11838 this.list.on('mouseover', this.onViewOver, this);
11839 this.list.on('mousemove', this.onViewMove, this);
11841 this.list.on('scroll', this.onViewScroll, this);
11844 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>';
11847 this.view = new Roo.View(this.list, this.tpl, {
11848 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11851 //this.view.wrapEl.setDisplayed(false);
11852 this.view.on('click', this.onViewClick, this);
11856 this.store.on('beforeload', this.onBeforeLoad, this);
11857 this.store.on('load', this.onLoad, this);
11858 this.store.on('loadexception', this.onLoadException, this);
11861 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11862 "up" : function(e){
11863 this.inKeyMode = true;
11867 "down" : function(e){
11868 this.inKeyMode = true;
11872 "enter" : function(e){
11873 if(this.fireEvent("specialkey", this, e)){
11874 this.onViewClick(false);
11880 "esc" : function(e){
11881 this.onTickableFooterButtonClick(e, false, false);
11884 "tab" : function(e){
11885 this.fireEvent("specialkey", this, e);
11887 this.onTickableFooterButtonClick(e, false, false);
11894 doRelay : function(e, fn, key){
11895 if(this.scope.isExpanded()){
11896 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11905 this.queryDelay = Math.max(this.queryDelay || 10,
11906 this.mode == 'local' ? 10 : 250);
11909 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11911 if(this.typeAhead){
11912 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11915 if(this.editable !== false){
11916 this.tickableInputEl().on("keyup", this.onKeyUp, this);
11921 onDestroy : function(){
11923 this.view.setStore(null);
11924 this.view.el.removeAllListeners();
11925 this.view.el.remove();
11926 this.view.purgeListeners();
11929 this.list.dom.innerHTML = '';
11933 this.store.un('beforeload', this.onBeforeLoad, this);
11934 this.store.un('load', this.onLoad, this);
11935 this.store.un('loadexception', this.onLoadException, this);
11937 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11941 fireKey : function(e){
11942 if(e.isNavKeyPress() && !this.list.isVisible()){
11943 this.fireEvent("specialkey", this, e);
11948 onResize: function(w, h){
11949 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11951 // if(typeof w != 'number'){
11952 // // we do not handle it!?!?
11955 // var tw = this.trigger.getWidth();
11956 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11957 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11959 // this.inputEl().setWidth( this.adjustWidth('input', x));
11961 // //this.trigger.setStyle('left', x+'px');
11963 // if(this.list && this.listWidth === undefined){
11964 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11965 // this.list.setWidth(lw);
11966 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11974 * Allow or prevent the user from directly editing the field text. If false is passed,
11975 * the user will only be able to select from the items defined in the dropdown list. This method
11976 * is the runtime equivalent of setting the 'editable' config option at config time.
11977 * @param {Boolean} value True to allow the user to directly edit the field text
11979 setEditable : function(value){
11980 if(value == this.editable){
11983 this.editable = value;
11985 this.inputEl().dom.setAttribute('readOnly', true);
11986 this.inputEl().on('mousedown', this.onTriggerClick, this);
11987 this.inputEl().addClass('x-combo-noedit');
11989 this.inputEl().dom.setAttribute('readOnly', false);
11990 this.inputEl().un('mousedown', this.onTriggerClick, this);
11991 this.inputEl().removeClass('x-combo-noedit');
11997 onBeforeLoad : function(combo,opts){
11998 if(!this.hasFocus){
12002 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12004 this.restrictHeight();
12005 this.selectedIndex = -1;
12009 onLoad : function(){
12011 this.hasQuery = false;
12013 if(!this.hasFocus){
12017 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12018 this.loading.hide();
12021 if(this.store.getCount() > 0){
12023 this.restrictHeight();
12024 if(this.lastQuery == this.allQuery){
12025 if(this.editable && !this.tickable){
12026 this.inputEl().dom.select();
12030 !this.selectByValue(this.value, true) &&
12033 !this.store.lastOptions ||
12034 typeof(this.store.lastOptions.add) == 'undefined' ||
12035 this.store.lastOptions.add != true
12038 this.select(0, true);
12041 if(this.autoFocus){
12044 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12045 this.taTask.delay(this.typeAheadDelay);
12049 this.onEmptyResults();
12055 onLoadException : function()
12057 this.hasQuery = false;
12059 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12060 this.loading.hide();
12063 if(this.tickable && this.editable){
12069 Roo.log(this.store.reader.jsonData);
12070 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12072 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12078 onTypeAhead : function(){
12079 if(this.store.getCount() > 0){
12080 var r = this.store.getAt(0);
12081 var newValue = r.data[this.displayField];
12082 var len = newValue.length;
12083 var selStart = this.getRawValue().length;
12085 if(selStart != len){
12086 this.setRawValue(newValue);
12087 this.selectText(selStart, newValue.length);
12093 onSelect : function(record, index){
12095 if(this.fireEvent('beforeselect', this, record, index) !== false){
12097 this.setFromData(index > -1 ? record.data : false);
12100 this.fireEvent('select', this, record, index);
12105 * Returns the currently selected field value or empty string if no value is set.
12106 * @return {String} value The selected value
12108 getValue : function(){
12111 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12114 if(this.valueField){
12115 return typeof this.value != 'undefined' ? this.value : '';
12117 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12122 * Clears any text/value currently set in the field
12124 clearValue : function(){
12125 if(this.hiddenField){
12126 this.hiddenField.dom.value = '';
12129 this.setRawValue('');
12130 this.lastSelectionText = '';
12131 this.lastData = false;
12133 var close = this.closeTriggerEl();
12142 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12143 * will be displayed in the field. If the value does not match the data value of an existing item,
12144 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12145 * Otherwise the field will be blank (although the value will still be set).
12146 * @param {String} value The value to match
12148 setValue : function(v){
12155 if(this.valueField){
12156 var r = this.findRecord(this.valueField, v);
12158 text = r.data[this.displayField];
12159 }else if(this.valueNotFoundText !== undefined){
12160 text = this.valueNotFoundText;
12163 this.lastSelectionText = text;
12164 if(this.hiddenField){
12165 this.hiddenField.dom.value = v;
12167 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12170 var close = this.closeTriggerEl();
12173 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12177 * @property {Object} the last set data for the element
12182 * Sets the value of the field based on a object which is related to the record format for the store.
12183 * @param {Object} value the value to set as. or false on reset?
12185 setFromData : function(o){
12192 var dv = ''; // display value
12193 var vv = ''; // value value..
12195 if (this.displayField) {
12196 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12198 // this is an error condition!!!
12199 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12202 if(this.valueField){
12203 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12206 var close = this.closeTriggerEl();
12209 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12212 if(this.hiddenField){
12213 this.hiddenField.dom.value = vv;
12215 this.lastSelectionText = dv;
12216 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12220 // no hidden field.. - we store the value in 'value', but still display
12221 // display field!!!!
12222 this.lastSelectionText = dv;
12223 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12230 reset : function(){
12231 // overridden so that last data is reset..
12238 this.setValue(this.originalValue);
12239 this.clearInvalid();
12240 this.lastData = false;
12242 this.view.clearSelections();
12246 findRecord : function(prop, value){
12248 if(this.store.getCount() > 0){
12249 this.store.each(function(r){
12250 if(r.data[prop] == value){
12260 getName: function()
12262 // returns hidden if it's set..
12263 if (!this.rendered) {return ''};
12264 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12268 onViewMove : function(e, t){
12269 this.inKeyMode = false;
12273 onViewOver : function(e, t){
12274 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12277 var item = this.view.findItemFromChild(t);
12280 var index = this.view.indexOf(item);
12281 this.select(index, false);
12286 onViewClick : function(view, doFocus, el, e)
12288 var index = this.view.getSelectedIndexes()[0];
12290 var r = this.store.getAt(index);
12294 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12301 Roo.each(this.tickItems, function(v,k){
12303 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12304 _this.tickItems.splice(k, 1);
12306 if(typeof(e) == 'undefined' && view == false){
12307 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12319 this.tickItems.push(r.data);
12321 if(typeof(e) == 'undefined' && view == false){
12322 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12329 this.onSelect(r, index);
12331 if(doFocus !== false && !this.blockFocus){
12332 this.inputEl().focus();
12337 restrictHeight : function(){
12338 //this.innerList.dom.style.height = '';
12339 //var inner = this.innerList.dom;
12340 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12341 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12342 //this.list.beginUpdate();
12343 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12344 this.list.alignTo(this.inputEl(), this.listAlign);
12345 this.list.alignTo(this.inputEl(), this.listAlign);
12346 //this.list.endUpdate();
12350 onEmptyResults : function(){
12352 if(this.tickable && this.editable){
12353 this.restrictHeight();
12361 * Returns true if the dropdown list is expanded, else false.
12363 isExpanded : function(){
12364 return this.list.isVisible();
12368 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12369 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12370 * @param {String} value The data value of the item to select
12371 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12372 * selected item if it is not currently in view (defaults to true)
12373 * @return {Boolean} True if the value matched an item in the list, else false
12375 selectByValue : function(v, scrollIntoView){
12376 if(v !== undefined && v !== null){
12377 var r = this.findRecord(this.valueField || this.displayField, v);
12379 this.select(this.store.indexOf(r), scrollIntoView);
12387 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12388 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12389 * @param {Number} index The zero-based index of the list item to select
12390 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12391 * selected item if it is not currently in view (defaults to true)
12393 select : function(index, scrollIntoView){
12394 this.selectedIndex = index;
12395 this.view.select(index);
12396 if(scrollIntoView !== false){
12397 var el = this.view.getNode(index);
12399 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12402 this.list.scrollChildIntoView(el, false);
12408 selectNext : function(){
12409 var ct = this.store.getCount();
12411 if(this.selectedIndex == -1){
12413 }else if(this.selectedIndex < ct-1){
12414 this.select(this.selectedIndex+1);
12420 selectPrev : function(){
12421 var ct = this.store.getCount();
12423 if(this.selectedIndex == -1){
12425 }else if(this.selectedIndex != 0){
12426 this.select(this.selectedIndex-1);
12432 onKeyUp : function(e){
12433 if(this.editable !== false && !e.isSpecialKey()){
12434 this.lastKey = e.getKey();
12435 this.dqTask.delay(this.queryDelay);
12440 validateBlur : function(){
12441 return !this.list || !this.list.isVisible();
12445 initQuery : function(){
12447 var v = this.getRawValue();
12449 if(this.tickable && this.editable){
12450 v = this.tickableInputEl().getValue();
12457 doForce : function(){
12458 if(this.inputEl().dom.value.length > 0){
12459 this.inputEl().dom.value =
12460 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12466 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12467 * query allowing the query action to be canceled if needed.
12468 * @param {String} query The SQL query to execute
12469 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12470 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12471 * saved in the current store (defaults to false)
12473 doQuery : function(q, forceAll){
12475 if(q === undefined || q === null){
12480 forceAll: forceAll,
12484 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12489 forceAll = qe.forceAll;
12490 if(forceAll === true || (q.length >= this.minChars)){
12492 this.hasQuery = true;
12494 if(this.lastQuery != q || this.alwaysQuery){
12495 this.lastQuery = q;
12496 if(this.mode == 'local'){
12497 this.selectedIndex = -1;
12499 this.store.clearFilter();
12502 if(this.specialFilter){
12503 this.fireEvent('specialfilter', this);
12508 this.store.filter(this.displayField, q);
12511 this.store.fireEvent("datachanged", this.store);
12518 this.store.baseParams[this.queryParam] = q;
12520 var options = {params : this.getParams(q)};
12523 options.add = true;
12524 options.params.start = this.page * this.pageSize;
12527 this.store.load(options);
12530 * this code will make the page width larger, at the beginning, the list not align correctly,
12531 * we should expand the list on onLoad
12532 * so command out it
12537 this.selectedIndex = -1;
12542 this.loadNext = false;
12546 getParams : function(q){
12548 //p[this.queryParam] = q;
12552 p.limit = this.pageSize;
12558 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12560 collapse : function(){
12561 if(!this.isExpanded()){
12568 this.hasFocus = false;
12570 this.cancelBtn.hide();
12571 this.trigger.show();
12574 this.tickableInputEl().dom.value = '';
12575 this.tickableInputEl().blur();
12580 Roo.get(document).un('mousedown', this.collapseIf, this);
12581 Roo.get(document).un('mousewheel', this.collapseIf, this);
12582 if (!this.editable) {
12583 Roo.get(document).un('keydown', this.listKeyPress, this);
12585 this.fireEvent('collapse', this);
12589 collapseIf : function(e){
12590 var in_combo = e.within(this.el);
12591 var in_list = e.within(this.list);
12592 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12594 if (in_combo || in_list || is_list) {
12595 //e.stopPropagation();
12600 this.onTickableFooterButtonClick(e, false, false);
12608 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12610 expand : function(){
12612 if(this.isExpanded() || !this.hasFocus){
12616 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12617 this.list.setWidth(lw);
12624 this.restrictHeight();
12628 this.tickItems = Roo.apply([], this.item);
12631 this.cancelBtn.show();
12632 this.trigger.hide();
12635 this.tickableInputEl().focus();
12640 Roo.get(document).on('mousedown', this.collapseIf, this);
12641 Roo.get(document).on('mousewheel', this.collapseIf, this);
12642 if (!this.editable) {
12643 Roo.get(document).on('keydown', this.listKeyPress, this);
12646 this.fireEvent('expand', this);
12650 // Implements the default empty TriggerField.onTriggerClick function
12651 onTriggerClick : function(e)
12653 Roo.log('trigger click');
12655 if(this.disabled || !this.triggerList){
12660 this.loadNext = false;
12662 if(this.isExpanded()){
12664 if (!this.blockFocus) {
12665 this.inputEl().focus();
12669 this.hasFocus = true;
12670 if(this.triggerAction == 'all') {
12671 this.doQuery(this.allQuery, true);
12673 this.doQuery(this.getRawValue());
12675 if (!this.blockFocus) {
12676 this.inputEl().focus();
12681 onTickableTriggerClick : function(e)
12688 this.loadNext = false;
12689 this.hasFocus = true;
12691 if(this.triggerAction == 'all') {
12692 this.doQuery(this.allQuery, true);
12694 this.doQuery(this.getRawValue());
12698 onSearchFieldClick : function(e)
12700 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12701 this.onTickableFooterButtonClick(e, false, false);
12705 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12710 this.loadNext = false;
12711 this.hasFocus = true;
12713 if(this.triggerAction == 'all') {
12714 this.doQuery(this.allQuery, true);
12716 this.doQuery(this.getRawValue());
12720 listKeyPress : function(e)
12722 //Roo.log('listkeypress');
12723 // scroll to first matching element based on key pres..
12724 if (e.isSpecialKey()) {
12727 var k = String.fromCharCode(e.getKey()).toUpperCase();
12730 var csel = this.view.getSelectedNodes();
12731 var cselitem = false;
12733 var ix = this.view.indexOf(csel[0]);
12734 cselitem = this.store.getAt(ix);
12735 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12741 this.store.each(function(v) {
12743 // start at existing selection.
12744 if (cselitem.id == v.id) {
12750 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12751 match = this.store.indexOf(v);
12757 if (match === false) {
12758 return true; // no more action?
12761 this.view.select(match);
12762 var sn = Roo.get(this.view.getSelectedNodes()[0])
12763 sn.scrollIntoView(sn.dom.parentNode, false);
12766 onViewScroll : function(e, t){
12768 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){
12772 this.hasQuery = true;
12774 this.loading = this.list.select('.loading', true).first();
12776 if(this.loading === null){
12777 this.list.createChild({
12779 cls: 'loading select2-more-results select2-active',
12780 html: 'Loading more results...'
12783 this.loading = this.list.select('.loading', true).first();
12785 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12787 this.loading.hide();
12790 this.loading.show();
12795 this.loadNext = true;
12797 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12802 addItem : function(o)
12804 var dv = ''; // display value
12806 if (this.displayField) {
12807 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12809 // this is an error condition!!!
12810 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12817 var choice = this.choices.createChild({
12819 cls: 'select2-search-choice',
12828 cls: 'select2-search-choice-close',
12833 }, this.searchField);
12835 var close = choice.select('a.select2-search-choice-close', true).first()
12837 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12845 this.inputEl().dom.value = '';
12850 onRemoveItem : function(e, _self, o)
12852 e.preventDefault();
12854 this.lastItem = Roo.apply([], this.item);
12856 var index = this.item.indexOf(o.data) * 1;
12859 Roo.log('not this item?!');
12863 this.item.splice(index, 1);
12868 this.fireEvent('remove', this, e);
12874 syncValue : function()
12876 if(!this.item.length){
12883 Roo.each(this.item, function(i){
12884 if(_this.valueField){
12885 value.push(i[_this.valueField]);
12892 this.value = value.join(',');
12894 if(this.hiddenField){
12895 this.hiddenField.dom.value = this.value;
12898 this.store.fireEvent("datachanged", this.store);
12901 clearItem : function()
12903 if(!this.multiple){
12909 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12917 if(this.tickable && !Roo.isTouch){
12918 this.view.refresh();
12922 inputEl: function ()
12924 if(Roo.isTouch && this.mobileTouchView){
12925 return this.el.select('input.form-control',true).first();
12929 return this.searchField;
12932 return this.el.select('input.form-control',true).first();
12936 onTickableFooterButtonClick : function(e, btn, el)
12938 e.preventDefault();
12940 this.lastItem = Roo.apply([], this.item);
12942 if(btn && btn.name == 'cancel'){
12943 this.tickItems = Roo.apply([], this.item);
12952 Roo.each(this.tickItems, function(o){
12960 validate : function()
12962 var v = this.getRawValue();
12965 v = this.getValue();
12968 if(this.disabled || this.allowBlank || v.length){
12973 this.markInvalid();
12977 tickableInputEl : function()
12979 if(!this.tickable || !this.editable){
12980 return this.inputEl();
12983 return this.inputEl().select('.select2-search-field-input', true).first();
12987 getAutoCreateTouchView : function()
12992 cls: 'form-group' //input-group
12998 type : this.inputType,
12999 cls : 'form-control x-combo-noedit',
13000 autocomplete: 'new-password',
13001 placeholder : this.placeholder || '',
13006 input.name = this.name;
13010 input.cls += ' input-' + this.size;
13013 if (this.disabled) {
13014 input.disabled = true;
13025 inputblock.cls += ' input-group';
13027 inputblock.cn.unshift({
13029 cls : 'input-group-addon',
13034 if(this.removable && !this.multiple){
13035 inputblock.cls += ' roo-removable';
13037 inputblock.cn.push({
13040 cls : 'roo-combo-removable-btn close'
13044 if(this.hasFeedback && !this.allowBlank){
13046 inputblock.cls += ' has-feedback';
13048 inputblock.cn.push({
13050 cls: 'glyphicon form-control-feedback'
13057 inputblock.cls += (this.before) ? '' : ' input-group';
13059 inputblock.cn.push({
13061 cls : 'input-group-addon',
13072 cls: 'form-hidden-field'
13086 cls: 'form-hidden-field'
13090 cls: 'select2-choices',
13094 cls: 'select2-search-field',
13107 cls: 'select2-container input-group',
13114 combobox.cls += ' select2-container-multi';
13117 var align = this.labelAlign || this.parentLabelAlign();
13121 if(this.fieldLabel.length){
13123 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13124 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13129 cls : 'control-label ' + lw,
13130 html : this.fieldLabel
13142 var settings = this;
13144 ['xs','sm','md','lg'].map(function(size){
13145 if (settings[size]) {
13146 cfg.cls += ' col-' + size + '-' + settings[size];
13153 initTouchView : function()
13155 this.renderTouchView();
13157 this.touchViewEl.on('scroll', function(){
13158 this.el.dom.scrollTop = 0;
13161 this.inputEl().on("click", this.showTouchView, this);
13162 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13163 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13165 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13167 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13168 this.store.on('load', this.onTouchViewLoad, this);
13169 this.store.on('loadexception', this.onTouchViewLoadException, this);
13171 if(this.hiddenName){
13173 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13175 this.hiddenField.dom.value =
13176 this.hiddenValue !== undefined ? this.hiddenValue :
13177 this.value !== undefined ? this.value : '';
13179 this.el.dom.removeAttribute('name');
13180 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13184 this.choices = this.el.select('ul.select2-choices', true).first();
13185 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13188 if(this.removable && !this.multiple){
13189 var close = this.closeTriggerEl();
13191 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13192 close.on('click', this.removeBtnClick, this, close);
13201 renderTouchView : function()
13203 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13204 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13206 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13207 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13209 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13210 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13211 this.touchViewBodyEl.setStyle('overflow', 'auto');
13213 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13214 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13216 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13217 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13221 showTouchView : function()
13223 this.touchViewHeaderEl.hide();
13225 if(this.fieldLabel.length){
13226 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13227 this.touchViewHeaderEl.show();
13230 this.touchViewEl.show();
13232 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13233 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13235 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13237 if(this.fieldLabel.length){
13238 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13241 this.touchViewBodyEl.setHeight(bodyHeight);
13245 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13247 this.touchViewEl.addClass('in');
13250 this.doTouchViewQuery();
13254 hideTouchView : function()
13256 this.touchViewEl.removeClass('in');
13260 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13262 this.touchViewEl.setStyle('display', 'none');
13267 setTouchViewValue : function()
13274 Roo.each(this.tickItems, function(o){
13279 this.hideTouchView();
13282 doTouchViewQuery : function()
13291 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13295 if(!this.alwaysQuery || this.mode == 'local'){
13296 this.onTouchViewLoad();
13303 onTouchViewBeforeLoad : function(combo,opts)
13309 onTouchViewLoad : function()
13311 if(this.store.getCount() < 1){
13312 this.onTouchViewEmptyResults();
13316 this.clearTouchView();
13318 var rawValue = this.getRawValue();
13320 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13322 this.tickItems = [];
13324 this.store.data.each(function(d, rowIndex){
13325 var row = this.touchViewListGroup.createChild(template);
13327 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13328 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13331 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13332 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13335 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13336 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13337 this.tickItems.push(d.data);
13340 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13344 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13346 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13348 if(this.fieldLabel.length){
13349 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13352 var listHeight = this.touchViewListGroup.getHeight();
13356 if(firstChecked && listHeight > bodyHeight){
13357 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13362 onTouchViewLoadException : function()
13364 this.hideTouchView();
13367 onTouchViewEmptyResults : function()
13369 this.clearTouchView();
13371 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13373 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13377 clearTouchView : function()
13379 this.touchViewListGroup.dom.innerHTML = '';
13382 onTouchViewClick : function(e, el, o)
13384 e.preventDefault();
13387 var rowIndex = o.rowIndex;
13389 var r = this.store.getAt(rowIndex);
13391 if(!this.multiple){
13392 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13393 c.dom.removeAttribute('checked');
13396 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13398 this.setFromData(r.data);
13400 var close = this.closeTriggerEl();
13406 this.hideTouchView();
13408 this.fireEvent('select', this, r, rowIndex);
13413 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13414 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13415 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13419 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13420 this.addItem(r.data);
13421 this.tickItems.push(r.data);
13427 * @cfg {Boolean} grow
13431 * @cfg {Number} growMin
13435 * @cfg {Number} growMax
13444 Roo.apply(Roo.bootstrap.ComboBox, {
13448 cls: 'modal-header',
13470 cls: 'list-group-item',
13474 cls: 'roo-combobox-list-group-item-value'
13478 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13492 listItemCheckbox : {
13494 cls: 'list-group-item',
13498 cls: 'roo-combobox-list-group-item-value'
13502 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13518 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13523 cls: 'modal-footer',
13531 cls: 'col-xs-6 text-left',
13534 cls: 'btn btn-danger roo-touch-view-cancel',
13540 cls: 'col-xs-6 text-right',
13543 cls: 'btn btn-success roo-touch-view-ok',
13554 Roo.apply(Roo.bootstrap.ComboBox, {
13556 touchViewTemplate : {
13558 cls: 'modal fade roo-combobox-touch-view',
13562 cls: 'modal-dialog',
13566 cls: 'modal-content',
13568 Roo.bootstrap.ComboBox.header,
13569 Roo.bootstrap.ComboBox.body,
13570 Roo.bootstrap.ComboBox.footer
13579 * Ext JS Library 1.1.1
13580 * Copyright(c) 2006-2007, Ext JS, LLC.
13582 * Originally Released Under LGPL - original licence link has changed is not relivant.
13585 * <script type="text/javascript">
13590 * @extends Roo.util.Observable
13591 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13592 * This class also supports single and multi selection modes. <br>
13593 * Create a data model bound view:
13595 var store = new Roo.data.Store(...);
13597 var view = new Roo.View({
13599 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13601 singleSelect: true,
13602 selectedClass: "ydataview-selected",
13606 // listen for node click?
13607 view.on("click", function(vw, index, node, e){
13608 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13612 dataModel.load("foobar.xml");
13614 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13616 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13617 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13619 * Note: old style constructor is still suported (container, template, config)
13622 * Create a new View
13623 * @param {Object} config The config object
13626 Roo.View = function(config, depreciated_tpl, depreciated_config){
13628 this.parent = false;
13630 if (typeof(depreciated_tpl) == 'undefined') {
13631 // new way.. - universal constructor.
13632 Roo.apply(this, config);
13633 this.el = Roo.get(this.el);
13636 this.el = Roo.get(config);
13637 this.tpl = depreciated_tpl;
13638 Roo.apply(this, depreciated_config);
13640 this.wrapEl = this.el.wrap().wrap();
13641 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13644 if(typeof(this.tpl) == "string"){
13645 this.tpl = new Roo.Template(this.tpl);
13647 // support xtype ctors..
13648 this.tpl = new Roo.factory(this.tpl, Roo);
13652 this.tpl.compile();
13657 * @event beforeclick
13658 * Fires before a click is processed. Returns false to cancel the default action.
13659 * @param {Roo.View} this
13660 * @param {Number} index The index of the target node
13661 * @param {HTMLElement} node The target node
13662 * @param {Roo.EventObject} e The raw event object
13664 "beforeclick" : true,
13667 * Fires when a template node is clicked.
13668 * @param {Roo.View} this
13669 * @param {Number} index The index of the target node
13670 * @param {HTMLElement} node The target node
13671 * @param {Roo.EventObject} e The raw event object
13676 * Fires when a template node is double clicked.
13677 * @param {Roo.View} this
13678 * @param {Number} index The index of the target node
13679 * @param {HTMLElement} node The target node
13680 * @param {Roo.EventObject} e The raw event object
13684 * @event contextmenu
13685 * Fires when a template node is right clicked.
13686 * @param {Roo.View} this
13687 * @param {Number} index The index of the target node
13688 * @param {HTMLElement} node The target node
13689 * @param {Roo.EventObject} e The raw event object
13691 "contextmenu" : true,
13693 * @event selectionchange
13694 * Fires when the selected nodes change.
13695 * @param {Roo.View} this
13696 * @param {Array} selections Array of the selected nodes
13698 "selectionchange" : true,
13701 * @event beforeselect
13702 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13703 * @param {Roo.View} this
13704 * @param {HTMLElement} node The node to be selected
13705 * @param {Array} selections Array of currently selected nodes
13707 "beforeselect" : true,
13709 * @event preparedata
13710 * Fires on every row to render, to allow you to change the data.
13711 * @param {Roo.View} this
13712 * @param {Object} data to be rendered (change this)
13714 "preparedata" : true
13722 "click": this.onClick,
13723 "dblclick": this.onDblClick,
13724 "contextmenu": this.onContextMenu,
13728 this.selections = [];
13730 this.cmp = new Roo.CompositeElementLite([]);
13732 this.store = Roo.factory(this.store, Roo.data);
13733 this.setStore(this.store, true);
13736 if ( this.footer && this.footer.xtype) {
13738 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13740 this.footer.dataSource = this.store
13741 this.footer.container = fctr;
13742 this.footer = Roo.factory(this.footer, Roo);
13743 fctr.insertFirst(this.el);
13745 // this is a bit insane - as the paging toolbar seems to detach the el..
13746 // dom.parentNode.parentNode.parentNode
13747 // they get detached?
13751 Roo.View.superclass.constructor.call(this);
13756 Roo.extend(Roo.View, Roo.util.Observable, {
13759 * @cfg {Roo.data.Store} store Data store to load data from.
13764 * @cfg {String|Roo.Element} el The container element.
13769 * @cfg {String|Roo.Template} tpl The template used by this View
13773 * @cfg {String} dataName the named area of the template to use as the data area
13774 * Works with domtemplates roo-name="name"
13778 * @cfg {String} selectedClass The css class to add to selected nodes
13780 selectedClass : "x-view-selected",
13782 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13787 * @cfg {String} text to display on mask (default Loading)
13791 * @cfg {Boolean} multiSelect Allow multiple selection
13793 multiSelect : false,
13795 * @cfg {Boolean} singleSelect Allow single selection
13797 singleSelect: false,
13800 * @cfg {Boolean} toggleSelect - selecting
13802 toggleSelect : false,
13805 * @cfg {Boolean} tickable - selecting
13810 * Returns the element this view is bound to.
13811 * @return {Roo.Element}
13813 getEl : function(){
13814 return this.wrapEl;
13820 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13822 refresh : function(){
13823 //Roo.log('refresh');
13826 // if we are using something like 'domtemplate', then
13827 // the what gets used is:
13828 // t.applySubtemplate(NAME, data, wrapping data..)
13829 // the outer template then get' applied with
13830 // the store 'extra data'
13831 // and the body get's added to the
13832 // roo-name="data" node?
13833 // <span class='roo-tpl-{name}'></span> ?????
13837 this.clearSelections();
13838 this.el.update("");
13840 var records = this.store.getRange();
13841 if(records.length < 1) {
13843 // is this valid?? = should it render a template??
13845 this.el.update(this.emptyText);
13849 if (this.dataName) {
13850 this.el.update(t.apply(this.store.meta)); //????
13851 el = this.el.child('.roo-tpl-' + this.dataName);
13854 for(var i = 0, len = records.length; i < len; i++){
13855 var data = this.prepareData(records[i].data, i, records[i]);
13856 this.fireEvent("preparedata", this, data, i, records[i]);
13858 var d = Roo.apply({}, data);
13861 Roo.apply(d, {'roo-id' : Roo.id()});
13865 Roo.each(this.parent.item, function(item){
13866 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13869 Roo.apply(d, {'roo-data-checked' : 'checked'});
13873 html[html.length] = Roo.util.Format.trim(
13875 t.applySubtemplate(this.dataName, d, this.store.meta) :
13882 el.update(html.join(""));
13883 this.nodes = el.dom.childNodes;
13884 this.updateIndexes(0);
13889 * Function to override to reformat the data that is sent to
13890 * the template for each node.
13891 * DEPRICATED - use the preparedata event handler.
13892 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13893 * a JSON object for an UpdateManager bound view).
13895 prepareData : function(data, index, record)
13897 this.fireEvent("preparedata", this, data, index, record);
13901 onUpdate : function(ds, record){
13902 // Roo.log('on update');
13903 this.clearSelections();
13904 var index = this.store.indexOf(record);
13905 var n = this.nodes[index];
13906 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13907 n.parentNode.removeChild(n);
13908 this.updateIndexes(index, index);
13914 onAdd : function(ds, records, index)
13916 //Roo.log(['on Add', ds, records, index] );
13917 this.clearSelections();
13918 if(this.nodes.length == 0){
13922 var n = this.nodes[index];
13923 for(var i = 0, len = records.length; i < len; i++){
13924 var d = this.prepareData(records[i].data, i, records[i]);
13926 this.tpl.insertBefore(n, d);
13929 this.tpl.append(this.el, d);
13932 this.updateIndexes(index);
13935 onRemove : function(ds, record, index){
13936 // Roo.log('onRemove');
13937 this.clearSelections();
13938 var el = this.dataName ?
13939 this.el.child('.roo-tpl-' + this.dataName) :
13942 el.dom.removeChild(this.nodes[index]);
13943 this.updateIndexes(index);
13947 * Refresh an individual node.
13948 * @param {Number} index
13950 refreshNode : function(index){
13951 this.onUpdate(this.store, this.store.getAt(index));
13954 updateIndexes : function(startIndex, endIndex){
13955 var ns = this.nodes;
13956 startIndex = startIndex || 0;
13957 endIndex = endIndex || ns.length - 1;
13958 for(var i = startIndex; i <= endIndex; i++){
13959 ns[i].nodeIndex = i;
13964 * Changes the data store this view uses and refresh the view.
13965 * @param {Store} store
13967 setStore : function(store, initial){
13968 if(!initial && this.store){
13969 this.store.un("datachanged", this.refresh);
13970 this.store.un("add", this.onAdd);
13971 this.store.un("remove", this.onRemove);
13972 this.store.un("update", this.onUpdate);
13973 this.store.un("clear", this.refresh);
13974 this.store.un("beforeload", this.onBeforeLoad);
13975 this.store.un("load", this.onLoad);
13976 this.store.un("loadexception", this.onLoad);
13980 store.on("datachanged", this.refresh, this);
13981 store.on("add", this.onAdd, this);
13982 store.on("remove", this.onRemove, this);
13983 store.on("update", this.onUpdate, this);
13984 store.on("clear", this.refresh, this);
13985 store.on("beforeload", this.onBeforeLoad, this);
13986 store.on("load", this.onLoad, this);
13987 store.on("loadexception", this.onLoad, this);
13995 * onbeforeLoad - masks the loading area.
13998 onBeforeLoad : function(store,opts)
14000 //Roo.log('onBeforeLoad');
14002 this.el.update("");
14004 this.el.mask(this.mask ? this.mask : "Loading" );
14006 onLoad : function ()
14013 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14014 * @param {HTMLElement} node
14015 * @return {HTMLElement} The template node
14017 findItemFromChild : function(node){
14018 var el = this.dataName ?
14019 this.el.child('.roo-tpl-' + this.dataName,true) :
14022 if(!node || node.parentNode == el){
14025 var p = node.parentNode;
14026 while(p && p != el){
14027 if(p.parentNode == el){
14036 onClick : function(e){
14037 var item = this.findItemFromChild(e.getTarget());
14039 var index = this.indexOf(item);
14040 if(this.onItemClick(item, index, e) !== false){
14041 this.fireEvent("click", this, index, item, e);
14044 this.clearSelections();
14049 onContextMenu : function(e){
14050 var item = this.findItemFromChild(e.getTarget());
14052 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14057 onDblClick : function(e){
14058 var item = this.findItemFromChild(e.getTarget());
14060 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14064 onItemClick : function(item, index, e)
14066 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14069 if (this.toggleSelect) {
14070 var m = this.isSelected(item) ? 'unselect' : 'select';
14073 _t[m](item, true, false);
14076 if(this.multiSelect || this.singleSelect){
14077 if(this.multiSelect && e.shiftKey && this.lastSelection){
14078 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14080 this.select(item, this.multiSelect && e.ctrlKey);
14081 this.lastSelection = item;
14084 if(!this.tickable){
14085 e.preventDefault();
14093 * Get the number of selected nodes.
14096 getSelectionCount : function(){
14097 return this.selections.length;
14101 * Get the currently selected nodes.
14102 * @return {Array} An array of HTMLElements
14104 getSelectedNodes : function(){
14105 return this.selections;
14109 * Get the indexes of the selected nodes.
14112 getSelectedIndexes : function(){
14113 var indexes = [], s = this.selections;
14114 for(var i = 0, len = s.length; i < len; i++){
14115 indexes.push(s[i].nodeIndex);
14121 * Clear all selections
14122 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14124 clearSelections : function(suppressEvent){
14125 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14126 this.cmp.elements = this.selections;
14127 this.cmp.removeClass(this.selectedClass);
14128 this.selections = [];
14129 if(!suppressEvent){
14130 this.fireEvent("selectionchange", this, this.selections);
14136 * Returns true if the passed node is selected
14137 * @param {HTMLElement/Number} node The node or node index
14138 * @return {Boolean}
14140 isSelected : function(node){
14141 var s = this.selections;
14145 node = this.getNode(node);
14146 return s.indexOf(node) !== -1;
14151 * @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
14152 * @param {Boolean} keepExisting (optional) true to keep existing selections
14153 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14155 select : function(nodeInfo, keepExisting, suppressEvent){
14156 if(nodeInfo instanceof Array){
14158 this.clearSelections(true);
14160 for(var i = 0, len = nodeInfo.length; i < len; i++){
14161 this.select(nodeInfo[i], true, true);
14165 var node = this.getNode(nodeInfo);
14166 if(!node || this.isSelected(node)){
14167 return; // already selected.
14170 this.clearSelections(true);
14173 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14174 Roo.fly(node).addClass(this.selectedClass);
14175 this.selections.push(node);
14176 if(!suppressEvent){
14177 this.fireEvent("selectionchange", this, this.selections);
14185 * @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
14186 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14187 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14189 unselect : function(nodeInfo, keepExisting, suppressEvent)
14191 if(nodeInfo instanceof Array){
14192 Roo.each(this.selections, function(s) {
14193 this.unselect(s, nodeInfo);
14197 var node = this.getNode(nodeInfo);
14198 if(!node || !this.isSelected(node)){
14199 //Roo.log("not selected");
14200 return; // not selected.
14204 Roo.each(this.selections, function(s) {
14206 Roo.fly(node).removeClass(this.selectedClass);
14213 this.selections= ns;
14214 this.fireEvent("selectionchange", this, this.selections);
14218 * Gets a template node.
14219 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14220 * @return {HTMLElement} The node or null if it wasn't found
14222 getNode : function(nodeInfo){
14223 if(typeof nodeInfo == "string"){
14224 return document.getElementById(nodeInfo);
14225 }else if(typeof nodeInfo == "number"){
14226 return this.nodes[nodeInfo];
14232 * Gets a range template nodes.
14233 * @param {Number} startIndex
14234 * @param {Number} endIndex
14235 * @return {Array} An array of nodes
14237 getNodes : function(start, end){
14238 var ns = this.nodes;
14239 start = start || 0;
14240 end = typeof end == "undefined" ? ns.length - 1 : end;
14243 for(var i = start; i <= end; i++){
14247 for(var i = start; i >= end; i--){
14255 * Finds the index of the passed node
14256 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14257 * @return {Number} The index of the node or -1
14259 indexOf : function(node){
14260 node = this.getNode(node);
14261 if(typeof node.nodeIndex == "number"){
14262 return node.nodeIndex;
14264 var ns = this.nodes;
14265 for(var i = 0, len = ns.length; i < len; i++){
14276 * based on jquery fullcalendar
14280 Roo.bootstrap = Roo.bootstrap || {};
14282 * @class Roo.bootstrap.Calendar
14283 * @extends Roo.bootstrap.Component
14284 * Bootstrap Calendar class
14285 * @cfg {Boolean} loadMask (true|false) default false
14286 * @cfg {Object} header generate the user specific header of the calendar, default false
14289 * Create a new Container
14290 * @param {Object} config The config object
14295 Roo.bootstrap.Calendar = function(config){
14296 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14300 * Fires when a date is selected
14301 * @param {DatePicker} this
14302 * @param {Date} date The selected date
14306 * @event monthchange
14307 * Fires when the displayed month changes
14308 * @param {DatePicker} this
14309 * @param {Date} date The selected month
14311 'monthchange': true,
14313 * @event evententer
14314 * Fires when mouse over an event
14315 * @param {Calendar} this
14316 * @param {event} Event
14318 'evententer': true,
14320 * @event eventleave
14321 * Fires when the mouse leaves an
14322 * @param {Calendar} this
14325 'eventleave': true,
14327 * @event eventclick
14328 * Fires when the mouse click an
14329 * @param {Calendar} this
14338 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14341 * @cfg {Number} startDay
14342 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14350 getAutoCreate : function(){
14353 var fc_button = function(name, corner, style, content ) {
14354 return Roo.apply({},{
14356 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14358 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14361 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14372 style : 'width:100%',
14379 cls : 'fc-header-left',
14381 fc_button('prev', 'left', 'arrow', '‹' ),
14382 fc_button('next', 'right', 'arrow', '›' ),
14383 { tag: 'span', cls: 'fc-header-space' },
14384 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14392 cls : 'fc-header-center',
14396 cls: 'fc-header-title',
14399 html : 'month / year'
14407 cls : 'fc-header-right',
14409 /* fc_button('month', 'left', '', 'month' ),
14410 fc_button('week', '', '', 'week' ),
14411 fc_button('day', 'right', '', 'day' )
14423 header = this.header;
14426 var cal_heads = function() {
14428 // fixme - handle this.
14430 for (var i =0; i < Date.dayNames.length; i++) {
14431 var d = Date.dayNames[i];
14434 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14435 html : d.substring(0,3)
14439 ret[0].cls += ' fc-first';
14440 ret[6].cls += ' fc-last';
14443 var cal_cell = function(n) {
14446 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14451 cls: 'fc-day-number',
14455 cls: 'fc-day-content',
14459 style: 'position: relative;' // height: 17px;
14471 var cal_rows = function() {
14474 for (var r = 0; r < 6; r++) {
14481 for (var i =0; i < Date.dayNames.length; i++) {
14482 var d = Date.dayNames[i];
14483 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14486 row.cn[0].cls+=' fc-first';
14487 row.cn[0].cn[0].style = 'min-height:90px';
14488 row.cn[6].cls+=' fc-last';
14492 ret[0].cls += ' fc-first';
14493 ret[4].cls += ' fc-prev-last';
14494 ret[5].cls += ' fc-last';
14501 cls: 'fc-border-separate',
14502 style : 'width:100%',
14510 cls : 'fc-first fc-last',
14528 cls : 'fc-content',
14529 style : "position: relative;",
14532 cls : 'fc-view fc-view-month fc-grid',
14533 style : 'position: relative',
14534 unselectable : 'on',
14537 cls : 'fc-event-container',
14538 style : 'position:absolute;z-index:8;top:0;left:0;'
14556 initEvents : function()
14559 throw "can not find store for calendar";
14565 style: "text-align:center",
14569 style: "background-color:white;width:50%;margin:250 auto",
14573 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14584 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14586 var size = this.el.select('.fc-content', true).first().getSize();
14587 this.maskEl.setSize(size.width, size.height);
14588 this.maskEl.enableDisplayMode("block");
14589 if(!this.loadMask){
14590 this.maskEl.hide();
14593 this.store = Roo.factory(this.store, Roo.data);
14594 this.store.on('load', this.onLoad, this);
14595 this.store.on('beforeload', this.onBeforeLoad, this);
14599 this.cells = this.el.select('.fc-day',true);
14600 //Roo.log(this.cells);
14601 this.textNodes = this.el.query('.fc-day-number');
14602 this.cells.addClassOnOver('fc-state-hover');
14604 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14605 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14606 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14607 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14609 this.on('monthchange', this.onMonthChange, this);
14611 this.update(new Date().clearTime());
14614 resize : function() {
14615 var sz = this.el.getSize();
14617 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14618 this.el.select('.fc-day-content div',true).setHeight(34);
14623 showPrevMonth : function(e){
14624 this.update(this.activeDate.add("mo", -1));
14626 showToday : function(e){
14627 this.update(new Date().clearTime());
14630 showNextMonth : function(e){
14631 this.update(this.activeDate.add("mo", 1));
14635 showPrevYear : function(){
14636 this.update(this.activeDate.add("y", -1));
14640 showNextYear : function(){
14641 this.update(this.activeDate.add("y", 1));
14646 update : function(date)
14648 var vd = this.activeDate;
14649 this.activeDate = date;
14650 // if(vd && this.el){
14651 // var t = date.getTime();
14652 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14653 // Roo.log('using add remove');
14655 // this.fireEvent('monthchange', this, date);
14657 // this.cells.removeClass("fc-state-highlight");
14658 // this.cells.each(function(c){
14659 // if(c.dateValue == t){
14660 // c.addClass("fc-state-highlight");
14661 // setTimeout(function(){
14662 // try{c.dom.firstChild.focus();}catch(e){}
14672 var days = date.getDaysInMonth();
14674 var firstOfMonth = date.getFirstDateOfMonth();
14675 var startingPos = firstOfMonth.getDay()-this.startDay;
14677 if(startingPos < this.startDay){
14681 var pm = date.add(Date.MONTH, -1);
14682 var prevStart = pm.getDaysInMonth()-startingPos;
14684 this.cells = this.el.select('.fc-day',true);
14685 this.textNodes = this.el.query('.fc-day-number');
14686 this.cells.addClassOnOver('fc-state-hover');
14688 var cells = this.cells.elements;
14689 var textEls = this.textNodes;
14691 Roo.each(cells, function(cell){
14692 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14695 days += startingPos;
14697 // convert everything to numbers so it's fast
14698 var day = 86400000;
14699 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14702 //Roo.log(prevStart);
14704 var today = new Date().clearTime().getTime();
14705 var sel = date.clearTime().getTime();
14706 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14707 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14708 var ddMatch = this.disabledDatesRE;
14709 var ddText = this.disabledDatesText;
14710 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14711 var ddaysText = this.disabledDaysText;
14712 var format = this.format;
14714 var setCellClass = function(cal, cell){
14718 //Roo.log('set Cell Class');
14720 var t = d.getTime();
14724 cell.dateValue = t;
14726 cell.className += " fc-today";
14727 cell.className += " fc-state-highlight";
14728 cell.title = cal.todayText;
14731 // disable highlight in other month..
14732 //cell.className += " fc-state-highlight";
14737 cell.className = " fc-state-disabled";
14738 cell.title = cal.minText;
14742 cell.className = " fc-state-disabled";
14743 cell.title = cal.maxText;
14747 if(ddays.indexOf(d.getDay()) != -1){
14748 cell.title = ddaysText;
14749 cell.className = " fc-state-disabled";
14752 if(ddMatch && format){
14753 var fvalue = d.dateFormat(format);
14754 if(ddMatch.test(fvalue)){
14755 cell.title = ddText.replace("%0", fvalue);
14756 cell.className = " fc-state-disabled";
14760 if (!cell.initialClassName) {
14761 cell.initialClassName = cell.dom.className;
14764 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14769 for(; i < startingPos; i++) {
14770 textEls[i].innerHTML = (++prevStart);
14771 d.setDate(d.getDate()+1);
14773 cells[i].className = "fc-past fc-other-month";
14774 setCellClass(this, cells[i]);
14779 for(; i < days; i++){
14780 intDay = i - startingPos + 1;
14781 textEls[i].innerHTML = (intDay);
14782 d.setDate(d.getDate()+1);
14784 cells[i].className = ''; // "x-date-active";
14785 setCellClass(this, cells[i]);
14789 for(; i < 42; i++) {
14790 textEls[i].innerHTML = (++extraDays);
14791 d.setDate(d.getDate()+1);
14793 cells[i].className = "fc-future fc-other-month";
14794 setCellClass(this, cells[i]);
14797 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14799 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14801 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14802 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14804 if(totalRows != 6){
14805 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14806 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14809 this.fireEvent('monthchange', this, date);
14813 if(!this.internalRender){
14814 var main = this.el.dom.firstChild;
14815 var w = main.offsetWidth;
14816 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14817 Roo.fly(main).setWidth(w);
14818 this.internalRender = true;
14819 // opera does not respect the auto grow header center column
14820 // then, after it gets a width opera refuses to recalculate
14821 // without a second pass
14822 if(Roo.isOpera && !this.secondPass){
14823 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14824 this.secondPass = true;
14825 this.update.defer(10, this, [date]);
14832 findCell : function(dt) {
14833 dt = dt.clearTime().getTime();
14835 this.cells.each(function(c){
14836 //Roo.log("check " +c.dateValue + '?=' + dt);
14837 if(c.dateValue == dt){
14847 findCells : function(ev) {
14848 var s = ev.start.clone().clearTime().getTime();
14850 var e= ev.end.clone().clearTime().getTime();
14853 this.cells.each(function(c){
14854 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14856 if(c.dateValue > e){
14859 if(c.dateValue < s){
14868 // findBestRow: function(cells)
14872 // for (var i =0 ; i < cells.length;i++) {
14873 // ret = Math.max(cells[i].rows || 0,ret);
14880 addItem : function(ev)
14882 // look for vertical location slot in
14883 var cells = this.findCells(ev);
14885 // ev.row = this.findBestRow(cells);
14887 // work out the location.
14891 for(var i =0; i < cells.length; i++) {
14893 cells[i].row = cells[0].row;
14896 cells[i].row = cells[i].row + 1;
14906 if (crow.start.getY() == cells[i].getY()) {
14908 crow.end = cells[i];
14925 cells[0].events.push(ev);
14927 this.calevents.push(ev);
14930 clearEvents: function() {
14932 if(!this.calevents){
14936 Roo.each(this.cells.elements, function(c){
14942 Roo.each(this.calevents, function(e) {
14943 Roo.each(e.els, function(el) {
14944 el.un('mouseenter' ,this.onEventEnter, this);
14945 el.un('mouseleave' ,this.onEventLeave, this);
14950 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14956 renderEvents: function()
14960 this.cells.each(function(c) {
14969 if(c.row != c.events.length){
14970 r = 4 - (4 - (c.row - c.events.length));
14973 c.events = ev.slice(0, r);
14974 c.more = ev.slice(r);
14976 if(c.more.length && c.more.length == 1){
14977 c.events.push(c.more.pop());
14980 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14984 this.cells.each(function(c) {
14986 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14989 for (var e = 0; e < c.events.length; e++){
14990 var ev = c.events[e];
14991 var rows = ev.rows;
14993 for(var i = 0; i < rows.length; i++) {
14995 // how many rows should it span..
14998 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14999 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15001 unselectable : "on",
15004 cls: 'fc-event-inner',
15008 // cls: 'fc-event-time',
15009 // html : cells.length > 1 ? '' : ev.time
15013 cls: 'fc-event-title',
15014 html : String.format('{0}', ev.title)
15021 cls: 'ui-resizable-handle ui-resizable-e',
15022 html : '  '
15029 cfg.cls += ' fc-event-start';
15031 if ((i+1) == rows.length) {
15032 cfg.cls += ' fc-event-end';
15035 var ctr = _this.el.select('.fc-event-container',true).first();
15036 var cg = ctr.createChild(cfg);
15038 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15039 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15041 var r = (c.more.length) ? 1 : 0;
15042 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15043 cg.setWidth(ebox.right - sbox.x -2);
15045 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15046 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15047 cg.on('click', _this.onEventClick, _this, ev);
15058 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15059 style : 'position: absolute',
15060 unselectable : "on",
15063 cls: 'fc-event-inner',
15067 cls: 'fc-event-title',
15075 cls: 'ui-resizable-handle ui-resizable-e',
15076 html : '  '
15082 var ctr = _this.el.select('.fc-event-container',true).first();
15083 var cg = ctr.createChild(cfg);
15085 var sbox = c.select('.fc-day-content',true).first().getBox();
15086 var ebox = c.select('.fc-day-content',true).first().getBox();
15088 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15089 cg.setWidth(ebox.right - sbox.x -2);
15091 cg.on('click', _this.onMoreEventClick, _this, c.more);
15101 onEventEnter: function (e, el,event,d) {
15102 this.fireEvent('evententer', this, el, event);
15105 onEventLeave: function (e, el,event,d) {
15106 this.fireEvent('eventleave', this, el, event);
15109 onEventClick: function (e, el,event,d) {
15110 this.fireEvent('eventclick', this, el, event);
15113 onMonthChange: function () {
15117 onMoreEventClick: function(e, el, more)
15121 this.calpopover.placement = 'right';
15122 this.calpopover.setTitle('More');
15124 this.calpopover.setContent('');
15126 var ctr = this.calpopover.el.select('.popover-content', true).first();
15128 Roo.each(more, function(m){
15130 cls : 'fc-event-hori fc-event-draggable',
15133 var cg = ctr.createChild(cfg);
15135 cg.on('click', _this.onEventClick, _this, m);
15138 this.calpopover.show(el);
15143 onLoad: function ()
15145 this.calevents = [];
15148 if(this.store.getCount() > 0){
15149 this.store.data.each(function(d){
15152 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15153 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15154 time : d.data.start_time,
15155 title : d.data.title,
15156 description : d.data.description,
15157 venue : d.data.venue
15162 this.renderEvents();
15164 if(this.calevents.length && this.loadMask){
15165 this.maskEl.hide();
15169 onBeforeLoad: function()
15171 this.clearEvents();
15173 this.maskEl.show();
15187 * @class Roo.bootstrap.Popover
15188 * @extends Roo.bootstrap.Component
15189 * Bootstrap Popover class
15190 * @cfg {String} html contents of the popover (or false to use children..)
15191 * @cfg {String} title of popover (or false to hide)
15192 * @cfg {String} placement how it is placed
15193 * @cfg {String} trigger click || hover (or false to trigger manually)
15194 * @cfg {String} over what (parent or false to trigger manually.)
15195 * @cfg {Number} delay - delay before showing
15198 * Create a new Popover
15199 * @param {Object} config The config object
15202 Roo.bootstrap.Popover = function(config){
15203 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15206 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15208 title: 'Fill in a title',
15211 placement : 'right',
15212 trigger : 'hover', // hover
15218 can_build_overlaid : false,
15220 getChildContainer : function()
15222 return this.el.select('.popover-content',true).first();
15225 getAutoCreate : function(){
15226 Roo.log('make popover?');
15228 cls : 'popover roo-dynamic',
15229 style: 'display:block',
15235 cls : 'popover-inner',
15239 cls: 'popover-title',
15243 cls : 'popover-content',
15254 setTitle: function(str)
15257 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15259 setContent: function(str)
15262 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15264 // as it get's added to the bottom of the page.
15265 onRender : function(ct, position)
15267 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15269 var cfg = Roo.apply({}, this.getAutoCreate());
15273 cfg.cls += ' ' + this.cls;
15276 cfg.style = this.style;
15278 Roo.log("adding to ")
15279 this.el = Roo.get(document.body).createChild(cfg, position);
15285 initEvents : function()
15287 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15288 this.el.enableDisplayMode('block');
15290 if (this.over === false) {
15293 if (this.triggers === false) {
15296 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15297 var triggers = this.trigger ? this.trigger.split(' ') : [];
15298 Roo.each(triggers, function(trigger) {
15300 if (trigger == 'click') {
15301 on_el.on('click', this.toggle, this);
15302 } else if (trigger != 'manual') {
15303 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15304 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15306 on_el.on(eventIn ,this.enter, this);
15307 on_el.on(eventOut, this.leave, this);
15318 toggle : function () {
15319 this.hoverState == 'in' ? this.leave() : this.enter();
15322 enter : function () {
15325 clearTimeout(this.timeout);
15327 this.hoverState = 'in';
15329 if (!this.delay || !this.delay.show) {
15334 this.timeout = setTimeout(function () {
15335 if (_t.hoverState == 'in') {
15338 }, this.delay.show)
15340 leave : function() {
15341 clearTimeout(this.timeout);
15343 this.hoverState = 'out';
15345 if (!this.delay || !this.delay.hide) {
15350 this.timeout = setTimeout(function () {
15351 if (_t.hoverState == 'out') {
15354 }, this.delay.hide)
15357 show : function (on_el)
15360 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15363 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15364 if (this.html !== false) {
15365 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15367 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15368 if (!this.title.length) {
15369 this.el.select('.popover-title',true).hide();
15372 var placement = typeof this.placement == 'function' ?
15373 this.placement.call(this, this.el, on_el) :
15376 var autoToken = /\s?auto?\s?/i;
15377 var autoPlace = autoToken.test(placement);
15379 placement = placement.replace(autoToken, '') || 'top';
15383 //this.el.setXY([0,0]);
15385 this.el.dom.style.display='block';
15386 this.el.addClass(placement);
15388 //this.el.appendTo(on_el);
15390 var p = this.getPosition();
15391 var box = this.el.getBox();
15396 var align = Roo.bootstrap.Popover.alignment[placement];
15397 this.el.alignTo(on_el, align[0],align[1]);
15398 //var arrow = this.el.select('.arrow',true).first();
15399 //arrow.set(align[2],
15401 this.el.addClass('in');
15404 if (this.el.hasClass('fade')) {
15411 this.el.setXY([0,0]);
15412 this.el.removeClass('in');
15414 this.hoverState = null;
15420 Roo.bootstrap.Popover.alignment = {
15421 'left' : ['r-l', [-10,0], 'right'],
15422 'right' : ['l-r', [10,0], 'left'],
15423 'bottom' : ['t-b', [0,10], 'top'],
15424 'top' : [ 'b-t', [0,-10], 'bottom']
15435 * @class Roo.bootstrap.Progress
15436 * @extends Roo.bootstrap.Component
15437 * Bootstrap Progress class
15438 * @cfg {Boolean} striped striped of the progress bar
15439 * @cfg {Boolean} active animated of the progress bar
15443 * Create a new Progress
15444 * @param {Object} config The config object
15447 Roo.bootstrap.Progress = function(config){
15448 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15451 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15456 getAutoCreate : function(){
15464 cfg.cls += ' progress-striped';
15468 cfg.cls += ' active';
15487 * @class Roo.bootstrap.ProgressBar
15488 * @extends Roo.bootstrap.Component
15489 * Bootstrap ProgressBar class
15490 * @cfg {Number} aria_valuenow aria-value now
15491 * @cfg {Number} aria_valuemin aria-value min
15492 * @cfg {Number} aria_valuemax aria-value max
15493 * @cfg {String} label label for the progress bar
15494 * @cfg {String} panel (success | info | warning | danger )
15495 * @cfg {String} role role of the progress bar
15496 * @cfg {String} sr_only text
15500 * Create a new ProgressBar
15501 * @param {Object} config The config object
15504 Roo.bootstrap.ProgressBar = function(config){
15505 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15508 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15512 aria_valuemax : 100,
15518 getAutoCreate : function()
15523 cls: 'progress-bar',
15524 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15536 cfg.role = this.role;
15539 if(this.aria_valuenow){
15540 cfg['aria-valuenow'] = this.aria_valuenow;
15543 if(this.aria_valuemin){
15544 cfg['aria-valuemin'] = this.aria_valuemin;
15547 if(this.aria_valuemax){
15548 cfg['aria-valuemax'] = this.aria_valuemax;
15551 if(this.label && !this.sr_only){
15552 cfg.html = this.label;
15556 cfg.cls += ' progress-bar-' + this.panel;
15562 update : function(aria_valuenow)
15564 this.aria_valuenow = aria_valuenow;
15566 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15581 * @class Roo.bootstrap.TabGroup
15582 * @extends Roo.bootstrap.Column
15583 * Bootstrap Column class
15584 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15585 * @cfg {Boolean} carousel true to make the group behave like a carousel
15586 * @cfg {Number} bullets show the panel pointer.. default 0
15587 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15588 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15589 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15592 * Create a new TabGroup
15593 * @param {Object} config The config object
15596 Roo.bootstrap.TabGroup = function(config){
15597 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15599 this.navId = Roo.id();
15602 Roo.bootstrap.TabGroup.register(this);
15606 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15609 transition : false,
15614 slideOnTouch : false,
15616 getAutoCreate : function()
15618 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15620 cfg.cls += ' tab-content';
15622 Roo.log('get auto create...............');
15624 if (this.carousel) {
15625 cfg.cls += ' carousel slide';
15628 cls : 'carousel-inner'
15631 if(this.bullets > 0 && !Roo.isTouch){
15634 cls : 'carousel-bullets',
15638 if(this.bullets_cls){
15639 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15642 for (var i = 0; i < this.bullets; i++){
15644 cls : 'bullet bullet-' + i
15652 cfg.cn[0].cn = bullets;
15659 initEvents: function()
15661 Roo.log('-------- init events on tab group ---------');
15663 if(this.bullets > 0 && !Roo.isTouch){
15669 if(Roo.isTouch && this.slideOnTouch){
15670 this.el.on("touchstart", this.onTouchStart, this);
15673 if(this.autoslide){
15676 this.slideFn = window.setInterval(function() {
15677 _this.showPanelNext();
15683 onTouchStart : function(e, el, o)
15685 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15689 this.showPanelNext();
15692 getChildContainer : function()
15694 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15698 * register a Navigation item
15699 * @param {Roo.bootstrap.NavItem} the navitem to add
15701 register : function(item)
15703 this.tabs.push( item);
15704 item.navId = this.navId; // not really needed..
15708 getActivePanel : function()
15711 Roo.each(this.tabs, function(t) {
15721 getPanelByName : function(n)
15724 Roo.each(this.tabs, function(t) {
15725 if (t.tabId == n) {
15733 indexOfPanel : function(p)
15736 Roo.each(this.tabs, function(t,i) {
15737 if (t.tabId == p.tabId) {
15746 * show a specific panel
15747 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15748 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15750 showPanel : function (pan)
15752 if(this.transition){
15753 Roo.log("waiting for the transitionend");
15757 if (typeof(pan) == 'number') {
15758 pan = this.tabs[pan];
15760 if (typeof(pan) == 'string') {
15761 pan = this.getPanelByName(pan);
15763 if (pan.tabId == this.getActivePanel().tabId) {
15766 var cur = this.getActivePanel();
15768 if (false === cur.fireEvent('beforedeactivate')) {
15772 if(this.bullets > 0 && !Roo.isTouch){
15773 this.setActiveBullet(this.indexOfPanel(pan));
15776 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15778 this.transition = true;
15779 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15780 var lr = dir == 'next' ? 'left' : 'right';
15781 pan.el.addClass(dir); // or prev
15782 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15783 cur.el.addClass(lr); // or right
15784 pan.el.addClass(lr);
15787 cur.el.on('transitionend', function() {
15788 Roo.log("trans end?");
15790 pan.el.removeClass([lr,dir]);
15791 pan.setActive(true);
15793 cur.el.removeClass([lr]);
15794 cur.setActive(false);
15796 _this.transition = false;
15798 }, this, { single: true } );
15803 cur.setActive(false);
15804 pan.setActive(true);
15809 showPanelNext : function()
15811 var i = this.indexOfPanel(this.getActivePanel());
15813 if (i >= this.tabs.length - 1 && !this.autoslide) {
15817 if (i >= this.tabs.length - 1 && this.autoslide) {
15821 this.showPanel(this.tabs[i+1]);
15824 showPanelPrev : function()
15826 var i = this.indexOfPanel(this.getActivePanel());
15828 if (i < 1 && !this.autoslide) {
15832 if (i < 1 && this.autoslide) {
15833 i = this.tabs.length;
15836 this.showPanel(this.tabs[i-1]);
15839 initBullet : function()
15847 for (var i = 0; i < this.bullets; i++){
15848 var bullet = this.el.select('.bullet-' + i, true).first();
15854 bullet.on('click', (function(e, el, o, ii, t){
15856 e.preventDefault();
15858 _this.showPanel(ii);
15860 if(_this.autoslide && _this.slideFn){
15861 clearInterval(_this.slideFn);
15862 _this.slideFn = window.setInterval(function() {
15863 _this.showPanelNext();
15867 }).createDelegate(this, [i, bullet], true));
15871 setActiveBullet : function(i)
15877 Roo.each(this.el.select('.bullet', true).elements, function(el){
15878 el.removeClass('selected');
15881 var bullet = this.el.select('.bullet-' + i, true).first();
15887 bullet.addClass('selected');
15898 Roo.apply(Roo.bootstrap.TabGroup, {
15902 * register a Navigation Group
15903 * @param {Roo.bootstrap.NavGroup} the navgroup to add
15905 register : function(navgrp)
15907 this.groups[navgrp.navId] = navgrp;
15911 * fetch a Navigation Group based on the navigation ID
15912 * if one does not exist , it will get created.
15913 * @param {string} the navgroup to add
15914 * @returns {Roo.bootstrap.NavGroup} the navgroup
15916 get: function(navId) {
15917 if (typeof(this.groups[navId]) == 'undefined') {
15918 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15920 return this.groups[navId] ;
15935 * @class Roo.bootstrap.TabPanel
15936 * @extends Roo.bootstrap.Component
15937 * Bootstrap TabPanel class
15938 * @cfg {Boolean} active panel active
15939 * @cfg {String} html panel content
15940 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15941 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15945 * Create a new TabPanel
15946 * @param {Object} config The config object
15949 Roo.bootstrap.TabPanel = function(config){
15950 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15954 * Fires when the active status changes
15955 * @param {Roo.bootstrap.TabPanel} this
15956 * @param {Boolean} state the new state
15961 * @event beforedeactivate
15962 * Fires before a tab is de-activated - can be used to do validation on a form.
15963 * @param {Roo.bootstrap.TabPanel} this
15964 * @return {Boolean} false if there is an error
15967 'beforedeactivate': true
15970 this.tabId = this.tabId || Roo.id();
15974 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
15981 getAutoCreate : function(){
15984 // item is needed for carousel - not sure if it has any effect otherwise
15985 cls: 'tab-pane item',
15986 html: this.html || ''
15990 cfg.cls += ' active';
15994 cfg.tabId = this.tabId;
16001 initEvents: function()
16003 Roo.log('-------- init events on tab panel ---------');
16005 var p = this.parent();
16006 this.navId = this.navId || p.navId;
16008 if (typeof(this.navId) != 'undefined') {
16009 // not really needed.. but just in case.. parent should be a NavGroup.
16010 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16011 Roo.log(['register', tg, this]);
16014 var i = tg.tabs.length - 1;
16016 if(this.active && tg.bullets > 0 && i < tg.bullets){
16017 tg.setActiveBullet(i);
16024 onRender : function(ct, position)
16026 // Roo.log("Call onRender: " + this.xtype);
16028 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16036 setActive: function(state)
16038 Roo.log("panel - set active " + this.tabId + "=" + state);
16040 this.active = state;
16042 this.el.removeClass('active');
16044 } else if (!this.el.hasClass('active')) {
16045 this.el.addClass('active');
16048 this.fireEvent('changed', this, state);
16065 * @class Roo.bootstrap.DateField
16066 * @extends Roo.bootstrap.Input
16067 * Bootstrap DateField class
16068 * @cfg {Number} weekStart default 0
16069 * @cfg {String} viewMode default empty, (months|years)
16070 * @cfg {String} minViewMode default empty, (months|years)
16071 * @cfg {Number} startDate default -Infinity
16072 * @cfg {Number} endDate default Infinity
16073 * @cfg {Boolean} todayHighlight default false
16074 * @cfg {Boolean} todayBtn default false
16075 * @cfg {Boolean} calendarWeeks default false
16076 * @cfg {Object} daysOfWeekDisabled default empty
16077 * @cfg {Boolean} singleMode default false (true | false)
16079 * @cfg {Boolean} keyboardNavigation default true
16080 * @cfg {String} language default en
16083 * Create a new DateField
16084 * @param {Object} config The config object
16087 Roo.bootstrap.DateField = function(config){
16088 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16092 * Fires when this field show.
16093 * @param {Roo.bootstrap.DateField} this
16094 * @param {Mixed} date The date value
16099 * Fires when this field hide.
16100 * @param {Roo.bootstrap.DateField} this
16101 * @param {Mixed} date The date value
16106 * Fires when select a date.
16107 * @param {Roo.bootstrap.DateField} this
16108 * @param {Mixed} date The date value
16114 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16117 * @cfg {String} format
16118 * The default date format string which can be overriden for localization support. The format must be
16119 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16123 * @cfg {String} altFormats
16124 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16125 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16127 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16135 todayHighlight : false,
16141 keyboardNavigation: true,
16143 calendarWeeks: false,
16145 startDate: -Infinity,
16149 daysOfWeekDisabled: [],
16153 singleMode : false,
16155 UTCDate: function()
16157 return new Date(Date.UTC.apply(Date, arguments));
16160 UTCToday: function()
16162 var today = new Date();
16163 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16166 getDate: function() {
16167 var d = this.getUTCDate();
16168 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16171 getUTCDate: function() {
16175 setDate: function(d) {
16176 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16179 setUTCDate: function(d) {
16181 this.setValue(this.formatDate(this.date));
16184 onRender: function(ct, position)
16187 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16189 this.language = this.language || 'en';
16190 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16191 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16193 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16194 this.format = this.format || 'm/d/y';
16195 this.isInline = false;
16196 this.isInput = true;
16197 this.component = this.el.select('.add-on', true).first() || false;
16198 this.component = (this.component && this.component.length === 0) ? false : this.component;
16199 this.hasInput = this.component && this.inputEL().length;
16201 if (typeof(this.minViewMode === 'string')) {
16202 switch (this.minViewMode) {
16204 this.minViewMode = 1;
16207 this.minViewMode = 2;
16210 this.minViewMode = 0;
16215 if (typeof(this.viewMode === 'string')) {
16216 switch (this.viewMode) {
16229 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16231 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16233 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16235 this.picker().on('mousedown', this.onMousedown, this);
16236 this.picker().on('click', this.onClick, this);
16238 this.picker().addClass('datepicker-dropdown');
16240 this.startViewMode = this.viewMode;
16242 if(this.singleMode){
16243 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16244 v.setVisibilityMode(Roo.Element.DISPLAY)
16248 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16249 v.setStyle('width', '189px');
16253 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16254 if(!this.calendarWeeks){
16259 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16260 v.attr('colspan', function(i, val){
16261 return parseInt(val) + 1;
16266 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16268 this.setStartDate(this.startDate);
16269 this.setEndDate(this.endDate);
16271 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16278 if(this.isInline) {
16283 picker : function()
16285 return this.pickerEl;
16286 // return this.el.select('.datepicker', true).first();
16289 fillDow: function()
16291 var dowCnt = this.weekStart;
16300 if(this.calendarWeeks){
16308 while (dowCnt < this.weekStart + 7) {
16312 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16316 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16319 fillMonths: function()
16322 var months = this.picker().select('>.datepicker-months td', true).first();
16324 months.dom.innerHTML = '';
16330 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16333 months.createChild(month);
16340 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;
16342 if (this.date < this.startDate) {
16343 this.viewDate = new Date(this.startDate);
16344 } else if (this.date > this.endDate) {
16345 this.viewDate = new Date(this.endDate);
16347 this.viewDate = new Date(this.date);
16355 var d = new Date(this.viewDate),
16356 year = d.getUTCFullYear(),
16357 month = d.getUTCMonth(),
16358 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16359 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16360 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16361 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16362 currentDate = this.date && this.date.valueOf(),
16363 today = this.UTCToday();
16365 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16367 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16369 // this.picker.select('>tfoot th.today').
16370 // .text(dates[this.language].today)
16371 // .toggle(this.todayBtn !== false);
16373 this.updateNavArrows();
16376 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16378 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16380 prevMonth.setUTCDate(day);
16382 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16384 var nextMonth = new Date(prevMonth);
16386 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16388 nextMonth = nextMonth.valueOf();
16390 var fillMonths = false;
16392 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16394 while(prevMonth.valueOf() < nextMonth) {
16397 if (prevMonth.getUTCDay() === this.weekStart) {
16399 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16407 if(this.calendarWeeks){
16408 // ISO 8601: First week contains first thursday.
16409 // ISO also states week starts on Monday, but we can be more abstract here.
16411 // Start of current week: based on weekstart/current date
16412 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16413 // Thursday of this week
16414 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16415 // First Thursday of year, year from thursday
16416 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16417 // Calendar week: ms between thursdays, div ms per day, div 7 days
16418 calWeek = (th - yth) / 864e5 / 7 + 1;
16420 fillMonths.cn.push({
16428 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16430 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16433 if (this.todayHighlight &&
16434 prevMonth.getUTCFullYear() == today.getFullYear() &&
16435 prevMonth.getUTCMonth() == today.getMonth() &&
16436 prevMonth.getUTCDate() == today.getDate()) {
16437 clsName += ' today';
16440 if (currentDate && prevMonth.valueOf() === currentDate) {
16441 clsName += ' active';
16444 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16445 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16446 clsName += ' disabled';
16449 fillMonths.cn.push({
16451 cls: 'day ' + clsName,
16452 html: prevMonth.getDate()
16455 prevMonth.setDate(prevMonth.getDate()+1);
16458 var currentYear = this.date && this.date.getUTCFullYear();
16459 var currentMonth = this.date && this.date.getUTCMonth();
16461 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16463 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16464 v.removeClass('active');
16466 if(currentYear === year && k === currentMonth){
16467 v.addClass('active');
16470 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16471 v.addClass('disabled');
16477 year = parseInt(year/10, 10) * 10;
16479 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16481 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16484 for (var i = -1; i < 11; i++) {
16485 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16487 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16495 showMode: function(dir)
16498 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16501 Roo.each(this.picker().select('>div',true).elements, function(v){
16502 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16505 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16510 if(this.isInline) return;
16512 this.picker().removeClass(['bottom', 'top']);
16514 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16516 * place to the top of element!
16520 this.picker().addClass('top');
16521 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16526 this.picker().addClass('bottom');
16528 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16531 parseDate : function(value)
16533 if(!value || value instanceof Date){
16536 var v = Date.parseDate(value, this.format);
16537 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16538 v = Date.parseDate(value, 'Y-m-d');
16540 if(!v && this.altFormats){
16541 if(!this.altFormatsArray){
16542 this.altFormatsArray = this.altFormats.split("|");
16544 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16545 v = Date.parseDate(value, this.altFormatsArray[i]);
16551 formatDate : function(date, fmt)
16553 return (!date || !(date instanceof Date)) ?
16554 date : date.dateFormat(fmt || this.format);
16557 onFocus : function()
16559 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16563 onBlur : function()
16565 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16567 var d = this.inputEl().getValue();
16576 this.picker().show();
16580 this.fireEvent('show', this, this.date);
16585 if(this.isInline) return;
16586 this.picker().hide();
16587 this.viewMode = this.startViewMode;
16590 this.fireEvent('hide', this, this.date);
16594 onMousedown: function(e)
16596 e.stopPropagation();
16597 e.preventDefault();
16602 Roo.bootstrap.DateField.superclass.keyup.call(this);
16606 setValue: function(v)
16609 // v can be a string or a date..
16612 var d = new Date(this.parseDate(v) ).clearTime();
16614 if(isNaN(d.getTime())){
16615 this.date = this.viewDate = '';
16616 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16620 v = this.formatDate(d);
16622 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16624 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16628 this.fireEvent('select', this, this.date);
16632 getValue: function()
16634 return this.formatDate(this.date);
16637 fireKey: function(e)
16639 if (!this.picker().isVisible()){
16640 if (e.keyCode == 27) // allow escape to hide and re-show picker
16645 var dateChanged = false,
16647 newDate, newViewDate;
16652 e.preventDefault();
16656 if (!this.keyboardNavigation) break;
16657 dir = e.keyCode == 37 ? -1 : 1;
16660 newDate = this.moveYear(this.date, dir);
16661 newViewDate = this.moveYear(this.viewDate, dir);
16662 } else if (e.shiftKey){
16663 newDate = this.moveMonth(this.date, dir);
16664 newViewDate = this.moveMonth(this.viewDate, dir);
16666 newDate = new Date(this.date);
16667 newDate.setUTCDate(this.date.getUTCDate() + dir);
16668 newViewDate = new Date(this.viewDate);
16669 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16671 if (this.dateWithinRange(newDate)){
16672 this.date = newDate;
16673 this.viewDate = newViewDate;
16674 this.setValue(this.formatDate(this.date));
16676 e.preventDefault();
16677 dateChanged = true;
16682 if (!this.keyboardNavigation) break;
16683 dir = e.keyCode == 38 ? -1 : 1;
16685 newDate = this.moveYear(this.date, dir);
16686 newViewDate = this.moveYear(this.viewDate, dir);
16687 } else if (e.shiftKey){
16688 newDate = this.moveMonth(this.date, dir);
16689 newViewDate = this.moveMonth(this.viewDate, dir);
16691 newDate = new Date(this.date);
16692 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16693 newViewDate = new Date(this.viewDate);
16694 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16696 if (this.dateWithinRange(newDate)){
16697 this.date = newDate;
16698 this.viewDate = newViewDate;
16699 this.setValue(this.formatDate(this.date));
16701 e.preventDefault();
16702 dateChanged = true;
16706 this.setValue(this.formatDate(this.date));
16708 e.preventDefault();
16711 this.setValue(this.formatDate(this.date));
16725 onClick: function(e)
16727 e.stopPropagation();
16728 e.preventDefault();
16730 var target = e.getTarget();
16732 if(target.nodeName.toLowerCase() === 'i'){
16733 target = Roo.get(target).dom.parentNode;
16736 var nodeName = target.nodeName;
16737 var className = target.className;
16738 var html = target.innerHTML;
16739 //Roo.log(nodeName);
16741 switch(nodeName.toLowerCase()) {
16743 switch(className) {
16749 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16750 switch(this.viewMode){
16752 this.viewDate = this.moveMonth(this.viewDate, dir);
16756 this.viewDate = this.moveYear(this.viewDate, dir);
16762 var date = new Date();
16763 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16765 this.setValue(this.formatDate(this.date));
16772 if (className.indexOf('disabled') < 0) {
16773 this.viewDate.setUTCDate(1);
16774 if (className.indexOf('month') > -1) {
16775 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16777 var year = parseInt(html, 10) || 0;
16778 this.viewDate.setUTCFullYear(year);
16782 if(this.singleMode){
16783 this.setValue(this.formatDate(this.viewDate));
16794 //Roo.log(className);
16795 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16796 var day = parseInt(html, 10) || 1;
16797 var year = this.viewDate.getUTCFullYear(),
16798 month = this.viewDate.getUTCMonth();
16800 if (className.indexOf('old') > -1) {
16807 } else if (className.indexOf('new') > -1) {
16815 //Roo.log([year,month,day]);
16816 this.date = this.UTCDate(year, month, day,0,0,0,0);
16817 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16819 //Roo.log(this.formatDate(this.date));
16820 this.setValue(this.formatDate(this.date));
16827 setStartDate: function(startDate)
16829 this.startDate = startDate || -Infinity;
16830 if (this.startDate !== -Infinity) {
16831 this.startDate = this.parseDate(this.startDate);
16834 this.updateNavArrows();
16837 setEndDate: function(endDate)
16839 this.endDate = endDate || Infinity;
16840 if (this.endDate !== Infinity) {
16841 this.endDate = this.parseDate(this.endDate);
16844 this.updateNavArrows();
16847 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16849 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16850 if (typeof(this.daysOfWeekDisabled) !== 'object') {
16851 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16853 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16854 return parseInt(d, 10);
16857 this.updateNavArrows();
16860 updateNavArrows: function()
16862 if(this.singleMode){
16866 var d = new Date(this.viewDate),
16867 year = d.getUTCFullYear(),
16868 month = d.getUTCMonth();
16870 Roo.each(this.picker().select('.prev', true).elements, function(v){
16872 switch (this.viewMode) {
16875 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16881 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16888 Roo.each(this.picker().select('.next', true).elements, function(v){
16890 switch (this.viewMode) {
16893 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16899 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16907 moveMonth: function(date, dir)
16909 if (!dir) return date;
16910 var new_date = new Date(date.valueOf()),
16911 day = new_date.getUTCDate(),
16912 month = new_date.getUTCMonth(),
16913 mag = Math.abs(dir),
16915 dir = dir > 0 ? 1 : -1;
16918 // If going back one month, make sure month is not current month
16919 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16921 return new_date.getUTCMonth() == month;
16923 // If going forward one month, make sure month is as expected
16924 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16926 return new_date.getUTCMonth() != new_month;
16928 new_month = month + dir;
16929 new_date.setUTCMonth(new_month);
16930 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16931 if (new_month < 0 || new_month > 11)
16932 new_month = (new_month + 12) % 12;
16934 // For magnitudes >1, move one month at a time...
16935 for (var i=0; i<mag; i++)
16936 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16937 new_date = this.moveMonth(new_date, dir);
16938 // ...then reset the day, keeping it in the new month
16939 new_month = new_date.getUTCMonth();
16940 new_date.setUTCDate(day);
16942 return new_month != new_date.getUTCMonth();
16945 // Common date-resetting loop -- if date is beyond end of month, make it
16948 new_date.setUTCDate(--day);
16949 new_date.setUTCMonth(new_month);
16954 moveYear: function(date, dir)
16956 return this.moveMonth(date, dir*12);
16959 dateWithinRange: function(date)
16961 return date >= this.startDate && date <= this.endDate;
16967 this.picker().remove();
16972 Roo.apply(Roo.bootstrap.DateField, {
16983 html: '<i class="fa fa-arrow-left"/>'
16993 html: '<i class="fa fa-arrow-right"/>'
17035 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17036 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17037 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17038 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17039 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17052 navFnc: 'FullYear',
17057 navFnc: 'FullYear',
17062 Roo.apply(Roo.bootstrap.DateField, {
17066 cls: 'datepicker dropdown-menu roo-dynamic',
17070 cls: 'datepicker-days',
17074 cls: 'table-condensed',
17076 Roo.bootstrap.DateField.head,
17080 Roo.bootstrap.DateField.footer
17087 cls: 'datepicker-months',
17091 cls: 'table-condensed',
17093 Roo.bootstrap.DateField.head,
17094 Roo.bootstrap.DateField.content,
17095 Roo.bootstrap.DateField.footer
17102 cls: 'datepicker-years',
17106 cls: 'table-condensed',
17108 Roo.bootstrap.DateField.head,
17109 Roo.bootstrap.DateField.content,
17110 Roo.bootstrap.DateField.footer
17129 * @class Roo.bootstrap.TimeField
17130 * @extends Roo.bootstrap.Input
17131 * Bootstrap DateField class
17135 * Create a new TimeField
17136 * @param {Object} config The config object
17139 Roo.bootstrap.TimeField = function(config){
17140 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17144 * Fires when this field show.
17145 * @param {Roo.bootstrap.DateField} thisthis
17146 * @param {Mixed} date The date value
17151 * Fires when this field hide.
17152 * @param {Roo.bootstrap.DateField} this
17153 * @param {Mixed} date The date value
17158 * Fires when select a date.
17159 * @param {Roo.bootstrap.DateField} this
17160 * @param {Mixed} date The date value
17166 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17169 * @cfg {String} format
17170 * The default time format string which can be overriden for localization support. The format must be
17171 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17175 onRender: function(ct, position)
17178 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17180 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17182 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17184 this.pop = this.picker().select('>.datepicker-time',true).first();
17185 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17187 this.picker().on('mousedown', this.onMousedown, this);
17188 this.picker().on('click', this.onClick, this);
17190 this.picker().addClass('datepicker-dropdown');
17195 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17196 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17197 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17198 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17199 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17200 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17204 fireKey: function(e){
17205 if (!this.picker().isVisible()){
17206 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17212 e.preventDefault();
17220 this.onTogglePeriod();
17223 this.onIncrementMinutes();
17226 this.onDecrementMinutes();
17235 onClick: function(e) {
17236 e.stopPropagation();
17237 e.preventDefault();
17240 picker : function()
17242 return this.el.select('.datepicker', true).first();
17245 fillTime: function()
17247 var time = this.pop.select('tbody', true).first();
17249 time.dom.innerHTML = '';
17264 cls: 'hours-up glyphicon glyphicon-chevron-up'
17284 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17305 cls: 'timepicker-hour',
17320 cls: 'timepicker-minute',
17335 cls: 'btn btn-primary period',
17357 cls: 'hours-down glyphicon glyphicon-chevron-down'
17377 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17395 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17402 var hours = this.time.getHours();
17403 var minutes = this.time.getMinutes();
17416 hours = hours - 12;
17420 hours = '0' + hours;
17424 minutes = '0' + minutes;
17427 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17428 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17429 this.pop.select('button', true).first().dom.innerHTML = period;
17435 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17437 var cls = ['bottom'];
17439 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17446 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17451 this.picker().addClass(cls.join('-'));
17455 Roo.each(cls, function(c){
17457 _this.picker().setTop(_this.inputEl().getHeight());
17461 _this.picker().setTop(0 - _this.picker().getHeight());
17466 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17470 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17477 onFocus : function()
17479 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17483 onBlur : function()
17485 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17491 this.picker().show();
17496 this.fireEvent('show', this, this.date);
17501 this.picker().hide();
17504 this.fireEvent('hide', this, this.date);
17507 setTime : function()
17510 this.setValue(this.time.format(this.format));
17512 this.fireEvent('select', this, this.date);
17517 onMousedown: function(e){
17518 e.stopPropagation();
17519 e.preventDefault();
17522 onIncrementHours: function()
17524 Roo.log('onIncrementHours');
17525 this.time = this.time.add(Date.HOUR, 1);
17530 onDecrementHours: function()
17532 Roo.log('onDecrementHours');
17533 this.time = this.time.add(Date.HOUR, -1);
17537 onIncrementMinutes: function()
17539 Roo.log('onIncrementMinutes');
17540 this.time = this.time.add(Date.MINUTE, 1);
17544 onDecrementMinutes: function()
17546 Roo.log('onDecrementMinutes');
17547 this.time = this.time.add(Date.MINUTE, -1);
17551 onTogglePeriod: function()
17553 Roo.log('onTogglePeriod');
17554 this.time = this.time.add(Date.HOUR, 12);
17561 Roo.apply(Roo.bootstrap.TimeField, {
17591 cls: 'btn btn-info ok',
17603 Roo.apply(Roo.bootstrap.TimeField, {
17607 cls: 'datepicker dropdown-menu',
17611 cls: 'datepicker-time',
17615 cls: 'table-condensed',
17617 Roo.bootstrap.TimeField.content,
17618 Roo.bootstrap.TimeField.footer
17637 * @class Roo.bootstrap.MonthField
17638 * @extends Roo.bootstrap.Input
17639 * Bootstrap MonthField class
17641 * @cfg {String} language default en
17644 * Create a new MonthField
17645 * @param {Object} config The config object
17648 Roo.bootstrap.MonthField = function(config){
17649 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17654 * Fires when this field show.
17655 * @param {Roo.bootstrap.MonthField} this
17656 * @param {Mixed} date The date value
17661 * Fires when this field hide.
17662 * @param {Roo.bootstrap.MonthField} this
17663 * @param {Mixed} date The date value
17668 * Fires when select a date.
17669 * @param {Roo.bootstrap.MonthField} this
17670 * @param {String} oldvalue The old value
17671 * @param {String} newvalue The new value
17677 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17679 onRender: function(ct, position)
17682 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17684 this.language = this.language || 'en';
17685 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17686 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17688 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17689 this.isInline = false;
17690 this.isInput = true;
17691 this.component = this.el.select('.add-on', true).first() || false;
17692 this.component = (this.component && this.component.length === 0) ? false : this.component;
17693 this.hasInput = this.component && this.inputEL().length;
17695 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17697 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17699 this.picker().on('mousedown', this.onMousedown, this);
17700 this.picker().on('click', this.onClick, this);
17702 this.picker().addClass('datepicker-dropdown');
17704 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17705 v.setStyle('width', '189px');
17712 if(this.isInline) {
17718 setValue: function(v, suppressEvent)
17720 var o = this.getValue();
17722 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17726 if(suppressEvent !== true){
17727 this.fireEvent('select', this, o, v);
17732 getValue: function()
17737 onClick: function(e)
17739 e.stopPropagation();
17740 e.preventDefault();
17742 var target = e.getTarget();
17744 if(target.nodeName.toLowerCase() === 'i'){
17745 target = Roo.get(target).dom.parentNode;
17748 var nodeName = target.nodeName;
17749 var className = target.className;
17750 var html = target.innerHTML;
17752 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17756 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17758 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17764 picker : function()
17766 return this.pickerEl;
17769 fillMonths: function()
17772 var months = this.picker().select('>.datepicker-months td', true).first();
17774 months.dom.innerHTML = '';
17780 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17783 months.createChild(month);
17792 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17793 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17796 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17797 e.removeClass('active');
17799 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17800 e.addClass('active');
17807 if(this.isInline) return;
17809 this.picker().removeClass(['bottom', 'top']);
17811 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17813 * place to the top of element!
17817 this.picker().addClass('top');
17818 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17823 this.picker().addClass('bottom');
17825 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17828 onFocus : function()
17830 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17834 onBlur : function()
17836 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17838 var d = this.inputEl().getValue();
17847 this.picker().show();
17848 this.picker().select('>.datepicker-months', true).first().show();
17852 this.fireEvent('show', this, this.date);
17857 if(this.isInline) return;
17858 this.picker().hide();
17859 this.fireEvent('hide', this, this.date);
17863 onMousedown: function(e)
17865 e.stopPropagation();
17866 e.preventDefault();
17871 Roo.bootstrap.MonthField.superclass.keyup.call(this);
17875 fireKey: function(e)
17877 if (!this.picker().isVisible()){
17878 if (e.keyCode == 27) // allow escape to hide and re-show picker
17888 e.preventDefault();
17892 dir = e.keyCode == 37 ? -1 : 1;
17894 this.vIndex = this.vIndex + dir;
17896 if(this.vIndex < 0){
17900 if(this.vIndex > 11){
17904 if(isNaN(this.vIndex)){
17908 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17914 dir = e.keyCode == 38 ? -1 : 1;
17916 this.vIndex = this.vIndex + dir * 4;
17918 if(this.vIndex < 0){
17922 if(this.vIndex > 11){
17926 if(isNaN(this.vIndex)){
17930 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17935 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17936 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17940 e.preventDefault();
17943 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17944 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17960 this.picker().remove();
17965 Roo.apply(Roo.bootstrap.MonthField, {
17984 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17985 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17990 Roo.apply(Roo.bootstrap.MonthField, {
17994 cls: 'datepicker dropdown-menu roo-dynamic',
17998 cls: 'datepicker-months',
18002 cls: 'table-condensed',
18004 Roo.bootstrap.DateField.content
18024 * @class Roo.bootstrap.CheckBox
18025 * @extends Roo.bootstrap.Input
18026 * Bootstrap CheckBox class
18028 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18029 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18030 * @cfg {String} boxLabel The text that appears beside the checkbox
18031 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18032 * @cfg {Boolean} checked initnal the element
18033 * @cfg {Boolean} inline inline the element (default false)
18034 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18037 * Create a new CheckBox
18038 * @param {Object} config The config object
18041 Roo.bootstrap.CheckBox = function(config){
18042 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18047 * Fires when the element is checked or unchecked.
18048 * @param {Roo.bootstrap.CheckBox} this This input
18049 * @param {Boolean} checked The new checked value
18056 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18058 inputType: 'checkbox',
18066 getAutoCreate : function()
18068 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18074 cfg.cls = 'form-group ' + this.inputType; //input-group
18077 cfg.cls += ' ' + this.inputType + '-inline';
18083 type : this.inputType,
18084 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18085 cls : 'roo-' + this.inputType, //'form-box',
18086 placeholder : this.placeholder || ''
18090 if (this.weight) { // Validity check?
18091 cfg.cls += " " + this.inputType + "-" + this.weight;
18094 if (this.disabled) {
18095 input.disabled=true;
18099 input.checked = this.checked;
18103 input.name = this.name;
18107 input.cls += ' input-' + this.size;
18112 ['xs','sm','md','lg'].map(function(size){
18113 if (settings[size]) {
18114 cfg.cls += ' col-' + size + '-' + settings[size];
18118 var inputblock = input;
18120 if (this.before || this.after) {
18123 cls : 'input-group',
18128 inputblock.cn.push({
18130 cls : 'input-group-addon',
18135 inputblock.cn.push(input);
18138 inputblock.cn.push({
18140 cls : 'input-group-addon',
18147 if (align ==='left' && this.fieldLabel.length) {
18148 Roo.log("left and has label");
18154 cls : 'control-label col-md-' + this.labelWidth,
18155 html : this.fieldLabel
18159 cls : "col-md-" + (12 - this.labelWidth),
18166 } else if ( this.fieldLabel.length) {
18171 tag: this.boxLabel ? 'span' : 'label',
18173 cls: 'control-label box-input-label',
18174 //cls : 'input-group-addon',
18175 html : this.fieldLabel
18185 Roo.log(" no label && no align");
18186 cfg.cn = [ inputblock ] ;
18191 var boxLabelCfg = {
18193 //'for': id, // box label is handled by onclick - so no for...
18195 html: this.boxLabel
18199 boxLabelCfg.tooltip = this.tooltip;
18202 cfg.cn.push(boxLabelCfg);
18212 * return the real input element.
18214 inputEl: function ()
18216 return this.el.select('input.roo-' + this.inputType,true).first();
18219 labelEl: function()
18221 return this.el.select('label.control-label',true).first();
18223 /* depricated... */
18227 return this.labelEl();
18230 initEvents : function()
18232 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18234 this.inputEl().on('click', this.onClick, this);
18236 if (this.boxLabel) {
18237 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18240 this.startValue = this.getValue();
18243 Roo.bootstrap.CheckBox.register(this);
18247 onClick : function()
18249 this.setChecked(!this.checked);
18252 setChecked : function(state,suppressEvent)
18254 this.startValue = this.getValue();
18256 if(this.inputType == 'radio'){
18258 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18259 e.dom.checked = false;
18262 this.inputEl().dom.checked = true;
18264 this.inputEl().dom.value = this.inputValue;
18266 if(suppressEvent !== true){
18267 this.fireEvent('check', this, true);
18275 this.checked = state;
18277 this.inputEl().dom.checked = state;
18279 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18281 if(suppressEvent !== true){
18282 this.fireEvent('check', this, state);
18288 getValue : function()
18290 if(this.inputType == 'radio'){
18291 return this.getGroupValue();
18294 return this.inputEl().getValue();
18298 getGroupValue : function()
18300 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18304 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18307 setValue : function(v,suppressEvent)
18309 if(this.inputType == 'radio'){
18310 this.setGroupValue(v, suppressEvent);
18314 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18319 setGroupValue : function(v, suppressEvent)
18321 this.startValue = this.getValue();
18323 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18324 e.dom.checked = false;
18326 if(e.dom.value == v){
18327 e.dom.checked = true;
18331 if(suppressEvent !== true){
18332 this.fireEvent('check', this, true);
18340 validate : function()
18344 (this.inputType == 'radio' && this.validateRadio()) ||
18345 (this.inputType == 'checkbox' && this.validateCheckbox())
18351 this.markInvalid();
18355 validateRadio : function()
18359 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18360 if(!e.dom.checked){
18372 validateCheckbox : function()
18375 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18378 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18386 for(var i in group){
18391 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18398 * Mark this field as valid
18400 markValid : function()
18402 if(this.allowBlank){
18408 this.fireEvent('valid', this);
18410 if(this.inputType == 'radio'){
18411 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18412 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18413 e.findParent('.form-group', false, true).addClass(_this.validClass);
18420 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18421 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18425 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18431 for(var i in group){
18432 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18433 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18438 * Mark this field as invalid
18439 * @param {String} msg The validation message
18441 markInvalid : function(msg)
18443 if(this.allowBlank){
18449 this.fireEvent('invalid', this, msg);
18451 if(this.inputType == 'radio'){
18452 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18453 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18454 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18461 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18462 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18466 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18472 for(var i in group){
18473 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18474 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18481 Roo.apply(Roo.bootstrap.CheckBox, {
18486 * register a CheckBox Group
18487 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18489 register : function(checkbox)
18491 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18492 this.groups[checkbox.groupId] = {};
18495 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18499 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18503 * fetch a CheckBox Group based on the group ID
18504 * @param {string} the group ID
18505 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18507 get: function(groupId) {
18508 if (typeof(this.groups[groupId]) == 'undefined') {
18512 return this.groups[groupId] ;
18524 *<div class="radio">
18526 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18527 Option one is this and that—be sure to include why it's great
18534 *<label class="radio-inline">fieldLabel</label>
18535 *<label class="radio-inline">
18536 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18544 * @class Roo.bootstrap.Radio
18545 * @extends Roo.bootstrap.CheckBox
18546 * Bootstrap Radio class
18549 * Create a new Radio
18550 * @param {Object} config The config object
18553 Roo.bootstrap.Radio = function(config){
18554 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18558 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18560 inputType: 'radio',
18564 getAutoCreate : function()
18566 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18567 align = align || 'left'; // default...
18574 tag : this.inline ? 'span' : 'div',
18579 var inline = this.inline ? ' radio-inline' : '';
18583 // does not need for, as we wrap the input with it..
18585 cls : 'control-label box-label' + inline,
18588 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18592 //cls : 'control-label' + inline,
18593 html : this.fieldLabel,
18594 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18603 type : this.inputType,
18604 //value : (!this.checked) ? this.valueOff : this.inputValue,
18605 value : this.inputValue,
18607 placeholder : this.placeholder || '' // ?? needed????
18610 if (this.weight) { // Validity check?
18611 input.cls += " radio-" + this.weight;
18613 if (this.disabled) {
18614 input.disabled=true;
18618 input.checked = this.checked;
18622 input.name = this.name;
18626 input.cls += ' input-' + this.size;
18629 //?? can span's inline have a width??
18632 ['xs','sm','md','lg'].map(function(size){
18633 if (settings[size]) {
18634 cfg.cls += ' col-' + size + '-' + settings[size];
18638 var inputblock = input;
18640 if (this.before || this.after) {
18643 cls : 'input-group',
18648 inputblock.cn.push({
18650 cls : 'input-group-addon',
18654 inputblock.cn.push(input);
18656 inputblock.cn.push({
18658 cls : 'input-group-addon',
18666 if (this.fieldLabel && this.fieldLabel.length) {
18667 cfg.cn.push(fieldLabel);
18670 // normal bootstrap puts the input inside the label.
18671 // however with our styled version - it has to go after the input.
18673 //lbl.cn.push(inputblock);
18677 cls: 'radio' + inline,
18684 cfg.cn.push( lblwrap);
18689 html: this.boxLabel
18698 initEvents : function()
18700 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18702 this.inputEl().on('click', this.onClick, this);
18703 if (this.boxLabel) {
18704 Roo.log('find label')
18705 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18710 inputEl: function ()
18712 return this.el.select('input.roo-radio',true).first();
18714 onClick : function()
18717 this.setChecked(true);
18720 setChecked : function(state,suppressEvent)
18723 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18724 v.dom.checked = false;
18727 Roo.log(this.inputEl().dom);
18728 this.checked = state;
18729 this.inputEl().dom.checked = state;
18731 if(suppressEvent !== true){
18732 this.fireEvent('check', this, state);
18735 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18739 getGroupValue : function()
18742 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18743 if(v.dom.checked == true){
18744 value = v.dom.value;
18752 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18753 * @return {Mixed} value The field value
18755 getValue : function(){
18756 return this.getGroupValue();
18762 //<script type="text/javascript">
18765 * Based Ext JS Library 1.1.1
18766 * Copyright(c) 2006-2007, Ext JS, LLC.
18772 * @class Roo.HtmlEditorCore
18773 * @extends Roo.Component
18774 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18776 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18779 Roo.HtmlEditorCore = function(config){
18782 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18787 * @event initialize
18788 * Fires when the editor is fully initialized (including the iframe)
18789 * @param {Roo.HtmlEditorCore} this
18794 * Fires when the editor is first receives the focus. Any insertion must wait
18795 * until after this event.
18796 * @param {Roo.HtmlEditorCore} this
18800 * @event beforesync
18801 * Fires before the textarea is updated with content from the editor iframe. Return false
18802 * to cancel the sync.
18803 * @param {Roo.HtmlEditorCore} this
18804 * @param {String} html
18808 * @event beforepush
18809 * Fires before the iframe editor is updated with content from the textarea. Return false
18810 * to cancel the push.
18811 * @param {Roo.HtmlEditorCore} this
18812 * @param {String} html
18817 * Fires when the textarea is updated with content from the editor iframe.
18818 * @param {Roo.HtmlEditorCore} this
18819 * @param {String} html
18824 * Fires when the iframe editor is updated with content from the textarea.
18825 * @param {Roo.HtmlEditorCore} this
18826 * @param {String} html
18831 * @event editorevent
18832 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18833 * @param {Roo.HtmlEditorCore} this
18839 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18841 // defaults : white / black...
18842 this.applyBlacklists();
18849 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
18853 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
18859 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18864 * @cfg {Number} height (in pixels)
18868 * @cfg {Number} width (in pixels)
18873 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18876 stylesheets: false,
18881 // private properties
18882 validationEvent : false,
18884 initialized : false,
18886 sourceEditMode : false,
18887 onFocus : Roo.emptyFn,
18889 hideMode:'offsets',
18893 // blacklist + whitelisted elements..
18900 * Protected method that will not generally be called directly. It
18901 * is called when the editor initializes the iframe with HTML contents. Override this method if you
18902 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18904 getDocMarkup : function(){
18908 // inherit styels from page...??
18909 if (this.stylesheets === false) {
18911 Roo.get(document.head).select('style').each(function(node) {
18912 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18915 Roo.get(document.head).select('link').each(function(node) {
18916 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18919 } else if (!this.stylesheets.length) {
18921 st = '<style type="text/css">' +
18922 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18928 st += '<style type="text/css">' +
18929 'IMG { cursor: pointer } ' +
18933 return '<html><head>' + st +
18934 //<style type="text/css">' +
18935 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18937 ' </head><body class="roo-htmleditor-body"></body></html>';
18941 onRender : function(ct, position)
18944 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18945 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18948 this.el.dom.style.border = '0 none';
18949 this.el.dom.setAttribute('tabIndex', -1);
18950 this.el.addClass('x-hidden hide');
18954 if(Roo.isIE){ // fix IE 1px bogus margin
18955 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18959 this.frameId = Roo.id();
18963 var iframe = this.owner.wrap.createChild({
18965 cls: 'form-control', // bootstrap..
18967 name: this.frameId,
18968 frameBorder : 'no',
18969 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
18974 this.iframe = iframe.dom;
18976 this.assignDocWin();
18978 this.doc.designMode = 'on';
18981 this.doc.write(this.getDocMarkup());
18985 var task = { // must defer to wait for browser to be ready
18987 //console.log("run task?" + this.doc.readyState);
18988 this.assignDocWin();
18989 if(this.doc.body || this.doc.readyState == 'complete'){
18991 this.doc.designMode="on";
18995 Roo.TaskMgr.stop(task);
18996 this.initEditor.defer(10, this);
19003 Roo.TaskMgr.start(task);
19008 onResize : function(w, h)
19010 Roo.log('resize: ' +w + ',' + h );
19011 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19015 if(typeof w == 'number'){
19017 this.iframe.style.width = w + 'px';
19019 if(typeof h == 'number'){
19021 this.iframe.style.height = h + 'px';
19023 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19030 * Toggles the editor between standard and source edit mode.
19031 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19033 toggleSourceEdit : function(sourceEditMode){
19035 this.sourceEditMode = sourceEditMode === true;
19037 if(this.sourceEditMode){
19039 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19042 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19043 //this.iframe.className = '';
19046 //this.setSize(this.owner.wrap.getSize());
19047 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19054 * Protected method that will not generally be called directly. If you need/want
19055 * custom HTML cleanup, this is the method you should override.
19056 * @param {String} html The HTML to be cleaned
19057 * return {String} The cleaned HTML
19059 cleanHtml : function(html){
19060 html = String(html);
19061 if(html.length > 5){
19062 if(Roo.isSafari){ // strip safari nonsense
19063 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19066 if(html == ' '){
19073 * HTML Editor -> Textarea
19074 * Protected method that will not generally be called directly. Syncs the contents
19075 * of the editor iframe with the textarea.
19077 syncValue : function(){
19078 if(this.initialized){
19079 var bd = (this.doc.body || this.doc.documentElement);
19080 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19081 var html = bd.innerHTML;
19083 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19084 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19086 html = '<div style="'+m[0]+'">' + html + '</div>';
19089 html = this.cleanHtml(html);
19090 // fix up the special chars.. normaly like back quotes in word...
19091 // however we do not want to do this with chinese..
19092 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19093 var cc = b.charCodeAt();
19095 (cc >= 0x4E00 && cc < 0xA000 ) ||
19096 (cc >= 0x3400 && cc < 0x4E00 ) ||
19097 (cc >= 0xf900 && cc < 0xfb00 )
19103 if(this.owner.fireEvent('beforesync', this, html) !== false){
19104 this.el.dom.value = html;
19105 this.owner.fireEvent('sync', this, html);
19111 * Protected method that will not generally be called directly. Pushes the value of the textarea
19112 * into the iframe editor.
19114 pushValue : function(){
19115 if(this.initialized){
19116 var v = this.el.dom.value.trim();
19118 // if(v.length < 1){
19122 if(this.owner.fireEvent('beforepush', this, v) !== false){
19123 var d = (this.doc.body || this.doc.documentElement);
19125 this.cleanUpPaste();
19126 this.el.dom.value = d.innerHTML;
19127 this.owner.fireEvent('push', this, v);
19133 deferFocus : function(){
19134 this.focus.defer(10, this);
19138 focus : function(){
19139 if(this.win && !this.sourceEditMode){
19146 assignDocWin: function()
19148 var iframe = this.iframe;
19151 this.doc = iframe.contentWindow.document;
19152 this.win = iframe.contentWindow;
19154 // if (!Roo.get(this.frameId)) {
19157 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19158 // this.win = Roo.get(this.frameId).dom.contentWindow;
19160 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19164 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19165 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19170 initEditor : function(){
19171 //console.log("INIT EDITOR");
19172 this.assignDocWin();
19176 this.doc.designMode="on";
19178 this.doc.write(this.getDocMarkup());
19181 var dbody = (this.doc.body || this.doc.documentElement);
19182 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19183 // this copies styles from the containing element into thsi one..
19184 // not sure why we need all of this..
19185 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19187 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19188 //ss['background-attachment'] = 'fixed'; // w3c
19189 dbody.bgProperties = 'fixed'; // ie
19190 //Roo.DomHelper.applyStyles(dbody, ss);
19191 Roo.EventManager.on(this.doc, {
19192 //'mousedown': this.onEditorEvent,
19193 'mouseup': this.onEditorEvent,
19194 'dblclick': this.onEditorEvent,
19195 'click': this.onEditorEvent,
19196 'keyup': this.onEditorEvent,
19201 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19203 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19204 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19206 this.initialized = true;
19208 this.owner.fireEvent('initialize', this);
19213 onDestroy : function(){
19219 //for (var i =0; i < this.toolbars.length;i++) {
19220 // // fixme - ask toolbars for heights?
19221 // this.toolbars[i].onDestroy();
19224 //this.wrap.dom.innerHTML = '';
19225 //this.wrap.remove();
19230 onFirstFocus : function(){
19232 this.assignDocWin();
19235 this.activated = true;
19238 if(Roo.isGecko){ // prevent silly gecko errors
19240 var s = this.win.getSelection();
19241 if(!s.focusNode || s.focusNode.nodeType != 3){
19242 var r = s.getRangeAt(0);
19243 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19248 this.execCmd('useCSS', true);
19249 this.execCmd('styleWithCSS', false);
19252 this.owner.fireEvent('activate', this);
19256 adjustFont: function(btn){
19257 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19258 //if(Roo.isSafari){ // safari
19261 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19262 if(Roo.isSafari){ // safari
19263 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19264 v = (v < 10) ? 10 : v;
19265 v = (v > 48) ? 48 : v;
19266 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19271 v = Math.max(1, v+adjust);
19273 this.execCmd('FontSize', v );
19276 onEditorEvent : function(e)
19278 this.owner.fireEvent('editorevent', this, e);
19279 // this.updateToolbar();
19280 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19283 insertTag : function(tg)
19285 // could be a bit smarter... -> wrap the current selected tRoo..
19286 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19288 range = this.createRange(this.getSelection());
19289 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19290 wrappingNode.appendChild(range.extractContents());
19291 range.insertNode(wrappingNode);
19298 this.execCmd("formatblock", tg);
19302 insertText : function(txt)
19306 var range = this.createRange();
19307 range.deleteContents();
19308 //alert(Sender.getAttribute('label'));
19310 range.insertNode(this.doc.createTextNode(txt));
19316 * Executes a Midas editor command on the editor document and performs necessary focus and
19317 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19318 * @param {String} cmd The Midas command
19319 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19321 relayCmd : function(cmd, value){
19323 this.execCmd(cmd, value);
19324 this.owner.fireEvent('editorevent', this);
19325 //this.updateToolbar();
19326 this.owner.deferFocus();
19330 * Executes a Midas editor command directly on the editor document.
19331 * For visual commands, you should use {@link #relayCmd} instead.
19332 * <b>This should only be called after the editor is initialized.</b>
19333 * @param {String} cmd The Midas command
19334 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19336 execCmd : function(cmd, value){
19337 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19344 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19346 * @param {String} text | dom node..
19348 insertAtCursor : function(text)
19353 if(!this.activated){
19359 var r = this.doc.selection.createRange();
19370 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19374 // from jquery ui (MIT licenced)
19376 var win = this.win;
19378 if (win.getSelection && win.getSelection().getRangeAt) {
19379 range = win.getSelection().getRangeAt(0);
19380 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19381 range.insertNode(node);
19382 } else if (win.document.selection && win.document.selection.createRange) {
19383 // no firefox support
19384 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19385 win.document.selection.createRange().pasteHTML(txt);
19387 // no firefox support
19388 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19389 this.execCmd('InsertHTML', txt);
19398 mozKeyPress : function(e){
19400 var c = e.getCharCode(), cmd;
19403 c = String.fromCharCode(c).toLowerCase();
19417 this.cleanUpPaste.defer(100, this);
19425 e.preventDefault();
19433 fixKeys : function(){ // load time branching for fastest keydown performance
19435 return function(e){
19436 var k = e.getKey(), r;
19439 r = this.doc.selection.createRange();
19442 r.pasteHTML('    ');
19449 r = this.doc.selection.createRange();
19451 var target = r.parentElement();
19452 if(!target || target.tagName.toLowerCase() != 'li'){
19454 r.pasteHTML('<br />');
19460 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19461 this.cleanUpPaste.defer(100, this);
19467 }else if(Roo.isOpera){
19468 return function(e){
19469 var k = e.getKey();
19473 this.execCmd('InsertHTML','    ');
19476 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19477 this.cleanUpPaste.defer(100, this);
19482 }else if(Roo.isSafari){
19483 return function(e){
19484 var k = e.getKey();
19488 this.execCmd('InsertText','\t');
19492 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19493 this.cleanUpPaste.defer(100, this);
19501 getAllAncestors: function()
19503 var p = this.getSelectedNode();
19506 a.push(p); // push blank onto stack..
19507 p = this.getParentElement();
19511 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19515 a.push(this.doc.body);
19519 lastSelNode : false,
19522 getSelection : function()
19524 this.assignDocWin();
19525 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19528 getSelectedNode: function()
19530 // this may only work on Gecko!!!
19532 // should we cache this!!!!
19537 var range = this.createRange(this.getSelection()).cloneRange();
19540 var parent = range.parentElement();
19542 var testRange = range.duplicate();
19543 testRange.moveToElementText(parent);
19544 if (testRange.inRange(range)) {
19547 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19550 parent = parent.parentElement;
19555 // is ancestor a text element.
19556 var ac = range.commonAncestorContainer;
19557 if (ac.nodeType == 3) {
19558 ac = ac.parentNode;
19561 var ar = ac.childNodes;
19564 var other_nodes = [];
19565 var has_other_nodes = false;
19566 for (var i=0;i<ar.length;i++) {
19567 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19570 // fullly contained node.
19572 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19577 // probably selected..
19578 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19579 other_nodes.push(ar[i]);
19583 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19588 has_other_nodes = true;
19590 if (!nodes.length && other_nodes.length) {
19591 nodes= other_nodes;
19593 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19599 createRange: function(sel)
19601 // this has strange effects when using with
19602 // top toolbar - not sure if it's a great idea.
19603 //this.editor.contentWindow.focus();
19604 if (typeof sel != "undefined") {
19606 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19608 return this.doc.createRange();
19611 return this.doc.createRange();
19614 getParentElement: function()
19617 this.assignDocWin();
19618 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19620 var range = this.createRange(sel);
19623 var p = range.commonAncestorContainer;
19624 while (p.nodeType == 3) { // text node
19635 * Range intersection.. the hard stuff...
19639 * [ -- selected range --- ]
19643 * if end is before start or hits it. fail.
19644 * if start is after end or hits it fail.
19646 * if either hits (but other is outside. - then it's not
19652 // @see http://www.thismuchiknow.co.uk/?p=64.
19653 rangeIntersectsNode : function(range, node)
19655 var nodeRange = node.ownerDocument.createRange();
19657 nodeRange.selectNode(node);
19659 nodeRange.selectNodeContents(node);
19662 var rangeStartRange = range.cloneRange();
19663 rangeStartRange.collapse(true);
19665 var rangeEndRange = range.cloneRange();
19666 rangeEndRange.collapse(false);
19668 var nodeStartRange = nodeRange.cloneRange();
19669 nodeStartRange.collapse(true);
19671 var nodeEndRange = nodeRange.cloneRange();
19672 nodeEndRange.collapse(false);
19674 return rangeStartRange.compareBoundaryPoints(
19675 Range.START_TO_START, nodeEndRange) == -1 &&
19676 rangeEndRange.compareBoundaryPoints(
19677 Range.START_TO_START, nodeStartRange) == 1;
19681 rangeCompareNode : function(range, node)
19683 var nodeRange = node.ownerDocument.createRange();
19685 nodeRange.selectNode(node);
19687 nodeRange.selectNodeContents(node);
19691 range.collapse(true);
19693 nodeRange.collapse(true);
19695 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19696 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19698 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19700 var nodeIsBefore = ss == 1;
19701 var nodeIsAfter = ee == -1;
19703 if (nodeIsBefore && nodeIsAfter)
19705 if (!nodeIsBefore && nodeIsAfter)
19706 return 1; //right trailed.
19708 if (nodeIsBefore && !nodeIsAfter)
19709 return 2; // left trailed.
19714 // private? - in a new class?
19715 cleanUpPaste : function()
19717 // cleans up the whole document..
19718 Roo.log('cleanuppaste');
19720 this.cleanUpChildren(this.doc.body);
19721 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19722 if (clean != this.doc.body.innerHTML) {
19723 this.doc.body.innerHTML = clean;
19728 cleanWordChars : function(input) {// change the chars to hex code
19729 var he = Roo.HtmlEditorCore;
19731 var output = input;
19732 Roo.each(he.swapCodes, function(sw) {
19733 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19735 output = output.replace(swapper, sw[1]);
19742 cleanUpChildren : function (n)
19744 if (!n.childNodes.length) {
19747 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19748 this.cleanUpChild(n.childNodes[i]);
19755 cleanUpChild : function (node)
19758 //console.log(node);
19759 if (node.nodeName == "#text") {
19760 // clean up silly Windows -- stuff?
19763 if (node.nodeName == "#comment") {
19764 node.parentNode.removeChild(node);
19765 // clean up silly Windows -- stuff?
19768 var lcname = node.tagName.toLowerCase();
19769 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19770 // whitelist of tags..
19772 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19774 node.parentNode.removeChild(node);
19779 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19781 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19782 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19784 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19785 // remove_keep_children = true;
19788 if (remove_keep_children) {
19789 this.cleanUpChildren(node);
19790 // inserts everything just before this node...
19791 while (node.childNodes.length) {
19792 var cn = node.childNodes[0];
19793 node.removeChild(cn);
19794 node.parentNode.insertBefore(cn, node);
19796 node.parentNode.removeChild(node);
19800 if (!node.attributes || !node.attributes.length) {
19801 this.cleanUpChildren(node);
19805 function cleanAttr(n,v)
19808 if (v.match(/^\./) || v.match(/^\//)) {
19811 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19814 if (v.match(/^#/)) {
19817 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19818 node.removeAttribute(n);
19822 var cwhite = this.cwhite;
19823 var cblack = this.cblack;
19825 function cleanStyle(n,v)
19827 if (v.match(/expression/)) { //XSS?? should we even bother..
19828 node.removeAttribute(n);
19832 var parts = v.split(/;/);
19835 Roo.each(parts, function(p) {
19836 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19840 var l = p.split(':').shift().replace(/\s+/g,'');
19841 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19843 if ( cwhite.length && cblack.indexOf(l) > -1) {
19844 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19845 //node.removeAttribute(n);
19849 // only allow 'c whitelisted system attributes'
19850 if ( cwhite.length && cwhite.indexOf(l) < 0) {
19851 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19852 //node.removeAttribute(n);
19862 if (clean.length) {
19863 node.setAttribute(n, clean.join(';'));
19865 node.removeAttribute(n);
19871 for (var i = node.attributes.length-1; i > -1 ; i--) {
19872 var a = node.attributes[i];
19875 if (a.name.toLowerCase().substr(0,2)=='on') {
19876 node.removeAttribute(a.name);
19879 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19880 node.removeAttribute(a.name);
19883 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19884 cleanAttr(a.name,a.value); // fixme..
19887 if (a.name == 'style') {
19888 cleanStyle(a.name,a.value);
19891 /// clean up MS crap..
19892 // tecnically this should be a list of valid class'es..
19895 if (a.name == 'class') {
19896 if (a.value.match(/^Mso/)) {
19897 node.className = '';
19900 if (a.value.match(/body/)) {
19901 node.className = '';
19912 this.cleanUpChildren(node);
19918 * Clean up MS wordisms...
19920 cleanWord : function(node)
19925 this.cleanWord(this.doc.body);
19928 if (node.nodeName == "#text") {
19929 // clean up silly Windows -- stuff?
19932 if (node.nodeName == "#comment") {
19933 node.parentNode.removeChild(node);
19934 // clean up silly Windows -- stuff?
19938 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19939 node.parentNode.removeChild(node);
19943 // remove - but keep children..
19944 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19945 while (node.childNodes.length) {
19946 var cn = node.childNodes[0];
19947 node.removeChild(cn);
19948 node.parentNode.insertBefore(cn, node);
19950 node.parentNode.removeChild(node);
19951 this.iterateChildren(node, this.cleanWord);
19955 if (node.className.length) {
19957 var cn = node.className.split(/\W+/);
19959 Roo.each(cn, function(cls) {
19960 if (cls.match(/Mso[a-zA-Z]+/)) {
19965 node.className = cna.length ? cna.join(' ') : '';
19967 node.removeAttribute("class");
19971 if (node.hasAttribute("lang")) {
19972 node.removeAttribute("lang");
19975 if (node.hasAttribute("style")) {
19977 var styles = node.getAttribute("style").split(";");
19979 Roo.each(styles, function(s) {
19980 if (!s.match(/:/)) {
19983 var kv = s.split(":");
19984 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19987 // what ever is left... we allow.
19990 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19991 if (!nstyle.length) {
19992 node.removeAttribute('style');
19995 this.iterateChildren(node, this.cleanWord);
20001 * iterateChildren of a Node, calling fn each time, using this as the scole..
20002 * @param {DomNode} node node to iterate children of.
20003 * @param {Function} fn method of this class to call on each item.
20005 iterateChildren : function(node, fn)
20007 if (!node.childNodes.length) {
20010 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20011 fn.call(this, node.childNodes[i])
20017 * cleanTableWidths.
20019 * Quite often pasting from word etc.. results in tables with column and widths.
20020 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20023 cleanTableWidths : function(node)
20028 this.cleanTableWidths(this.doc.body);
20033 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20036 Roo.log(node.tagName);
20037 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20038 this.iterateChildren(node, this.cleanTableWidths);
20041 if (node.hasAttribute('width')) {
20042 node.removeAttribute('width');
20046 if (node.hasAttribute("style")) {
20049 var styles = node.getAttribute("style").split(";");
20051 Roo.each(styles, function(s) {
20052 if (!s.match(/:/)) {
20055 var kv = s.split(":");
20056 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20059 // what ever is left... we allow.
20062 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20063 if (!nstyle.length) {
20064 node.removeAttribute('style');
20068 this.iterateChildren(node, this.cleanTableWidths);
20076 domToHTML : function(currentElement, depth, nopadtext) {
20078 depth = depth || 0;
20079 nopadtext = nopadtext || false;
20081 if (!currentElement) {
20082 return this.domToHTML(this.doc.body);
20085 //Roo.log(currentElement);
20087 var allText = false;
20088 var nodeName = currentElement.nodeName;
20089 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20091 if (nodeName == '#text') {
20093 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20098 if (nodeName != 'BODY') {
20101 // Prints the node tagName, such as <A>, <IMG>, etc
20104 for(i = 0; i < currentElement.attributes.length;i++) {
20106 var aname = currentElement.attributes.item(i).name;
20107 if (!currentElement.attributes.item(i).value.length) {
20110 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20113 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20122 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20125 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20130 // Traverse the tree
20132 var currentElementChild = currentElement.childNodes.item(i);
20133 var allText = true;
20134 var innerHTML = '';
20136 while (currentElementChild) {
20137 // Formatting code (indent the tree so it looks nice on the screen)
20138 var nopad = nopadtext;
20139 if (lastnode == 'SPAN') {
20143 if (currentElementChild.nodeName == '#text') {
20144 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20145 toadd = nopadtext ? toadd : toadd.trim();
20146 if (!nopad && toadd.length > 80) {
20147 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20149 innerHTML += toadd;
20152 currentElementChild = currentElement.childNodes.item(i);
20158 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20160 // Recursively traverse the tree structure of the child node
20161 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20162 lastnode = currentElementChild.nodeName;
20164 currentElementChild=currentElement.childNodes.item(i);
20170 // The remaining code is mostly for formatting the tree
20171 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20176 ret+= "</"+tagName+">";
20182 applyBlacklists : function()
20184 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20185 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20189 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20190 if (b.indexOf(tag) > -1) {
20193 this.white.push(tag);
20197 Roo.each(w, function(tag) {
20198 if (b.indexOf(tag) > -1) {
20201 if (this.white.indexOf(tag) > -1) {
20204 this.white.push(tag);
20209 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20210 if (w.indexOf(tag) > -1) {
20213 this.black.push(tag);
20217 Roo.each(b, function(tag) {
20218 if (w.indexOf(tag) > -1) {
20221 if (this.black.indexOf(tag) > -1) {
20224 this.black.push(tag);
20229 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20230 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20234 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20235 if (b.indexOf(tag) > -1) {
20238 this.cwhite.push(tag);
20242 Roo.each(w, function(tag) {
20243 if (b.indexOf(tag) > -1) {
20246 if (this.cwhite.indexOf(tag) > -1) {
20249 this.cwhite.push(tag);
20254 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20255 if (w.indexOf(tag) > -1) {
20258 this.cblack.push(tag);
20262 Roo.each(b, function(tag) {
20263 if (w.indexOf(tag) > -1) {
20266 if (this.cblack.indexOf(tag) > -1) {
20269 this.cblack.push(tag);
20274 setStylesheets : function(stylesheets)
20276 if(typeof(stylesheets) == 'string'){
20277 Roo.get(this.iframe.contentDocument.head).createChild({
20279 rel : 'stylesheet',
20288 Roo.each(stylesheets, function(s) {
20293 Roo.get(_this.iframe.contentDocument.head).createChild({
20295 rel : 'stylesheet',
20304 removeStylesheets : function()
20308 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20313 // hide stuff that is not compatible
20327 * @event specialkey
20331 * @cfg {String} fieldClass @hide
20334 * @cfg {String} focusClass @hide
20337 * @cfg {String} autoCreate @hide
20340 * @cfg {String} inputType @hide
20343 * @cfg {String} invalidClass @hide
20346 * @cfg {String} invalidText @hide
20349 * @cfg {String} msgFx @hide
20352 * @cfg {String} validateOnBlur @hide
20356 Roo.HtmlEditorCore.white = [
20357 'area', 'br', 'img', 'input', 'hr', 'wbr',
20359 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20360 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20361 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20362 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20363 'table', 'ul', 'xmp',
20365 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20368 'dir', 'menu', 'ol', 'ul', 'dl',
20374 Roo.HtmlEditorCore.black = [
20375 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20377 'base', 'basefont', 'bgsound', 'blink', 'body',
20378 'frame', 'frameset', 'head', 'html', 'ilayer',
20379 'iframe', 'layer', 'link', 'meta', 'object',
20380 'script', 'style' ,'title', 'xml' // clean later..
20382 Roo.HtmlEditorCore.clean = [
20383 'script', 'style', 'title', 'xml'
20385 Roo.HtmlEditorCore.remove = [
20390 Roo.HtmlEditorCore.ablack = [
20394 Roo.HtmlEditorCore.aclean = [
20395 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20399 Roo.HtmlEditorCore.pwhite= [
20400 'http', 'https', 'mailto'
20403 // white listed style attributes.
20404 Roo.HtmlEditorCore.cwhite= [
20405 // 'text-align', /// default is to allow most things..
20411 // black listed style attributes.
20412 Roo.HtmlEditorCore.cblack= [
20413 // 'font-size' -- this can be set by the project
20417 Roo.HtmlEditorCore.swapCodes =[
20436 * @class Roo.bootstrap.HtmlEditor
20437 * @extends Roo.bootstrap.TextArea
20438 * Bootstrap HtmlEditor class
20441 * Create a new HtmlEditor
20442 * @param {Object} config The config object
20445 Roo.bootstrap.HtmlEditor = function(config){
20446 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20447 if (!this.toolbars) {
20448 this.toolbars = [];
20450 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20453 * @event initialize
20454 * Fires when the editor is fully initialized (including the iframe)
20455 * @param {HtmlEditor} this
20460 * Fires when the editor is first receives the focus. Any insertion must wait
20461 * until after this event.
20462 * @param {HtmlEditor} this
20466 * @event beforesync
20467 * Fires before the textarea is updated with content from the editor iframe. Return false
20468 * to cancel the sync.
20469 * @param {HtmlEditor} this
20470 * @param {String} html
20474 * @event beforepush
20475 * Fires before the iframe editor is updated with content from the textarea. Return false
20476 * to cancel the push.
20477 * @param {HtmlEditor} this
20478 * @param {String} html
20483 * Fires when the textarea is updated with content from the editor iframe.
20484 * @param {HtmlEditor} this
20485 * @param {String} html
20490 * Fires when the iframe editor is updated with content from the textarea.
20491 * @param {HtmlEditor} this
20492 * @param {String} html
20496 * @event editmodechange
20497 * Fires when the editor switches edit modes
20498 * @param {HtmlEditor} this
20499 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20501 editmodechange: true,
20503 * @event editorevent
20504 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20505 * @param {HtmlEditor} this
20509 * @event firstfocus
20510 * Fires when on first focus - needed by toolbars..
20511 * @param {HtmlEditor} this
20516 * Auto save the htmlEditor value as a file into Events
20517 * @param {HtmlEditor} this
20521 * @event savedpreview
20522 * preview the saved version of htmlEditor
20523 * @param {HtmlEditor} this
20530 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20534 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20539 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20544 * @cfg {Number} height (in pixels)
20548 * @cfg {Number} width (in pixels)
20553 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20556 stylesheets: false,
20561 // private properties
20562 validationEvent : false,
20564 initialized : false,
20567 onFocus : Roo.emptyFn,
20569 hideMode:'offsets',
20572 tbContainer : false,
20574 toolbarContainer :function() {
20575 return this.wrap.select('.x-html-editor-tb',true).first();
20579 * Protected method that will not generally be called directly. It
20580 * is called when the editor creates its toolbar. Override this method if you need to
20581 * add custom toolbar buttons.
20582 * @param {HtmlEditor} editor
20584 createToolbar : function(){
20586 Roo.log("create toolbars");
20588 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20589 this.toolbars[0].render(this.toolbarContainer());
20593 // if (!editor.toolbars || !editor.toolbars.length) {
20594 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20597 // for (var i =0 ; i < editor.toolbars.length;i++) {
20598 // editor.toolbars[i] = Roo.factory(
20599 // typeof(editor.toolbars[i]) == 'string' ?
20600 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20601 // Roo.bootstrap.HtmlEditor);
20602 // editor.toolbars[i].init(editor);
20608 onRender : function(ct, position)
20610 // Roo.log("Call onRender: " + this.xtype);
20612 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20614 this.wrap = this.inputEl().wrap({
20615 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20618 this.editorcore.onRender(ct, position);
20620 if (this.resizable) {
20621 this.resizeEl = new Roo.Resizable(this.wrap, {
20625 minHeight : this.height,
20626 height: this.height,
20627 handles : this.resizable,
20630 resize : function(r, w, h) {
20631 _t.onResize(w,h); // -something
20637 this.createToolbar(this);
20640 if(!this.width && this.resizable){
20641 this.setSize(this.wrap.getSize());
20643 if (this.resizeEl) {
20644 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20645 // should trigger onReize..
20651 onResize : function(w, h)
20653 Roo.log('resize: ' +w + ',' + h );
20654 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20658 if(this.inputEl() ){
20659 if(typeof w == 'number'){
20660 var aw = w - this.wrap.getFrameWidth('lr');
20661 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20664 if(typeof h == 'number'){
20665 var tbh = -11; // fixme it needs to tool bar size!
20666 for (var i =0; i < this.toolbars.length;i++) {
20667 // fixme - ask toolbars for heights?
20668 tbh += this.toolbars[i].el.getHeight();
20669 //if (this.toolbars[i].footer) {
20670 // tbh += this.toolbars[i].footer.el.getHeight();
20678 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20679 ah -= 5; // knock a few pixes off for look..
20680 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20684 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20685 this.editorcore.onResize(ew,eh);
20690 * Toggles the editor between standard and source edit mode.
20691 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20693 toggleSourceEdit : function(sourceEditMode)
20695 this.editorcore.toggleSourceEdit(sourceEditMode);
20697 if(this.editorcore.sourceEditMode){
20698 Roo.log('editor - showing textarea');
20701 // Roo.log(this.syncValue());
20703 this.inputEl().removeClass(['hide', 'x-hidden']);
20704 this.inputEl().dom.removeAttribute('tabIndex');
20705 this.inputEl().focus();
20707 Roo.log('editor - hiding textarea');
20709 // Roo.log(this.pushValue());
20712 this.inputEl().addClass(['hide', 'x-hidden']);
20713 this.inputEl().dom.setAttribute('tabIndex', -1);
20714 //this.deferFocus();
20717 if(this.resizable){
20718 this.setSize(this.wrap.getSize());
20721 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20724 // private (for BoxComponent)
20725 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20727 // private (for BoxComponent)
20728 getResizeEl : function(){
20732 // private (for BoxComponent)
20733 getPositionEl : function(){
20738 initEvents : function(){
20739 this.originalValue = this.getValue();
20743 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20746 // markInvalid : Roo.emptyFn,
20748 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20751 // clearInvalid : Roo.emptyFn,
20753 setValue : function(v){
20754 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20755 this.editorcore.pushValue();
20760 deferFocus : function(){
20761 this.focus.defer(10, this);
20765 focus : function(){
20766 this.editorcore.focus();
20772 onDestroy : function(){
20778 for (var i =0; i < this.toolbars.length;i++) {
20779 // fixme - ask toolbars for heights?
20780 this.toolbars[i].onDestroy();
20783 this.wrap.dom.innerHTML = '';
20784 this.wrap.remove();
20789 onFirstFocus : function(){
20790 //Roo.log("onFirstFocus");
20791 this.editorcore.onFirstFocus();
20792 for (var i =0; i < this.toolbars.length;i++) {
20793 this.toolbars[i].onFirstFocus();
20799 syncValue : function()
20801 this.editorcore.syncValue();
20804 pushValue : function()
20806 this.editorcore.pushValue();
20810 // hide stuff that is not compatible
20824 * @event specialkey
20828 * @cfg {String} fieldClass @hide
20831 * @cfg {String} focusClass @hide
20834 * @cfg {String} autoCreate @hide
20837 * @cfg {String} inputType @hide
20840 * @cfg {String} invalidClass @hide
20843 * @cfg {String} invalidText @hide
20846 * @cfg {String} msgFx @hide
20849 * @cfg {String} validateOnBlur @hide
20858 Roo.namespace('Roo.bootstrap.htmleditor');
20860 * @class Roo.bootstrap.HtmlEditorToolbar1
20865 new Roo.bootstrap.HtmlEditor({
20868 new Roo.bootstrap.HtmlEditorToolbar1({
20869 disable : { fonts: 1 , format: 1, ..., ... , ...],
20875 * @cfg {Object} disable List of elements to disable..
20876 * @cfg {Array} btns List of additional buttons.
20880 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20883 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20886 Roo.apply(this, config);
20888 // default disabled, based on 'good practice'..
20889 this.disable = this.disable || {};
20890 Roo.applyIf(this.disable, {
20893 specialElements : true
20895 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20897 this.editor = config.editor;
20898 this.editorcore = config.editor.editorcore;
20900 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20902 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20903 // dont call parent... till later.
20905 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
20910 editorcore : false,
20915 "h1","h2","h3","h4","h5","h6",
20917 "abbr", "acronym", "address", "cite", "samp", "var",
20921 onRender : function(ct, position)
20923 // Roo.log("Call onRender: " + this.xtype);
20925 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20927 this.el.dom.style.marginBottom = '0';
20929 var editorcore = this.editorcore;
20930 var editor= this.editor;
20933 var btn = function(id,cmd , toggle, handler){
20935 var event = toggle ? 'toggle' : 'click';
20940 xns: Roo.bootstrap,
20943 enableToggle:toggle !== false,
20945 pressed : toggle ? false : null,
20948 a.listeners[toggle ? 'toggle' : 'click'] = function() {
20949 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
20958 xns: Roo.bootstrap,
20959 glyphicon : 'font',
20963 xns: Roo.bootstrap,
20967 Roo.each(this.formats, function(f) {
20968 style.menu.items.push({
20970 xns: Roo.bootstrap,
20971 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20976 editorcore.insertTag(this.tagname);
20983 children.push(style);
20986 btn('bold',false,true);
20987 btn('italic',false,true);
20988 btn('align-left', 'justifyleft',true);
20989 btn('align-center', 'justifycenter',true);
20990 btn('align-right' , 'justifyright',true);
20991 btn('link', false, false, function(btn) {
20992 //Roo.log("create link?");
20993 var url = prompt(this.createLinkText, this.defaultLinkValue);
20994 if(url && url != 'http:/'+'/'){
20995 this.editorcore.relayCmd('createlink', url);
20998 btn('list','insertunorderedlist',true);
20999 btn('pencil', false,true, function(btn){
21002 this.toggleSourceEdit(btn.pressed);
21008 xns: Roo.bootstrap,
21013 xns: Roo.bootstrap,
21018 cog.menu.items.push({
21020 xns: Roo.bootstrap,
21021 html : Clean styles,
21026 editorcore.insertTag(this.tagname);
21035 this.xtype = 'NavSimplebar';
21037 for(var i=0;i< children.length;i++) {
21039 this.buttons.add(this.addxtypeChild(children[i]));
21043 editor.on('editorevent', this.updateToolbar, this);
21045 onBtnClick : function(id)
21047 this.editorcore.relayCmd(id);
21048 this.editorcore.focus();
21052 * Protected method that will not generally be called directly. It triggers
21053 * a toolbar update by reading the markup state of the current selection in the editor.
21055 updateToolbar: function(){
21057 if(!this.editorcore.activated){
21058 this.editor.onFirstFocus(); // is this neeed?
21062 var btns = this.buttons;
21063 var doc = this.editorcore.doc;
21064 btns.get('bold').setActive(doc.queryCommandState('bold'));
21065 btns.get('italic').setActive(doc.queryCommandState('italic'));
21066 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21068 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21069 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21070 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21072 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21073 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21076 var ans = this.editorcore.getAllAncestors();
21077 if (this.formatCombo) {
21080 var store = this.formatCombo.store;
21081 this.formatCombo.setValue("");
21082 for (var i =0; i < ans.length;i++) {
21083 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21085 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21093 // hides menus... - so this cant be on a menu...
21094 Roo.bootstrap.MenuMgr.hideAll();
21096 Roo.bootstrap.MenuMgr.hideAll();
21097 //this.editorsyncValue();
21099 onFirstFocus: function() {
21100 this.buttons.each(function(item){
21104 toggleSourceEdit : function(sourceEditMode){
21107 if(sourceEditMode){
21108 Roo.log("disabling buttons");
21109 this.buttons.each( function(item){
21110 if(item.cmd != 'pencil'){
21116 Roo.log("enabling buttons");
21117 if(this.editorcore.initialized){
21118 this.buttons.each( function(item){
21124 Roo.log("calling toggole on editor");
21125 // tell the editor that it's been pressed..
21126 this.editor.toggleSourceEdit(sourceEditMode);
21136 * @class Roo.bootstrap.Table.AbstractSelectionModel
21137 * @extends Roo.util.Observable
21138 * Abstract base class for grid SelectionModels. It provides the interface that should be
21139 * implemented by descendant classes. This class should not be directly instantiated.
21142 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21143 this.locked = false;
21144 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21148 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21149 /** @ignore Called by the grid automatically. Do not call directly. */
21150 init : function(grid){
21156 * Locks the selections.
21159 this.locked = true;
21163 * Unlocks the selections.
21165 unlock : function(){
21166 this.locked = false;
21170 * Returns true if the selections are locked.
21171 * @return {Boolean}
21173 isLocked : function(){
21174 return this.locked;
21178 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21179 * @class Roo.bootstrap.Table.RowSelectionModel
21180 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21181 * It supports multiple selections and keyboard selection/navigation.
21183 * @param {Object} config
21186 Roo.bootstrap.Table.RowSelectionModel = function(config){
21187 Roo.apply(this, config);
21188 this.selections = new Roo.util.MixedCollection(false, function(o){
21193 this.lastActive = false;
21197 * @event selectionchange
21198 * Fires when the selection changes
21199 * @param {SelectionModel} this
21201 "selectionchange" : true,
21203 * @event afterselectionchange
21204 * Fires after the selection changes (eg. by key press or clicking)
21205 * @param {SelectionModel} this
21207 "afterselectionchange" : true,
21209 * @event beforerowselect
21210 * Fires when a row is selected being selected, return false to cancel.
21211 * @param {SelectionModel} this
21212 * @param {Number} rowIndex The selected index
21213 * @param {Boolean} keepExisting False if other selections will be cleared
21215 "beforerowselect" : true,
21218 * Fires when a row is selected.
21219 * @param {SelectionModel} this
21220 * @param {Number} rowIndex The selected index
21221 * @param {Roo.data.Record} r The record
21223 "rowselect" : true,
21225 * @event rowdeselect
21226 * Fires when a row is deselected.
21227 * @param {SelectionModel} this
21228 * @param {Number} rowIndex The selected index
21230 "rowdeselect" : true
21232 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21233 this.locked = false;
21236 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21238 * @cfg {Boolean} singleSelect
21239 * True to allow selection of only one row at a time (defaults to false)
21241 singleSelect : false,
21244 initEvents : function(){
21246 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21247 this.grid.on("mousedown", this.handleMouseDown, this);
21248 }else{ // allow click to work like normal
21249 this.grid.on("rowclick", this.handleDragableRowClick, this);
21252 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21253 "up" : function(e){
21255 this.selectPrevious(e.shiftKey);
21256 }else if(this.last !== false && this.lastActive !== false){
21257 var last = this.last;
21258 this.selectRange(this.last, this.lastActive-1);
21259 this.grid.getView().focusRow(this.lastActive);
21260 if(last !== false){
21264 this.selectFirstRow();
21266 this.fireEvent("afterselectionchange", this);
21268 "down" : function(e){
21270 this.selectNext(e.shiftKey);
21271 }else if(this.last !== false && this.lastActive !== false){
21272 var last = this.last;
21273 this.selectRange(this.last, this.lastActive+1);
21274 this.grid.getView().focusRow(this.lastActive);
21275 if(last !== false){
21279 this.selectFirstRow();
21281 this.fireEvent("afterselectionchange", this);
21286 var view = this.grid.view;
21287 view.on("refresh", this.onRefresh, this);
21288 view.on("rowupdated", this.onRowUpdated, this);
21289 view.on("rowremoved", this.onRemove, this);
21293 onRefresh : function(){
21294 var ds = this.grid.dataSource, i, v = this.grid.view;
21295 var s = this.selections;
21296 s.each(function(r){
21297 if((i = ds.indexOfId(r.id)) != -1){
21306 onRemove : function(v, index, r){
21307 this.selections.remove(r);
21311 onRowUpdated : function(v, index, r){
21312 if(this.isSelected(r)){
21313 v.onRowSelect(index);
21319 * @param {Array} records The records to select
21320 * @param {Boolean} keepExisting (optional) True to keep existing selections
21322 selectRecords : function(records, keepExisting){
21324 this.clearSelections();
21326 var ds = this.grid.dataSource;
21327 for(var i = 0, len = records.length; i < len; i++){
21328 this.selectRow(ds.indexOf(records[i]), true);
21333 * Gets the number of selected rows.
21336 getCount : function(){
21337 return this.selections.length;
21341 * Selects the first row in the grid.
21343 selectFirstRow : function(){
21348 * Select the last row.
21349 * @param {Boolean} keepExisting (optional) True to keep existing selections
21351 selectLastRow : function(keepExisting){
21352 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21356 * Selects the row immediately following the last selected row.
21357 * @param {Boolean} keepExisting (optional) True to keep existing selections
21359 selectNext : function(keepExisting){
21360 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21361 this.selectRow(this.last+1, keepExisting);
21362 this.grid.getView().focusRow(this.last);
21367 * Selects the row that precedes the last selected row.
21368 * @param {Boolean} keepExisting (optional) True to keep existing selections
21370 selectPrevious : function(keepExisting){
21372 this.selectRow(this.last-1, keepExisting);
21373 this.grid.getView().focusRow(this.last);
21378 * Returns the selected records
21379 * @return {Array} Array of selected records
21381 getSelections : function(){
21382 return [].concat(this.selections.items);
21386 * Returns the first selected record.
21389 getSelected : function(){
21390 return this.selections.itemAt(0);
21395 * Clears all selections.
21397 clearSelections : function(fast){
21398 if(this.locked) return;
21400 var ds = this.grid.dataSource;
21401 var s = this.selections;
21402 s.each(function(r){
21403 this.deselectRow(ds.indexOfId(r.id));
21407 this.selections.clear();
21414 * Selects all rows.
21416 selectAll : function(){
21417 if(this.locked) return;
21418 this.selections.clear();
21419 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21420 this.selectRow(i, true);
21425 * Returns True if there is a selection.
21426 * @return {Boolean}
21428 hasSelection : function(){
21429 return this.selections.length > 0;
21433 * Returns True if the specified row is selected.
21434 * @param {Number/Record} record The record or index of the record to check
21435 * @return {Boolean}
21437 isSelected : function(index){
21438 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21439 return (r && this.selections.key(r.id) ? true : false);
21443 * Returns True if the specified record id is selected.
21444 * @param {String} id The id of record to check
21445 * @return {Boolean}
21447 isIdSelected : function(id){
21448 return (this.selections.key(id) ? true : false);
21452 handleMouseDown : function(e, t){
21453 var view = this.grid.getView(), rowIndex;
21454 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21457 if(e.shiftKey && this.last !== false){
21458 var last = this.last;
21459 this.selectRange(last, rowIndex, e.ctrlKey);
21460 this.last = last; // reset the last
21461 view.focusRow(rowIndex);
21463 var isSelected = this.isSelected(rowIndex);
21464 if(e.button !== 0 && isSelected){
21465 view.focusRow(rowIndex);
21466 }else if(e.ctrlKey && isSelected){
21467 this.deselectRow(rowIndex);
21468 }else if(!isSelected){
21469 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21470 view.focusRow(rowIndex);
21473 this.fireEvent("afterselectionchange", this);
21476 handleDragableRowClick : function(grid, rowIndex, e)
21478 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21479 this.selectRow(rowIndex, false);
21480 grid.view.focusRow(rowIndex);
21481 this.fireEvent("afterselectionchange", this);
21486 * Selects multiple rows.
21487 * @param {Array} rows Array of the indexes of the row to select
21488 * @param {Boolean} keepExisting (optional) True to keep existing selections
21490 selectRows : function(rows, keepExisting){
21492 this.clearSelections();
21494 for(var i = 0, len = rows.length; i < len; i++){
21495 this.selectRow(rows[i], true);
21500 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21501 * @param {Number} startRow The index of the first row in the range
21502 * @param {Number} endRow The index of the last row in the range
21503 * @param {Boolean} keepExisting (optional) True to retain existing selections
21505 selectRange : function(startRow, endRow, keepExisting){
21506 if(this.locked) return;
21508 this.clearSelections();
21510 if(startRow <= endRow){
21511 for(var i = startRow; i <= endRow; i++){
21512 this.selectRow(i, true);
21515 for(var i = startRow; i >= endRow; i--){
21516 this.selectRow(i, true);
21522 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21523 * @param {Number} startRow The index of the first row in the range
21524 * @param {Number} endRow The index of the last row in the range
21526 deselectRange : function(startRow, endRow, preventViewNotify){
21527 if(this.locked) return;
21528 for(var i = startRow; i <= endRow; i++){
21529 this.deselectRow(i, preventViewNotify);
21535 * @param {Number} row The index of the row to select
21536 * @param {Boolean} keepExisting (optional) True to keep existing selections
21538 selectRow : function(index, keepExisting, preventViewNotify){
21539 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21540 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21541 if(!keepExisting || this.singleSelect){
21542 this.clearSelections();
21544 var r = this.grid.dataSource.getAt(index);
21545 this.selections.add(r);
21546 this.last = this.lastActive = index;
21547 if(!preventViewNotify){
21548 this.grid.getView().onRowSelect(index);
21550 this.fireEvent("rowselect", this, index, r);
21551 this.fireEvent("selectionchange", this);
21557 * @param {Number} row The index of the row to deselect
21559 deselectRow : function(index, preventViewNotify){
21560 if(this.locked) return;
21561 if(this.last == index){
21564 if(this.lastActive == index){
21565 this.lastActive = false;
21567 var r = this.grid.dataSource.getAt(index);
21568 this.selections.remove(r);
21569 if(!preventViewNotify){
21570 this.grid.getView().onRowDeselect(index);
21572 this.fireEvent("rowdeselect", this, index);
21573 this.fireEvent("selectionchange", this);
21577 restoreLast : function(){
21579 this.last = this._last;
21584 acceptsNav : function(row, col, cm){
21585 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21589 onEditorKey : function(field, e){
21590 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21595 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21597 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21599 }else if(k == e.ENTER && !e.ctrlKey){
21603 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21605 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21607 }else if(k == e.ESC){
21611 g.startEditing(newCell[0], newCell[1]);
21616 * Ext JS Library 1.1.1
21617 * Copyright(c) 2006-2007, Ext JS, LLC.
21619 * Originally Released Under LGPL - original licence link has changed is not relivant.
21622 * <script type="text/javascript">
21626 * @class Roo.bootstrap.PagingToolbar
21628 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21630 * Create a new PagingToolbar
21631 * @param {Object} config The config object
21633 Roo.bootstrap.PagingToolbar = function(config)
21635 // old args format still supported... - xtype is prefered..
21636 // created from xtype...
21637 var ds = config.dataSource;
21638 this.toolbarItems = [];
21639 if (config.items) {
21640 this.toolbarItems = config.items;
21641 // config.items = [];
21644 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21651 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21655 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21657 * @cfg {Roo.data.Store} dataSource
21658 * The underlying data store providing the paged data
21661 * @cfg {String/HTMLElement/Element} container
21662 * container The id or element that will contain the toolbar
21665 * @cfg {Boolean} displayInfo
21666 * True to display the displayMsg (defaults to false)
21669 * @cfg {Number} pageSize
21670 * The number of records to display per page (defaults to 20)
21674 * @cfg {String} displayMsg
21675 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21677 displayMsg : 'Displaying {0} - {1} of {2}',
21679 * @cfg {String} emptyMsg
21680 * The message to display when no records are found (defaults to "No data to display")
21682 emptyMsg : 'No data to display',
21684 * Customizable piece of the default paging text (defaults to "Page")
21687 beforePageText : "Page",
21689 * Customizable piece of the default paging text (defaults to "of %0")
21692 afterPageText : "of {0}",
21694 * Customizable piece of the default paging text (defaults to "First Page")
21697 firstText : "First Page",
21699 * Customizable piece of the default paging text (defaults to "Previous Page")
21702 prevText : "Previous Page",
21704 * Customizable piece of the default paging text (defaults to "Next Page")
21707 nextText : "Next Page",
21709 * Customizable piece of the default paging text (defaults to "Last Page")
21712 lastText : "Last Page",
21714 * Customizable piece of the default paging text (defaults to "Refresh")
21717 refreshText : "Refresh",
21721 onRender : function(ct, position)
21723 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21724 this.navgroup.parentId = this.id;
21725 this.navgroup.onRender(this.el, null);
21726 // add the buttons to the navgroup
21728 if(this.displayInfo){
21729 Roo.log(this.el.select('ul.navbar-nav',true).first());
21730 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21731 this.displayEl = this.el.select('.x-paging-info', true).first();
21732 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21733 // this.displayEl = navel.el.select('span',true).first();
21739 Roo.each(_this.buttons, function(e){
21740 Roo.factory(e).onRender(_this.el, null);
21744 Roo.each(_this.toolbarItems, function(e) {
21745 _this.navgroup.addItem(e);
21749 this.first = this.navgroup.addItem({
21750 tooltip: this.firstText,
21752 icon : 'fa fa-backward',
21754 preventDefault: true,
21755 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21758 this.prev = this.navgroup.addItem({
21759 tooltip: this.prevText,
21761 icon : 'fa fa-step-backward',
21763 preventDefault: true,
21764 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21766 //this.addSeparator();
21769 var field = this.navgroup.addItem( {
21771 cls : 'x-paging-position',
21773 html : this.beforePageText +
21774 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21775 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21778 this.field = field.el.select('input', true).first();
21779 this.field.on("keydown", this.onPagingKeydown, this);
21780 this.field.on("focus", function(){this.dom.select();});
21783 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21784 //this.field.setHeight(18);
21785 //this.addSeparator();
21786 this.next = this.navgroup.addItem({
21787 tooltip: this.nextText,
21789 html : ' <i class="fa fa-step-forward">',
21791 preventDefault: true,
21792 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21794 this.last = this.navgroup.addItem({
21795 tooltip: this.lastText,
21796 icon : 'fa fa-forward',
21799 preventDefault: true,
21800 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21802 //this.addSeparator();
21803 this.loading = this.navgroup.addItem({
21804 tooltip: this.refreshText,
21805 icon: 'fa fa-refresh',
21806 preventDefault: true,
21807 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21813 updateInfo : function(){
21814 if(this.displayEl){
21815 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21816 var msg = count == 0 ?
21820 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21822 this.displayEl.update(msg);
21827 onLoad : function(ds, r, o){
21828 this.cursor = o.params ? o.params.start : 0;
21829 var d = this.getPageData(),
21833 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21834 this.field.dom.value = ap;
21835 this.first.setDisabled(ap == 1);
21836 this.prev.setDisabled(ap == 1);
21837 this.next.setDisabled(ap == ps);
21838 this.last.setDisabled(ap == ps);
21839 this.loading.enable();
21844 getPageData : function(){
21845 var total = this.ds.getTotalCount();
21848 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21849 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21854 onLoadError : function(){
21855 this.loading.enable();
21859 onPagingKeydown : function(e){
21860 var k = e.getKey();
21861 var d = this.getPageData();
21863 var v = this.field.dom.value, pageNum;
21864 if(!v || isNaN(pageNum = parseInt(v, 10))){
21865 this.field.dom.value = d.activePage;
21868 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21869 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21872 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))
21874 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21875 this.field.dom.value = pageNum;
21876 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21879 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21881 var v = this.field.dom.value, pageNum;
21882 var increment = (e.shiftKey) ? 10 : 1;
21883 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21885 if(!v || isNaN(pageNum = parseInt(v, 10))) {
21886 this.field.dom.value = d.activePage;
21889 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21891 this.field.dom.value = parseInt(v, 10) + increment;
21892 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21893 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21900 beforeLoad : function(){
21902 this.loading.disable();
21907 onClick : function(which){
21916 ds.load({params:{start: 0, limit: this.pageSize}});
21919 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21922 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21925 var total = ds.getTotalCount();
21926 var extra = total % this.pageSize;
21927 var lastStart = extra ? (total - extra) : total-this.pageSize;
21928 ds.load({params:{start: lastStart, limit: this.pageSize}});
21931 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21937 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21938 * @param {Roo.data.Store} store The data store to unbind
21940 unbind : function(ds){
21941 ds.un("beforeload", this.beforeLoad, this);
21942 ds.un("load", this.onLoad, this);
21943 ds.un("loadexception", this.onLoadError, this);
21944 ds.un("remove", this.updateInfo, this);
21945 ds.un("add", this.updateInfo, this);
21946 this.ds = undefined;
21950 * Binds the paging toolbar to the specified {@link Roo.data.Store}
21951 * @param {Roo.data.Store} store The data store to bind
21953 bind : function(ds){
21954 ds.on("beforeload", this.beforeLoad, this);
21955 ds.on("load", this.onLoad, this);
21956 ds.on("loadexception", this.onLoadError, this);
21957 ds.on("remove", this.updateInfo, this);
21958 ds.on("add", this.updateInfo, this);
21969 * @class Roo.bootstrap.MessageBar
21970 * @extends Roo.bootstrap.Component
21971 * Bootstrap MessageBar class
21972 * @cfg {String} html contents of the MessageBar
21973 * @cfg {String} weight (info | success | warning | danger) default info
21974 * @cfg {String} beforeClass insert the bar before the given class
21975 * @cfg {Boolean} closable (true | false) default false
21976 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21979 * Create a new Element
21980 * @param {Object} config The config object
21983 Roo.bootstrap.MessageBar = function(config){
21984 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21987 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
21993 beforeClass: 'bootstrap-sticky-wrap',
21995 getAutoCreate : function(){
21999 cls: 'alert alert-dismissable alert-' + this.weight,
22004 html: this.html || ''
22010 cfg.cls += ' alert-messages-fixed';
22024 onRender : function(ct, position)
22026 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22029 var cfg = Roo.apply({}, this.getAutoCreate());
22033 cfg.cls += ' ' + this.cls;
22036 cfg.style = this.style;
22038 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22040 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22043 this.el.select('>button.close').on('click', this.hide, this);
22049 if (!this.rendered) {
22055 this.fireEvent('show', this);
22061 if (!this.rendered) {
22067 this.fireEvent('hide', this);
22070 update : function()
22072 // var e = this.el.dom.firstChild;
22074 // if(this.closable){
22075 // e = e.nextSibling;
22078 // e.data = this.html || '';
22080 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22096 * @class Roo.bootstrap.Graph
22097 * @extends Roo.bootstrap.Component
22098 * Bootstrap Graph class
22102 @cfg {String} graphtype bar | vbar | pie
22103 @cfg {number} g_x coodinator | centre x (pie)
22104 @cfg {number} g_y coodinator | centre y (pie)
22105 @cfg {number} g_r radius (pie)
22106 @cfg {number} g_height height of the chart (respected by all elements in the set)
22107 @cfg {number} g_width width of the chart (respected by all elements in the set)
22108 @cfg {Object} title The title of the chart
22111 -opts (object) options for the chart
22113 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22114 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22116 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.
22117 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22119 o stretch (boolean)
22121 -opts (object) options for the pie
22124 o startAngle (number)
22125 o endAngle (number)
22129 * Create a new Input
22130 * @param {Object} config The config object
22133 Roo.bootstrap.Graph = function(config){
22134 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22140 * The img click event for the img.
22141 * @param {Roo.EventObject} e
22147 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22158 //g_colors: this.colors,
22165 getAutoCreate : function(){
22176 onRender : function(ct,position){
22177 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22178 this.raphael = Raphael(this.el.dom);
22180 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22181 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22182 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22183 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22185 r.text(160, 10, "Single Series Chart").attr(txtattr);
22186 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22187 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22188 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22190 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22191 r.barchart(330, 10, 300, 220, data1);
22192 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22193 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22196 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22197 // r.barchart(30, 30, 560, 250, xdata, {
22198 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22199 // axis : "0 0 1 1",
22200 // axisxlabels : xdata
22201 // //yvalues : cols,
22204 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22206 // this.load(null,xdata,{
22207 // axis : "0 0 1 1",
22208 // axisxlabels : xdata
22213 load : function(graphtype,xdata,opts){
22214 this.raphael.clear();
22216 graphtype = this.graphtype;
22221 var r = this.raphael,
22222 fin = function () {
22223 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22225 fout = function () {
22226 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22228 pfin = function() {
22229 this.sector.stop();
22230 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22233 this.label[0].stop();
22234 this.label[0].attr({ r: 7.5 });
22235 this.label[1].attr({ "font-weight": 800 });
22238 pfout = function() {
22239 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22242 this.label[0].animate({ r: 5 }, 500, "bounce");
22243 this.label[1].attr({ "font-weight": 400 });
22249 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22252 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22255 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22256 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22258 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22265 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22270 setTitle: function(o)
22275 initEvents: function() {
22278 this.el.on('click', this.onClick, this);
22282 onClick : function(e)
22284 Roo.log('img onclick');
22285 this.fireEvent('click', this, e);
22297 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22300 * @class Roo.bootstrap.dash.NumberBox
22301 * @extends Roo.bootstrap.Component
22302 * Bootstrap NumberBox class
22303 * @cfg {String} headline Box headline
22304 * @cfg {String} content Box content
22305 * @cfg {String} icon Box icon
22306 * @cfg {String} footer Footer text
22307 * @cfg {String} fhref Footer href
22310 * Create a new NumberBox
22311 * @param {Object} config The config object
22315 Roo.bootstrap.dash.NumberBox = function(config){
22316 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22320 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22329 getAutoCreate : function(){
22333 cls : 'small-box ',
22341 cls : 'roo-headline',
22342 html : this.headline
22346 cls : 'roo-content',
22347 html : this.content
22361 cls : 'ion ' + this.icon
22370 cls : 'small-box-footer',
22371 href : this.fhref || '#',
22375 cfg.cn.push(footer);
22382 onRender : function(ct,position){
22383 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22390 setHeadline: function (value)
22392 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22395 setFooter: function (value, href)
22397 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22400 this.el.select('a.small-box-footer',true).first().attr('href', href);
22405 setContent: function (value)
22407 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22410 initEvents: function()
22424 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22427 * @class Roo.bootstrap.dash.TabBox
22428 * @extends Roo.bootstrap.Component
22429 * Bootstrap TabBox class
22430 * @cfg {String} title Title of the TabBox
22431 * @cfg {String} icon Icon of the TabBox
22432 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22433 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22436 * Create a new TabBox
22437 * @param {Object} config The config object
22441 Roo.bootstrap.dash.TabBox = function(config){
22442 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22447 * When a pane is added
22448 * @param {Roo.bootstrap.dash.TabPane} pane
22452 * @event activatepane
22453 * When a pane is activated
22454 * @param {Roo.bootstrap.dash.TabPane} pane
22456 "activatepane" : true
22464 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22469 tabScrollable : false,
22471 getChildContainer : function()
22473 return this.el.select('.tab-content', true).first();
22476 getAutoCreate : function(){
22480 cls: 'pull-left header',
22488 cls: 'fa ' + this.icon
22494 cls: 'nav nav-tabs pull-right',
22500 if(this.tabScrollable){
22507 cls: 'nav nav-tabs pull-right',
22518 cls: 'nav-tabs-custom',
22523 cls: 'tab-content no-padding',
22531 initEvents : function()
22533 //Roo.log('add add pane handler');
22534 this.on('addpane', this.onAddPane, this);
22537 * Updates the box title
22538 * @param {String} html to set the title to.
22540 setTitle : function(value)
22542 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22544 onAddPane : function(pane)
22546 this.panes.push(pane);
22547 //Roo.log('addpane');
22549 // tabs are rendere left to right..
22550 if(!this.showtabs){
22554 var ctr = this.el.select('.nav-tabs', true).first();
22557 var existing = ctr.select('.nav-tab',true);
22558 var qty = existing.getCount();;
22561 var tab = ctr.createChild({
22563 cls : 'nav-tab' + (qty ? '' : ' active'),
22571 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22574 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22576 pane.el.addClass('active');
22581 onTabClick : function(ev,un,ob,pane)
22583 //Roo.log('tab - prev default');
22584 ev.preventDefault();
22587 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22588 pane.tab.addClass('active');
22589 //Roo.log(pane.title);
22590 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22591 // technically we should have a deactivate event.. but maybe add later.
22592 // and it should not de-activate the selected tab...
22593 this.fireEvent('activatepane', pane);
22594 pane.el.addClass('active');
22595 pane.fireEvent('activate');
22600 getActivePane : function()
22603 Roo.each(this.panes, function(p) {
22604 if(p.el.hasClass('active')){
22625 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22627 * @class Roo.bootstrap.TabPane
22628 * @extends Roo.bootstrap.Component
22629 * Bootstrap TabPane class
22630 * @cfg {Boolean} active (false | true) Default false
22631 * @cfg {String} title title of panel
22635 * Create a new TabPane
22636 * @param {Object} config The config object
22639 Roo.bootstrap.dash.TabPane = function(config){
22640 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22646 * When a pane is activated
22647 * @param {Roo.bootstrap.dash.TabPane} pane
22654 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22659 // the tabBox that this is attached to.
22662 getAutoCreate : function()
22670 cfg.cls += ' active';
22675 initEvents : function()
22677 //Roo.log('trigger add pane handler');
22678 this.parent().fireEvent('addpane', this)
22682 * Updates the tab title
22683 * @param {String} html to set the title to.
22685 setTitle: function(str)
22691 this.tab.select('a', true).first().dom.innerHTML = str;
22708 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22711 * @class Roo.bootstrap.menu.Menu
22712 * @extends Roo.bootstrap.Component
22713 * Bootstrap Menu class - container for Menu
22714 * @cfg {String} html Text of the menu
22715 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22716 * @cfg {String} icon Font awesome icon
22717 * @cfg {String} pos Menu align to (top | bottom) default bottom
22721 * Create a new Menu
22722 * @param {Object} config The config object
22726 Roo.bootstrap.menu.Menu = function(config){
22727 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22731 * @event beforeshow
22732 * Fires before this menu is displayed
22733 * @param {Roo.bootstrap.menu.Menu} this
22737 * @event beforehide
22738 * Fires before this menu is hidden
22739 * @param {Roo.bootstrap.menu.Menu} this
22744 * Fires after this menu is displayed
22745 * @param {Roo.bootstrap.menu.Menu} this
22750 * Fires after this menu is hidden
22751 * @param {Roo.bootstrap.menu.Menu} this
22756 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22757 * @param {Roo.bootstrap.menu.Menu} this
22758 * @param {Roo.EventObject} e
22765 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22769 weight : 'default',
22774 getChildContainer : function() {
22775 if(this.isSubMenu){
22779 return this.el.select('ul.dropdown-menu', true).first();
22782 getAutoCreate : function()
22787 cls : 'roo-menu-text',
22795 cls : 'fa ' + this.icon
22806 cls : 'dropdown-button btn btn-' + this.weight,
22811 cls : 'dropdown-toggle btn btn-' + this.weight,
22821 cls : 'dropdown-menu'
22827 if(this.pos == 'top'){
22828 cfg.cls += ' dropup';
22831 if(this.isSubMenu){
22834 cls : 'dropdown-menu'
22841 onRender : function(ct, position)
22843 this.isSubMenu = ct.hasClass('dropdown-submenu');
22845 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22848 initEvents : function()
22850 if(this.isSubMenu){
22854 this.hidden = true;
22856 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22857 this.triggerEl.on('click', this.onTriggerPress, this);
22859 this.buttonEl = this.el.select('button.dropdown-button', true).first();
22860 this.buttonEl.on('click', this.onClick, this);
22866 if(this.isSubMenu){
22870 return this.el.select('ul.dropdown-menu', true).first();
22873 onClick : function(e)
22875 this.fireEvent("click", this, e);
22878 onTriggerPress : function(e)
22880 if (this.isVisible()) {
22887 isVisible : function(){
22888 return !this.hidden;
22893 this.fireEvent("beforeshow", this);
22895 this.hidden = false;
22896 this.el.addClass('open');
22898 Roo.get(document).on("mouseup", this.onMouseUp, this);
22900 this.fireEvent("show", this);
22907 this.fireEvent("beforehide", this);
22909 this.hidden = true;
22910 this.el.removeClass('open');
22912 Roo.get(document).un("mouseup", this.onMouseUp);
22914 this.fireEvent("hide", this);
22917 onMouseUp : function()
22931 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22934 * @class Roo.bootstrap.menu.Item
22935 * @extends Roo.bootstrap.Component
22936 * Bootstrap MenuItem class
22937 * @cfg {Boolean} submenu (true | false) default false
22938 * @cfg {String} html text of the item
22939 * @cfg {String} href the link
22940 * @cfg {Boolean} disable (true | false) default false
22941 * @cfg {Boolean} preventDefault (true | false) default true
22942 * @cfg {String} icon Font awesome icon
22943 * @cfg {String} pos Submenu align to (left | right) default right
22947 * Create a new Item
22948 * @param {Object} config The config object
22952 Roo.bootstrap.menu.Item = function(config){
22953 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22957 * Fires when the mouse is hovering over this menu
22958 * @param {Roo.bootstrap.menu.Item} this
22959 * @param {Roo.EventObject} e
22964 * Fires when the mouse exits this menu
22965 * @param {Roo.bootstrap.menu.Item} this
22966 * @param {Roo.EventObject} e
22972 * The raw click event for the entire grid.
22973 * @param {Roo.EventObject} e
22979 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
22984 preventDefault: true,
22989 getAutoCreate : function()
22994 cls : 'roo-menu-item-text',
23002 cls : 'fa ' + this.icon
23011 href : this.href || '#',
23018 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23022 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23024 if(this.pos == 'left'){
23025 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23032 initEvents : function()
23034 this.el.on('mouseover', this.onMouseOver, this);
23035 this.el.on('mouseout', this.onMouseOut, this);
23037 this.el.select('a', true).first().on('click', this.onClick, this);
23041 onClick : function(e)
23043 if(this.preventDefault){
23044 e.preventDefault();
23047 this.fireEvent("click", this, e);
23050 onMouseOver : function(e)
23052 if(this.submenu && this.pos == 'left'){
23053 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23056 this.fireEvent("mouseover", this, e);
23059 onMouseOut : function(e)
23061 this.fireEvent("mouseout", this, e);
23073 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23076 * @class Roo.bootstrap.menu.Separator
23077 * @extends Roo.bootstrap.Component
23078 * Bootstrap Separator class
23081 * Create a new Separator
23082 * @param {Object} config The config object
23086 Roo.bootstrap.menu.Separator = function(config){
23087 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23090 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23092 getAutoCreate : function(){
23113 * @class Roo.bootstrap.Tooltip
23114 * Bootstrap Tooltip class
23115 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23116 * to determine which dom element triggers the tooltip.
23118 * It needs to add support for additional attributes like tooltip-position
23121 * Create a new Toolti
23122 * @param {Object} config The config object
23125 Roo.bootstrap.Tooltip = function(config){
23126 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23129 Roo.apply(Roo.bootstrap.Tooltip, {
23131 * @function init initialize tooltip monitoring.
23135 currentTip : false,
23136 currentRegion : false,
23142 Roo.get(document).on('mouseover', this.enter ,this);
23143 Roo.get(document).on('mouseout', this.leave, this);
23146 this.currentTip = new Roo.bootstrap.Tooltip();
23149 enter : function(ev)
23151 var dom = ev.getTarget();
23153 //Roo.log(['enter',dom]);
23154 var el = Roo.fly(dom);
23155 if (this.currentEl) {
23157 //Roo.log(this.currentEl);
23158 //Roo.log(this.currentEl.contains(dom));
23159 if (this.currentEl == el) {
23162 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23170 if (this.currentTip.el) {
23171 this.currentTip.el.hide(); // force hiding...
23176 // you can not look for children, as if el is the body.. then everythign is the child..
23177 if (!el.attr('tooltip')) { //
23178 if (!el.select("[tooltip]").elements.length) {
23181 // is the mouse over this child...?
23182 bindEl = el.select("[tooltip]").first();
23183 var xy = ev.getXY();
23184 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23185 //Roo.log("not in region.");
23188 //Roo.log("child element over..");
23191 this.currentEl = bindEl;
23192 this.currentTip.bind(bindEl);
23193 this.currentRegion = Roo.lib.Region.getRegion(dom);
23194 this.currentTip.enter();
23197 leave : function(ev)
23199 var dom = ev.getTarget();
23200 //Roo.log(['leave',dom]);
23201 if (!this.currentEl) {
23206 if (dom != this.currentEl.dom) {
23209 var xy = ev.getXY();
23210 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23213 // only activate leave if mouse cursor is outside... bounding box..
23218 if (this.currentTip) {
23219 this.currentTip.leave();
23221 //Roo.log('clear currentEl');
23222 this.currentEl = false;
23227 'left' : ['r-l', [-2,0], 'right'],
23228 'right' : ['l-r', [2,0], 'left'],
23229 'bottom' : ['t-b', [0,2], 'top'],
23230 'top' : [ 'b-t', [0,-2], 'bottom']
23236 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23241 delay : null, // can be { show : 300 , hide: 500}
23245 hoverState : null, //???
23247 placement : 'bottom',
23249 getAutoCreate : function(){
23256 cls : 'tooltip-arrow'
23259 cls : 'tooltip-inner'
23266 bind : function(el)
23272 enter : function () {
23274 if (this.timeout != null) {
23275 clearTimeout(this.timeout);
23278 this.hoverState = 'in';
23279 //Roo.log("enter - show");
23280 if (!this.delay || !this.delay.show) {
23285 this.timeout = setTimeout(function () {
23286 if (_t.hoverState == 'in') {
23289 }, this.delay.show);
23293 clearTimeout(this.timeout);
23295 this.hoverState = 'out';
23296 if (!this.delay || !this.delay.hide) {
23302 this.timeout = setTimeout(function () {
23303 //Roo.log("leave - timeout");
23305 if (_t.hoverState == 'out') {
23307 Roo.bootstrap.Tooltip.currentEl = false;
23315 this.render(document.body);
23318 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23320 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23322 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23324 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23326 var placement = typeof this.placement == 'function' ?
23327 this.placement.call(this, this.el, on_el) :
23330 var autoToken = /\s?auto?\s?/i;
23331 var autoPlace = autoToken.test(placement);
23333 placement = placement.replace(autoToken, '') || 'top';
23337 //this.el.setXY([0,0]);
23339 //this.el.dom.style.display='block';
23340 this.el.addClass(placement);
23342 //this.el.appendTo(on_el);
23344 var p = this.getPosition();
23345 var box = this.el.getBox();
23350 var align = Roo.bootstrap.Tooltip.alignment[placement];
23351 this.el.alignTo(this.bindEl, align[0],align[1]);
23352 //var arrow = this.el.select('.arrow',true).first();
23353 //arrow.set(align[2],
23355 this.el.addClass('in fade');
23356 this.hoverState = null;
23358 if (this.el.hasClass('fade')) {
23369 //this.el.setXY([0,0]);
23370 this.el.removeClass('in');
23386 * @class Roo.bootstrap.LocationPicker
23387 * @extends Roo.bootstrap.Component
23388 * Bootstrap LocationPicker class
23389 * @cfg {Number} latitude Position when init default 0
23390 * @cfg {Number} longitude Position when init default 0
23391 * @cfg {Number} zoom default 15
23392 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23393 * @cfg {Boolean} mapTypeControl default false
23394 * @cfg {Boolean} disableDoubleClickZoom default false
23395 * @cfg {Boolean} scrollwheel default true
23396 * @cfg {Boolean} streetViewControl default false
23397 * @cfg {Number} radius default 0
23398 * @cfg {String} locationName
23399 * @cfg {Boolean} draggable default true
23400 * @cfg {Boolean} enableAutocomplete default false
23401 * @cfg {Boolean} enableReverseGeocode default true
23402 * @cfg {String} markerTitle
23405 * Create a new LocationPicker
23406 * @param {Object} config The config object
23410 Roo.bootstrap.LocationPicker = function(config){
23412 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23417 * Fires when the picker initialized.
23418 * @param {Roo.bootstrap.LocationPicker} this
23419 * @param {Google Location} location
23423 * @event positionchanged
23424 * Fires when the picker position changed.
23425 * @param {Roo.bootstrap.LocationPicker} this
23426 * @param {Google Location} location
23428 positionchanged : true,
23431 * Fires when the map resize.
23432 * @param {Roo.bootstrap.LocationPicker} this
23437 * Fires when the map show.
23438 * @param {Roo.bootstrap.LocationPicker} this
23443 * Fires when the map hide.
23444 * @param {Roo.bootstrap.LocationPicker} this
23449 * Fires when click the map.
23450 * @param {Roo.bootstrap.LocationPicker} this
23451 * @param {Map event} e
23455 * @event mapRightClick
23456 * Fires when right click the map.
23457 * @param {Roo.bootstrap.LocationPicker} this
23458 * @param {Map event} e
23460 mapRightClick : true,
23462 * @event markerClick
23463 * Fires when click the marker.
23464 * @param {Roo.bootstrap.LocationPicker} this
23465 * @param {Map event} e
23467 markerClick : true,
23469 * @event markerRightClick
23470 * Fires when right click the marker.
23471 * @param {Roo.bootstrap.LocationPicker} this
23472 * @param {Map event} e
23474 markerRightClick : true,
23476 * @event OverlayViewDraw
23477 * Fires when OverlayView Draw
23478 * @param {Roo.bootstrap.LocationPicker} this
23480 OverlayViewDraw : true,
23482 * @event OverlayViewOnAdd
23483 * Fires when OverlayView Draw
23484 * @param {Roo.bootstrap.LocationPicker} this
23486 OverlayViewOnAdd : true,
23488 * @event OverlayViewOnRemove
23489 * Fires when OverlayView Draw
23490 * @param {Roo.bootstrap.LocationPicker} this
23492 OverlayViewOnRemove : true,
23494 * @event OverlayViewShow
23495 * Fires when OverlayView Draw
23496 * @param {Roo.bootstrap.LocationPicker} this
23497 * @param {Pixel} cpx
23499 OverlayViewShow : true,
23501 * @event OverlayViewHide
23502 * Fires when OverlayView Draw
23503 * @param {Roo.bootstrap.LocationPicker} this
23505 OverlayViewHide : true
23510 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23512 gMapContext: false,
23518 mapTypeControl: false,
23519 disableDoubleClickZoom: false,
23521 streetViewControl: false,
23525 enableAutocomplete: false,
23526 enableReverseGeocode: true,
23529 getAutoCreate: function()
23534 cls: 'roo-location-picker'
23540 initEvents: function(ct, position)
23542 if(!this.el.getWidth() || this.isApplied()){
23546 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23551 initial: function()
23553 if(!this.mapTypeId){
23554 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23557 this.gMapContext = this.GMapContext();
23559 this.initOverlayView();
23561 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23565 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23566 _this.setPosition(_this.gMapContext.marker.position);
23569 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23570 _this.fireEvent('mapClick', this, event);
23574 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23575 _this.fireEvent('mapRightClick', this, event);
23579 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23580 _this.fireEvent('markerClick', this, event);
23584 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23585 _this.fireEvent('markerRightClick', this, event);
23589 this.setPosition(this.gMapContext.location);
23591 this.fireEvent('initial', this, this.gMapContext.location);
23594 initOverlayView: function()
23598 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23602 _this.fireEvent('OverlayViewDraw', _this);
23607 _this.fireEvent('OverlayViewOnAdd', _this);
23610 onRemove: function()
23612 _this.fireEvent('OverlayViewOnRemove', _this);
23615 show: function(cpx)
23617 _this.fireEvent('OverlayViewShow', _this, cpx);
23622 _this.fireEvent('OverlayViewHide', _this);
23628 fromLatLngToContainerPixel: function(event)
23630 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23633 isApplied: function()
23635 return this.getGmapContext() == false ? false : true;
23638 getGmapContext: function()
23640 return this.gMapContext
23643 GMapContext: function()
23645 var position = new google.maps.LatLng(this.latitude, this.longitude);
23647 var _map = new google.maps.Map(this.el.dom, {
23650 mapTypeId: this.mapTypeId,
23651 mapTypeControl: this.mapTypeControl,
23652 disableDoubleClickZoom: this.disableDoubleClickZoom,
23653 scrollwheel: this.scrollwheel,
23654 streetViewControl: this.streetViewControl,
23655 locationName: this.locationName,
23656 draggable: this.draggable,
23657 enableAutocomplete: this.enableAutocomplete,
23658 enableReverseGeocode: this.enableReverseGeocode
23661 var _marker = new google.maps.Marker({
23662 position: position,
23664 title: this.markerTitle,
23665 draggable: this.draggable
23672 location: position,
23673 radius: this.radius,
23674 locationName: this.locationName,
23675 addressComponents: {
23676 formatted_address: null,
23677 addressLine1: null,
23678 addressLine2: null,
23680 streetNumber: null,
23684 stateOrProvince: null
23687 domContainer: this.el.dom,
23688 geodecoder: new google.maps.Geocoder()
23692 drawCircle: function(center, radius, options)
23694 if (this.gMapContext.circle != null) {
23695 this.gMapContext.circle.setMap(null);
23699 options = Roo.apply({}, options, {
23700 strokeColor: "#0000FF",
23701 strokeOpacity: .35,
23703 fillColor: "#0000FF",
23707 options.map = this.gMapContext.map;
23708 options.radius = radius;
23709 options.center = center;
23710 this.gMapContext.circle = new google.maps.Circle(options);
23711 return this.gMapContext.circle;
23717 setPosition: function(location)
23719 this.gMapContext.location = location;
23720 this.gMapContext.marker.setPosition(location);
23721 this.gMapContext.map.panTo(location);
23722 this.drawCircle(location, this.gMapContext.radius, {});
23726 if (this.gMapContext.settings.enableReverseGeocode) {
23727 this.gMapContext.geodecoder.geocode({
23728 latLng: this.gMapContext.location
23729 }, function(results, status) {
23731 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23732 _this.gMapContext.locationName = results[0].formatted_address;
23733 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23735 _this.fireEvent('positionchanged', this, location);
23742 this.fireEvent('positionchanged', this, location);
23747 google.maps.event.trigger(this.gMapContext.map, "resize");
23749 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23751 this.fireEvent('resize', this);
23754 setPositionByLatLng: function(latitude, longitude)
23756 this.setPosition(new google.maps.LatLng(latitude, longitude));
23759 getCurrentPosition: function()
23762 latitude: this.gMapContext.location.lat(),
23763 longitude: this.gMapContext.location.lng()
23767 getAddressName: function()
23769 return this.gMapContext.locationName;
23772 getAddressComponents: function()
23774 return this.gMapContext.addressComponents;
23777 address_component_from_google_geocode: function(address_components)
23781 for (var i = 0; i < address_components.length; i++) {
23782 var component = address_components[i];
23783 if (component.types.indexOf("postal_code") >= 0) {
23784 result.postalCode = component.short_name;
23785 } else if (component.types.indexOf("street_number") >= 0) {
23786 result.streetNumber = component.short_name;
23787 } else if (component.types.indexOf("route") >= 0) {
23788 result.streetName = component.short_name;
23789 } else if (component.types.indexOf("neighborhood") >= 0) {
23790 result.city = component.short_name;
23791 } else if (component.types.indexOf("locality") >= 0) {
23792 result.city = component.short_name;
23793 } else if (component.types.indexOf("sublocality") >= 0) {
23794 result.district = component.short_name;
23795 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23796 result.stateOrProvince = component.short_name;
23797 } else if (component.types.indexOf("country") >= 0) {
23798 result.country = component.short_name;
23802 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23803 result.addressLine2 = "";
23807 setZoomLevel: function(zoom)
23809 this.gMapContext.map.setZoom(zoom);
23822 this.fireEvent('show', this);
23833 this.fireEvent('hide', this);
23838 Roo.apply(Roo.bootstrap.LocationPicker, {
23840 OverlayView : function(map, options)
23842 options = options || {};
23856 * @class Roo.bootstrap.Alert
23857 * @extends Roo.bootstrap.Component
23858 * Bootstrap Alert class
23859 * @cfg {String} title The title of alert
23860 * @cfg {String} html The content of alert
23861 * @cfg {String} weight ( success | info | warning | danger )
23862 * @cfg {String} faicon font-awesomeicon
23865 * Create a new alert
23866 * @param {Object} config The config object
23870 Roo.bootstrap.Alert = function(config){
23871 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23875 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
23882 getAutoCreate : function()
23891 cls : 'roo-alert-icon'
23896 cls : 'roo-alert-title',
23901 cls : 'roo-alert-text',
23908 cfg.cn[0].cls += ' fa ' + this.faicon;
23912 cfg.cls += ' alert-' + this.weight;
23918 initEvents: function()
23920 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23923 setTitle : function(str)
23925 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23928 setText : function(str)
23930 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23933 setWeight : function(weight)
23936 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23939 this.weight = weight;
23941 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23944 setIcon : function(icon)
23947 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23952 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23973 * @class Roo.bootstrap.UploadCropbox
23974 * @extends Roo.bootstrap.Component
23975 * Bootstrap UploadCropbox class
23976 * @cfg {String} emptyText show when image has been loaded
23977 * @cfg {String} rotateNotify show when image too small to rotate
23978 * @cfg {Number} errorTimeout default 3000
23979 * @cfg {Number} minWidth default 300
23980 * @cfg {Number} minHeight default 300
23981 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23984 * Create a new UploadCropbox
23985 * @param {Object} config The config object
23988 Roo.bootstrap.UploadCropbox = function(config){
23989 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23993 * @event beforeselectfile
23994 * Fire before select file
23995 * @param {Roo.bootstrap.UploadCropbox} this
23997 "beforeselectfile" : true,
24000 * Fire after initEvent
24001 * @param {Roo.bootstrap.UploadCropbox} this
24006 * Fire after initEvent
24007 * @param {Roo.bootstrap.UploadCropbox} this
24008 * @param {String} data
24013 * Fire when preparing the file data
24014 * @param {Roo.bootstrap.UploadCropbox} this
24015 * @param {Object} file
24020 * Fire when get exception
24021 * @param {Roo.bootstrap.UploadCropbox} this
24022 * @param {Object} options
24024 "exception" : true,
24026 * @event beforeloadcanvas
24027 * Fire before load the canvas
24028 * @param {Roo.bootstrap.UploadCropbox} this
24029 * @param {String} src
24031 "beforeloadcanvas" : true,
24034 * Fire when trash image
24035 * @param {Roo.bootstrap.UploadCropbox} this
24040 * Fire when download the image
24041 * @param {Roo.bootstrap.UploadCropbox} this
24045 * @event footerbuttonclick
24046 * Fire when footerbuttonclick
24047 * @param {Roo.bootstrap.UploadCropbox} this
24048 * @param {String} type
24050 "footerbuttonclick" : true,
24054 * @param {Roo.bootstrap.UploadCropbox} this
24060 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24063 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24065 emptyText : 'Click to upload image',
24066 rotateNotify : 'Image is too small to rotate',
24067 errorTimeout : 3000,
24081 cropType : 'image/jpeg',
24083 canvasLoaded : false,
24085 getAutoCreate : function()
24089 cls : 'roo-upload-cropbox',
24093 cls : 'roo-upload-cropbox-body',
24094 style : 'cursor:pointer',
24098 cls : 'roo-upload-cropbox-preview'
24102 cls : 'roo-upload-cropbox-thumb'
24106 cls : 'roo-upload-cropbox-empty-notify',
24107 html : this.emptyText
24111 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24112 html : this.rotateNotify
24118 cls : 'roo-upload-cropbox-footer',
24121 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24131 onRender : function(ct, position)
24133 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24135 if (this.buttons.length) {
24137 Roo.each(this.buttons, function(bb) {
24139 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24141 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24147 initEvents : function()
24149 this.urlAPI = (window.createObjectURL && window) ||
24150 (window.URL && URL.revokeObjectURL && URL) ||
24151 (window.webkitURL && webkitURL);
24153 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24154 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24156 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24157 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24159 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24160 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24161 this.thumbEl.hide();
24163 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24164 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24166 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24167 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24168 this.errorEl.hide();
24170 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24171 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24172 this.footerEl.hide();
24174 this.setThumbBoxSize();
24180 this.fireEvent('initial', this);
24187 window.addEventListener("resize", function() { _this.resize(); } );
24189 this.bodyEl.on('click', this.beforeSelectFile, this);
24192 this.bodyEl.on('touchstart', this.onTouchStart, this);
24193 this.bodyEl.on('touchmove', this.onTouchMove, this);
24194 this.bodyEl.on('touchend', this.onTouchEnd, this);
24198 this.bodyEl.on('mousedown', this.onMouseDown, this);
24199 this.bodyEl.on('mousemove', this.onMouseMove, this);
24200 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24201 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24202 Roo.get(document).on('mouseup', this.onMouseUp, this);
24209 this.baseScale = 1;
24211 this.baseRotate = 1;
24212 this.dragable = false;
24213 this.pinching = false;
24216 this.cropData = false;
24217 this.notifyEl.dom.innerHTML = this.emptyText;
24221 resize : function()
24223 if(this.fireEvent('resize', this) != false){
24224 this.setThumbBoxPosition();
24225 this.setCanvasPosition();
24229 onFooterButtonClick : function(e, el, o, type)
24232 case 'rotate-left' :
24233 this.onRotateLeft(e);
24235 case 'rotate-right' :
24236 this.onRotateRight(e);
24239 this.beforeSelectFile(e);
24254 this.fireEvent('footerbuttonclick', this, type);
24257 beforeSelectFile : function(e)
24259 this.fireEvent('beforeselectfile', this);
24262 trash : function(e)
24264 this.fireEvent('trash', this);
24267 download : function(e)
24269 this.fireEvent('download', this);
24272 loadCanvas : function(src)
24274 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24278 this.imageEl = document.createElement('img');
24282 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24284 this.imageEl.src = src;
24288 onLoadCanvas : function()
24290 this.bodyEl.un('click', this.beforeSelectFile, this);
24292 this.notifyEl.hide();
24293 this.thumbEl.show();
24294 this.footerEl.show();
24296 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24297 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24299 this.setThumbBoxPosition();
24300 this.baseRotateLevel();
24301 this.baseScaleLevel();
24307 this.canvasLoaded = true;
24311 setCanvasPosition : function()
24313 if(!this.canvasEl){
24317 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24318 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24320 this.previewEl.setLeft(pw);
24321 this.previewEl.setTop(ph);
24325 onMouseDown : function(e)
24329 this.dragable = true;
24330 this.pinching = false;
24332 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24333 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24337 onMouseMove : function(e)
24341 if(!this.canvasLoaded){
24345 if (!this.dragable){
24349 var minX = Math.ceil(this.thumbEl.getLeft(true));
24350 var minY = Math.ceil(this.thumbEl.getTop(true));
24352 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24353 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24355 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24356 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24358 x = x - this.mouseX;
24359 y = y - this.mouseY;
24361 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24362 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24364 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24365 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24367 this.previewEl.setLeft(bgX);
24368 this.previewEl.setTop(bgY);
24370 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24371 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24374 onMouseUp : function(e)
24378 this.dragable = false;
24381 onMouseWheel : function(e)
24385 this.startScale = this.scale;
24387 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24389 if(!this.zoomable()){
24390 this.scale = this.startScale;
24399 zoomable : function()
24401 var minScale = this.thumbEl.getWidth() / this.minWidth;
24403 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24404 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24407 (this.rotate == 0 || this.rotate == 180) &&
24409 width / minScale < this.minWidth ||
24410 width / minScale > this.imageEl.OriginWidth ||
24411 height / minScale < this.minHeight ||
24412 height / minScale > this.imageEl.OriginHeight
24419 (this.rotate == 90 || this.rotate == 270) &&
24421 width / minScale < this.minHeight ||
24422 width / minScale > this.imageEl.OriginWidth ||
24423 height / minScale < this.minWidth ||
24424 height / minScale > this.imageEl.OriginHeight
24434 onRotateLeft : function(e)
24436 var minScale = this.thumbEl.getWidth() / this.minWidth;
24438 if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24440 var bw = this.canvasEl.width / this.getScaleLevel();
24441 var bh = this.canvasEl.height / this.getScaleLevel();
24443 this.startScale = this.scale;
24445 while (this.getScaleLevel() < minScale){
24447 this.scale = this.scale + 1;
24449 if(!this.zoomable()){
24454 bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24455 bh * this.getScaleLevel() < this.thumbEl.getWidth()
24460 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24467 this.scale = this.startScale;
24469 this.onRotateFail();
24474 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24480 onRotateRight : function(e)
24482 var minScale = this.thumbEl.getWidth() / this.minWidth;
24484 if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24486 var bw = this.canvasEl.width / this.getScaleLevel();
24487 var bh = this.canvasEl.height / this.getScaleLevel();
24489 this.startScale = this.scale;
24491 while (this.getScaleLevel() < minScale){
24493 this.scale = this.scale + 1;
24495 if(!this.zoomable()){
24500 bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24501 bh * this.getScaleLevel() < this.thumbEl.getWidth()
24506 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24513 this.scale = this.startScale;
24515 this.onRotateFail();
24520 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24525 onRotateFail : function()
24527 this.errorEl.show(true);
24531 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24536 this.previewEl.dom.innerHTML = '';
24538 var canvasEl = document.createElement("canvas");
24540 var contextEl = canvasEl.getContext("2d");
24542 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24543 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24544 var center = this.imageEl.OriginWidth / 2;
24546 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24547 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24548 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24549 center = this.imageEl.OriginHeight / 2;
24552 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24554 contextEl.translate(center, center);
24555 contextEl.rotate(this.rotate * Math.PI / 180);
24557 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24559 this.canvasEl = document.createElement("canvas");
24561 this.contextEl = this.canvasEl.getContext("2d");
24563 switch (this.rotate) {
24566 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24567 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24569 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24574 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24575 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24577 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24578 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);
24582 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24587 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24588 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24590 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24591 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);
24595 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);
24600 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24601 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24603 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24604 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24608 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);
24615 this.previewEl.appendChild(this.canvasEl);
24617 this.setCanvasPosition();
24622 if(!this.canvasLoaded){
24625 var canvas = document.createElement("canvas");
24627 var context = canvas.getContext("2d");
24629 canvas.width = this.minWidth;
24630 canvas.height = this.minHeight;
24632 var cropWidth = this.thumbEl.getWidth();
24633 var cropHeight = this.thumbEl.getHeight();
24635 var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24636 var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24638 if(this.canvasEl.width - cropWidth < x){
24639 x = this.canvasEl.width - cropWidth;
24642 if(this.canvasEl.height - cropHeight < y){
24643 y = this.canvasEl.height - cropHeight;
24649 context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24651 this.cropData = canvas.toDataURL(this.cropType);
24653 this.fireEvent('crop', this, this.cropData);
24657 setThumbBoxSize : function()
24660 var width = Math.ceil(this.minWidth * height / this.minHeight);
24662 if(this.minWidth > this.minHeight){
24664 height = Math.ceil(this.minHeight * width / this.minWidth);
24667 this.thumbEl.setStyle({
24668 width : width + 'px',
24669 height : height + 'px'
24676 setThumbBoxPosition : function()
24678 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24679 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24681 this.thumbEl.setLeft(x);
24682 this.thumbEl.setTop(y);
24686 baseRotateLevel : function()
24688 this.baseRotate = 1;
24691 typeof(this.exif) != 'undefined' &&
24692 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24693 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24695 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24698 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24702 baseScaleLevel : function()
24706 if(this.baseRotate == 6 || this.baseRotate == 8){
24708 width = this.thumbEl.getHeight();
24709 this.baseScale = height / this.imageEl.OriginHeight;
24711 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24712 height = this.thumbEl.getWidth();
24713 this.baseScale = height / this.imageEl.OriginHeight;
24716 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24717 height = this.thumbEl.getWidth();
24718 this.baseScale = height / this.imageEl.OriginHeight;
24720 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24721 width = this.thumbEl.getHeight();
24722 this.baseScale = width / this.imageEl.OriginWidth;
24729 width = this.thumbEl.getWidth();
24730 this.baseScale = width / this.imageEl.OriginWidth;
24732 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24733 height = this.thumbEl.getHeight();
24734 this.baseScale = height / this.imageEl.OriginHeight;
24738 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24740 height = this.thumbEl.getHeight();
24741 this.baseScale = height / this.imageEl.OriginHeight;
24743 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24744 width = this.thumbEl.getWidth();
24745 this.baseScale = width / this.imageEl.OriginWidth;
24753 getScaleLevel : function()
24755 return this.baseScale * Math.pow(1.1, this.scale);
24758 onTouchStart : function(e)
24760 if(!this.canvasLoaded){
24761 this.beforeSelectFile(e);
24765 var touches = e.browserEvent.touches;
24771 if(touches.length == 1){
24772 this.onMouseDown(e);
24776 if(touches.length != 2){
24782 for(var i = 0, finger; finger = touches[i]; i++){
24783 coords.push(finger.pageX, finger.pageY);
24786 var x = Math.pow(coords[0] - coords[2], 2);
24787 var y = Math.pow(coords[1] - coords[3], 2);
24789 this.startDistance = Math.sqrt(x + y);
24791 this.startScale = this.scale;
24793 this.pinching = true;
24794 this.dragable = false;
24798 onTouchMove : function(e)
24800 if(!this.pinching && !this.dragable){
24804 var touches = e.browserEvent.touches;
24811 this.onMouseMove(e);
24817 for(var i = 0, finger; finger = touches[i]; i++){
24818 coords.push(finger.pageX, finger.pageY);
24821 var x = Math.pow(coords[0] - coords[2], 2);
24822 var y = Math.pow(coords[1] - coords[3], 2);
24824 this.endDistance = Math.sqrt(x + y);
24826 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24828 if(!this.zoomable()){
24829 this.scale = this.startScale;
24837 onTouchEnd : function(e)
24839 this.pinching = false;
24840 this.dragable = false;
24844 prepare : function(input)
24849 if(typeof(input) === 'string'){
24850 this.loadCanvas(input);
24854 if(!input.files || !input.files[0] || !this.urlAPI){
24858 this.file = input.files[0];
24859 this.cropType = this.file.type;
24863 if(this.fireEvent('prepare', this, this.file) != false){
24865 var reader = new FileReader();
24867 reader.onload = function (e) {
24868 if (e.target.error) {
24869 Roo.log(e.target.error);
24873 var buffer = e.target.result,
24874 dataView = new DataView(buffer),
24876 maxOffset = dataView.byteLength - 4,
24880 if (dataView.getUint16(0) === 0xffd8) {
24881 while (offset < maxOffset) {
24882 markerBytes = dataView.getUint16(offset);
24884 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24885 markerLength = dataView.getUint16(offset + 2) + 2;
24886 if (offset + markerLength > dataView.byteLength) {
24887 Roo.log('Invalid meta data: Invalid segment size.');
24891 if(markerBytes == 0xffe1){
24892 _this.parseExifData(
24899 offset += markerLength;
24909 var url = _this.urlAPI.createObjectURL(_this.file);
24911 _this.loadCanvas(url);
24916 reader.readAsArrayBuffer(this.file);
24922 parseExifData : function(dataView, offset, length)
24924 var tiffOffset = offset + 10,
24928 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24929 // No Exif data, might be XMP data instead
24933 // Check for the ASCII code for "Exif" (0x45786966):
24934 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24935 // No Exif data, might be XMP data instead
24938 if (tiffOffset + 8 > dataView.byteLength) {
24939 Roo.log('Invalid Exif data: Invalid segment size.');
24942 // Check for the two null bytes:
24943 if (dataView.getUint16(offset + 8) !== 0x0000) {
24944 Roo.log('Invalid Exif data: Missing byte alignment offset.');
24947 // Check the byte alignment:
24948 switch (dataView.getUint16(tiffOffset)) {
24950 littleEndian = true;
24953 littleEndian = false;
24956 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24959 // Check for the TIFF tag marker (0x002A):
24960 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24961 Roo.log('Invalid Exif data: Missing TIFF marker.');
24964 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24965 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24967 this.parseExifTags(
24970 tiffOffset + dirOffset,
24975 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24980 if (dirOffset + 6 > dataView.byteLength) {
24981 Roo.log('Invalid Exif data: Invalid directory offset.');
24984 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24985 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24986 if (dirEndOffset + 4 > dataView.byteLength) {
24987 Roo.log('Invalid Exif data: Invalid directory size.');
24990 for (i = 0; i < tagsNumber; i += 1) {
24994 dirOffset + 2 + 12 * i, // tag offset
24998 // Return the offset to the next directory:
24999 return dataView.getUint32(dirEndOffset, littleEndian);
25002 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25004 var tag = dataView.getUint16(offset, littleEndian);
25006 this.exif[tag] = this.getExifValue(
25010 dataView.getUint16(offset + 2, littleEndian), // tag type
25011 dataView.getUint32(offset + 4, littleEndian), // tag length
25016 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25018 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25027 Roo.log('Invalid Exif data: Invalid tag type.');
25031 tagSize = tagType.size * length;
25032 // Determine if the value is contained in the dataOffset bytes,
25033 // or if the value at the dataOffset is a pointer to the actual data:
25034 dataOffset = tagSize > 4 ?
25035 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25036 if (dataOffset + tagSize > dataView.byteLength) {
25037 Roo.log('Invalid Exif data: Invalid data offset.');
25040 if (length === 1) {
25041 return tagType.getValue(dataView, dataOffset, littleEndian);
25044 for (i = 0; i < length; i += 1) {
25045 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25048 if (tagType.ascii) {
25050 // Concatenate the chars:
25051 for (i = 0; i < values.length; i += 1) {
25053 // Ignore the terminating NULL byte(s):
25054 if (c === '\u0000') {
25066 Roo.apply(Roo.bootstrap.UploadCropbox, {
25068 'Orientation': 0x0112
25072 1: 0, //'top-left',
25074 3: 180, //'bottom-right',
25075 // 4: 'bottom-left',
25077 6: 90, //'right-top',
25078 // 7: 'right-bottom',
25079 8: 270 //'left-bottom'
25083 // byte, 8-bit unsigned int:
25085 getValue: function (dataView, dataOffset) {
25086 return dataView.getUint8(dataOffset);
25090 // ascii, 8-bit byte:
25092 getValue: function (dataView, dataOffset) {
25093 return String.fromCharCode(dataView.getUint8(dataOffset));
25098 // short, 16 bit int:
25100 getValue: function (dataView, dataOffset, littleEndian) {
25101 return dataView.getUint16(dataOffset, littleEndian);
25105 // long, 32 bit int:
25107 getValue: function (dataView, dataOffset, littleEndian) {
25108 return dataView.getUint32(dataOffset, littleEndian);
25112 // rational = two long values, first is numerator, second is denominator:
25114 getValue: function (dataView, dataOffset, littleEndian) {
25115 return dataView.getUint32(dataOffset, littleEndian) /
25116 dataView.getUint32(dataOffset + 4, littleEndian);
25120 // slong, 32 bit signed int:
25122 getValue: function (dataView, dataOffset, littleEndian) {
25123 return dataView.getInt32(dataOffset, littleEndian);
25127 // srational, two slongs, first is numerator, second is denominator:
25129 getValue: function (dataView, dataOffset, littleEndian) {
25130 return dataView.getInt32(dataOffset, littleEndian) /
25131 dataView.getInt32(dataOffset + 4, littleEndian);
25141 cls : 'btn-group roo-upload-cropbox-rotate-left',
25142 action : 'rotate-left',
25146 cls : 'btn btn-default',
25147 html : '<i class="fa fa-undo"></i>'
25153 cls : 'btn-group roo-upload-cropbox-picture',
25154 action : 'picture',
25158 cls : 'btn btn-default',
25159 html : '<i class="fa fa-picture-o"></i>'
25165 cls : 'btn-group roo-upload-cropbox-rotate-right',
25166 action : 'rotate-right',
25170 cls : 'btn btn-default',
25171 html : '<i class="fa fa-repeat"></i>'
25179 cls : 'btn-group roo-upload-cropbox-rotate-left',
25180 action : 'rotate-left',
25184 cls : 'btn btn-default',
25185 html : '<i class="fa fa-undo"></i>'
25191 cls : 'btn-group roo-upload-cropbox-download',
25192 action : 'download',
25196 cls : 'btn btn-default',
25197 html : '<i class="fa fa-download"></i>'
25203 cls : 'btn-group roo-upload-cropbox-crop',
25208 cls : 'btn btn-default',
25209 html : '<i class="fa fa-crop"></i>'
25215 cls : 'btn-group roo-upload-cropbox-trash',
25220 cls : 'btn btn-default',
25221 html : '<i class="fa fa-trash"></i>'
25227 cls : 'btn-group roo-upload-cropbox-rotate-right',
25228 action : 'rotate-right',
25232 cls : 'btn btn-default',
25233 html : '<i class="fa fa-repeat"></i>'
25246 * @class Roo.bootstrap.DocumentManager
25247 * @extends Roo.bootstrap.Component
25248 * Bootstrap DocumentManager class
25249 * @cfg {String} paramName default 'imageUpload'
25250 * @cfg {String} method default POST
25251 * @cfg {String} url action url
25252 * @cfg {Number} boxes number of boxes default 12
25253 * @cfg {Boolean} multiple multiple upload default true
25254 * @cfg {Number} minWidth default 300
25255 * @cfg {Number} minHeight default 300
25256 * @cfg {Number} thumbSize default 300
25257 * @cfg {String} fieldLabel
25258 * @cfg {Number} labelWidth default 4
25259 * @cfg {String} labelAlign (left|top) default left
25262 * Create a new DocumentManager
25263 * @param {Object} config The config object
25266 Roo.bootstrap.DocumentManager = function(config){
25267 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25272 * Fire when initial the DocumentManager
25273 * @param {Roo.bootstrap.DocumentManager} this
25278 * inspect selected file
25279 * @param {Roo.bootstrap.DocumentManager} this
25280 * @param {File} file
25285 * Fire when xhr load exception
25286 * @param {Roo.bootstrap.DocumentManager} this
25287 * @param {XMLHttpRequest} xhr
25289 "exception" : true,
25292 * prepare the form data
25293 * @param {Roo.bootstrap.DocumentManager} this
25294 * @param {Object} formData
25299 * Fire when remove the file
25300 * @param {Roo.bootstrap.DocumentManager} this
25301 * @param {Object} file
25306 * Fire after refresh the file
25307 * @param {Roo.bootstrap.DocumentManager} this
25312 * Fire after click the image
25313 * @param {Roo.bootstrap.DocumentManager} this
25314 * @param {Object} file
25321 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25332 paramName : 'imageUpload',
25335 labelAlign : 'left',
25337 getAutoCreate : function()
25339 var managerWidget = {
25341 cls : 'roo-document-manager',
25345 cls : 'roo-document-manager-selector',
25350 cls : 'roo-document-manager-uploader',
25354 cls : 'roo-document-manager-upload-btn',
25355 html : '<i class="fa fa-plus"></i>'
25366 cls : 'column col-md-12',
25371 if(this.fieldLabel.length){
25376 cls : 'column col-md-12',
25377 html : this.fieldLabel
25381 cls : 'column col-md-12',
25386 if(this.labelAlign == 'left'){
25390 cls : 'column col-md-' + this.labelWidth,
25391 html : this.fieldLabel
25395 cls : 'column col-md-' + (12 - this.labelWidth),
25405 cls : 'row clearfix',
25413 initEvents : function()
25415 this.managerEl = this.el.select('.roo-document-manager', true).first();
25416 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25418 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25419 this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25420 this.selectorEl.hide();
25423 this.selectorEl.attr('multiple', 'multiple');
25426 this.selectorEl.on('change', this.onSelect, this);
25428 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25429 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25431 this.uploader.on('click', this.onUpload, this);
25435 window.addEventListener("resize", function() { _this.refresh(); } );
25437 this.fireEvent('initial', this);
25440 onUpload : function(e)
25442 e.preventDefault();
25444 this.selectorEl.dom.click();
25448 onSelect : function(e)
25450 e.preventDefault();
25452 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25456 Roo.each(this.selectorEl.dom.files, function(file){
25457 if(this.fireEvent('inspect', this, file) != false){
25458 this.files.push(file);
25466 process : function()
25468 this.selectorEl.dom.value = '';
25470 if(!this.files.length){
25474 if(this.files.length > this.boxes){
25475 this.files = this.files.slice(0, this.boxes);
25478 var xhr = new XMLHttpRequest();
25480 Roo.each(this.files, function(file, index){
25481 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25487 this.managerEl.createChild({
25489 cls : 'roo-document-manager-loading',
25493 tooltip : file.name,
25494 cls : 'roo-document-manager-thumb',
25495 html : '<i class="fa fa-spinner fa-pulse"></i>'
25503 if(this.files.length > this.boxes - 1 ){
25504 this.uploader.hide();
25508 "Accept": "application/json",
25509 "Cache-Control": "no-cache",
25510 "X-Requested-With": "XMLHttpRequest"
25513 xhr.open(this.method, this.url, true);
25515 for (var headerName in headers) {
25516 var headerValue = headers[headerName];
25518 xhr.setRequestHeader(headerName, headerValue);
25524 xhr.onload = function()
25526 _this.xhrOnLoad(xhr);
25529 xhr.onerror = function()
25531 _this.xhrOnError(xhr);
25534 var formData = new FormData();
25536 formData.append('returnHTML', 'NO');
25538 Roo.each(this.files, function(file, index){
25540 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25544 formData.append(this.getParamName(index), file, file.name);
25548 if(this.fireEvent('prepare', this, formData) != false){
25549 xhr.send(formData);
25554 getParamName : function(i)
25556 if(!this.multiple){
25557 return this.paramName;
25560 return this.paramName + "_" + i;
25563 refresh : function()
25565 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25572 Roo.each(this.files, function(file){
25574 if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25583 var previewEl = this.managerEl.createChild({
25585 cls : 'roo-document-manager-preview',
25589 tooltip : file.filename,
25590 cls : 'roo-document-manager-thumb',
25591 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25601 var close = previewEl.select('button.close', true).first();
25603 close.on('click', this.onRemove, this, file);
25605 file.target = previewEl;
25607 var image = previewEl.select('img', true).first();
25609 image.on('click', this.onClick, this, file);
25617 this.files = files;
25619 this.uploader.show();
25621 if(this.files.length > this.boxes - 1){
25622 this.uploader.hide();
25625 Roo.isTouch ? this.closable(false) : this.closable(true);
25627 this.fireEvent('refresh', this);
25630 onRemove : function(e, el, o)
25632 e.preventDefault();
25634 this.fireEvent('remove', this, o);
25638 remove : function(o)
25642 Roo.each(this.files, function(file){
25643 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25652 this.files = files;
25657 onClick : function(e, el, o)
25659 e.preventDefault();
25661 this.fireEvent('click', this, o);
25665 closable : function(closable)
25667 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25669 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25681 xhrOnLoad : function(xhr)
25683 if (xhr.readyState !== 4) {
25685 this.fireEvent('exception', this, xhr);
25689 var response = Roo.decode(xhr.responseText);
25691 if(!response.success){
25693 this.fireEvent('exception', this, xhr);
25699 Roo.each(this.files, function(file, index){
25701 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25705 this.files[index] = response.data[i];
25716 xhrOnError : function()
25718 Roo.log('xhr on error');
25720 var response = Roo.decode(xhr.responseText);
25733 * @class Roo.bootstrap.DocumentViewer
25734 * @extends Roo.bootstrap.Component
25735 * Bootstrap DocumentViewer class
25738 * Create a new DocumentViewer
25739 * @param {Object} config The config object
25742 Roo.bootstrap.DocumentViewer = function(config){
25743 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25748 * Fire after initEvent
25749 * @param {Roo.bootstrap.DocumentViewer} this
25755 * @param {Roo.bootstrap.DocumentViewer} this
25760 * Fire after trash button
25761 * @param {Roo.bootstrap.DocumentViewer} this
25768 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
25770 getAutoCreate : function()
25774 cls : 'roo-document-viewer',
25778 cls : 'roo-document-viewer-body',
25782 cls : 'roo-document-viewer-thumb',
25786 cls : 'roo-document-viewer-image'
25794 cls : 'roo-document-viewer-footer',
25797 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25805 cls : 'btn btn-default roo-document-viewer-trash',
25806 html : '<i class="fa fa-trash"></i>'
25819 initEvents : function()
25822 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25823 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25825 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25826 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25828 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25829 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25831 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25832 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25834 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25835 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25837 this.bodyEl.on('click', this.onClick, this);
25839 this.trashBtn.on('click', this.onTrash, this);
25843 initial : function()
25845 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25848 this.fireEvent('initial', this);
25852 onClick : function(e)
25854 e.preventDefault();
25856 this.fireEvent('click', this);
25859 onTrash : function(e)
25861 e.preventDefault();
25863 this.fireEvent('trash', this);