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){
12918 inputEl: function ()
12920 if(Roo.isTouch && this.mobileTouchView){
12921 return this.el.select('input.form-control',true).first();
12925 return this.searchField;
12928 return this.el.select('input.form-control',true).first();
12932 onTickableFooterButtonClick : function(e, btn, el)
12934 e.preventDefault();
12936 this.lastItem = Roo.apply([], this.item);
12938 if(btn && btn.name == 'cancel'){
12939 this.tickItems = Roo.apply([], this.item);
12948 Roo.each(this.tickItems, function(o){
12956 validate : function()
12958 var v = this.getRawValue();
12961 v = this.getValue();
12964 if(this.disabled || this.allowBlank || v.length){
12969 this.markInvalid();
12973 tickableInputEl : function()
12975 if(!this.tickable || !this.editable){
12976 return this.inputEl();
12979 return this.inputEl().select('.select2-search-field-input', true).first();
12983 getAutoCreateTouchView : function()
12988 cls: 'form-group' //input-group
12994 type : this.inputType,
12995 cls : 'form-control x-combo-noedit',
12996 autocomplete: 'new-password',
12997 placeholder : this.placeholder || '',
13002 input.name = this.name;
13006 input.cls += ' input-' + this.size;
13009 if (this.disabled) {
13010 input.disabled = true;
13021 inputblock.cls += ' input-group';
13023 inputblock.cn.unshift({
13025 cls : 'input-group-addon',
13030 if(this.removable && !this.multiple){
13031 inputblock.cls += ' roo-removable';
13033 inputblock.cn.push({
13036 cls : 'roo-combo-removable-btn close'
13040 if(this.hasFeedback && !this.allowBlank){
13042 inputblock.cls += ' has-feedback';
13044 inputblock.cn.push({
13046 cls: 'glyphicon form-control-feedback'
13053 inputblock.cls += (this.before) ? '' : ' input-group';
13055 inputblock.cn.push({
13057 cls : 'input-group-addon',
13068 cls: 'form-hidden-field'
13082 cls: 'form-hidden-field'
13086 cls: 'select2-choices',
13090 cls: 'select2-search-field',
13103 cls: 'select2-container input-group',
13110 combobox.cls += ' select2-container-multi';
13113 var align = this.labelAlign || this.parentLabelAlign();
13117 if(this.fieldLabel.length){
13119 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13120 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13125 cls : 'control-label ' + lw,
13126 html : this.fieldLabel
13138 var settings = this;
13140 ['xs','sm','md','lg'].map(function(size){
13141 if (settings[size]) {
13142 cfg.cls += ' col-' + size + '-' + settings[size];
13149 initTouchView : function()
13151 this.renderTouchView();
13153 this.touchViewEl.on('scroll', function(){
13154 this.el.dom.scrollTop = 0;
13157 this.inputEl().on("click", this.showTouchView, this);
13158 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13159 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13161 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13163 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13164 this.store.on('load', this.onTouchViewLoad, this);
13165 this.store.on('loadexception', this.onTouchViewLoadException, this);
13167 if(this.hiddenName){
13169 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13171 this.hiddenField.dom.value =
13172 this.hiddenValue !== undefined ? this.hiddenValue :
13173 this.value !== undefined ? this.value : '';
13175 this.el.dom.removeAttribute('name');
13176 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13180 this.choices = this.el.select('ul.select2-choices', true).first();
13181 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13184 if(this.removable && !this.multiple){
13185 var close = this.closeTriggerEl();
13187 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13188 close.on('click', this.removeBtnClick, this, close);
13197 renderTouchView : function()
13199 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13200 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13202 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13203 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13205 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13206 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13207 this.touchViewBodyEl.setStyle('overflow', 'auto');
13209 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13210 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13212 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13213 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13217 showTouchView : function()
13219 this.touchViewHeaderEl.hide();
13221 if(this.fieldLabel.length){
13222 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13223 this.touchViewHeaderEl.show();
13226 this.touchViewEl.show();
13228 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13229 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13231 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13233 if(this.fieldLabel.length){
13234 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13237 this.touchViewBodyEl.setHeight(bodyHeight);
13241 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13243 this.touchViewEl.addClass('in');
13246 this.doTouchViewQuery();
13250 hideTouchView : function()
13252 this.touchViewEl.removeClass('in');
13256 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13258 this.touchViewEl.setStyle('display', 'none');
13263 setTouchViewValue : function()
13270 Roo.each(this.tickItems, function(o){
13275 this.hideTouchView();
13278 doTouchViewQuery : function()
13287 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13291 if(!this.alwaysQuery || this.mode == 'local'){
13292 this.onTouchViewLoad();
13299 onTouchViewBeforeLoad : function(combo,opts)
13305 onTouchViewLoad : function()
13307 if(this.store.getCount() < 1){
13308 this.onTouchViewEmptyResults();
13312 this.clearTouchView();
13314 var rawValue = this.getRawValue();
13316 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13318 this.tickItems = [];
13320 this.store.data.each(function(d, rowIndex){
13321 var row = this.touchViewListGroup.createChild(template);
13323 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13324 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13327 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13328 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13331 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13332 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13333 this.tickItems.push(d.data);
13336 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13340 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13342 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13344 if(this.fieldLabel.length){
13345 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13348 var listHeight = this.touchViewListGroup.getHeight();
13350 if(firstChecked && listHeight > bodyHeight){
13351 (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13356 onTouchViewLoadException : function()
13358 this.hideTouchView();
13361 onTouchViewEmptyResults : function()
13363 this.clearTouchView();
13365 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13367 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13371 clearTouchView : function()
13373 this.touchViewListGroup.dom.innerHTML = '';
13376 onTouchViewClick : function(e, el, o)
13378 e.preventDefault();
13381 var rowIndex = o.rowIndex;
13383 var r = this.store.getAt(rowIndex);
13385 if(!this.multiple){
13386 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13387 c.dom.removeAttribute('checked');
13390 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13392 this.setFromData(r.data);
13394 var close = this.closeTriggerEl();
13400 this.hideTouchView();
13402 this.fireEvent('select', this, r, rowIndex);
13407 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13408 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13409 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13413 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13414 this.addItem(r.data);
13415 this.tickItems.push(r.data);
13421 * @cfg {Boolean} grow
13425 * @cfg {Number} growMin
13429 * @cfg {Number} growMax
13438 Roo.apply(Roo.bootstrap.ComboBox, {
13442 cls: 'modal-header',
13464 cls: 'list-group-item',
13468 cls: 'roo-combobox-list-group-item-value'
13472 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13486 listItemCheckbox : {
13488 cls: 'list-group-item',
13492 cls: 'roo-combobox-list-group-item-value'
13496 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13512 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13517 cls: 'modal-footer',
13525 cls: 'col-xs-6 text-left',
13528 cls: 'btn btn-danger roo-touch-view-cancel',
13534 cls: 'col-xs-6 text-right',
13537 cls: 'btn btn-success roo-touch-view-ok',
13548 Roo.apply(Roo.bootstrap.ComboBox, {
13550 touchViewTemplate : {
13552 cls: 'modal fade roo-combobox-touch-view',
13556 cls: 'modal-dialog',
13560 cls: 'modal-content',
13562 Roo.bootstrap.ComboBox.header,
13563 Roo.bootstrap.ComboBox.body,
13564 Roo.bootstrap.ComboBox.footer
13573 * Ext JS Library 1.1.1
13574 * Copyright(c) 2006-2007, Ext JS, LLC.
13576 * Originally Released Under LGPL - original licence link has changed is not relivant.
13579 * <script type="text/javascript">
13584 * @extends Roo.util.Observable
13585 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13586 * This class also supports single and multi selection modes. <br>
13587 * Create a data model bound view:
13589 var store = new Roo.data.Store(...);
13591 var view = new Roo.View({
13593 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13595 singleSelect: true,
13596 selectedClass: "ydataview-selected",
13600 // listen for node click?
13601 view.on("click", function(vw, index, node, e){
13602 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13606 dataModel.load("foobar.xml");
13608 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13610 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13611 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13613 * Note: old style constructor is still suported (container, template, config)
13616 * Create a new View
13617 * @param {Object} config The config object
13620 Roo.View = function(config, depreciated_tpl, depreciated_config){
13622 this.parent = false;
13624 if (typeof(depreciated_tpl) == 'undefined') {
13625 // new way.. - universal constructor.
13626 Roo.apply(this, config);
13627 this.el = Roo.get(this.el);
13630 this.el = Roo.get(config);
13631 this.tpl = depreciated_tpl;
13632 Roo.apply(this, depreciated_config);
13634 this.wrapEl = this.el.wrap().wrap();
13635 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13638 if(typeof(this.tpl) == "string"){
13639 this.tpl = new Roo.Template(this.tpl);
13641 // support xtype ctors..
13642 this.tpl = new Roo.factory(this.tpl, Roo);
13646 this.tpl.compile();
13651 * @event beforeclick
13652 * Fires before a click is processed. Returns false to cancel the default action.
13653 * @param {Roo.View} this
13654 * @param {Number} index The index of the target node
13655 * @param {HTMLElement} node The target node
13656 * @param {Roo.EventObject} e The raw event object
13658 "beforeclick" : true,
13661 * Fires when a template node is clicked.
13662 * @param {Roo.View} this
13663 * @param {Number} index The index of the target node
13664 * @param {HTMLElement} node The target node
13665 * @param {Roo.EventObject} e The raw event object
13670 * Fires when a template node is double clicked.
13671 * @param {Roo.View} this
13672 * @param {Number} index The index of the target node
13673 * @param {HTMLElement} node The target node
13674 * @param {Roo.EventObject} e The raw event object
13678 * @event contextmenu
13679 * Fires when a template node is right clicked.
13680 * @param {Roo.View} this
13681 * @param {Number} index The index of the target node
13682 * @param {HTMLElement} node The target node
13683 * @param {Roo.EventObject} e The raw event object
13685 "contextmenu" : true,
13687 * @event selectionchange
13688 * Fires when the selected nodes change.
13689 * @param {Roo.View} this
13690 * @param {Array} selections Array of the selected nodes
13692 "selectionchange" : true,
13695 * @event beforeselect
13696 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13697 * @param {Roo.View} this
13698 * @param {HTMLElement} node The node to be selected
13699 * @param {Array} selections Array of currently selected nodes
13701 "beforeselect" : true,
13703 * @event preparedata
13704 * Fires on every row to render, to allow you to change the data.
13705 * @param {Roo.View} this
13706 * @param {Object} data to be rendered (change this)
13708 "preparedata" : true
13716 "click": this.onClick,
13717 "dblclick": this.onDblClick,
13718 "contextmenu": this.onContextMenu,
13722 this.selections = [];
13724 this.cmp = new Roo.CompositeElementLite([]);
13726 this.store = Roo.factory(this.store, Roo.data);
13727 this.setStore(this.store, true);
13730 if ( this.footer && this.footer.xtype) {
13732 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13734 this.footer.dataSource = this.store
13735 this.footer.container = fctr;
13736 this.footer = Roo.factory(this.footer, Roo);
13737 fctr.insertFirst(this.el);
13739 // this is a bit insane - as the paging toolbar seems to detach the el..
13740 // dom.parentNode.parentNode.parentNode
13741 // they get detached?
13745 Roo.View.superclass.constructor.call(this);
13750 Roo.extend(Roo.View, Roo.util.Observable, {
13753 * @cfg {Roo.data.Store} store Data store to load data from.
13758 * @cfg {String|Roo.Element} el The container element.
13763 * @cfg {String|Roo.Template} tpl The template used by this View
13767 * @cfg {String} dataName the named area of the template to use as the data area
13768 * Works with domtemplates roo-name="name"
13772 * @cfg {String} selectedClass The css class to add to selected nodes
13774 selectedClass : "x-view-selected",
13776 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13781 * @cfg {String} text to display on mask (default Loading)
13785 * @cfg {Boolean} multiSelect Allow multiple selection
13787 multiSelect : false,
13789 * @cfg {Boolean} singleSelect Allow single selection
13791 singleSelect: false,
13794 * @cfg {Boolean} toggleSelect - selecting
13796 toggleSelect : false,
13799 * @cfg {Boolean} tickable - selecting
13804 * Returns the element this view is bound to.
13805 * @return {Roo.Element}
13807 getEl : function(){
13808 return this.wrapEl;
13814 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13816 refresh : function(){
13817 //Roo.log('refresh');
13820 // if we are using something like 'domtemplate', then
13821 // the what gets used is:
13822 // t.applySubtemplate(NAME, data, wrapping data..)
13823 // the outer template then get' applied with
13824 // the store 'extra data'
13825 // and the body get's added to the
13826 // roo-name="data" node?
13827 // <span class='roo-tpl-{name}'></span> ?????
13831 this.clearSelections();
13832 this.el.update("");
13834 var records = this.store.getRange();
13835 if(records.length < 1) {
13837 // is this valid?? = should it render a template??
13839 this.el.update(this.emptyText);
13843 if (this.dataName) {
13844 this.el.update(t.apply(this.store.meta)); //????
13845 el = this.el.child('.roo-tpl-' + this.dataName);
13848 for(var i = 0, len = records.length; i < len; i++){
13849 var data = this.prepareData(records[i].data, i, records[i]);
13850 this.fireEvent("preparedata", this, data, i, records[i]);
13852 var d = Roo.apply({}, data);
13855 Roo.apply(d, {'roo-id' : Roo.id()});
13859 Roo.each(this.parent.item, function(item){
13860 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13863 Roo.apply(d, {'roo-data-checked' : 'checked'});
13867 html[html.length] = Roo.util.Format.trim(
13869 t.applySubtemplate(this.dataName, d, this.store.meta) :
13876 el.update(html.join(""));
13877 this.nodes = el.dom.childNodes;
13878 this.updateIndexes(0);
13883 * Function to override to reformat the data that is sent to
13884 * the template for each node.
13885 * DEPRICATED - use the preparedata event handler.
13886 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13887 * a JSON object for an UpdateManager bound view).
13889 prepareData : function(data, index, record)
13891 this.fireEvent("preparedata", this, data, index, record);
13895 onUpdate : function(ds, record){
13896 // Roo.log('on update');
13897 this.clearSelections();
13898 var index = this.store.indexOf(record);
13899 var n = this.nodes[index];
13900 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13901 n.parentNode.removeChild(n);
13902 this.updateIndexes(index, index);
13908 onAdd : function(ds, records, index)
13910 //Roo.log(['on Add', ds, records, index] );
13911 this.clearSelections();
13912 if(this.nodes.length == 0){
13916 var n = this.nodes[index];
13917 for(var i = 0, len = records.length; i < len; i++){
13918 var d = this.prepareData(records[i].data, i, records[i]);
13920 this.tpl.insertBefore(n, d);
13923 this.tpl.append(this.el, d);
13926 this.updateIndexes(index);
13929 onRemove : function(ds, record, index){
13930 // Roo.log('onRemove');
13931 this.clearSelections();
13932 var el = this.dataName ?
13933 this.el.child('.roo-tpl-' + this.dataName) :
13936 el.dom.removeChild(this.nodes[index]);
13937 this.updateIndexes(index);
13941 * Refresh an individual node.
13942 * @param {Number} index
13944 refreshNode : function(index){
13945 this.onUpdate(this.store, this.store.getAt(index));
13948 updateIndexes : function(startIndex, endIndex){
13949 var ns = this.nodes;
13950 startIndex = startIndex || 0;
13951 endIndex = endIndex || ns.length - 1;
13952 for(var i = startIndex; i <= endIndex; i++){
13953 ns[i].nodeIndex = i;
13958 * Changes the data store this view uses and refresh the view.
13959 * @param {Store} store
13961 setStore : function(store, initial){
13962 if(!initial && this.store){
13963 this.store.un("datachanged", this.refresh);
13964 this.store.un("add", this.onAdd);
13965 this.store.un("remove", this.onRemove);
13966 this.store.un("update", this.onUpdate);
13967 this.store.un("clear", this.refresh);
13968 this.store.un("beforeload", this.onBeforeLoad);
13969 this.store.un("load", this.onLoad);
13970 this.store.un("loadexception", this.onLoad);
13974 store.on("datachanged", this.refresh, this);
13975 store.on("add", this.onAdd, this);
13976 store.on("remove", this.onRemove, this);
13977 store.on("update", this.onUpdate, this);
13978 store.on("clear", this.refresh, this);
13979 store.on("beforeload", this.onBeforeLoad, this);
13980 store.on("load", this.onLoad, this);
13981 store.on("loadexception", this.onLoad, this);
13989 * onbeforeLoad - masks the loading area.
13992 onBeforeLoad : function(store,opts)
13994 //Roo.log('onBeforeLoad');
13996 this.el.update("");
13998 this.el.mask(this.mask ? this.mask : "Loading" );
14000 onLoad : function ()
14007 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14008 * @param {HTMLElement} node
14009 * @return {HTMLElement} The template node
14011 findItemFromChild : function(node){
14012 var el = this.dataName ?
14013 this.el.child('.roo-tpl-' + this.dataName,true) :
14016 if(!node || node.parentNode == el){
14019 var p = node.parentNode;
14020 while(p && p != el){
14021 if(p.parentNode == el){
14030 onClick : function(e){
14031 var item = this.findItemFromChild(e.getTarget());
14033 var index = this.indexOf(item);
14034 if(this.onItemClick(item, index, e) !== false){
14035 this.fireEvent("click", this, index, item, e);
14038 this.clearSelections();
14043 onContextMenu : function(e){
14044 var item = this.findItemFromChild(e.getTarget());
14046 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14051 onDblClick : function(e){
14052 var item = this.findItemFromChild(e.getTarget());
14054 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14058 onItemClick : function(item, index, e)
14060 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14063 if (this.toggleSelect) {
14064 var m = this.isSelected(item) ? 'unselect' : 'select';
14067 _t[m](item, true, false);
14070 if(this.multiSelect || this.singleSelect){
14071 if(this.multiSelect && e.shiftKey && this.lastSelection){
14072 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14074 this.select(item, this.multiSelect && e.ctrlKey);
14075 this.lastSelection = item;
14078 if(!this.tickable){
14079 e.preventDefault();
14087 * Get the number of selected nodes.
14090 getSelectionCount : function(){
14091 return this.selections.length;
14095 * Get the currently selected nodes.
14096 * @return {Array} An array of HTMLElements
14098 getSelectedNodes : function(){
14099 return this.selections;
14103 * Get the indexes of the selected nodes.
14106 getSelectedIndexes : function(){
14107 var indexes = [], s = this.selections;
14108 for(var i = 0, len = s.length; i < len; i++){
14109 indexes.push(s[i].nodeIndex);
14115 * Clear all selections
14116 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14118 clearSelections : function(suppressEvent){
14119 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14120 this.cmp.elements = this.selections;
14121 this.cmp.removeClass(this.selectedClass);
14122 this.selections = [];
14123 if(!suppressEvent){
14124 this.fireEvent("selectionchange", this, this.selections);
14130 * Returns true if the passed node is selected
14131 * @param {HTMLElement/Number} node The node or node index
14132 * @return {Boolean}
14134 isSelected : function(node){
14135 var s = this.selections;
14139 node = this.getNode(node);
14140 return s.indexOf(node) !== -1;
14145 * @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
14146 * @param {Boolean} keepExisting (optional) true to keep existing selections
14147 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14149 select : function(nodeInfo, keepExisting, suppressEvent){
14150 if(nodeInfo instanceof Array){
14152 this.clearSelections(true);
14154 for(var i = 0, len = nodeInfo.length; i < len; i++){
14155 this.select(nodeInfo[i], true, true);
14159 var node = this.getNode(nodeInfo);
14160 if(!node || this.isSelected(node)){
14161 return; // already selected.
14164 this.clearSelections(true);
14167 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14168 Roo.fly(node).addClass(this.selectedClass);
14169 this.selections.push(node);
14170 if(!suppressEvent){
14171 this.fireEvent("selectionchange", this, this.selections);
14179 * @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
14180 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14181 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14183 unselect : function(nodeInfo, keepExisting, suppressEvent)
14185 if(nodeInfo instanceof Array){
14186 Roo.each(this.selections, function(s) {
14187 this.unselect(s, nodeInfo);
14191 var node = this.getNode(nodeInfo);
14192 if(!node || !this.isSelected(node)){
14193 //Roo.log("not selected");
14194 return; // not selected.
14198 Roo.each(this.selections, function(s) {
14200 Roo.fly(node).removeClass(this.selectedClass);
14207 this.selections= ns;
14208 this.fireEvent("selectionchange", this, this.selections);
14212 * Gets a template node.
14213 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14214 * @return {HTMLElement} The node or null if it wasn't found
14216 getNode : function(nodeInfo){
14217 if(typeof nodeInfo == "string"){
14218 return document.getElementById(nodeInfo);
14219 }else if(typeof nodeInfo == "number"){
14220 return this.nodes[nodeInfo];
14226 * Gets a range template nodes.
14227 * @param {Number} startIndex
14228 * @param {Number} endIndex
14229 * @return {Array} An array of nodes
14231 getNodes : function(start, end){
14232 var ns = this.nodes;
14233 start = start || 0;
14234 end = typeof end == "undefined" ? ns.length - 1 : end;
14237 for(var i = start; i <= end; i++){
14241 for(var i = start; i >= end; i--){
14249 * Finds the index of the passed node
14250 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14251 * @return {Number} The index of the node or -1
14253 indexOf : function(node){
14254 node = this.getNode(node);
14255 if(typeof node.nodeIndex == "number"){
14256 return node.nodeIndex;
14258 var ns = this.nodes;
14259 for(var i = 0, len = ns.length; i < len; i++){
14270 * based on jquery fullcalendar
14274 Roo.bootstrap = Roo.bootstrap || {};
14276 * @class Roo.bootstrap.Calendar
14277 * @extends Roo.bootstrap.Component
14278 * Bootstrap Calendar class
14279 * @cfg {Boolean} loadMask (true|false) default false
14280 * @cfg {Object} header generate the user specific header of the calendar, default false
14283 * Create a new Container
14284 * @param {Object} config The config object
14289 Roo.bootstrap.Calendar = function(config){
14290 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14294 * Fires when a date is selected
14295 * @param {DatePicker} this
14296 * @param {Date} date The selected date
14300 * @event monthchange
14301 * Fires when the displayed month changes
14302 * @param {DatePicker} this
14303 * @param {Date} date The selected month
14305 'monthchange': true,
14307 * @event evententer
14308 * Fires when mouse over an event
14309 * @param {Calendar} this
14310 * @param {event} Event
14312 'evententer': true,
14314 * @event eventleave
14315 * Fires when the mouse leaves an
14316 * @param {Calendar} this
14319 'eventleave': true,
14321 * @event eventclick
14322 * Fires when the mouse click an
14323 * @param {Calendar} this
14332 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14335 * @cfg {Number} startDay
14336 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14344 getAutoCreate : function(){
14347 var fc_button = function(name, corner, style, content ) {
14348 return Roo.apply({},{
14350 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14352 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14355 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14366 style : 'width:100%',
14373 cls : 'fc-header-left',
14375 fc_button('prev', 'left', 'arrow', '‹' ),
14376 fc_button('next', 'right', 'arrow', '›' ),
14377 { tag: 'span', cls: 'fc-header-space' },
14378 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14386 cls : 'fc-header-center',
14390 cls: 'fc-header-title',
14393 html : 'month / year'
14401 cls : 'fc-header-right',
14403 /* fc_button('month', 'left', '', 'month' ),
14404 fc_button('week', '', '', 'week' ),
14405 fc_button('day', 'right', '', 'day' )
14417 header = this.header;
14420 var cal_heads = function() {
14422 // fixme - handle this.
14424 for (var i =0; i < Date.dayNames.length; i++) {
14425 var d = Date.dayNames[i];
14428 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14429 html : d.substring(0,3)
14433 ret[0].cls += ' fc-first';
14434 ret[6].cls += ' fc-last';
14437 var cal_cell = function(n) {
14440 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14445 cls: 'fc-day-number',
14449 cls: 'fc-day-content',
14453 style: 'position: relative;' // height: 17px;
14465 var cal_rows = function() {
14468 for (var r = 0; r < 6; r++) {
14475 for (var i =0; i < Date.dayNames.length; i++) {
14476 var d = Date.dayNames[i];
14477 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14480 row.cn[0].cls+=' fc-first';
14481 row.cn[0].cn[0].style = 'min-height:90px';
14482 row.cn[6].cls+=' fc-last';
14486 ret[0].cls += ' fc-first';
14487 ret[4].cls += ' fc-prev-last';
14488 ret[5].cls += ' fc-last';
14495 cls: 'fc-border-separate',
14496 style : 'width:100%',
14504 cls : 'fc-first fc-last',
14522 cls : 'fc-content',
14523 style : "position: relative;",
14526 cls : 'fc-view fc-view-month fc-grid',
14527 style : 'position: relative',
14528 unselectable : 'on',
14531 cls : 'fc-event-container',
14532 style : 'position:absolute;z-index:8;top:0;left:0;'
14550 initEvents : function()
14553 throw "can not find store for calendar";
14559 style: "text-align:center",
14563 style: "background-color:white;width:50%;margin:250 auto",
14567 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14578 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14580 var size = this.el.select('.fc-content', true).first().getSize();
14581 this.maskEl.setSize(size.width, size.height);
14582 this.maskEl.enableDisplayMode("block");
14583 if(!this.loadMask){
14584 this.maskEl.hide();
14587 this.store = Roo.factory(this.store, Roo.data);
14588 this.store.on('load', this.onLoad, this);
14589 this.store.on('beforeload', this.onBeforeLoad, this);
14593 this.cells = this.el.select('.fc-day',true);
14594 //Roo.log(this.cells);
14595 this.textNodes = this.el.query('.fc-day-number');
14596 this.cells.addClassOnOver('fc-state-hover');
14598 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14599 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14600 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14601 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14603 this.on('monthchange', this.onMonthChange, this);
14605 this.update(new Date().clearTime());
14608 resize : function() {
14609 var sz = this.el.getSize();
14611 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14612 this.el.select('.fc-day-content div',true).setHeight(34);
14617 showPrevMonth : function(e){
14618 this.update(this.activeDate.add("mo", -1));
14620 showToday : function(e){
14621 this.update(new Date().clearTime());
14624 showNextMonth : function(e){
14625 this.update(this.activeDate.add("mo", 1));
14629 showPrevYear : function(){
14630 this.update(this.activeDate.add("y", -1));
14634 showNextYear : function(){
14635 this.update(this.activeDate.add("y", 1));
14640 update : function(date)
14642 var vd = this.activeDate;
14643 this.activeDate = date;
14644 // if(vd && this.el){
14645 // var t = date.getTime();
14646 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14647 // Roo.log('using add remove');
14649 // this.fireEvent('monthchange', this, date);
14651 // this.cells.removeClass("fc-state-highlight");
14652 // this.cells.each(function(c){
14653 // if(c.dateValue == t){
14654 // c.addClass("fc-state-highlight");
14655 // setTimeout(function(){
14656 // try{c.dom.firstChild.focus();}catch(e){}
14666 var days = date.getDaysInMonth();
14668 var firstOfMonth = date.getFirstDateOfMonth();
14669 var startingPos = firstOfMonth.getDay()-this.startDay;
14671 if(startingPos < this.startDay){
14675 var pm = date.add(Date.MONTH, -1);
14676 var prevStart = pm.getDaysInMonth()-startingPos;
14678 this.cells = this.el.select('.fc-day',true);
14679 this.textNodes = this.el.query('.fc-day-number');
14680 this.cells.addClassOnOver('fc-state-hover');
14682 var cells = this.cells.elements;
14683 var textEls = this.textNodes;
14685 Roo.each(cells, function(cell){
14686 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14689 days += startingPos;
14691 // convert everything to numbers so it's fast
14692 var day = 86400000;
14693 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14696 //Roo.log(prevStart);
14698 var today = new Date().clearTime().getTime();
14699 var sel = date.clearTime().getTime();
14700 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14701 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14702 var ddMatch = this.disabledDatesRE;
14703 var ddText = this.disabledDatesText;
14704 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14705 var ddaysText = this.disabledDaysText;
14706 var format = this.format;
14708 var setCellClass = function(cal, cell){
14712 //Roo.log('set Cell Class');
14714 var t = d.getTime();
14718 cell.dateValue = t;
14720 cell.className += " fc-today";
14721 cell.className += " fc-state-highlight";
14722 cell.title = cal.todayText;
14725 // disable highlight in other month..
14726 //cell.className += " fc-state-highlight";
14731 cell.className = " fc-state-disabled";
14732 cell.title = cal.minText;
14736 cell.className = " fc-state-disabled";
14737 cell.title = cal.maxText;
14741 if(ddays.indexOf(d.getDay()) != -1){
14742 cell.title = ddaysText;
14743 cell.className = " fc-state-disabled";
14746 if(ddMatch && format){
14747 var fvalue = d.dateFormat(format);
14748 if(ddMatch.test(fvalue)){
14749 cell.title = ddText.replace("%0", fvalue);
14750 cell.className = " fc-state-disabled";
14754 if (!cell.initialClassName) {
14755 cell.initialClassName = cell.dom.className;
14758 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14763 for(; i < startingPos; i++) {
14764 textEls[i].innerHTML = (++prevStart);
14765 d.setDate(d.getDate()+1);
14767 cells[i].className = "fc-past fc-other-month";
14768 setCellClass(this, cells[i]);
14773 for(; i < days; i++){
14774 intDay = i - startingPos + 1;
14775 textEls[i].innerHTML = (intDay);
14776 d.setDate(d.getDate()+1);
14778 cells[i].className = ''; // "x-date-active";
14779 setCellClass(this, cells[i]);
14783 for(; i < 42; i++) {
14784 textEls[i].innerHTML = (++extraDays);
14785 d.setDate(d.getDate()+1);
14787 cells[i].className = "fc-future fc-other-month";
14788 setCellClass(this, cells[i]);
14791 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14793 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14795 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14796 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14798 if(totalRows != 6){
14799 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14800 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14803 this.fireEvent('monthchange', this, date);
14807 if(!this.internalRender){
14808 var main = this.el.dom.firstChild;
14809 var w = main.offsetWidth;
14810 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14811 Roo.fly(main).setWidth(w);
14812 this.internalRender = true;
14813 // opera does not respect the auto grow header center column
14814 // then, after it gets a width opera refuses to recalculate
14815 // without a second pass
14816 if(Roo.isOpera && !this.secondPass){
14817 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14818 this.secondPass = true;
14819 this.update.defer(10, this, [date]);
14826 findCell : function(dt) {
14827 dt = dt.clearTime().getTime();
14829 this.cells.each(function(c){
14830 //Roo.log("check " +c.dateValue + '?=' + dt);
14831 if(c.dateValue == dt){
14841 findCells : function(ev) {
14842 var s = ev.start.clone().clearTime().getTime();
14844 var e= ev.end.clone().clearTime().getTime();
14847 this.cells.each(function(c){
14848 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14850 if(c.dateValue > e){
14853 if(c.dateValue < s){
14862 // findBestRow: function(cells)
14866 // for (var i =0 ; i < cells.length;i++) {
14867 // ret = Math.max(cells[i].rows || 0,ret);
14874 addItem : function(ev)
14876 // look for vertical location slot in
14877 var cells = this.findCells(ev);
14879 // ev.row = this.findBestRow(cells);
14881 // work out the location.
14885 for(var i =0; i < cells.length; i++) {
14887 cells[i].row = cells[0].row;
14890 cells[i].row = cells[i].row + 1;
14900 if (crow.start.getY() == cells[i].getY()) {
14902 crow.end = cells[i];
14919 cells[0].events.push(ev);
14921 this.calevents.push(ev);
14924 clearEvents: function() {
14926 if(!this.calevents){
14930 Roo.each(this.cells.elements, function(c){
14936 Roo.each(this.calevents, function(e) {
14937 Roo.each(e.els, function(el) {
14938 el.un('mouseenter' ,this.onEventEnter, this);
14939 el.un('mouseleave' ,this.onEventLeave, this);
14944 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14950 renderEvents: function()
14954 this.cells.each(function(c) {
14963 if(c.row != c.events.length){
14964 r = 4 - (4 - (c.row - c.events.length));
14967 c.events = ev.slice(0, r);
14968 c.more = ev.slice(r);
14970 if(c.more.length && c.more.length == 1){
14971 c.events.push(c.more.pop());
14974 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14978 this.cells.each(function(c) {
14980 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14983 for (var e = 0; e < c.events.length; e++){
14984 var ev = c.events[e];
14985 var rows = ev.rows;
14987 for(var i = 0; i < rows.length; i++) {
14989 // how many rows should it span..
14992 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14993 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14995 unselectable : "on",
14998 cls: 'fc-event-inner',
15002 // cls: 'fc-event-time',
15003 // html : cells.length > 1 ? '' : ev.time
15007 cls: 'fc-event-title',
15008 html : String.format('{0}', ev.title)
15015 cls: 'ui-resizable-handle ui-resizable-e',
15016 html : '  '
15023 cfg.cls += ' fc-event-start';
15025 if ((i+1) == rows.length) {
15026 cfg.cls += ' fc-event-end';
15029 var ctr = _this.el.select('.fc-event-container',true).first();
15030 var cg = ctr.createChild(cfg);
15032 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15033 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15035 var r = (c.more.length) ? 1 : 0;
15036 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15037 cg.setWidth(ebox.right - sbox.x -2);
15039 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15040 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15041 cg.on('click', _this.onEventClick, _this, ev);
15052 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15053 style : 'position: absolute',
15054 unselectable : "on",
15057 cls: 'fc-event-inner',
15061 cls: 'fc-event-title',
15069 cls: 'ui-resizable-handle ui-resizable-e',
15070 html : '  '
15076 var ctr = _this.el.select('.fc-event-container',true).first();
15077 var cg = ctr.createChild(cfg);
15079 var sbox = c.select('.fc-day-content',true).first().getBox();
15080 var ebox = c.select('.fc-day-content',true).first().getBox();
15082 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15083 cg.setWidth(ebox.right - sbox.x -2);
15085 cg.on('click', _this.onMoreEventClick, _this, c.more);
15095 onEventEnter: function (e, el,event,d) {
15096 this.fireEvent('evententer', this, el, event);
15099 onEventLeave: function (e, el,event,d) {
15100 this.fireEvent('eventleave', this, el, event);
15103 onEventClick: function (e, el,event,d) {
15104 this.fireEvent('eventclick', this, el, event);
15107 onMonthChange: function () {
15111 onMoreEventClick: function(e, el, more)
15115 this.calpopover.placement = 'right';
15116 this.calpopover.setTitle('More');
15118 this.calpopover.setContent('');
15120 var ctr = this.calpopover.el.select('.popover-content', true).first();
15122 Roo.each(more, function(m){
15124 cls : 'fc-event-hori fc-event-draggable',
15127 var cg = ctr.createChild(cfg);
15129 cg.on('click', _this.onEventClick, _this, m);
15132 this.calpopover.show(el);
15137 onLoad: function ()
15139 this.calevents = [];
15142 if(this.store.getCount() > 0){
15143 this.store.data.each(function(d){
15146 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15147 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15148 time : d.data.start_time,
15149 title : d.data.title,
15150 description : d.data.description,
15151 venue : d.data.venue
15156 this.renderEvents();
15158 if(this.calevents.length && this.loadMask){
15159 this.maskEl.hide();
15163 onBeforeLoad: function()
15165 this.clearEvents();
15167 this.maskEl.show();
15181 * @class Roo.bootstrap.Popover
15182 * @extends Roo.bootstrap.Component
15183 * Bootstrap Popover class
15184 * @cfg {String} html contents of the popover (or false to use children..)
15185 * @cfg {String} title of popover (or false to hide)
15186 * @cfg {String} placement how it is placed
15187 * @cfg {String} trigger click || hover (or false to trigger manually)
15188 * @cfg {String} over what (parent or false to trigger manually.)
15189 * @cfg {Number} delay - delay before showing
15192 * Create a new Popover
15193 * @param {Object} config The config object
15196 Roo.bootstrap.Popover = function(config){
15197 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15200 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15202 title: 'Fill in a title',
15205 placement : 'right',
15206 trigger : 'hover', // hover
15212 can_build_overlaid : false,
15214 getChildContainer : function()
15216 return this.el.select('.popover-content',true).first();
15219 getAutoCreate : function(){
15220 Roo.log('make popover?');
15222 cls : 'popover roo-dynamic',
15223 style: 'display:block',
15229 cls : 'popover-inner',
15233 cls: 'popover-title',
15237 cls : 'popover-content',
15248 setTitle: function(str)
15251 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15253 setContent: function(str)
15256 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15258 // as it get's added to the bottom of the page.
15259 onRender : function(ct, position)
15261 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15263 var cfg = Roo.apply({}, this.getAutoCreate());
15267 cfg.cls += ' ' + this.cls;
15270 cfg.style = this.style;
15272 Roo.log("adding to ")
15273 this.el = Roo.get(document.body).createChild(cfg, position);
15279 initEvents : function()
15281 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15282 this.el.enableDisplayMode('block');
15284 if (this.over === false) {
15287 if (this.triggers === false) {
15290 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15291 var triggers = this.trigger ? this.trigger.split(' ') : [];
15292 Roo.each(triggers, function(trigger) {
15294 if (trigger == 'click') {
15295 on_el.on('click', this.toggle, this);
15296 } else if (trigger != 'manual') {
15297 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15298 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15300 on_el.on(eventIn ,this.enter, this);
15301 on_el.on(eventOut, this.leave, this);
15312 toggle : function () {
15313 this.hoverState == 'in' ? this.leave() : this.enter();
15316 enter : function () {
15319 clearTimeout(this.timeout);
15321 this.hoverState = 'in';
15323 if (!this.delay || !this.delay.show) {
15328 this.timeout = setTimeout(function () {
15329 if (_t.hoverState == 'in') {
15332 }, this.delay.show)
15334 leave : function() {
15335 clearTimeout(this.timeout);
15337 this.hoverState = 'out';
15339 if (!this.delay || !this.delay.hide) {
15344 this.timeout = setTimeout(function () {
15345 if (_t.hoverState == 'out') {
15348 }, this.delay.hide)
15351 show : function (on_el)
15354 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15357 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15358 if (this.html !== false) {
15359 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15361 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15362 if (!this.title.length) {
15363 this.el.select('.popover-title',true).hide();
15366 var placement = typeof this.placement == 'function' ?
15367 this.placement.call(this, this.el, on_el) :
15370 var autoToken = /\s?auto?\s?/i;
15371 var autoPlace = autoToken.test(placement);
15373 placement = placement.replace(autoToken, '') || 'top';
15377 //this.el.setXY([0,0]);
15379 this.el.dom.style.display='block';
15380 this.el.addClass(placement);
15382 //this.el.appendTo(on_el);
15384 var p = this.getPosition();
15385 var box = this.el.getBox();
15390 var align = Roo.bootstrap.Popover.alignment[placement];
15391 this.el.alignTo(on_el, align[0],align[1]);
15392 //var arrow = this.el.select('.arrow',true).first();
15393 //arrow.set(align[2],
15395 this.el.addClass('in');
15398 if (this.el.hasClass('fade')) {
15405 this.el.setXY([0,0]);
15406 this.el.removeClass('in');
15408 this.hoverState = null;
15414 Roo.bootstrap.Popover.alignment = {
15415 'left' : ['r-l', [-10,0], 'right'],
15416 'right' : ['l-r', [10,0], 'left'],
15417 'bottom' : ['t-b', [0,10], 'top'],
15418 'top' : [ 'b-t', [0,-10], 'bottom']
15429 * @class Roo.bootstrap.Progress
15430 * @extends Roo.bootstrap.Component
15431 * Bootstrap Progress class
15432 * @cfg {Boolean} striped striped of the progress bar
15433 * @cfg {Boolean} active animated of the progress bar
15437 * Create a new Progress
15438 * @param {Object} config The config object
15441 Roo.bootstrap.Progress = function(config){
15442 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15445 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15450 getAutoCreate : function(){
15458 cfg.cls += ' progress-striped';
15462 cfg.cls += ' active';
15481 * @class Roo.bootstrap.ProgressBar
15482 * @extends Roo.bootstrap.Component
15483 * Bootstrap ProgressBar class
15484 * @cfg {Number} aria_valuenow aria-value now
15485 * @cfg {Number} aria_valuemin aria-value min
15486 * @cfg {Number} aria_valuemax aria-value max
15487 * @cfg {String} label label for the progress bar
15488 * @cfg {String} panel (success | info | warning | danger )
15489 * @cfg {String} role role of the progress bar
15490 * @cfg {String} sr_only text
15494 * Create a new ProgressBar
15495 * @param {Object} config The config object
15498 Roo.bootstrap.ProgressBar = function(config){
15499 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15502 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15506 aria_valuemax : 100,
15512 getAutoCreate : function()
15517 cls: 'progress-bar',
15518 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15530 cfg.role = this.role;
15533 if(this.aria_valuenow){
15534 cfg['aria-valuenow'] = this.aria_valuenow;
15537 if(this.aria_valuemin){
15538 cfg['aria-valuemin'] = this.aria_valuemin;
15541 if(this.aria_valuemax){
15542 cfg['aria-valuemax'] = this.aria_valuemax;
15545 if(this.label && !this.sr_only){
15546 cfg.html = this.label;
15550 cfg.cls += ' progress-bar-' + this.panel;
15556 update : function(aria_valuenow)
15558 this.aria_valuenow = aria_valuenow;
15560 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15575 * @class Roo.bootstrap.TabGroup
15576 * @extends Roo.bootstrap.Column
15577 * Bootstrap Column class
15578 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15579 * @cfg {Boolean} carousel true to make the group behave like a carousel
15580 * @cfg {Number} bullets show the panel pointer.. default 0
15581 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15582 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15583 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15586 * Create a new TabGroup
15587 * @param {Object} config The config object
15590 Roo.bootstrap.TabGroup = function(config){
15591 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15593 this.navId = Roo.id();
15596 Roo.bootstrap.TabGroup.register(this);
15600 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15603 transition : false,
15608 slideOnTouch : false,
15610 getAutoCreate : function()
15612 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15614 cfg.cls += ' tab-content';
15616 Roo.log('get auto create...............');
15618 if (this.carousel) {
15619 cfg.cls += ' carousel slide';
15622 cls : 'carousel-inner'
15625 if(this.bullets > 0 && !Roo.isTouch){
15628 cls : 'carousel-bullets',
15632 if(this.bullets_cls){
15633 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15636 for (var i = 0; i < this.bullets; i++){
15638 cls : 'bullet bullet-' + i
15646 cfg.cn[0].cn = bullets;
15653 initEvents: function()
15655 Roo.log('-------- init events on tab group ---------');
15657 if(this.bullets > 0 && !Roo.isTouch){
15663 if(Roo.isTouch && this.slideOnTouch){
15664 this.el.on("touchstart", this.onTouchStart, this);
15667 if(this.autoslide){
15670 this.slideFn = window.setInterval(function() {
15671 _this.showPanelNext();
15677 onTouchStart : function(e, el, o)
15679 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15683 this.showPanelNext();
15686 getChildContainer : function()
15688 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15692 * register a Navigation item
15693 * @param {Roo.bootstrap.NavItem} the navitem to add
15695 register : function(item)
15697 this.tabs.push( item);
15698 item.navId = this.navId; // not really needed..
15702 getActivePanel : function()
15705 Roo.each(this.tabs, function(t) {
15715 getPanelByName : function(n)
15718 Roo.each(this.tabs, function(t) {
15719 if (t.tabId == n) {
15727 indexOfPanel : function(p)
15730 Roo.each(this.tabs, function(t,i) {
15731 if (t.tabId == p.tabId) {
15740 * show a specific panel
15741 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15742 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15744 showPanel : function (pan)
15746 if(this.transition){
15747 Roo.log("waiting for the transitionend");
15751 if (typeof(pan) == 'number') {
15752 pan = this.tabs[pan];
15754 if (typeof(pan) == 'string') {
15755 pan = this.getPanelByName(pan);
15757 if (pan.tabId == this.getActivePanel().tabId) {
15760 var cur = this.getActivePanel();
15762 if (false === cur.fireEvent('beforedeactivate')) {
15766 if(this.bullets > 0 && !Roo.isTouch){
15767 this.setActiveBullet(this.indexOfPanel(pan));
15770 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15772 this.transition = true;
15773 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15774 var lr = dir == 'next' ? 'left' : 'right';
15775 pan.el.addClass(dir); // or prev
15776 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15777 cur.el.addClass(lr); // or right
15778 pan.el.addClass(lr);
15781 cur.el.on('transitionend', function() {
15782 Roo.log("trans end?");
15784 pan.el.removeClass([lr,dir]);
15785 pan.setActive(true);
15787 cur.el.removeClass([lr]);
15788 cur.setActive(false);
15790 _this.transition = false;
15792 }, this, { single: true } );
15797 cur.setActive(false);
15798 pan.setActive(true);
15803 showPanelNext : function()
15805 var i = this.indexOfPanel(this.getActivePanel());
15807 if (i >= this.tabs.length - 1 && !this.autoslide) {
15811 if (i >= this.tabs.length - 1 && this.autoslide) {
15815 this.showPanel(this.tabs[i+1]);
15818 showPanelPrev : function()
15820 var i = this.indexOfPanel(this.getActivePanel());
15822 if (i < 1 && !this.autoslide) {
15826 if (i < 1 && this.autoslide) {
15827 i = this.tabs.length;
15830 this.showPanel(this.tabs[i-1]);
15833 initBullet : function()
15841 for (var i = 0; i < this.bullets; i++){
15842 var bullet = this.el.select('.bullet-' + i, true).first();
15848 bullet.on('click', (function(e, el, o, ii, t){
15850 e.preventDefault();
15852 _this.showPanel(ii);
15854 if(_this.autoslide && _this.slideFn){
15855 clearInterval(_this.slideFn);
15856 _this.slideFn = window.setInterval(function() {
15857 _this.showPanelNext();
15861 }).createDelegate(this, [i, bullet], true));
15865 setActiveBullet : function(i)
15871 Roo.each(this.el.select('.bullet', true).elements, function(el){
15872 el.removeClass('selected');
15875 var bullet = this.el.select('.bullet-' + i, true).first();
15881 bullet.addClass('selected');
15892 Roo.apply(Roo.bootstrap.TabGroup, {
15896 * register a Navigation Group
15897 * @param {Roo.bootstrap.NavGroup} the navgroup to add
15899 register : function(navgrp)
15901 this.groups[navgrp.navId] = navgrp;
15905 * fetch a Navigation Group based on the navigation ID
15906 * if one does not exist , it will get created.
15907 * @param {string} the navgroup to add
15908 * @returns {Roo.bootstrap.NavGroup} the navgroup
15910 get: function(navId) {
15911 if (typeof(this.groups[navId]) == 'undefined') {
15912 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15914 return this.groups[navId] ;
15929 * @class Roo.bootstrap.TabPanel
15930 * @extends Roo.bootstrap.Component
15931 * Bootstrap TabPanel class
15932 * @cfg {Boolean} active panel active
15933 * @cfg {String} html panel content
15934 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15935 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15939 * Create a new TabPanel
15940 * @param {Object} config The config object
15943 Roo.bootstrap.TabPanel = function(config){
15944 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15948 * Fires when the active status changes
15949 * @param {Roo.bootstrap.TabPanel} this
15950 * @param {Boolean} state the new state
15955 * @event beforedeactivate
15956 * Fires before a tab is de-activated - can be used to do validation on a form.
15957 * @param {Roo.bootstrap.TabPanel} this
15958 * @return {Boolean} false if there is an error
15961 'beforedeactivate': true
15964 this.tabId = this.tabId || Roo.id();
15968 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
15975 getAutoCreate : function(){
15978 // item is needed for carousel - not sure if it has any effect otherwise
15979 cls: 'tab-pane item',
15980 html: this.html || ''
15984 cfg.cls += ' active';
15988 cfg.tabId = this.tabId;
15995 initEvents: function()
15997 Roo.log('-------- init events on tab panel ---------');
15999 var p = this.parent();
16000 this.navId = this.navId || p.navId;
16002 if (typeof(this.navId) != 'undefined') {
16003 // not really needed.. but just in case.. parent should be a NavGroup.
16004 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16005 Roo.log(['register', tg, this]);
16008 var i = tg.tabs.length - 1;
16010 if(this.active && tg.bullets > 0 && i < tg.bullets){
16011 tg.setActiveBullet(i);
16018 onRender : function(ct, position)
16020 // Roo.log("Call onRender: " + this.xtype);
16022 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16030 setActive: function(state)
16032 Roo.log("panel - set active " + this.tabId + "=" + state);
16034 this.active = state;
16036 this.el.removeClass('active');
16038 } else if (!this.el.hasClass('active')) {
16039 this.el.addClass('active');
16042 this.fireEvent('changed', this, state);
16059 * @class Roo.bootstrap.DateField
16060 * @extends Roo.bootstrap.Input
16061 * Bootstrap DateField class
16062 * @cfg {Number} weekStart default 0
16063 * @cfg {String} viewMode default empty, (months|years)
16064 * @cfg {String} minViewMode default empty, (months|years)
16065 * @cfg {Number} startDate default -Infinity
16066 * @cfg {Number} endDate default Infinity
16067 * @cfg {Boolean} todayHighlight default false
16068 * @cfg {Boolean} todayBtn default false
16069 * @cfg {Boolean} calendarWeeks default false
16070 * @cfg {Object} daysOfWeekDisabled default empty
16071 * @cfg {Boolean} singleMode default false (true | false)
16073 * @cfg {Boolean} keyboardNavigation default true
16074 * @cfg {String} language default en
16077 * Create a new DateField
16078 * @param {Object} config The config object
16081 Roo.bootstrap.DateField = function(config){
16082 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16086 * Fires when this field show.
16087 * @param {Roo.bootstrap.DateField} this
16088 * @param {Mixed} date The date value
16093 * Fires when this field hide.
16094 * @param {Roo.bootstrap.DateField} this
16095 * @param {Mixed} date The date value
16100 * Fires when select a date.
16101 * @param {Roo.bootstrap.DateField} this
16102 * @param {Mixed} date The date value
16108 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16111 * @cfg {String} format
16112 * The default date format string which can be overriden for localization support. The format must be
16113 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16117 * @cfg {String} altFormats
16118 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16119 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16121 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16129 todayHighlight : false,
16135 keyboardNavigation: true,
16137 calendarWeeks: false,
16139 startDate: -Infinity,
16143 daysOfWeekDisabled: [],
16147 singleMode : false,
16149 UTCDate: function()
16151 return new Date(Date.UTC.apply(Date, arguments));
16154 UTCToday: function()
16156 var today = new Date();
16157 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16160 getDate: function() {
16161 var d = this.getUTCDate();
16162 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16165 getUTCDate: function() {
16169 setDate: function(d) {
16170 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16173 setUTCDate: function(d) {
16175 this.setValue(this.formatDate(this.date));
16178 onRender: function(ct, position)
16181 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16183 this.language = this.language || 'en';
16184 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16185 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16187 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16188 this.format = this.format || 'm/d/y';
16189 this.isInline = false;
16190 this.isInput = true;
16191 this.component = this.el.select('.add-on', true).first() || false;
16192 this.component = (this.component && this.component.length === 0) ? false : this.component;
16193 this.hasInput = this.component && this.inputEL().length;
16195 if (typeof(this.minViewMode === 'string')) {
16196 switch (this.minViewMode) {
16198 this.minViewMode = 1;
16201 this.minViewMode = 2;
16204 this.minViewMode = 0;
16209 if (typeof(this.viewMode === 'string')) {
16210 switch (this.viewMode) {
16223 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16225 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16227 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16229 this.picker().on('mousedown', this.onMousedown, this);
16230 this.picker().on('click', this.onClick, this);
16232 this.picker().addClass('datepicker-dropdown');
16234 this.startViewMode = this.viewMode;
16236 if(this.singleMode){
16237 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16238 v.setVisibilityMode(Roo.Element.DISPLAY)
16242 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16243 v.setStyle('width', '189px');
16247 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16248 if(!this.calendarWeeks){
16253 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16254 v.attr('colspan', function(i, val){
16255 return parseInt(val) + 1;
16260 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16262 this.setStartDate(this.startDate);
16263 this.setEndDate(this.endDate);
16265 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16272 if(this.isInline) {
16277 picker : function()
16279 return this.pickerEl;
16280 // return this.el.select('.datepicker', true).first();
16283 fillDow: function()
16285 var dowCnt = this.weekStart;
16294 if(this.calendarWeeks){
16302 while (dowCnt < this.weekStart + 7) {
16306 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16310 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16313 fillMonths: function()
16316 var months = this.picker().select('>.datepicker-months td', true).first();
16318 months.dom.innerHTML = '';
16324 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16327 months.createChild(month);
16334 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;
16336 if (this.date < this.startDate) {
16337 this.viewDate = new Date(this.startDate);
16338 } else if (this.date > this.endDate) {
16339 this.viewDate = new Date(this.endDate);
16341 this.viewDate = new Date(this.date);
16349 var d = new Date(this.viewDate),
16350 year = d.getUTCFullYear(),
16351 month = d.getUTCMonth(),
16352 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16353 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16354 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16355 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16356 currentDate = this.date && this.date.valueOf(),
16357 today = this.UTCToday();
16359 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16361 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16363 // this.picker.select('>tfoot th.today').
16364 // .text(dates[this.language].today)
16365 // .toggle(this.todayBtn !== false);
16367 this.updateNavArrows();
16370 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16372 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16374 prevMonth.setUTCDate(day);
16376 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16378 var nextMonth = new Date(prevMonth);
16380 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16382 nextMonth = nextMonth.valueOf();
16384 var fillMonths = false;
16386 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16388 while(prevMonth.valueOf() < nextMonth) {
16391 if (prevMonth.getUTCDay() === this.weekStart) {
16393 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16401 if(this.calendarWeeks){
16402 // ISO 8601: First week contains first thursday.
16403 // ISO also states week starts on Monday, but we can be more abstract here.
16405 // Start of current week: based on weekstart/current date
16406 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16407 // Thursday of this week
16408 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16409 // First Thursday of year, year from thursday
16410 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16411 // Calendar week: ms between thursdays, div ms per day, div 7 days
16412 calWeek = (th - yth) / 864e5 / 7 + 1;
16414 fillMonths.cn.push({
16422 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16424 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16427 if (this.todayHighlight &&
16428 prevMonth.getUTCFullYear() == today.getFullYear() &&
16429 prevMonth.getUTCMonth() == today.getMonth() &&
16430 prevMonth.getUTCDate() == today.getDate()) {
16431 clsName += ' today';
16434 if (currentDate && prevMonth.valueOf() === currentDate) {
16435 clsName += ' active';
16438 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16439 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16440 clsName += ' disabled';
16443 fillMonths.cn.push({
16445 cls: 'day ' + clsName,
16446 html: prevMonth.getDate()
16449 prevMonth.setDate(prevMonth.getDate()+1);
16452 var currentYear = this.date && this.date.getUTCFullYear();
16453 var currentMonth = this.date && this.date.getUTCMonth();
16455 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16457 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16458 v.removeClass('active');
16460 if(currentYear === year && k === currentMonth){
16461 v.addClass('active');
16464 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16465 v.addClass('disabled');
16471 year = parseInt(year/10, 10) * 10;
16473 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16475 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16478 for (var i = -1; i < 11; i++) {
16479 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16481 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16489 showMode: function(dir)
16492 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16495 Roo.each(this.picker().select('>div',true).elements, function(v){
16496 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16499 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16504 if(this.isInline) return;
16506 this.picker().removeClass(['bottom', 'top']);
16508 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16510 * place to the top of element!
16514 this.picker().addClass('top');
16515 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16520 this.picker().addClass('bottom');
16522 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16525 parseDate : function(value)
16527 if(!value || value instanceof Date){
16530 var v = Date.parseDate(value, this.format);
16531 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16532 v = Date.parseDate(value, 'Y-m-d');
16534 if(!v && this.altFormats){
16535 if(!this.altFormatsArray){
16536 this.altFormatsArray = this.altFormats.split("|");
16538 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16539 v = Date.parseDate(value, this.altFormatsArray[i]);
16545 formatDate : function(date, fmt)
16547 return (!date || !(date instanceof Date)) ?
16548 date : date.dateFormat(fmt || this.format);
16551 onFocus : function()
16553 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16557 onBlur : function()
16559 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16561 var d = this.inputEl().getValue();
16570 this.picker().show();
16574 this.fireEvent('show', this, this.date);
16579 if(this.isInline) return;
16580 this.picker().hide();
16581 this.viewMode = this.startViewMode;
16584 this.fireEvent('hide', this, this.date);
16588 onMousedown: function(e)
16590 e.stopPropagation();
16591 e.preventDefault();
16596 Roo.bootstrap.DateField.superclass.keyup.call(this);
16600 setValue: function(v)
16603 // v can be a string or a date..
16606 var d = new Date(this.parseDate(v) ).clearTime();
16608 if(isNaN(d.getTime())){
16609 this.date = this.viewDate = '';
16610 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16614 v = this.formatDate(d);
16616 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16618 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16622 this.fireEvent('select', this, this.date);
16626 getValue: function()
16628 return this.formatDate(this.date);
16631 fireKey: function(e)
16633 if (!this.picker().isVisible()){
16634 if (e.keyCode == 27) // allow escape to hide and re-show picker
16639 var dateChanged = false,
16641 newDate, newViewDate;
16646 e.preventDefault();
16650 if (!this.keyboardNavigation) break;
16651 dir = e.keyCode == 37 ? -1 : 1;
16654 newDate = this.moveYear(this.date, dir);
16655 newViewDate = this.moveYear(this.viewDate, dir);
16656 } else if (e.shiftKey){
16657 newDate = this.moveMonth(this.date, dir);
16658 newViewDate = this.moveMonth(this.viewDate, dir);
16660 newDate = new Date(this.date);
16661 newDate.setUTCDate(this.date.getUTCDate() + dir);
16662 newViewDate = new Date(this.viewDate);
16663 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16665 if (this.dateWithinRange(newDate)){
16666 this.date = newDate;
16667 this.viewDate = newViewDate;
16668 this.setValue(this.formatDate(this.date));
16670 e.preventDefault();
16671 dateChanged = true;
16676 if (!this.keyboardNavigation) break;
16677 dir = e.keyCode == 38 ? -1 : 1;
16679 newDate = this.moveYear(this.date, dir);
16680 newViewDate = this.moveYear(this.viewDate, dir);
16681 } else if (e.shiftKey){
16682 newDate = this.moveMonth(this.date, dir);
16683 newViewDate = this.moveMonth(this.viewDate, dir);
16685 newDate = new Date(this.date);
16686 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16687 newViewDate = new Date(this.viewDate);
16688 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16690 if (this.dateWithinRange(newDate)){
16691 this.date = newDate;
16692 this.viewDate = newViewDate;
16693 this.setValue(this.formatDate(this.date));
16695 e.preventDefault();
16696 dateChanged = true;
16700 this.setValue(this.formatDate(this.date));
16702 e.preventDefault();
16705 this.setValue(this.formatDate(this.date));
16719 onClick: function(e)
16721 e.stopPropagation();
16722 e.preventDefault();
16724 var target = e.getTarget();
16726 if(target.nodeName.toLowerCase() === 'i'){
16727 target = Roo.get(target).dom.parentNode;
16730 var nodeName = target.nodeName;
16731 var className = target.className;
16732 var html = target.innerHTML;
16733 //Roo.log(nodeName);
16735 switch(nodeName.toLowerCase()) {
16737 switch(className) {
16743 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16744 switch(this.viewMode){
16746 this.viewDate = this.moveMonth(this.viewDate, dir);
16750 this.viewDate = this.moveYear(this.viewDate, dir);
16756 var date = new Date();
16757 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16759 this.setValue(this.formatDate(this.date));
16766 if (className.indexOf('disabled') < 0) {
16767 this.viewDate.setUTCDate(1);
16768 if (className.indexOf('month') > -1) {
16769 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16771 var year = parseInt(html, 10) || 0;
16772 this.viewDate.setUTCFullYear(year);
16776 if(this.singleMode){
16777 this.setValue(this.formatDate(this.viewDate));
16788 //Roo.log(className);
16789 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16790 var day = parseInt(html, 10) || 1;
16791 var year = this.viewDate.getUTCFullYear(),
16792 month = this.viewDate.getUTCMonth();
16794 if (className.indexOf('old') > -1) {
16801 } else if (className.indexOf('new') > -1) {
16809 //Roo.log([year,month,day]);
16810 this.date = this.UTCDate(year, month, day,0,0,0,0);
16811 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16813 //Roo.log(this.formatDate(this.date));
16814 this.setValue(this.formatDate(this.date));
16821 setStartDate: function(startDate)
16823 this.startDate = startDate || -Infinity;
16824 if (this.startDate !== -Infinity) {
16825 this.startDate = this.parseDate(this.startDate);
16828 this.updateNavArrows();
16831 setEndDate: function(endDate)
16833 this.endDate = endDate || Infinity;
16834 if (this.endDate !== Infinity) {
16835 this.endDate = this.parseDate(this.endDate);
16838 this.updateNavArrows();
16841 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16843 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16844 if (typeof(this.daysOfWeekDisabled) !== 'object') {
16845 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16847 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16848 return parseInt(d, 10);
16851 this.updateNavArrows();
16854 updateNavArrows: function()
16856 if(this.singleMode){
16860 var d = new Date(this.viewDate),
16861 year = d.getUTCFullYear(),
16862 month = d.getUTCMonth();
16864 Roo.each(this.picker().select('.prev', true).elements, function(v){
16866 switch (this.viewMode) {
16869 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16875 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16882 Roo.each(this.picker().select('.next', true).elements, function(v){
16884 switch (this.viewMode) {
16887 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16893 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16901 moveMonth: function(date, dir)
16903 if (!dir) return date;
16904 var new_date = new Date(date.valueOf()),
16905 day = new_date.getUTCDate(),
16906 month = new_date.getUTCMonth(),
16907 mag = Math.abs(dir),
16909 dir = dir > 0 ? 1 : -1;
16912 // If going back one month, make sure month is not current month
16913 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16915 return new_date.getUTCMonth() == month;
16917 // If going forward one month, make sure month is as expected
16918 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16920 return new_date.getUTCMonth() != new_month;
16922 new_month = month + dir;
16923 new_date.setUTCMonth(new_month);
16924 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16925 if (new_month < 0 || new_month > 11)
16926 new_month = (new_month + 12) % 12;
16928 // For magnitudes >1, move one month at a time...
16929 for (var i=0; i<mag; i++)
16930 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16931 new_date = this.moveMonth(new_date, dir);
16932 // ...then reset the day, keeping it in the new month
16933 new_month = new_date.getUTCMonth();
16934 new_date.setUTCDate(day);
16936 return new_month != new_date.getUTCMonth();
16939 // Common date-resetting loop -- if date is beyond end of month, make it
16942 new_date.setUTCDate(--day);
16943 new_date.setUTCMonth(new_month);
16948 moveYear: function(date, dir)
16950 return this.moveMonth(date, dir*12);
16953 dateWithinRange: function(date)
16955 return date >= this.startDate && date <= this.endDate;
16961 this.picker().remove();
16966 Roo.apply(Roo.bootstrap.DateField, {
16977 html: '<i class="fa fa-arrow-left"/>'
16987 html: '<i class="fa fa-arrow-right"/>'
17029 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17030 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17031 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17032 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17033 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17046 navFnc: 'FullYear',
17051 navFnc: 'FullYear',
17056 Roo.apply(Roo.bootstrap.DateField, {
17060 cls: 'datepicker dropdown-menu roo-dynamic',
17064 cls: 'datepicker-days',
17068 cls: 'table-condensed',
17070 Roo.bootstrap.DateField.head,
17074 Roo.bootstrap.DateField.footer
17081 cls: 'datepicker-months',
17085 cls: 'table-condensed',
17087 Roo.bootstrap.DateField.head,
17088 Roo.bootstrap.DateField.content,
17089 Roo.bootstrap.DateField.footer
17096 cls: 'datepicker-years',
17100 cls: 'table-condensed',
17102 Roo.bootstrap.DateField.head,
17103 Roo.bootstrap.DateField.content,
17104 Roo.bootstrap.DateField.footer
17123 * @class Roo.bootstrap.TimeField
17124 * @extends Roo.bootstrap.Input
17125 * Bootstrap DateField class
17129 * Create a new TimeField
17130 * @param {Object} config The config object
17133 Roo.bootstrap.TimeField = function(config){
17134 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17138 * Fires when this field show.
17139 * @param {Roo.bootstrap.DateField} thisthis
17140 * @param {Mixed} date The date value
17145 * Fires when this field hide.
17146 * @param {Roo.bootstrap.DateField} this
17147 * @param {Mixed} date The date value
17152 * Fires when select a date.
17153 * @param {Roo.bootstrap.DateField} this
17154 * @param {Mixed} date The date value
17160 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17163 * @cfg {String} format
17164 * The default time format string which can be overriden for localization support. The format must be
17165 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17169 onRender: function(ct, position)
17172 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17174 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17176 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17178 this.pop = this.picker().select('>.datepicker-time',true).first();
17179 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17181 this.picker().on('mousedown', this.onMousedown, this);
17182 this.picker().on('click', this.onClick, this);
17184 this.picker().addClass('datepicker-dropdown');
17189 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17190 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17191 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17192 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17193 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17194 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17198 fireKey: function(e){
17199 if (!this.picker().isVisible()){
17200 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17206 e.preventDefault();
17214 this.onTogglePeriod();
17217 this.onIncrementMinutes();
17220 this.onDecrementMinutes();
17229 onClick: function(e) {
17230 e.stopPropagation();
17231 e.preventDefault();
17234 picker : function()
17236 return this.el.select('.datepicker', true).first();
17239 fillTime: function()
17241 var time = this.pop.select('tbody', true).first();
17243 time.dom.innerHTML = '';
17258 cls: 'hours-up glyphicon glyphicon-chevron-up'
17278 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17299 cls: 'timepicker-hour',
17314 cls: 'timepicker-minute',
17329 cls: 'btn btn-primary period',
17351 cls: 'hours-down glyphicon glyphicon-chevron-down'
17371 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17389 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17396 var hours = this.time.getHours();
17397 var minutes = this.time.getMinutes();
17410 hours = hours - 12;
17414 hours = '0' + hours;
17418 minutes = '0' + minutes;
17421 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17422 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17423 this.pop.select('button', true).first().dom.innerHTML = period;
17429 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17431 var cls = ['bottom'];
17433 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17440 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17445 this.picker().addClass(cls.join('-'));
17449 Roo.each(cls, function(c){
17451 _this.picker().setTop(_this.inputEl().getHeight());
17455 _this.picker().setTop(0 - _this.picker().getHeight());
17460 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17464 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17471 onFocus : function()
17473 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17477 onBlur : function()
17479 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17485 this.picker().show();
17490 this.fireEvent('show', this, this.date);
17495 this.picker().hide();
17498 this.fireEvent('hide', this, this.date);
17501 setTime : function()
17504 this.setValue(this.time.format(this.format));
17506 this.fireEvent('select', this, this.date);
17511 onMousedown: function(e){
17512 e.stopPropagation();
17513 e.preventDefault();
17516 onIncrementHours: function()
17518 Roo.log('onIncrementHours');
17519 this.time = this.time.add(Date.HOUR, 1);
17524 onDecrementHours: function()
17526 Roo.log('onDecrementHours');
17527 this.time = this.time.add(Date.HOUR, -1);
17531 onIncrementMinutes: function()
17533 Roo.log('onIncrementMinutes');
17534 this.time = this.time.add(Date.MINUTE, 1);
17538 onDecrementMinutes: function()
17540 Roo.log('onDecrementMinutes');
17541 this.time = this.time.add(Date.MINUTE, -1);
17545 onTogglePeriod: function()
17547 Roo.log('onTogglePeriod');
17548 this.time = this.time.add(Date.HOUR, 12);
17555 Roo.apply(Roo.bootstrap.TimeField, {
17585 cls: 'btn btn-info ok',
17597 Roo.apply(Roo.bootstrap.TimeField, {
17601 cls: 'datepicker dropdown-menu',
17605 cls: 'datepicker-time',
17609 cls: 'table-condensed',
17611 Roo.bootstrap.TimeField.content,
17612 Roo.bootstrap.TimeField.footer
17631 * @class Roo.bootstrap.MonthField
17632 * @extends Roo.bootstrap.Input
17633 * Bootstrap MonthField class
17635 * @cfg {String} language default en
17638 * Create a new MonthField
17639 * @param {Object} config The config object
17642 Roo.bootstrap.MonthField = function(config){
17643 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17648 * Fires when this field show.
17649 * @param {Roo.bootstrap.MonthField} this
17650 * @param {Mixed} date The date value
17655 * Fires when this field hide.
17656 * @param {Roo.bootstrap.MonthField} this
17657 * @param {Mixed} date The date value
17662 * Fires when select a date.
17663 * @param {Roo.bootstrap.MonthField} this
17664 * @param {String} oldvalue The old value
17665 * @param {String} newvalue The new value
17671 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17673 onRender: function(ct, position)
17676 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17678 this.language = this.language || 'en';
17679 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17680 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17682 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17683 this.isInline = false;
17684 this.isInput = true;
17685 this.component = this.el.select('.add-on', true).first() || false;
17686 this.component = (this.component && this.component.length === 0) ? false : this.component;
17687 this.hasInput = this.component && this.inputEL().length;
17689 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17691 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17693 this.picker().on('mousedown', this.onMousedown, this);
17694 this.picker().on('click', this.onClick, this);
17696 this.picker().addClass('datepicker-dropdown');
17698 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17699 v.setStyle('width', '189px');
17706 if(this.isInline) {
17712 setValue: function(v, suppressEvent)
17714 var o = this.getValue();
17716 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17720 if(suppressEvent !== true){
17721 this.fireEvent('select', this, o, v);
17726 getValue: function()
17731 onClick: function(e)
17733 e.stopPropagation();
17734 e.preventDefault();
17736 var target = e.getTarget();
17738 if(target.nodeName.toLowerCase() === 'i'){
17739 target = Roo.get(target).dom.parentNode;
17742 var nodeName = target.nodeName;
17743 var className = target.className;
17744 var html = target.innerHTML;
17746 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17750 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17752 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17758 picker : function()
17760 return this.pickerEl;
17763 fillMonths: function()
17766 var months = this.picker().select('>.datepicker-months td', true).first();
17768 months.dom.innerHTML = '';
17774 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17777 months.createChild(month);
17786 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17787 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17790 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17791 e.removeClass('active');
17793 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17794 e.addClass('active');
17801 if(this.isInline) return;
17803 this.picker().removeClass(['bottom', 'top']);
17805 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17807 * place to the top of element!
17811 this.picker().addClass('top');
17812 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17817 this.picker().addClass('bottom');
17819 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17822 onFocus : function()
17824 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17828 onBlur : function()
17830 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17832 var d = this.inputEl().getValue();
17841 this.picker().show();
17842 this.picker().select('>.datepicker-months', true).first().show();
17846 this.fireEvent('show', this, this.date);
17851 if(this.isInline) return;
17852 this.picker().hide();
17853 this.fireEvent('hide', this, this.date);
17857 onMousedown: function(e)
17859 e.stopPropagation();
17860 e.preventDefault();
17865 Roo.bootstrap.MonthField.superclass.keyup.call(this);
17869 fireKey: function(e)
17871 if (!this.picker().isVisible()){
17872 if (e.keyCode == 27) // allow escape to hide and re-show picker
17882 e.preventDefault();
17886 dir = e.keyCode == 37 ? -1 : 1;
17888 this.vIndex = this.vIndex + dir;
17890 if(this.vIndex < 0){
17894 if(this.vIndex > 11){
17898 if(isNaN(this.vIndex)){
17902 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17908 dir = e.keyCode == 38 ? -1 : 1;
17910 this.vIndex = this.vIndex + dir * 4;
17912 if(this.vIndex < 0){
17916 if(this.vIndex > 11){
17920 if(isNaN(this.vIndex)){
17924 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17929 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17930 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17934 e.preventDefault();
17937 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17938 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17954 this.picker().remove();
17959 Roo.apply(Roo.bootstrap.MonthField, {
17978 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17979 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17984 Roo.apply(Roo.bootstrap.MonthField, {
17988 cls: 'datepicker dropdown-menu roo-dynamic',
17992 cls: 'datepicker-months',
17996 cls: 'table-condensed',
17998 Roo.bootstrap.DateField.content
18018 * @class Roo.bootstrap.CheckBox
18019 * @extends Roo.bootstrap.Input
18020 * Bootstrap CheckBox class
18022 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18023 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18024 * @cfg {String} boxLabel The text that appears beside the checkbox
18025 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18026 * @cfg {Boolean} checked initnal the element
18027 * @cfg {Boolean} inline inline the element (default false)
18028 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18031 * Create a new CheckBox
18032 * @param {Object} config The config object
18035 Roo.bootstrap.CheckBox = function(config){
18036 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18041 * Fires when the element is checked or unchecked.
18042 * @param {Roo.bootstrap.CheckBox} this This input
18043 * @param {Boolean} checked The new checked value
18050 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18052 inputType: 'checkbox',
18060 getAutoCreate : function()
18062 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18068 cfg.cls = 'form-group ' + this.inputType; //input-group
18071 cfg.cls += ' ' + this.inputType + '-inline';
18077 type : this.inputType,
18078 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18079 cls : 'roo-' + this.inputType, //'form-box',
18080 placeholder : this.placeholder || ''
18084 if (this.weight) { // Validity check?
18085 cfg.cls += " " + this.inputType + "-" + this.weight;
18088 if (this.disabled) {
18089 input.disabled=true;
18093 input.checked = this.checked;
18097 input.name = this.name;
18101 input.cls += ' input-' + this.size;
18106 ['xs','sm','md','lg'].map(function(size){
18107 if (settings[size]) {
18108 cfg.cls += ' col-' + size + '-' + settings[size];
18112 var inputblock = input;
18114 if (this.before || this.after) {
18117 cls : 'input-group',
18122 inputblock.cn.push({
18124 cls : 'input-group-addon',
18129 inputblock.cn.push(input);
18132 inputblock.cn.push({
18134 cls : 'input-group-addon',
18141 if (align ==='left' && this.fieldLabel.length) {
18142 Roo.log("left and has label");
18148 cls : 'control-label col-md-' + this.labelWidth,
18149 html : this.fieldLabel
18153 cls : "col-md-" + (12 - this.labelWidth),
18160 } else if ( this.fieldLabel.length) {
18165 tag: this.boxLabel ? 'span' : 'label',
18167 cls: 'control-label box-input-label',
18168 //cls : 'input-group-addon',
18169 html : this.fieldLabel
18179 Roo.log(" no label && no align");
18180 cfg.cn = [ inputblock ] ;
18185 var boxLabelCfg = {
18187 //'for': id, // box label is handled by onclick - so no for...
18189 html: this.boxLabel
18193 boxLabelCfg.tooltip = this.tooltip;
18196 cfg.cn.push(boxLabelCfg);
18206 * return the real input element.
18208 inputEl: function ()
18210 return this.el.select('input.roo-' + this.inputType,true).first();
18213 labelEl: function()
18215 return this.el.select('label.control-label',true).first();
18217 /* depricated... */
18221 return this.labelEl();
18224 initEvents : function()
18226 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18228 this.inputEl().on('click', this.onClick, this);
18230 if (this.boxLabel) {
18231 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18234 this.startValue = this.getValue();
18237 Roo.bootstrap.CheckBox.register(this);
18241 onClick : function()
18243 this.setChecked(!this.checked);
18246 setChecked : function(state,suppressEvent)
18248 this.startValue = this.getValue();
18250 if(this.inputType == 'radio'){
18252 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18253 e.dom.checked = false;
18256 this.inputEl().dom.checked = true;
18258 this.inputEl().dom.value = this.inputValue;
18260 if(suppressEvent !== true){
18261 this.fireEvent('check', this, true);
18269 this.checked = state;
18271 this.inputEl().dom.checked = state;
18273 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18275 if(suppressEvent !== true){
18276 this.fireEvent('check', this, state);
18282 getValue : function()
18284 if(this.inputType == 'radio'){
18285 return this.getGroupValue();
18288 return this.inputEl().getValue();
18292 getGroupValue : function()
18294 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18298 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18301 setValue : function(v,suppressEvent)
18303 if(this.inputType == 'radio'){
18304 this.setGroupValue(v, suppressEvent);
18308 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18313 setGroupValue : function(v, suppressEvent)
18315 this.startValue = this.getValue();
18317 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18318 e.dom.checked = false;
18320 if(e.dom.value == v){
18321 e.dom.checked = true;
18325 if(suppressEvent !== true){
18326 this.fireEvent('check', this, true);
18334 validate : function()
18338 (this.inputType == 'radio' && this.validateRadio()) ||
18339 (this.inputType == 'checkbox' && this.validateCheckbox())
18345 this.markInvalid();
18349 validateRadio : function()
18353 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18354 if(!e.dom.checked){
18366 validateCheckbox : function()
18369 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18372 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18380 for(var i in group){
18385 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18392 * Mark this field as valid
18394 markValid : function()
18396 if(this.allowBlank){
18402 this.fireEvent('valid', this);
18404 if(this.inputType == 'radio'){
18405 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18406 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18407 e.findParent('.form-group', false, true).addClass(_this.validClass);
18414 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18415 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18419 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18425 for(var i in group){
18426 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18427 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18432 * Mark this field as invalid
18433 * @param {String} msg The validation message
18435 markInvalid : function(msg)
18437 if(this.allowBlank){
18443 this.fireEvent('invalid', this, msg);
18445 if(this.inputType == 'radio'){
18446 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18447 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18448 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18455 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18456 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18460 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18466 for(var i in group){
18467 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18468 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18475 Roo.apply(Roo.bootstrap.CheckBox, {
18480 * register a CheckBox Group
18481 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18483 register : function(checkbox)
18485 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18486 this.groups[checkbox.groupId] = {};
18489 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18493 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18497 * fetch a CheckBox Group based on the group ID
18498 * @param {string} the group ID
18499 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18501 get: function(groupId) {
18502 if (typeof(this.groups[groupId]) == 'undefined') {
18506 return this.groups[groupId] ;
18518 *<div class="radio">
18520 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18521 Option one is this and that—be sure to include why it's great
18528 *<label class="radio-inline">fieldLabel</label>
18529 *<label class="radio-inline">
18530 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18538 * @class Roo.bootstrap.Radio
18539 * @extends Roo.bootstrap.CheckBox
18540 * Bootstrap Radio class
18543 * Create a new Radio
18544 * @param {Object} config The config object
18547 Roo.bootstrap.Radio = function(config){
18548 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18552 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18554 inputType: 'radio',
18558 getAutoCreate : function()
18560 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18561 align = align || 'left'; // default...
18568 tag : this.inline ? 'span' : 'div',
18573 var inline = this.inline ? ' radio-inline' : '';
18577 // does not need for, as we wrap the input with it..
18579 cls : 'control-label box-label' + inline,
18582 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18586 //cls : 'control-label' + inline,
18587 html : this.fieldLabel,
18588 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18597 type : this.inputType,
18598 //value : (!this.checked) ? this.valueOff : this.inputValue,
18599 value : this.inputValue,
18601 placeholder : this.placeholder || '' // ?? needed????
18604 if (this.weight) { // Validity check?
18605 input.cls += " radio-" + this.weight;
18607 if (this.disabled) {
18608 input.disabled=true;
18612 input.checked = this.checked;
18616 input.name = this.name;
18620 input.cls += ' input-' + this.size;
18623 //?? can span's inline have a width??
18626 ['xs','sm','md','lg'].map(function(size){
18627 if (settings[size]) {
18628 cfg.cls += ' col-' + size + '-' + settings[size];
18632 var inputblock = input;
18634 if (this.before || this.after) {
18637 cls : 'input-group',
18642 inputblock.cn.push({
18644 cls : 'input-group-addon',
18648 inputblock.cn.push(input);
18650 inputblock.cn.push({
18652 cls : 'input-group-addon',
18660 if (this.fieldLabel && this.fieldLabel.length) {
18661 cfg.cn.push(fieldLabel);
18664 // normal bootstrap puts the input inside the label.
18665 // however with our styled version - it has to go after the input.
18667 //lbl.cn.push(inputblock);
18671 cls: 'radio' + inline,
18678 cfg.cn.push( lblwrap);
18683 html: this.boxLabel
18692 initEvents : function()
18694 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18696 this.inputEl().on('click', this.onClick, this);
18697 if (this.boxLabel) {
18698 Roo.log('find label')
18699 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18704 inputEl: function ()
18706 return this.el.select('input.roo-radio',true).first();
18708 onClick : function()
18711 this.setChecked(true);
18714 setChecked : function(state,suppressEvent)
18717 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18718 v.dom.checked = false;
18721 Roo.log(this.inputEl().dom);
18722 this.checked = state;
18723 this.inputEl().dom.checked = state;
18725 if(suppressEvent !== true){
18726 this.fireEvent('check', this, state);
18729 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18733 getGroupValue : function()
18736 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18737 if(v.dom.checked == true){
18738 value = v.dom.value;
18746 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18747 * @return {Mixed} value The field value
18749 getValue : function(){
18750 return this.getGroupValue();
18756 //<script type="text/javascript">
18759 * Based Ext JS Library 1.1.1
18760 * Copyright(c) 2006-2007, Ext JS, LLC.
18766 * @class Roo.HtmlEditorCore
18767 * @extends Roo.Component
18768 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18770 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18773 Roo.HtmlEditorCore = function(config){
18776 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18781 * @event initialize
18782 * Fires when the editor is fully initialized (including the iframe)
18783 * @param {Roo.HtmlEditorCore} this
18788 * Fires when the editor is first receives the focus. Any insertion must wait
18789 * until after this event.
18790 * @param {Roo.HtmlEditorCore} this
18794 * @event beforesync
18795 * Fires before the textarea is updated with content from the editor iframe. Return false
18796 * to cancel the sync.
18797 * @param {Roo.HtmlEditorCore} this
18798 * @param {String} html
18802 * @event beforepush
18803 * Fires before the iframe editor is updated with content from the textarea. Return false
18804 * to cancel the push.
18805 * @param {Roo.HtmlEditorCore} this
18806 * @param {String} html
18811 * Fires when the textarea is updated with content from the editor iframe.
18812 * @param {Roo.HtmlEditorCore} this
18813 * @param {String} html
18818 * Fires when the iframe editor is updated with content from the textarea.
18819 * @param {Roo.HtmlEditorCore} this
18820 * @param {String} html
18825 * @event editorevent
18826 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18827 * @param {Roo.HtmlEditorCore} this
18833 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18835 // defaults : white / black...
18836 this.applyBlacklists();
18843 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
18847 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
18853 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18858 * @cfg {Number} height (in pixels)
18862 * @cfg {Number} width (in pixels)
18867 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18870 stylesheets: false,
18875 // private properties
18876 validationEvent : false,
18878 initialized : false,
18880 sourceEditMode : false,
18881 onFocus : Roo.emptyFn,
18883 hideMode:'offsets',
18887 // blacklist + whitelisted elements..
18894 * Protected method that will not generally be called directly. It
18895 * is called when the editor initializes the iframe with HTML contents. Override this method if you
18896 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18898 getDocMarkup : function(){
18902 // inherit styels from page...??
18903 if (this.stylesheets === false) {
18905 Roo.get(document.head).select('style').each(function(node) {
18906 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18909 Roo.get(document.head).select('link').each(function(node) {
18910 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18913 } else if (!this.stylesheets.length) {
18915 st = '<style type="text/css">' +
18916 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18922 st += '<style type="text/css">' +
18923 'IMG { cursor: pointer } ' +
18927 return '<html><head>' + st +
18928 //<style type="text/css">' +
18929 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18931 ' </head><body class="roo-htmleditor-body"></body></html>';
18935 onRender : function(ct, position)
18938 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18939 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18942 this.el.dom.style.border = '0 none';
18943 this.el.dom.setAttribute('tabIndex', -1);
18944 this.el.addClass('x-hidden hide');
18948 if(Roo.isIE){ // fix IE 1px bogus margin
18949 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18953 this.frameId = Roo.id();
18957 var iframe = this.owner.wrap.createChild({
18959 cls: 'form-control', // bootstrap..
18961 name: this.frameId,
18962 frameBorder : 'no',
18963 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
18968 this.iframe = iframe.dom;
18970 this.assignDocWin();
18972 this.doc.designMode = 'on';
18975 this.doc.write(this.getDocMarkup());
18979 var task = { // must defer to wait for browser to be ready
18981 //console.log("run task?" + this.doc.readyState);
18982 this.assignDocWin();
18983 if(this.doc.body || this.doc.readyState == 'complete'){
18985 this.doc.designMode="on";
18989 Roo.TaskMgr.stop(task);
18990 this.initEditor.defer(10, this);
18997 Roo.TaskMgr.start(task);
19002 onResize : function(w, h)
19004 Roo.log('resize: ' +w + ',' + h );
19005 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19009 if(typeof w == 'number'){
19011 this.iframe.style.width = w + 'px';
19013 if(typeof h == 'number'){
19015 this.iframe.style.height = h + 'px';
19017 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19024 * Toggles the editor between standard and source edit mode.
19025 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19027 toggleSourceEdit : function(sourceEditMode){
19029 this.sourceEditMode = sourceEditMode === true;
19031 if(this.sourceEditMode){
19033 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19036 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19037 //this.iframe.className = '';
19040 //this.setSize(this.owner.wrap.getSize());
19041 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19048 * Protected method that will not generally be called directly. If you need/want
19049 * custom HTML cleanup, this is the method you should override.
19050 * @param {String} html The HTML to be cleaned
19051 * return {String} The cleaned HTML
19053 cleanHtml : function(html){
19054 html = String(html);
19055 if(html.length > 5){
19056 if(Roo.isSafari){ // strip safari nonsense
19057 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19060 if(html == ' '){
19067 * HTML Editor -> Textarea
19068 * Protected method that will not generally be called directly. Syncs the contents
19069 * of the editor iframe with the textarea.
19071 syncValue : function(){
19072 if(this.initialized){
19073 var bd = (this.doc.body || this.doc.documentElement);
19074 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19075 var html = bd.innerHTML;
19077 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19078 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19080 html = '<div style="'+m[0]+'">' + html + '</div>';
19083 html = this.cleanHtml(html);
19084 // fix up the special chars.. normaly like back quotes in word...
19085 // however we do not want to do this with chinese..
19086 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19087 var cc = b.charCodeAt();
19089 (cc >= 0x4E00 && cc < 0xA000 ) ||
19090 (cc >= 0x3400 && cc < 0x4E00 ) ||
19091 (cc >= 0xf900 && cc < 0xfb00 )
19097 if(this.owner.fireEvent('beforesync', this, html) !== false){
19098 this.el.dom.value = html;
19099 this.owner.fireEvent('sync', this, html);
19105 * Protected method that will not generally be called directly. Pushes the value of the textarea
19106 * into the iframe editor.
19108 pushValue : function(){
19109 if(this.initialized){
19110 var v = this.el.dom.value.trim();
19112 // if(v.length < 1){
19116 if(this.owner.fireEvent('beforepush', this, v) !== false){
19117 var d = (this.doc.body || this.doc.documentElement);
19119 this.cleanUpPaste();
19120 this.el.dom.value = d.innerHTML;
19121 this.owner.fireEvent('push', this, v);
19127 deferFocus : function(){
19128 this.focus.defer(10, this);
19132 focus : function(){
19133 if(this.win && !this.sourceEditMode){
19140 assignDocWin: function()
19142 var iframe = this.iframe;
19145 this.doc = iframe.contentWindow.document;
19146 this.win = iframe.contentWindow;
19148 // if (!Roo.get(this.frameId)) {
19151 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19152 // this.win = Roo.get(this.frameId).dom.contentWindow;
19154 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19158 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19159 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19164 initEditor : function(){
19165 //console.log("INIT EDITOR");
19166 this.assignDocWin();
19170 this.doc.designMode="on";
19172 this.doc.write(this.getDocMarkup());
19175 var dbody = (this.doc.body || this.doc.documentElement);
19176 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19177 // this copies styles from the containing element into thsi one..
19178 // not sure why we need all of this..
19179 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19181 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19182 //ss['background-attachment'] = 'fixed'; // w3c
19183 dbody.bgProperties = 'fixed'; // ie
19184 //Roo.DomHelper.applyStyles(dbody, ss);
19185 Roo.EventManager.on(this.doc, {
19186 //'mousedown': this.onEditorEvent,
19187 'mouseup': this.onEditorEvent,
19188 'dblclick': this.onEditorEvent,
19189 'click': this.onEditorEvent,
19190 'keyup': this.onEditorEvent,
19195 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19197 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19198 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19200 this.initialized = true;
19202 this.owner.fireEvent('initialize', this);
19207 onDestroy : function(){
19213 //for (var i =0; i < this.toolbars.length;i++) {
19214 // // fixme - ask toolbars for heights?
19215 // this.toolbars[i].onDestroy();
19218 //this.wrap.dom.innerHTML = '';
19219 //this.wrap.remove();
19224 onFirstFocus : function(){
19226 this.assignDocWin();
19229 this.activated = true;
19232 if(Roo.isGecko){ // prevent silly gecko errors
19234 var s = this.win.getSelection();
19235 if(!s.focusNode || s.focusNode.nodeType != 3){
19236 var r = s.getRangeAt(0);
19237 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19242 this.execCmd('useCSS', true);
19243 this.execCmd('styleWithCSS', false);
19246 this.owner.fireEvent('activate', this);
19250 adjustFont: function(btn){
19251 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19252 //if(Roo.isSafari){ // safari
19255 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19256 if(Roo.isSafari){ // safari
19257 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19258 v = (v < 10) ? 10 : v;
19259 v = (v > 48) ? 48 : v;
19260 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19265 v = Math.max(1, v+adjust);
19267 this.execCmd('FontSize', v );
19270 onEditorEvent : function(e)
19272 this.owner.fireEvent('editorevent', this, e);
19273 // this.updateToolbar();
19274 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19277 insertTag : function(tg)
19279 // could be a bit smarter... -> wrap the current selected tRoo..
19280 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19282 range = this.createRange(this.getSelection());
19283 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19284 wrappingNode.appendChild(range.extractContents());
19285 range.insertNode(wrappingNode);
19292 this.execCmd("formatblock", tg);
19296 insertText : function(txt)
19300 var range = this.createRange();
19301 range.deleteContents();
19302 //alert(Sender.getAttribute('label'));
19304 range.insertNode(this.doc.createTextNode(txt));
19310 * Executes a Midas editor command on the editor document and performs necessary focus and
19311 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19312 * @param {String} cmd The Midas command
19313 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19315 relayCmd : function(cmd, value){
19317 this.execCmd(cmd, value);
19318 this.owner.fireEvent('editorevent', this);
19319 //this.updateToolbar();
19320 this.owner.deferFocus();
19324 * Executes a Midas editor command directly on the editor document.
19325 * For visual commands, you should use {@link #relayCmd} instead.
19326 * <b>This should only be called after the editor is initialized.</b>
19327 * @param {String} cmd The Midas command
19328 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19330 execCmd : function(cmd, value){
19331 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19338 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19340 * @param {String} text | dom node..
19342 insertAtCursor : function(text)
19347 if(!this.activated){
19353 var r = this.doc.selection.createRange();
19364 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19368 // from jquery ui (MIT licenced)
19370 var win = this.win;
19372 if (win.getSelection && win.getSelection().getRangeAt) {
19373 range = win.getSelection().getRangeAt(0);
19374 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19375 range.insertNode(node);
19376 } else if (win.document.selection && win.document.selection.createRange) {
19377 // no firefox support
19378 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19379 win.document.selection.createRange().pasteHTML(txt);
19381 // no firefox support
19382 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19383 this.execCmd('InsertHTML', txt);
19392 mozKeyPress : function(e){
19394 var c = e.getCharCode(), cmd;
19397 c = String.fromCharCode(c).toLowerCase();
19411 this.cleanUpPaste.defer(100, this);
19419 e.preventDefault();
19427 fixKeys : function(){ // load time branching for fastest keydown performance
19429 return function(e){
19430 var k = e.getKey(), r;
19433 r = this.doc.selection.createRange();
19436 r.pasteHTML('    ');
19443 r = this.doc.selection.createRange();
19445 var target = r.parentElement();
19446 if(!target || target.tagName.toLowerCase() != 'li'){
19448 r.pasteHTML('<br />');
19454 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19455 this.cleanUpPaste.defer(100, this);
19461 }else if(Roo.isOpera){
19462 return function(e){
19463 var k = e.getKey();
19467 this.execCmd('InsertHTML','    ');
19470 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19471 this.cleanUpPaste.defer(100, this);
19476 }else if(Roo.isSafari){
19477 return function(e){
19478 var k = e.getKey();
19482 this.execCmd('InsertText','\t');
19486 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19487 this.cleanUpPaste.defer(100, this);
19495 getAllAncestors: function()
19497 var p = this.getSelectedNode();
19500 a.push(p); // push blank onto stack..
19501 p = this.getParentElement();
19505 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19509 a.push(this.doc.body);
19513 lastSelNode : false,
19516 getSelection : function()
19518 this.assignDocWin();
19519 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19522 getSelectedNode: function()
19524 // this may only work on Gecko!!!
19526 // should we cache this!!!!
19531 var range = this.createRange(this.getSelection()).cloneRange();
19534 var parent = range.parentElement();
19536 var testRange = range.duplicate();
19537 testRange.moveToElementText(parent);
19538 if (testRange.inRange(range)) {
19541 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19544 parent = parent.parentElement;
19549 // is ancestor a text element.
19550 var ac = range.commonAncestorContainer;
19551 if (ac.nodeType == 3) {
19552 ac = ac.parentNode;
19555 var ar = ac.childNodes;
19558 var other_nodes = [];
19559 var has_other_nodes = false;
19560 for (var i=0;i<ar.length;i++) {
19561 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19564 // fullly contained node.
19566 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19571 // probably selected..
19572 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19573 other_nodes.push(ar[i]);
19577 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19582 has_other_nodes = true;
19584 if (!nodes.length && other_nodes.length) {
19585 nodes= other_nodes;
19587 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19593 createRange: function(sel)
19595 // this has strange effects when using with
19596 // top toolbar - not sure if it's a great idea.
19597 //this.editor.contentWindow.focus();
19598 if (typeof sel != "undefined") {
19600 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19602 return this.doc.createRange();
19605 return this.doc.createRange();
19608 getParentElement: function()
19611 this.assignDocWin();
19612 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19614 var range = this.createRange(sel);
19617 var p = range.commonAncestorContainer;
19618 while (p.nodeType == 3) { // text node
19629 * Range intersection.. the hard stuff...
19633 * [ -- selected range --- ]
19637 * if end is before start or hits it. fail.
19638 * if start is after end or hits it fail.
19640 * if either hits (but other is outside. - then it's not
19646 // @see http://www.thismuchiknow.co.uk/?p=64.
19647 rangeIntersectsNode : function(range, node)
19649 var nodeRange = node.ownerDocument.createRange();
19651 nodeRange.selectNode(node);
19653 nodeRange.selectNodeContents(node);
19656 var rangeStartRange = range.cloneRange();
19657 rangeStartRange.collapse(true);
19659 var rangeEndRange = range.cloneRange();
19660 rangeEndRange.collapse(false);
19662 var nodeStartRange = nodeRange.cloneRange();
19663 nodeStartRange.collapse(true);
19665 var nodeEndRange = nodeRange.cloneRange();
19666 nodeEndRange.collapse(false);
19668 return rangeStartRange.compareBoundaryPoints(
19669 Range.START_TO_START, nodeEndRange) == -1 &&
19670 rangeEndRange.compareBoundaryPoints(
19671 Range.START_TO_START, nodeStartRange) == 1;
19675 rangeCompareNode : function(range, node)
19677 var nodeRange = node.ownerDocument.createRange();
19679 nodeRange.selectNode(node);
19681 nodeRange.selectNodeContents(node);
19685 range.collapse(true);
19687 nodeRange.collapse(true);
19689 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19690 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19692 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19694 var nodeIsBefore = ss == 1;
19695 var nodeIsAfter = ee == -1;
19697 if (nodeIsBefore && nodeIsAfter)
19699 if (!nodeIsBefore && nodeIsAfter)
19700 return 1; //right trailed.
19702 if (nodeIsBefore && !nodeIsAfter)
19703 return 2; // left trailed.
19708 // private? - in a new class?
19709 cleanUpPaste : function()
19711 // cleans up the whole document..
19712 Roo.log('cleanuppaste');
19714 this.cleanUpChildren(this.doc.body);
19715 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19716 if (clean != this.doc.body.innerHTML) {
19717 this.doc.body.innerHTML = clean;
19722 cleanWordChars : function(input) {// change the chars to hex code
19723 var he = Roo.HtmlEditorCore;
19725 var output = input;
19726 Roo.each(he.swapCodes, function(sw) {
19727 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19729 output = output.replace(swapper, sw[1]);
19736 cleanUpChildren : function (n)
19738 if (!n.childNodes.length) {
19741 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19742 this.cleanUpChild(n.childNodes[i]);
19749 cleanUpChild : function (node)
19752 //console.log(node);
19753 if (node.nodeName == "#text") {
19754 // clean up silly Windows -- stuff?
19757 if (node.nodeName == "#comment") {
19758 node.parentNode.removeChild(node);
19759 // clean up silly Windows -- stuff?
19762 var lcname = node.tagName.toLowerCase();
19763 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19764 // whitelist of tags..
19766 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19768 node.parentNode.removeChild(node);
19773 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19775 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19776 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19778 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19779 // remove_keep_children = true;
19782 if (remove_keep_children) {
19783 this.cleanUpChildren(node);
19784 // inserts everything just before this node...
19785 while (node.childNodes.length) {
19786 var cn = node.childNodes[0];
19787 node.removeChild(cn);
19788 node.parentNode.insertBefore(cn, node);
19790 node.parentNode.removeChild(node);
19794 if (!node.attributes || !node.attributes.length) {
19795 this.cleanUpChildren(node);
19799 function cleanAttr(n,v)
19802 if (v.match(/^\./) || v.match(/^\//)) {
19805 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19808 if (v.match(/^#/)) {
19811 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19812 node.removeAttribute(n);
19816 var cwhite = this.cwhite;
19817 var cblack = this.cblack;
19819 function cleanStyle(n,v)
19821 if (v.match(/expression/)) { //XSS?? should we even bother..
19822 node.removeAttribute(n);
19826 var parts = v.split(/;/);
19829 Roo.each(parts, function(p) {
19830 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19834 var l = p.split(':').shift().replace(/\s+/g,'');
19835 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19837 if ( cwhite.length && cblack.indexOf(l) > -1) {
19838 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19839 //node.removeAttribute(n);
19843 // only allow 'c whitelisted system attributes'
19844 if ( cwhite.length && cwhite.indexOf(l) < 0) {
19845 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19846 //node.removeAttribute(n);
19856 if (clean.length) {
19857 node.setAttribute(n, clean.join(';'));
19859 node.removeAttribute(n);
19865 for (var i = node.attributes.length-1; i > -1 ; i--) {
19866 var a = node.attributes[i];
19869 if (a.name.toLowerCase().substr(0,2)=='on') {
19870 node.removeAttribute(a.name);
19873 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19874 node.removeAttribute(a.name);
19877 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19878 cleanAttr(a.name,a.value); // fixme..
19881 if (a.name == 'style') {
19882 cleanStyle(a.name,a.value);
19885 /// clean up MS crap..
19886 // tecnically this should be a list of valid class'es..
19889 if (a.name == 'class') {
19890 if (a.value.match(/^Mso/)) {
19891 node.className = '';
19894 if (a.value.match(/body/)) {
19895 node.className = '';
19906 this.cleanUpChildren(node);
19912 * Clean up MS wordisms...
19914 cleanWord : function(node)
19919 this.cleanWord(this.doc.body);
19922 if (node.nodeName == "#text") {
19923 // clean up silly Windows -- stuff?
19926 if (node.nodeName == "#comment") {
19927 node.parentNode.removeChild(node);
19928 // clean up silly Windows -- stuff?
19932 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19933 node.parentNode.removeChild(node);
19937 // remove - but keep children..
19938 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19939 while (node.childNodes.length) {
19940 var cn = node.childNodes[0];
19941 node.removeChild(cn);
19942 node.parentNode.insertBefore(cn, node);
19944 node.parentNode.removeChild(node);
19945 this.iterateChildren(node, this.cleanWord);
19949 if (node.className.length) {
19951 var cn = node.className.split(/\W+/);
19953 Roo.each(cn, function(cls) {
19954 if (cls.match(/Mso[a-zA-Z]+/)) {
19959 node.className = cna.length ? cna.join(' ') : '';
19961 node.removeAttribute("class");
19965 if (node.hasAttribute("lang")) {
19966 node.removeAttribute("lang");
19969 if (node.hasAttribute("style")) {
19971 var styles = node.getAttribute("style").split(";");
19973 Roo.each(styles, function(s) {
19974 if (!s.match(/:/)) {
19977 var kv = s.split(":");
19978 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19981 // what ever is left... we allow.
19984 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19985 if (!nstyle.length) {
19986 node.removeAttribute('style');
19989 this.iterateChildren(node, this.cleanWord);
19995 * iterateChildren of a Node, calling fn each time, using this as the scole..
19996 * @param {DomNode} node node to iterate children of.
19997 * @param {Function} fn method of this class to call on each item.
19999 iterateChildren : function(node, fn)
20001 if (!node.childNodes.length) {
20004 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20005 fn.call(this, node.childNodes[i])
20011 * cleanTableWidths.
20013 * Quite often pasting from word etc.. results in tables with column and widths.
20014 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20017 cleanTableWidths : function(node)
20022 this.cleanTableWidths(this.doc.body);
20027 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20030 Roo.log(node.tagName);
20031 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20032 this.iterateChildren(node, this.cleanTableWidths);
20035 if (node.hasAttribute('width')) {
20036 node.removeAttribute('width');
20040 if (node.hasAttribute("style")) {
20043 var styles = node.getAttribute("style").split(";");
20045 Roo.each(styles, function(s) {
20046 if (!s.match(/:/)) {
20049 var kv = s.split(":");
20050 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20053 // what ever is left... we allow.
20056 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20057 if (!nstyle.length) {
20058 node.removeAttribute('style');
20062 this.iterateChildren(node, this.cleanTableWidths);
20070 domToHTML : function(currentElement, depth, nopadtext) {
20072 depth = depth || 0;
20073 nopadtext = nopadtext || false;
20075 if (!currentElement) {
20076 return this.domToHTML(this.doc.body);
20079 //Roo.log(currentElement);
20081 var allText = false;
20082 var nodeName = currentElement.nodeName;
20083 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20085 if (nodeName == '#text') {
20087 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20092 if (nodeName != 'BODY') {
20095 // Prints the node tagName, such as <A>, <IMG>, etc
20098 for(i = 0; i < currentElement.attributes.length;i++) {
20100 var aname = currentElement.attributes.item(i).name;
20101 if (!currentElement.attributes.item(i).value.length) {
20104 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20107 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20116 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20119 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20124 // Traverse the tree
20126 var currentElementChild = currentElement.childNodes.item(i);
20127 var allText = true;
20128 var innerHTML = '';
20130 while (currentElementChild) {
20131 // Formatting code (indent the tree so it looks nice on the screen)
20132 var nopad = nopadtext;
20133 if (lastnode == 'SPAN') {
20137 if (currentElementChild.nodeName == '#text') {
20138 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20139 toadd = nopadtext ? toadd : toadd.trim();
20140 if (!nopad && toadd.length > 80) {
20141 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20143 innerHTML += toadd;
20146 currentElementChild = currentElement.childNodes.item(i);
20152 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20154 // Recursively traverse the tree structure of the child node
20155 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20156 lastnode = currentElementChild.nodeName;
20158 currentElementChild=currentElement.childNodes.item(i);
20164 // The remaining code is mostly for formatting the tree
20165 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20170 ret+= "</"+tagName+">";
20176 applyBlacklists : function()
20178 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20179 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20183 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20184 if (b.indexOf(tag) > -1) {
20187 this.white.push(tag);
20191 Roo.each(w, function(tag) {
20192 if (b.indexOf(tag) > -1) {
20195 if (this.white.indexOf(tag) > -1) {
20198 this.white.push(tag);
20203 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20204 if (w.indexOf(tag) > -1) {
20207 this.black.push(tag);
20211 Roo.each(b, function(tag) {
20212 if (w.indexOf(tag) > -1) {
20215 if (this.black.indexOf(tag) > -1) {
20218 this.black.push(tag);
20223 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20224 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20228 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20229 if (b.indexOf(tag) > -1) {
20232 this.cwhite.push(tag);
20236 Roo.each(w, function(tag) {
20237 if (b.indexOf(tag) > -1) {
20240 if (this.cwhite.indexOf(tag) > -1) {
20243 this.cwhite.push(tag);
20248 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20249 if (w.indexOf(tag) > -1) {
20252 this.cblack.push(tag);
20256 Roo.each(b, function(tag) {
20257 if (w.indexOf(tag) > -1) {
20260 if (this.cblack.indexOf(tag) > -1) {
20263 this.cblack.push(tag);
20268 setStylesheets : function(stylesheets)
20270 if(typeof(stylesheets) == 'string'){
20271 Roo.get(this.iframe.contentDocument.head).createChild({
20273 rel : 'stylesheet',
20282 Roo.each(stylesheets, function(s) {
20287 Roo.get(_this.iframe.contentDocument.head).createChild({
20289 rel : 'stylesheet',
20298 removeStylesheets : function()
20302 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20307 // hide stuff that is not compatible
20321 * @event specialkey
20325 * @cfg {String} fieldClass @hide
20328 * @cfg {String} focusClass @hide
20331 * @cfg {String} autoCreate @hide
20334 * @cfg {String} inputType @hide
20337 * @cfg {String} invalidClass @hide
20340 * @cfg {String} invalidText @hide
20343 * @cfg {String} msgFx @hide
20346 * @cfg {String} validateOnBlur @hide
20350 Roo.HtmlEditorCore.white = [
20351 'area', 'br', 'img', 'input', 'hr', 'wbr',
20353 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20354 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20355 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20356 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20357 'table', 'ul', 'xmp',
20359 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20362 'dir', 'menu', 'ol', 'ul', 'dl',
20368 Roo.HtmlEditorCore.black = [
20369 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20371 'base', 'basefont', 'bgsound', 'blink', 'body',
20372 'frame', 'frameset', 'head', 'html', 'ilayer',
20373 'iframe', 'layer', 'link', 'meta', 'object',
20374 'script', 'style' ,'title', 'xml' // clean later..
20376 Roo.HtmlEditorCore.clean = [
20377 'script', 'style', 'title', 'xml'
20379 Roo.HtmlEditorCore.remove = [
20384 Roo.HtmlEditorCore.ablack = [
20388 Roo.HtmlEditorCore.aclean = [
20389 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20393 Roo.HtmlEditorCore.pwhite= [
20394 'http', 'https', 'mailto'
20397 // white listed style attributes.
20398 Roo.HtmlEditorCore.cwhite= [
20399 // 'text-align', /// default is to allow most things..
20405 // black listed style attributes.
20406 Roo.HtmlEditorCore.cblack= [
20407 // 'font-size' -- this can be set by the project
20411 Roo.HtmlEditorCore.swapCodes =[
20430 * @class Roo.bootstrap.HtmlEditor
20431 * @extends Roo.bootstrap.TextArea
20432 * Bootstrap HtmlEditor class
20435 * Create a new HtmlEditor
20436 * @param {Object} config The config object
20439 Roo.bootstrap.HtmlEditor = function(config){
20440 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20441 if (!this.toolbars) {
20442 this.toolbars = [];
20444 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20447 * @event initialize
20448 * Fires when the editor is fully initialized (including the iframe)
20449 * @param {HtmlEditor} this
20454 * Fires when the editor is first receives the focus. Any insertion must wait
20455 * until after this event.
20456 * @param {HtmlEditor} this
20460 * @event beforesync
20461 * Fires before the textarea is updated with content from the editor iframe. Return false
20462 * to cancel the sync.
20463 * @param {HtmlEditor} this
20464 * @param {String} html
20468 * @event beforepush
20469 * Fires before the iframe editor is updated with content from the textarea. Return false
20470 * to cancel the push.
20471 * @param {HtmlEditor} this
20472 * @param {String} html
20477 * Fires when the textarea is updated with content from the editor iframe.
20478 * @param {HtmlEditor} this
20479 * @param {String} html
20484 * Fires when the iframe editor is updated with content from the textarea.
20485 * @param {HtmlEditor} this
20486 * @param {String} html
20490 * @event editmodechange
20491 * Fires when the editor switches edit modes
20492 * @param {HtmlEditor} this
20493 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20495 editmodechange: true,
20497 * @event editorevent
20498 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20499 * @param {HtmlEditor} this
20503 * @event firstfocus
20504 * Fires when on first focus - needed by toolbars..
20505 * @param {HtmlEditor} this
20510 * Auto save the htmlEditor value as a file into Events
20511 * @param {HtmlEditor} this
20515 * @event savedpreview
20516 * preview the saved version of htmlEditor
20517 * @param {HtmlEditor} this
20524 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20528 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20533 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20538 * @cfg {Number} height (in pixels)
20542 * @cfg {Number} width (in pixels)
20547 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20550 stylesheets: false,
20555 // private properties
20556 validationEvent : false,
20558 initialized : false,
20561 onFocus : Roo.emptyFn,
20563 hideMode:'offsets',
20566 tbContainer : false,
20568 toolbarContainer :function() {
20569 return this.wrap.select('.x-html-editor-tb',true).first();
20573 * Protected method that will not generally be called directly. It
20574 * is called when the editor creates its toolbar. Override this method if you need to
20575 * add custom toolbar buttons.
20576 * @param {HtmlEditor} editor
20578 createToolbar : function(){
20580 Roo.log("create toolbars");
20582 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20583 this.toolbars[0].render(this.toolbarContainer());
20587 // if (!editor.toolbars || !editor.toolbars.length) {
20588 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20591 // for (var i =0 ; i < editor.toolbars.length;i++) {
20592 // editor.toolbars[i] = Roo.factory(
20593 // typeof(editor.toolbars[i]) == 'string' ?
20594 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20595 // Roo.bootstrap.HtmlEditor);
20596 // editor.toolbars[i].init(editor);
20602 onRender : function(ct, position)
20604 // Roo.log("Call onRender: " + this.xtype);
20606 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20608 this.wrap = this.inputEl().wrap({
20609 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20612 this.editorcore.onRender(ct, position);
20614 if (this.resizable) {
20615 this.resizeEl = new Roo.Resizable(this.wrap, {
20619 minHeight : this.height,
20620 height: this.height,
20621 handles : this.resizable,
20624 resize : function(r, w, h) {
20625 _t.onResize(w,h); // -something
20631 this.createToolbar(this);
20634 if(!this.width && this.resizable){
20635 this.setSize(this.wrap.getSize());
20637 if (this.resizeEl) {
20638 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20639 // should trigger onReize..
20645 onResize : function(w, h)
20647 Roo.log('resize: ' +w + ',' + h );
20648 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20652 if(this.inputEl() ){
20653 if(typeof w == 'number'){
20654 var aw = w - this.wrap.getFrameWidth('lr');
20655 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20658 if(typeof h == 'number'){
20659 var tbh = -11; // fixme it needs to tool bar size!
20660 for (var i =0; i < this.toolbars.length;i++) {
20661 // fixme - ask toolbars for heights?
20662 tbh += this.toolbars[i].el.getHeight();
20663 //if (this.toolbars[i].footer) {
20664 // tbh += this.toolbars[i].footer.el.getHeight();
20672 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20673 ah -= 5; // knock a few pixes off for look..
20674 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20678 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20679 this.editorcore.onResize(ew,eh);
20684 * Toggles the editor between standard and source edit mode.
20685 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20687 toggleSourceEdit : function(sourceEditMode)
20689 this.editorcore.toggleSourceEdit(sourceEditMode);
20691 if(this.editorcore.sourceEditMode){
20692 Roo.log('editor - showing textarea');
20695 // Roo.log(this.syncValue());
20697 this.inputEl().removeClass(['hide', 'x-hidden']);
20698 this.inputEl().dom.removeAttribute('tabIndex');
20699 this.inputEl().focus();
20701 Roo.log('editor - hiding textarea');
20703 // Roo.log(this.pushValue());
20706 this.inputEl().addClass(['hide', 'x-hidden']);
20707 this.inputEl().dom.setAttribute('tabIndex', -1);
20708 //this.deferFocus();
20711 if(this.resizable){
20712 this.setSize(this.wrap.getSize());
20715 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20718 // private (for BoxComponent)
20719 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20721 // private (for BoxComponent)
20722 getResizeEl : function(){
20726 // private (for BoxComponent)
20727 getPositionEl : function(){
20732 initEvents : function(){
20733 this.originalValue = this.getValue();
20737 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20740 // markInvalid : Roo.emptyFn,
20742 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20745 // clearInvalid : Roo.emptyFn,
20747 setValue : function(v){
20748 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20749 this.editorcore.pushValue();
20754 deferFocus : function(){
20755 this.focus.defer(10, this);
20759 focus : function(){
20760 this.editorcore.focus();
20766 onDestroy : function(){
20772 for (var i =0; i < this.toolbars.length;i++) {
20773 // fixme - ask toolbars for heights?
20774 this.toolbars[i].onDestroy();
20777 this.wrap.dom.innerHTML = '';
20778 this.wrap.remove();
20783 onFirstFocus : function(){
20784 //Roo.log("onFirstFocus");
20785 this.editorcore.onFirstFocus();
20786 for (var i =0; i < this.toolbars.length;i++) {
20787 this.toolbars[i].onFirstFocus();
20793 syncValue : function()
20795 this.editorcore.syncValue();
20798 pushValue : function()
20800 this.editorcore.pushValue();
20804 // hide stuff that is not compatible
20818 * @event specialkey
20822 * @cfg {String} fieldClass @hide
20825 * @cfg {String} focusClass @hide
20828 * @cfg {String} autoCreate @hide
20831 * @cfg {String} inputType @hide
20834 * @cfg {String} invalidClass @hide
20837 * @cfg {String} invalidText @hide
20840 * @cfg {String} msgFx @hide
20843 * @cfg {String} validateOnBlur @hide
20852 Roo.namespace('Roo.bootstrap.htmleditor');
20854 * @class Roo.bootstrap.HtmlEditorToolbar1
20859 new Roo.bootstrap.HtmlEditor({
20862 new Roo.bootstrap.HtmlEditorToolbar1({
20863 disable : { fonts: 1 , format: 1, ..., ... , ...],
20869 * @cfg {Object} disable List of elements to disable..
20870 * @cfg {Array} btns List of additional buttons.
20874 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20877 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20880 Roo.apply(this, config);
20882 // default disabled, based on 'good practice'..
20883 this.disable = this.disable || {};
20884 Roo.applyIf(this.disable, {
20887 specialElements : true
20889 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20891 this.editor = config.editor;
20892 this.editorcore = config.editor.editorcore;
20894 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20896 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20897 // dont call parent... till later.
20899 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
20904 editorcore : false,
20909 "h1","h2","h3","h4","h5","h6",
20911 "abbr", "acronym", "address", "cite", "samp", "var",
20915 onRender : function(ct, position)
20917 // Roo.log("Call onRender: " + this.xtype);
20919 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20921 this.el.dom.style.marginBottom = '0';
20923 var editorcore = this.editorcore;
20924 var editor= this.editor;
20927 var btn = function(id,cmd , toggle, handler){
20929 var event = toggle ? 'toggle' : 'click';
20934 xns: Roo.bootstrap,
20937 enableToggle:toggle !== false,
20939 pressed : toggle ? false : null,
20942 a.listeners[toggle ? 'toggle' : 'click'] = function() {
20943 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
20952 xns: Roo.bootstrap,
20953 glyphicon : 'font',
20957 xns: Roo.bootstrap,
20961 Roo.each(this.formats, function(f) {
20962 style.menu.items.push({
20964 xns: Roo.bootstrap,
20965 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20970 editorcore.insertTag(this.tagname);
20977 children.push(style);
20980 btn('bold',false,true);
20981 btn('italic',false,true);
20982 btn('align-left', 'justifyleft',true);
20983 btn('align-center', 'justifycenter',true);
20984 btn('align-right' , 'justifyright',true);
20985 btn('link', false, false, function(btn) {
20986 //Roo.log("create link?");
20987 var url = prompt(this.createLinkText, this.defaultLinkValue);
20988 if(url && url != 'http:/'+'/'){
20989 this.editorcore.relayCmd('createlink', url);
20992 btn('list','insertunorderedlist',true);
20993 btn('pencil', false,true, function(btn){
20996 this.toggleSourceEdit(btn.pressed);
21002 xns: Roo.bootstrap,
21007 xns: Roo.bootstrap,
21012 cog.menu.items.push({
21014 xns: Roo.bootstrap,
21015 html : Clean styles,
21020 editorcore.insertTag(this.tagname);
21029 this.xtype = 'NavSimplebar';
21031 for(var i=0;i< children.length;i++) {
21033 this.buttons.add(this.addxtypeChild(children[i]));
21037 editor.on('editorevent', this.updateToolbar, this);
21039 onBtnClick : function(id)
21041 this.editorcore.relayCmd(id);
21042 this.editorcore.focus();
21046 * Protected method that will not generally be called directly. It triggers
21047 * a toolbar update by reading the markup state of the current selection in the editor.
21049 updateToolbar: function(){
21051 if(!this.editorcore.activated){
21052 this.editor.onFirstFocus(); // is this neeed?
21056 var btns = this.buttons;
21057 var doc = this.editorcore.doc;
21058 btns.get('bold').setActive(doc.queryCommandState('bold'));
21059 btns.get('italic').setActive(doc.queryCommandState('italic'));
21060 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21062 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21063 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21064 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21066 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21067 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21070 var ans = this.editorcore.getAllAncestors();
21071 if (this.formatCombo) {
21074 var store = this.formatCombo.store;
21075 this.formatCombo.setValue("");
21076 for (var i =0; i < ans.length;i++) {
21077 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21079 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21087 // hides menus... - so this cant be on a menu...
21088 Roo.bootstrap.MenuMgr.hideAll();
21090 Roo.bootstrap.MenuMgr.hideAll();
21091 //this.editorsyncValue();
21093 onFirstFocus: function() {
21094 this.buttons.each(function(item){
21098 toggleSourceEdit : function(sourceEditMode){
21101 if(sourceEditMode){
21102 Roo.log("disabling buttons");
21103 this.buttons.each( function(item){
21104 if(item.cmd != 'pencil'){
21110 Roo.log("enabling buttons");
21111 if(this.editorcore.initialized){
21112 this.buttons.each( function(item){
21118 Roo.log("calling toggole on editor");
21119 // tell the editor that it's been pressed..
21120 this.editor.toggleSourceEdit(sourceEditMode);
21130 * @class Roo.bootstrap.Table.AbstractSelectionModel
21131 * @extends Roo.util.Observable
21132 * Abstract base class for grid SelectionModels. It provides the interface that should be
21133 * implemented by descendant classes. This class should not be directly instantiated.
21136 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21137 this.locked = false;
21138 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21142 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21143 /** @ignore Called by the grid automatically. Do not call directly. */
21144 init : function(grid){
21150 * Locks the selections.
21153 this.locked = true;
21157 * Unlocks the selections.
21159 unlock : function(){
21160 this.locked = false;
21164 * Returns true if the selections are locked.
21165 * @return {Boolean}
21167 isLocked : function(){
21168 return this.locked;
21172 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21173 * @class Roo.bootstrap.Table.RowSelectionModel
21174 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21175 * It supports multiple selections and keyboard selection/navigation.
21177 * @param {Object} config
21180 Roo.bootstrap.Table.RowSelectionModel = function(config){
21181 Roo.apply(this, config);
21182 this.selections = new Roo.util.MixedCollection(false, function(o){
21187 this.lastActive = false;
21191 * @event selectionchange
21192 * Fires when the selection changes
21193 * @param {SelectionModel} this
21195 "selectionchange" : true,
21197 * @event afterselectionchange
21198 * Fires after the selection changes (eg. by key press or clicking)
21199 * @param {SelectionModel} this
21201 "afterselectionchange" : true,
21203 * @event beforerowselect
21204 * Fires when a row is selected being selected, return false to cancel.
21205 * @param {SelectionModel} this
21206 * @param {Number} rowIndex The selected index
21207 * @param {Boolean} keepExisting False if other selections will be cleared
21209 "beforerowselect" : true,
21212 * Fires when a row is selected.
21213 * @param {SelectionModel} this
21214 * @param {Number} rowIndex The selected index
21215 * @param {Roo.data.Record} r The record
21217 "rowselect" : true,
21219 * @event rowdeselect
21220 * Fires when a row is deselected.
21221 * @param {SelectionModel} this
21222 * @param {Number} rowIndex The selected index
21224 "rowdeselect" : true
21226 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21227 this.locked = false;
21230 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21232 * @cfg {Boolean} singleSelect
21233 * True to allow selection of only one row at a time (defaults to false)
21235 singleSelect : false,
21238 initEvents : function(){
21240 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21241 this.grid.on("mousedown", this.handleMouseDown, this);
21242 }else{ // allow click to work like normal
21243 this.grid.on("rowclick", this.handleDragableRowClick, this);
21246 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21247 "up" : function(e){
21249 this.selectPrevious(e.shiftKey);
21250 }else if(this.last !== false && this.lastActive !== false){
21251 var last = this.last;
21252 this.selectRange(this.last, this.lastActive-1);
21253 this.grid.getView().focusRow(this.lastActive);
21254 if(last !== false){
21258 this.selectFirstRow();
21260 this.fireEvent("afterselectionchange", this);
21262 "down" : function(e){
21264 this.selectNext(e.shiftKey);
21265 }else if(this.last !== false && this.lastActive !== false){
21266 var last = this.last;
21267 this.selectRange(this.last, this.lastActive+1);
21268 this.grid.getView().focusRow(this.lastActive);
21269 if(last !== false){
21273 this.selectFirstRow();
21275 this.fireEvent("afterselectionchange", this);
21280 var view = this.grid.view;
21281 view.on("refresh", this.onRefresh, this);
21282 view.on("rowupdated", this.onRowUpdated, this);
21283 view.on("rowremoved", this.onRemove, this);
21287 onRefresh : function(){
21288 var ds = this.grid.dataSource, i, v = this.grid.view;
21289 var s = this.selections;
21290 s.each(function(r){
21291 if((i = ds.indexOfId(r.id)) != -1){
21300 onRemove : function(v, index, r){
21301 this.selections.remove(r);
21305 onRowUpdated : function(v, index, r){
21306 if(this.isSelected(r)){
21307 v.onRowSelect(index);
21313 * @param {Array} records The records to select
21314 * @param {Boolean} keepExisting (optional) True to keep existing selections
21316 selectRecords : function(records, keepExisting){
21318 this.clearSelections();
21320 var ds = this.grid.dataSource;
21321 for(var i = 0, len = records.length; i < len; i++){
21322 this.selectRow(ds.indexOf(records[i]), true);
21327 * Gets the number of selected rows.
21330 getCount : function(){
21331 return this.selections.length;
21335 * Selects the first row in the grid.
21337 selectFirstRow : function(){
21342 * Select the last row.
21343 * @param {Boolean} keepExisting (optional) True to keep existing selections
21345 selectLastRow : function(keepExisting){
21346 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21350 * Selects the row immediately following the last selected row.
21351 * @param {Boolean} keepExisting (optional) True to keep existing selections
21353 selectNext : function(keepExisting){
21354 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21355 this.selectRow(this.last+1, keepExisting);
21356 this.grid.getView().focusRow(this.last);
21361 * Selects the row that precedes the last selected row.
21362 * @param {Boolean} keepExisting (optional) True to keep existing selections
21364 selectPrevious : function(keepExisting){
21366 this.selectRow(this.last-1, keepExisting);
21367 this.grid.getView().focusRow(this.last);
21372 * Returns the selected records
21373 * @return {Array} Array of selected records
21375 getSelections : function(){
21376 return [].concat(this.selections.items);
21380 * Returns the first selected record.
21383 getSelected : function(){
21384 return this.selections.itemAt(0);
21389 * Clears all selections.
21391 clearSelections : function(fast){
21392 if(this.locked) return;
21394 var ds = this.grid.dataSource;
21395 var s = this.selections;
21396 s.each(function(r){
21397 this.deselectRow(ds.indexOfId(r.id));
21401 this.selections.clear();
21408 * Selects all rows.
21410 selectAll : function(){
21411 if(this.locked) return;
21412 this.selections.clear();
21413 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21414 this.selectRow(i, true);
21419 * Returns True if there is a selection.
21420 * @return {Boolean}
21422 hasSelection : function(){
21423 return this.selections.length > 0;
21427 * Returns True if the specified row is selected.
21428 * @param {Number/Record} record The record or index of the record to check
21429 * @return {Boolean}
21431 isSelected : function(index){
21432 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21433 return (r && this.selections.key(r.id) ? true : false);
21437 * Returns True if the specified record id is selected.
21438 * @param {String} id The id of record to check
21439 * @return {Boolean}
21441 isIdSelected : function(id){
21442 return (this.selections.key(id) ? true : false);
21446 handleMouseDown : function(e, t){
21447 var view = this.grid.getView(), rowIndex;
21448 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21451 if(e.shiftKey && this.last !== false){
21452 var last = this.last;
21453 this.selectRange(last, rowIndex, e.ctrlKey);
21454 this.last = last; // reset the last
21455 view.focusRow(rowIndex);
21457 var isSelected = this.isSelected(rowIndex);
21458 if(e.button !== 0 && isSelected){
21459 view.focusRow(rowIndex);
21460 }else if(e.ctrlKey && isSelected){
21461 this.deselectRow(rowIndex);
21462 }else if(!isSelected){
21463 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21464 view.focusRow(rowIndex);
21467 this.fireEvent("afterselectionchange", this);
21470 handleDragableRowClick : function(grid, rowIndex, e)
21472 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21473 this.selectRow(rowIndex, false);
21474 grid.view.focusRow(rowIndex);
21475 this.fireEvent("afterselectionchange", this);
21480 * Selects multiple rows.
21481 * @param {Array} rows Array of the indexes of the row to select
21482 * @param {Boolean} keepExisting (optional) True to keep existing selections
21484 selectRows : function(rows, keepExisting){
21486 this.clearSelections();
21488 for(var i = 0, len = rows.length; i < len; i++){
21489 this.selectRow(rows[i], true);
21494 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21495 * @param {Number} startRow The index of the first row in the range
21496 * @param {Number} endRow The index of the last row in the range
21497 * @param {Boolean} keepExisting (optional) True to retain existing selections
21499 selectRange : function(startRow, endRow, keepExisting){
21500 if(this.locked) return;
21502 this.clearSelections();
21504 if(startRow <= endRow){
21505 for(var i = startRow; i <= endRow; i++){
21506 this.selectRow(i, true);
21509 for(var i = startRow; i >= endRow; i--){
21510 this.selectRow(i, true);
21516 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21517 * @param {Number} startRow The index of the first row in the range
21518 * @param {Number} endRow The index of the last row in the range
21520 deselectRange : function(startRow, endRow, preventViewNotify){
21521 if(this.locked) return;
21522 for(var i = startRow; i <= endRow; i++){
21523 this.deselectRow(i, preventViewNotify);
21529 * @param {Number} row The index of the row to select
21530 * @param {Boolean} keepExisting (optional) True to keep existing selections
21532 selectRow : function(index, keepExisting, preventViewNotify){
21533 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21534 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21535 if(!keepExisting || this.singleSelect){
21536 this.clearSelections();
21538 var r = this.grid.dataSource.getAt(index);
21539 this.selections.add(r);
21540 this.last = this.lastActive = index;
21541 if(!preventViewNotify){
21542 this.grid.getView().onRowSelect(index);
21544 this.fireEvent("rowselect", this, index, r);
21545 this.fireEvent("selectionchange", this);
21551 * @param {Number} row The index of the row to deselect
21553 deselectRow : function(index, preventViewNotify){
21554 if(this.locked) return;
21555 if(this.last == index){
21558 if(this.lastActive == index){
21559 this.lastActive = false;
21561 var r = this.grid.dataSource.getAt(index);
21562 this.selections.remove(r);
21563 if(!preventViewNotify){
21564 this.grid.getView().onRowDeselect(index);
21566 this.fireEvent("rowdeselect", this, index);
21567 this.fireEvent("selectionchange", this);
21571 restoreLast : function(){
21573 this.last = this._last;
21578 acceptsNav : function(row, col, cm){
21579 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21583 onEditorKey : function(field, e){
21584 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21589 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21591 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21593 }else if(k == e.ENTER && !e.ctrlKey){
21597 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21599 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21601 }else if(k == e.ESC){
21605 g.startEditing(newCell[0], newCell[1]);
21610 * Ext JS Library 1.1.1
21611 * Copyright(c) 2006-2007, Ext JS, LLC.
21613 * Originally Released Under LGPL - original licence link has changed is not relivant.
21616 * <script type="text/javascript">
21620 * @class Roo.bootstrap.PagingToolbar
21622 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21624 * Create a new PagingToolbar
21625 * @param {Object} config The config object
21627 Roo.bootstrap.PagingToolbar = function(config)
21629 // old args format still supported... - xtype is prefered..
21630 // created from xtype...
21631 var ds = config.dataSource;
21632 this.toolbarItems = [];
21633 if (config.items) {
21634 this.toolbarItems = config.items;
21635 // config.items = [];
21638 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21645 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21649 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21651 * @cfg {Roo.data.Store} dataSource
21652 * The underlying data store providing the paged data
21655 * @cfg {String/HTMLElement/Element} container
21656 * container The id or element that will contain the toolbar
21659 * @cfg {Boolean} displayInfo
21660 * True to display the displayMsg (defaults to false)
21663 * @cfg {Number} pageSize
21664 * The number of records to display per page (defaults to 20)
21668 * @cfg {String} displayMsg
21669 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21671 displayMsg : 'Displaying {0} - {1} of {2}',
21673 * @cfg {String} emptyMsg
21674 * The message to display when no records are found (defaults to "No data to display")
21676 emptyMsg : 'No data to display',
21678 * Customizable piece of the default paging text (defaults to "Page")
21681 beforePageText : "Page",
21683 * Customizable piece of the default paging text (defaults to "of %0")
21686 afterPageText : "of {0}",
21688 * Customizable piece of the default paging text (defaults to "First Page")
21691 firstText : "First Page",
21693 * Customizable piece of the default paging text (defaults to "Previous Page")
21696 prevText : "Previous Page",
21698 * Customizable piece of the default paging text (defaults to "Next Page")
21701 nextText : "Next Page",
21703 * Customizable piece of the default paging text (defaults to "Last Page")
21706 lastText : "Last Page",
21708 * Customizable piece of the default paging text (defaults to "Refresh")
21711 refreshText : "Refresh",
21715 onRender : function(ct, position)
21717 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21718 this.navgroup.parentId = this.id;
21719 this.navgroup.onRender(this.el, null);
21720 // add the buttons to the navgroup
21722 if(this.displayInfo){
21723 Roo.log(this.el.select('ul.navbar-nav',true).first());
21724 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21725 this.displayEl = this.el.select('.x-paging-info', true).first();
21726 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21727 // this.displayEl = navel.el.select('span',true).first();
21733 Roo.each(_this.buttons, function(e){
21734 Roo.factory(e).onRender(_this.el, null);
21738 Roo.each(_this.toolbarItems, function(e) {
21739 _this.navgroup.addItem(e);
21743 this.first = this.navgroup.addItem({
21744 tooltip: this.firstText,
21746 icon : 'fa fa-backward',
21748 preventDefault: true,
21749 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21752 this.prev = this.navgroup.addItem({
21753 tooltip: this.prevText,
21755 icon : 'fa fa-step-backward',
21757 preventDefault: true,
21758 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21760 //this.addSeparator();
21763 var field = this.navgroup.addItem( {
21765 cls : 'x-paging-position',
21767 html : this.beforePageText +
21768 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21769 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21772 this.field = field.el.select('input', true).first();
21773 this.field.on("keydown", this.onPagingKeydown, this);
21774 this.field.on("focus", function(){this.dom.select();});
21777 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21778 //this.field.setHeight(18);
21779 //this.addSeparator();
21780 this.next = this.navgroup.addItem({
21781 tooltip: this.nextText,
21783 html : ' <i class="fa fa-step-forward">',
21785 preventDefault: true,
21786 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21788 this.last = this.navgroup.addItem({
21789 tooltip: this.lastText,
21790 icon : 'fa fa-forward',
21793 preventDefault: true,
21794 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21796 //this.addSeparator();
21797 this.loading = this.navgroup.addItem({
21798 tooltip: this.refreshText,
21799 icon: 'fa fa-refresh',
21800 preventDefault: true,
21801 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21807 updateInfo : function(){
21808 if(this.displayEl){
21809 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21810 var msg = count == 0 ?
21814 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21816 this.displayEl.update(msg);
21821 onLoad : function(ds, r, o){
21822 this.cursor = o.params ? o.params.start : 0;
21823 var d = this.getPageData(),
21827 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21828 this.field.dom.value = ap;
21829 this.first.setDisabled(ap == 1);
21830 this.prev.setDisabled(ap == 1);
21831 this.next.setDisabled(ap == ps);
21832 this.last.setDisabled(ap == ps);
21833 this.loading.enable();
21838 getPageData : function(){
21839 var total = this.ds.getTotalCount();
21842 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21843 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21848 onLoadError : function(){
21849 this.loading.enable();
21853 onPagingKeydown : function(e){
21854 var k = e.getKey();
21855 var d = this.getPageData();
21857 var v = this.field.dom.value, pageNum;
21858 if(!v || isNaN(pageNum = parseInt(v, 10))){
21859 this.field.dom.value = d.activePage;
21862 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21863 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21866 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))
21868 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21869 this.field.dom.value = pageNum;
21870 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21873 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21875 var v = this.field.dom.value, pageNum;
21876 var increment = (e.shiftKey) ? 10 : 1;
21877 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21879 if(!v || isNaN(pageNum = parseInt(v, 10))) {
21880 this.field.dom.value = d.activePage;
21883 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21885 this.field.dom.value = parseInt(v, 10) + increment;
21886 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21887 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21894 beforeLoad : function(){
21896 this.loading.disable();
21901 onClick : function(which){
21910 ds.load({params:{start: 0, limit: this.pageSize}});
21913 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21916 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21919 var total = ds.getTotalCount();
21920 var extra = total % this.pageSize;
21921 var lastStart = extra ? (total - extra) : total-this.pageSize;
21922 ds.load({params:{start: lastStart, limit: this.pageSize}});
21925 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21931 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21932 * @param {Roo.data.Store} store The data store to unbind
21934 unbind : function(ds){
21935 ds.un("beforeload", this.beforeLoad, this);
21936 ds.un("load", this.onLoad, this);
21937 ds.un("loadexception", this.onLoadError, this);
21938 ds.un("remove", this.updateInfo, this);
21939 ds.un("add", this.updateInfo, this);
21940 this.ds = undefined;
21944 * Binds the paging toolbar to the specified {@link Roo.data.Store}
21945 * @param {Roo.data.Store} store The data store to bind
21947 bind : function(ds){
21948 ds.on("beforeload", this.beforeLoad, this);
21949 ds.on("load", this.onLoad, this);
21950 ds.on("loadexception", this.onLoadError, this);
21951 ds.on("remove", this.updateInfo, this);
21952 ds.on("add", this.updateInfo, this);
21963 * @class Roo.bootstrap.MessageBar
21964 * @extends Roo.bootstrap.Component
21965 * Bootstrap MessageBar class
21966 * @cfg {String} html contents of the MessageBar
21967 * @cfg {String} weight (info | success | warning | danger) default info
21968 * @cfg {String} beforeClass insert the bar before the given class
21969 * @cfg {Boolean} closable (true | false) default false
21970 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21973 * Create a new Element
21974 * @param {Object} config The config object
21977 Roo.bootstrap.MessageBar = function(config){
21978 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21981 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
21987 beforeClass: 'bootstrap-sticky-wrap',
21989 getAutoCreate : function(){
21993 cls: 'alert alert-dismissable alert-' + this.weight,
21998 html: this.html || ''
22004 cfg.cls += ' alert-messages-fixed';
22018 onRender : function(ct, position)
22020 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22023 var cfg = Roo.apply({}, this.getAutoCreate());
22027 cfg.cls += ' ' + this.cls;
22030 cfg.style = this.style;
22032 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22034 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22037 this.el.select('>button.close').on('click', this.hide, this);
22043 if (!this.rendered) {
22049 this.fireEvent('show', this);
22055 if (!this.rendered) {
22061 this.fireEvent('hide', this);
22064 update : function()
22066 // var e = this.el.dom.firstChild;
22068 // if(this.closable){
22069 // e = e.nextSibling;
22072 // e.data = this.html || '';
22074 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22090 * @class Roo.bootstrap.Graph
22091 * @extends Roo.bootstrap.Component
22092 * Bootstrap Graph class
22096 @cfg {String} graphtype bar | vbar | pie
22097 @cfg {number} g_x coodinator | centre x (pie)
22098 @cfg {number} g_y coodinator | centre y (pie)
22099 @cfg {number} g_r radius (pie)
22100 @cfg {number} g_height height of the chart (respected by all elements in the set)
22101 @cfg {number} g_width width of the chart (respected by all elements in the set)
22102 @cfg {Object} title The title of the chart
22105 -opts (object) options for the chart
22107 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22108 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22110 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.
22111 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22113 o stretch (boolean)
22115 -opts (object) options for the pie
22118 o startAngle (number)
22119 o endAngle (number)
22123 * Create a new Input
22124 * @param {Object} config The config object
22127 Roo.bootstrap.Graph = function(config){
22128 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22134 * The img click event for the img.
22135 * @param {Roo.EventObject} e
22141 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22152 //g_colors: this.colors,
22159 getAutoCreate : function(){
22170 onRender : function(ct,position){
22171 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22172 this.raphael = Raphael(this.el.dom);
22174 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22175 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22176 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22177 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22179 r.text(160, 10, "Single Series Chart").attr(txtattr);
22180 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22181 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22182 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22184 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22185 r.barchart(330, 10, 300, 220, data1);
22186 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22187 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22190 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22191 // r.barchart(30, 30, 560, 250, xdata, {
22192 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22193 // axis : "0 0 1 1",
22194 // axisxlabels : xdata
22195 // //yvalues : cols,
22198 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22200 // this.load(null,xdata,{
22201 // axis : "0 0 1 1",
22202 // axisxlabels : xdata
22207 load : function(graphtype,xdata,opts){
22208 this.raphael.clear();
22210 graphtype = this.graphtype;
22215 var r = this.raphael,
22216 fin = function () {
22217 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22219 fout = function () {
22220 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22222 pfin = function() {
22223 this.sector.stop();
22224 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22227 this.label[0].stop();
22228 this.label[0].attr({ r: 7.5 });
22229 this.label[1].attr({ "font-weight": 800 });
22232 pfout = function() {
22233 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22236 this.label[0].animate({ r: 5 }, 500, "bounce");
22237 this.label[1].attr({ "font-weight": 400 });
22243 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22246 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22249 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22250 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22252 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22259 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22264 setTitle: function(o)
22269 initEvents: function() {
22272 this.el.on('click', this.onClick, this);
22276 onClick : function(e)
22278 Roo.log('img onclick');
22279 this.fireEvent('click', this, e);
22291 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22294 * @class Roo.bootstrap.dash.NumberBox
22295 * @extends Roo.bootstrap.Component
22296 * Bootstrap NumberBox class
22297 * @cfg {String} headline Box headline
22298 * @cfg {String} content Box content
22299 * @cfg {String} icon Box icon
22300 * @cfg {String} footer Footer text
22301 * @cfg {String} fhref Footer href
22304 * Create a new NumberBox
22305 * @param {Object} config The config object
22309 Roo.bootstrap.dash.NumberBox = function(config){
22310 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22314 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22323 getAutoCreate : function(){
22327 cls : 'small-box ',
22335 cls : 'roo-headline',
22336 html : this.headline
22340 cls : 'roo-content',
22341 html : this.content
22355 cls : 'ion ' + this.icon
22364 cls : 'small-box-footer',
22365 href : this.fhref || '#',
22369 cfg.cn.push(footer);
22376 onRender : function(ct,position){
22377 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22384 setHeadline: function (value)
22386 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22389 setFooter: function (value, href)
22391 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22394 this.el.select('a.small-box-footer',true).first().attr('href', href);
22399 setContent: function (value)
22401 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22404 initEvents: function()
22418 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22421 * @class Roo.bootstrap.dash.TabBox
22422 * @extends Roo.bootstrap.Component
22423 * Bootstrap TabBox class
22424 * @cfg {String} title Title of the TabBox
22425 * @cfg {String} icon Icon of the TabBox
22426 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22427 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22430 * Create a new TabBox
22431 * @param {Object} config The config object
22435 Roo.bootstrap.dash.TabBox = function(config){
22436 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22441 * When a pane is added
22442 * @param {Roo.bootstrap.dash.TabPane} pane
22446 * @event activatepane
22447 * When a pane is activated
22448 * @param {Roo.bootstrap.dash.TabPane} pane
22450 "activatepane" : true
22458 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22463 tabScrollable : false,
22465 getChildContainer : function()
22467 return this.el.select('.tab-content', true).first();
22470 getAutoCreate : function(){
22474 cls: 'pull-left header',
22482 cls: 'fa ' + this.icon
22488 cls: 'nav nav-tabs pull-right',
22494 if(this.tabScrollable){
22501 cls: 'nav nav-tabs pull-right',
22512 cls: 'nav-tabs-custom',
22517 cls: 'tab-content no-padding',
22525 initEvents : function()
22527 //Roo.log('add add pane handler');
22528 this.on('addpane', this.onAddPane, this);
22531 * Updates the box title
22532 * @param {String} html to set the title to.
22534 setTitle : function(value)
22536 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22538 onAddPane : function(pane)
22540 this.panes.push(pane);
22541 //Roo.log('addpane');
22543 // tabs are rendere left to right..
22544 if(!this.showtabs){
22548 var ctr = this.el.select('.nav-tabs', true).first();
22551 var existing = ctr.select('.nav-tab',true);
22552 var qty = existing.getCount();;
22555 var tab = ctr.createChild({
22557 cls : 'nav-tab' + (qty ? '' : ' active'),
22565 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22568 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22570 pane.el.addClass('active');
22575 onTabClick : function(ev,un,ob,pane)
22577 //Roo.log('tab - prev default');
22578 ev.preventDefault();
22581 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22582 pane.tab.addClass('active');
22583 //Roo.log(pane.title);
22584 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22585 // technically we should have a deactivate event.. but maybe add later.
22586 // and it should not de-activate the selected tab...
22587 this.fireEvent('activatepane', pane);
22588 pane.el.addClass('active');
22589 pane.fireEvent('activate');
22594 getActivePane : function()
22597 Roo.each(this.panes, function(p) {
22598 if(p.el.hasClass('active')){
22619 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22621 * @class Roo.bootstrap.TabPane
22622 * @extends Roo.bootstrap.Component
22623 * Bootstrap TabPane class
22624 * @cfg {Boolean} active (false | true) Default false
22625 * @cfg {String} title title of panel
22629 * Create a new TabPane
22630 * @param {Object} config The config object
22633 Roo.bootstrap.dash.TabPane = function(config){
22634 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22640 * When a pane is activated
22641 * @param {Roo.bootstrap.dash.TabPane} pane
22648 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22653 // the tabBox that this is attached to.
22656 getAutoCreate : function()
22664 cfg.cls += ' active';
22669 initEvents : function()
22671 //Roo.log('trigger add pane handler');
22672 this.parent().fireEvent('addpane', this)
22676 * Updates the tab title
22677 * @param {String} html to set the title to.
22679 setTitle: function(str)
22685 this.tab.select('a', true).first().dom.innerHTML = str;
22702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22705 * @class Roo.bootstrap.menu.Menu
22706 * @extends Roo.bootstrap.Component
22707 * Bootstrap Menu class - container for Menu
22708 * @cfg {String} html Text of the menu
22709 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22710 * @cfg {String} icon Font awesome icon
22711 * @cfg {String} pos Menu align to (top | bottom) default bottom
22715 * Create a new Menu
22716 * @param {Object} config The config object
22720 Roo.bootstrap.menu.Menu = function(config){
22721 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22725 * @event beforeshow
22726 * Fires before this menu is displayed
22727 * @param {Roo.bootstrap.menu.Menu} this
22731 * @event beforehide
22732 * Fires before this menu is hidden
22733 * @param {Roo.bootstrap.menu.Menu} this
22738 * Fires after this menu is displayed
22739 * @param {Roo.bootstrap.menu.Menu} this
22744 * Fires after this menu is hidden
22745 * @param {Roo.bootstrap.menu.Menu} this
22750 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22751 * @param {Roo.bootstrap.menu.Menu} this
22752 * @param {Roo.EventObject} e
22759 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22763 weight : 'default',
22768 getChildContainer : function() {
22769 if(this.isSubMenu){
22773 return this.el.select('ul.dropdown-menu', true).first();
22776 getAutoCreate : function()
22781 cls : 'roo-menu-text',
22789 cls : 'fa ' + this.icon
22800 cls : 'dropdown-button btn btn-' + this.weight,
22805 cls : 'dropdown-toggle btn btn-' + this.weight,
22815 cls : 'dropdown-menu'
22821 if(this.pos == 'top'){
22822 cfg.cls += ' dropup';
22825 if(this.isSubMenu){
22828 cls : 'dropdown-menu'
22835 onRender : function(ct, position)
22837 this.isSubMenu = ct.hasClass('dropdown-submenu');
22839 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22842 initEvents : function()
22844 if(this.isSubMenu){
22848 this.hidden = true;
22850 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22851 this.triggerEl.on('click', this.onTriggerPress, this);
22853 this.buttonEl = this.el.select('button.dropdown-button', true).first();
22854 this.buttonEl.on('click', this.onClick, this);
22860 if(this.isSubMenu){
22864 return this.el.select('ul.dropdown-menu', true).first();
22867 onClick : function(e)
22869 this.fireEvent("click", this, e);
22872 onTriggerPress : function(e)
22874 if (this.isVisible()) {
22881 isVisible : function(){
22882 return !this.hidden;
22887 this.fireEvent("beforeshow", this);
22889 this.hidden = false;
22890 this.el.addClass('open');
22892 Roo.get(document).on("mouseup", this.onMouseUp, this);
22894 this.fireEvent("show", this);
22901 this.fireEvent("beforehide", this);
22903 this.hidden = true;
22904 this.el.removeClass('open');
22906 Roo.get(document).un("mouseup", this.onMouseUp);
22908 this.fireEvent("hide", this);
22911 onMouseUp : function()
22925 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22928 * @class Roo.bootstrap.menu.Item
22929 * @extends Roo.bootstrap.Component
22930 * Bootstrap MenuItem class
22931 * @cfg {Boolean} submenu (true | false) default false
22932 * @cfg {String} html text of the item
22933 * @cfg {String} href the link
22934 * @cfg {Boolean} disable (true | false) default false
22935 * @cfg {Boolean} preventDefault (true | false) default true
22936 * @cfg {String} icon Font awesome icon
22937 * @cfg {String} pos Submenu align to (left | right) default right
22941 * Create a new Item
22942 * @param {Object} config The config object
22946 Roo.bootstrap.menu.Item = function(config){
22947 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22951 * Fires when the mouse is hovering over this menu
22952 * @param {Roo.bootstrap.menu.Item} this
22953 * @param {Roo.EventObject} e
22958 * Fires when the mouse exits this menu
22959 * @param {Roo.bootstrap.menu.Item} this
22960 * @param {Roo.EventObject} e
22966 * The raw click event for the entire grid.
22967 * @param {Roo.EventObject} e
22973 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
22978 preventDefault: true,
22983 getAutoCreate : function()
22988 cls : 'roo-menu-item-text',
22996 cls : 'fa ' + this.icon
23005 href : this.href || '#',
23012 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23016 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23018 if(this.pos == 'left'){
23019 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23026 initEvents : function()
23028 this.el.on('mouseover', this.onMouseOver, this);
23029 this.el.on('mouseout', this.onMouseOut, this);
23031 this.el.select('a', true).first().on('click', this.onClick, this);
23035 onClick : function(e)
23037 if(this.preventDefault){
23038 e.preventDefault();
23041 this.fireEvent("click", this, e);
23044 onMouseOver : function(e)
23046 if(this.submenu && this.pos == 'left'){
23047 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23050 this.fireEvent("mouseover", this, e);
23053 onMouseOut : function(e)
23055 this.fireEvent("mouseout", this, e);
23067 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23070 * @class Roo.bootstrap.menu.Separator
23071 * @extends Roo.bootstrap.Component
23072 * Bootstrap Separator class
23075 * Create a new Separator
23076 * @param {Object} config The config object
23080 Roo.bootstrap.menu.Separator = function(config){
23081 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23084 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23086 getAutoCreate : function(){
23107 * @class Roo.bootstrap.Tooltip
23108 * Bootstrap Tooltip class
23109 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23110 * to determine which dom element triggers the tooltip.
23112 * It needs to add support for additional attributes like tooltip-position
23115 * Create a new Toolti
23116 * @param {Object} config The config object
23119 Roo.bootstrap.Tooltip = function(config){
23120 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23123 Roo.apply(Roo.bootstrap.Tooltip, {
23125 * @function init initialize tooltip monitoring.
23129 currentTip : false,
23130 currentRegion : false,
23136 Roo.get(document).on('mouseover', this.enter ,this);
23137 Roo.get(document).on('mouseout', this.leave, this);
23140 this.currentTip = new Roo.bootstrap.Tooltip();
23143 enter : function(ev)
23145 var dom = ev.getTarget();
23147 //Roo.log(['enter',dom]);
23148 var el = Roo.fly(dom);
23149 if (this.currentEl) {
23151 //Roo.log(this.currentEl);
23152 //Roo.log(this.currentEl.contains(dom));
23153 if (this.currentEl == el) {
23156 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23164 if (this.currentTip.el) {
23165 this.currentTip.el.hide(); // force hiding...
23170 // you can not look for children, as if el is the body.. then everythign is the child..
23171 if (!el.attr('tooltip')) { //
23172 if (!el.select("[tooltip]").elements.length) {
23175 // is the mouse over this child...?
23176 bindEl = el.select("[tooltip]").first();
23177 var xy = ev.getXY();
23178 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23179 //Roo.log("not in region.");
23182 //Roo.log("child element over..");
23185 this.currentEl = bindEl;
23186 this.currentTip.bind(bindEl);
23187 this.currentRegion = Roo.lib.Region.getRegion(dom);
23188 this.currentTip.enter();
23191 leave : function(ev)
23193 var dom = ev.getTarget();
23194 //Roo.log(['leave',dom]);
23195 if (!this.currentEl) {
23200 if (dom != this.currentEl.dom) {
23203 var xy = ev.getXY();
23204 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23207 // only activate leave if mouse cursor is outside... bounding box..
23212 if (this.currentTip) {
23213 this.currentTip.leave();
23215 //Roo.log('clear currentEl');
23216 this.currentEl = false;
23221 'left' : ['r-l', [-2,0], 'right'],
23222 'right' : ['l-r', [2,0], 'left'],
23223 'bottom' : ['t-b', [0,2], 'top'],
23224 'top' : [ 'b-t', [0,-2], 'bottom']
23230 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23235 delay : null, // can be { show : 300 , hide: 500}
23239 hoverState : null, //???
23241 placement : 'bottom',
23243 getAutoCreate : function(){
23250 cls : 'tooltip-arrow'
23253 cls : 'tooltip-inner'
23260 bind : function(el)
23266 enter : function () {
23268 if (this.timeout != null) {
23269 clearTimeout(this.timeout);
23272 this.hoverState = 'in';
23273 //Roo.log("enter - show");
23274 if (!this.delay || !this.delay.show) {
23279 this.timeout = setTimeout(function () {
23280 if (_t.hoverState == 'in') {
23283 }, this.delay.show);
23287 clearTimeout(this.timeout);
23289 this.hoverState = 'out';
23290 if (!this.delay || !this.delay.hide) {
23296 this.timeout = setTimeout(function () {
23297 //Roo.log("leave - timeout");
23299 if (_t.hoverState == 'out') {
23301 Roo.bootstrap.Tooltip.currentEl = false;
23309 this.render(document.body);
23312 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23314 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23316 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23318 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23320 var placement = typeof this.placement == 'function' ?
23321 this.placement.call(this, this.el, on_el) :
23324 var autoToken = /\s?auto?\s?/i;
23325 var autoPlace = autoToken.test(placement);
23327 placement = placement.replace(autoToken, '') || 'top';
23331 //this.el.setXY([0,0]);
23333 //this.el.dom.style.display='block';
23334 this.el.addClass(placement);
23336 //this.el.appendTo(on_el);
23338 var p = this.getPosition();
23339 var box = this.el.getBox();
23344 var align = Roo.bootstrap.Tooltip.alignment[placement];
23345 this.el.alignTo(this.bindEl, align[0],align[1]);
23346 //var arrow = this.el.select('.arrow',true).first();
23347 //arrow.set(align[2],
23349 this.el.addClass('in fade');
23350 this.hoverState = null;
23352 if (this.el.hasClass('fade')) {
23363 //this.el.setXY([0,0]);
23364 this.el.removeClass('in');
23380 * @class Roo.bootstrap.LocationPicker
23381 * @extends Roo.bootstrap.Component
23382 * Bootstrap LocationPicker class
23383 * @cfg {Number} latitude Position when init default 0
23384 * @cfg {Number} longitude Position when init default 0
23385 * @cfg {Number} zoom default 15
23386 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23387 * @cfg {Boolean} mapTypeControl default false
23388 * @cfg {Boolean} disableDoubleClickZoom default false
23389 * @cfg {Boolean} scrollwheel default true
23390 * @cfg {Boolean} streetViewControl default false
23391 * @cfg {Number} radius default 0
23392 * @cfg {String} locationName
23393 * @cfg {Boolean} draggable default true
23394 * @cfg {Boolean} enableAutocomplete default false
23395 * @cfg {Boolean} enableReverseGeocode default true
23396 * @cfg {String} markerTitle
23399 * Create a new LocationPicker
23400 * @param {Object} config The config object
23404 Roo.bootstrap.LocationPicker = function(config){
23406 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23411 * Fires when the picker initialized.
23412 * @param {Roo.bootstrap.LocationPicker} this
23413 * @param {Google Location} location
23417 * @event positionchanged
23418 * Fires when the picker position changed.
23419 * @param {Roo.bootstrap.LocationPicker} this
23420 * @param {Google Location} location
23422 positionchanged : true,
23425 * Fires when the map resize.
23426 * @param {Roo.bootstrap.LocationPicker} this
23431 * Fires when the map show.
23432 * @param {Roo.bootstrap.LocationPicker} this
23437 * Fires when the map hide.
23438 * @param {Roo.bootstrap.LocationPicker} this
23443 * Fires when click the map.
23444 * @param {Roo.bootstrap.LocationPicker} this
23445 * @param {Map event} e
23449 * @event mapRightClick
23450 * Fires when right click the map.
23451 * @param {Roo.bootstrap.LocationPicker} this
23452 * @param {Map event} e
23454 mapRightClick : true,
23456 * @event markerClick
23457 * Fires when click the marker.
23458 * @param {Roo.bootstrap.LocationPicker} this
23459 * @param {Map event} e
23461 markerClick : true,
23463 * @event markerRightClick
23464 * Fires when right click the marker.
23465 * @param {Roo.bootstrap.LocationPicker} this
23466 * @param {Map event} e
23468 markerRightClick : true,
23470 * @event OverlayViewDraw
23471 * Fires when OverlayView Draw
23472 * @param {Roo.bootstrap.LocationPicker} this
23474 OverlayViewDraw : true,
23476 * @event OverlayViewOnAdd
23477 * Fires when OverlayView Draw
23478 * @param {Roo.bootstrap.LocationPicker} this
23480 OverlayViewOnAdd : true,
23482 * @event OverlayViewOnRemove
23483 * Fires when OverlayView Draw
23484 * @param {Roo.bootstrap.LocationPicker} this
23486 OverlayViewOnRemove : true,
23488 * @event OverlayViewShow
23489 * Fires when OverlayView Draw
23490 * @param {Roo.bootstrap.LocationPicker} this
23491 * @param {Pixel} cpx
23493 OverlayViewShow : true,
23495 * @event OverlayViewHide
23496 * Fires when OverlayView Draw
23497 * @param {Roo.bootstrap.LocationPicker} this
23499 OverlayViewHide : true
23504 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23506 gMapContext: false,
23512 mapTypeControl: false,
23513 disableDoubleClickZoom: false,
23515 streetViewControl: false,
23519 enableAutocomplete: false,
23520 enableReverseGeocode: true,
23523 getAutoCreate: function()
23528 cls: 'roo-location-picker'
23534 initEvents: function(ct, position)
23536 if(!this.el.getWidth() || this.isApplied()){
23540 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23545 initial: function()
23547 if(!this.mapTypeId){
23548 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23551 this.gMapContext = this.GMapContext();
23553 this.initOverlayView();
23555 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23559 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23560 _this.setPosition(_this.gMapContext.marker.position);
23563 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23564 _this.fireEvent('mapClick', this, event);
23568 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23569 _this.fireEvent('mapRightClick', this, event);
23573 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23574 _this.fireEvent('markerClick', this, event);
23578 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23579 _this.fireEvent('markerRightClick', this, event);
23583 this.setPosition(this.gMapContext.location);
23585 this.fireEvent('initial', this, this.gMapContext.location);
23588 initOverlayView: function()
23592 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23596 _this.fireEvent('OverlayViewDraw', _this);
23601 _this.fireEvent('OverlayViewOnAdd', _this);
23604 onRemove: function()
23606 _this.fireEvent('OverlayViewOnRemove', _this);
23609 show: function(cpx)
23611 _this.fireEvent('OverlayViewShow', _this, cpx);
23616 _this.fireEvent('OverlayViewHide', _this);
23622 fromLatLngToContainerPixel: function(event)
23624 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23627 isApplied: function()
23629 return this.getGmapContext() == false ? false : true;
23632 getGmapContext: function()
23634 return this.gMapContext
23637 GMapContext: function()
23639 var position = new google.maps.LatLng(this.latitude, this.longitude);
23641 var _map = new google.maps.Map(this.el.dom, {
23644 mapTypeId: this.mapTypeId,
23645 mapTypeControl: this.mapTypeControl,
23646 disableDoubleClickZoom: this.disableDoubleClickZoom,
23647 scrollwheel: this.scrollwheel,
23648 streetViewControl: this.streetViewControl,
23649 locationName: this.locationName,
23650 draggable: this.draggable,
23651 enableAutocomplete: this.enableAutocomplete,
23652 enableReverseGeocode: this.enableReverseGeocode
23655 var _marker = new google.maps.Marker({
23656 position: position,
23658 title: this.markerTitle,
23659 draggable: this.draggable
23666 location: position,
23667 radius: this.radius,
23668 locationName: this.locationName,
23669 addressComponents: {
23670 formatted_address: null,
23671 addressLine1: null,
23672 addressLine2: null,
23674 streetNumber: null,
23678 stateOrProvince: null
23681 domContainer: this.el.dom,
23682 geodecoder: new google.maps.Geocoder()
23686 drawCircle: function(center, radius, options)
23688 if (this.gMapContext.circle != null) {
23689 this.gMapContext.circle.setMap(null);
23693 options = Roo.apply({}, options, {
23694 strokeColor: "#0000FF",
23695 strokeOpacity: .35,
23697 fillColor: "#0000FF",
23701 options.map = this.gMapContext.map;
23702 options.radius = radius;
23703 options.center = center;
23704 this.gMapContext.circle = new google.maps.Circle(options);
23705 return this.gMapContext.circle;
23711 setPosition: function(location)
23713 this.gMapContext.location = location;
23714 this.gMapContext.marker.setPosition(location);
23715 this.gMapContext.map.panTo(location);
23716 this.drawCircle(location, this.gMapContext.radius, {});
23720 if (this.gMapContext.settings.enableReverseGeocode) {
23721 this.gMapContext.geodecoder.geocode({
23722 latLng: this.gMapContext.location
23723 }, function(results, status) {
23725 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23726 _this.gMapContext.locationName = results[0].formatted_address;
23727 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23729 _this.fireEvent('positionchanged', this, location);
23736 this.fireEvent('positionchanged', this, location);
23741 google.maps.event.trigger(this.gMapContext.map, "resize");
23743 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23745 this.fireEvent('resize', this);
23748 setPositionByLatLng: function(latitude, longitude)
23750 this.setPosition(new google.maps.LatLng(latitude, longitude));
23753 getCurrentPosition: function()
23756 latitude: this.gMapContext.location.lat(),
23757 longitude: this.gMapContext.location.lng()
23761 getAddressName: function()
23763 return this.gMapContext.locationName;
23766 getAddressComponents: function()
23768 return this.gMapContext.addressComponents;
23771 address_component_from_google_geocode: function(address_components)
23775 for (var i = 0; i < address_components.length; i++) {
23776 var component = address_components[i];
23777 if (component.types.indexOf("postal_code") >= 0) {
23778 result.postalCode = component.short_name;
23779 } else if (component.types.indexOf("street_number") >= 0) {
23780 result.streetNumber = component.short_name;
23781 } else if (component.types.indexOf("route") >= 0) {
23782 result.streetName = component.short_name;
23783 } else if (component.types.indexOf("neighborhood") >= 0) {
23784 result.city = component.short_name;
23785 } else if (component.types.indexOf("locality") >= 0) {
23786 result.city = component.short_name;
23787 } else if (component.types.indexOf("sublocality") >= 0) {
23788 result.district = component.short_name;
23789 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23790 result.stateOrProvince = component.short_name;
23791 } else if (component.types.indexOf("country") >= 0) {
23792 result.country = component.short_name;
23796 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23797 result.addressLine2 = "";
23801 setZoomLevel: function(zoom)
23803 this.gMapContext.map.setZoom(zoom);
23816 this.fireEvent('show', this);
23827 this.fireEvent('hide', this);
23832 Roo.apply(Roo.bootstrap.LocationPicker, {
23834 OverlayView : function(map, options)
23836 options = options || {};
23850 * @class Roo.bootstrap.Alert
23851 * @extends Roo.bootstrap.Component
23852 * Bootstrap Alert class
23853 * @cfg {String} title The title of alert
23854 * @cfg {String} html The content of alert
23855 * @cfg {String} weight ( success | info | warning | danger )
23856 * @cfg {String} faicon font-awesomeicon
23859 * Create a new alert
23860 * @param {Object} config The config object
23864 Roo.bootstrap.Alert = function(config){
23865 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23869 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
23876 getAutoCreate : function()
23885 cls : 'roo-alert-icon'
23890 cls : 'roo-alert-title',
23895 cls : 'roo-alert-text',
23902 cfg.cn[0].cls += ' fa ' + this.faicon;
23906 cfg.cls += ' alert-' + this.weight;
23912 initEvents: function()
23914 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23917 setTitle : function(str)
23919 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23922 setText : function(str)
23924 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23927 setWeight : function(weight)
23930 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23933 this.weight = weight;
23935 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23938 setIcon : function(icon)
23941 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23946 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23967 * @class Roo.bootstrap.UploadCropbox
23968 * @extends Roo.bootstrap.Component
23969 * Bootstrap UploadCropbox class
23970 * @cfg {String} emptyText show when image has been loaded
23971 * @cfg {String} rotateNotify show when image too small to rotate
23972 * @cfg {Number} errorTimeout default 3000
23973 * @cfg {Number} minWidth default 300
23974 * @cfg {Number} minHeight default 300
23975 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23978 * Create a new UploadCropbox
23979 * @param {Object} config The config object
23982 Roo.bootstrap.UploadCropbox = function(config){
23983 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23987 * @event beforeselectfile
23988 * Fire before select file
23989 * @param {Roo.bootstrap.UploadCropbox} this
23991 "beforeselectfile" : true,
23994 * Fire after initEvent
23995 * @param {Roo.bootstrap.UploadCropbox} this
24000 * Fire after initEvent
24001 * @param {Roo.bootstrap.UploadCropbox} this
24002 * @param {String} data
24007 * Fire when preparing the file data
24008 * @param {Roo.bootstrap.UploadCropbox} this
24009 * @param {Object} file
24014 * Fire when get exception
24015 * @param {Roo.bootstrap.UploadCropbox} this
24016 * @param {Object} options
24018 "exception" : true,
24020 * @event beforeloadcanvas
24021 * Fire before load the canvas
24022 * @param {Roo.bootstrap.UploadCropbox} this
24023 * @param {String} src
24025 "beforeloadcanvas" : true,
24028 * Fire when trash image
24029 * @param {Roo.bootstrap.UploadCropbox} this
24034 * Fire when download the image
24035 * @param {Roo.bootstrap.UploadCropbox} this
24039 * @event footerbuttonclick
24040 * Fire when footerbuttonclick
24041 * @param {Roo.bootstrap.UploadCropbox} this
24042 * @param {String} type
24044 "footerbuttonclick" : true,
24048 * @param {Roo.bootstrap.UploadCropbox} this
24054 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24057 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24059 emptyText : 'Click to upload image',
24060 rotateNotify : 'Image is too small to rotate',
24061 errorTimeout : 3000,
24075 cropType : 'image/jpeg',
24077 canvasLoaded : false,
24079 getAutoCreate : function()
24083 cls : 'roo-upload-cropbox',
24087 cls : 'roo-upload-cropbox-body',
24088 style : 'cursor:pointer',
24092 cls : 'roo-upload-cropbox-preview'
24096 cls : 'roo-upload-cropbox-thumb'
24100 cls : 'roo-upload-cropbox-empty-notify',
24101 html : this.emptyText
24105 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24106 html : this.rotateNotify
24112 cls : 'roo-upload-cropbox-footer',
24115 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24125 onRender : function(ct, position)
24127 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24129 if (this.buttons.length) {
24131 Roo.each(this.buttons, function(bb) {
24133 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24135 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24141 initEvents : function()
24143 this.urlAPI = (window.createObjectURL && window) ||
24144 (window.URL && URL.revokeObjectURL && URL) ||
24145 (window.webkitURL && webkitURL);
24147 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24148 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24150 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24151 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24153 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24154 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24155 this.thumbEl.hide();
24157 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24158 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24160 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24161 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24162 this.errorEl.hide();
24164 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24165 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24166 this.footerEl.hide();
24168 this.setThumbBoxSize();
24174 this.fireEvent('initial', this);
24181 window.addEventListener("resize", function() { _this.resize(); } );
24183 this.bodyEl.on('click', this.beforeSelectFile, this);
24186 this.bodyEl.on('touchstart', this.onTouchStart, this);
24187 this.bodyEl.on('touchmove', this.onTouchMove, this);
24188 this.bodyEl.on('touchend', this.onTouchEnd, this);
24192 this.bodyEl.on('mousedown', this.onMouseDown, this);
24193 this.bodyEl.on('mousemove', this.onMouseMove, this);
24194 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24195 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24196 Roo.get(document).on('mouseup', this.onMouseUp, this);
24203 this.baseScale = 1;
24205 this.baseRotate = 1;
24206 this.dragable = false;
24207 this.pinching = false;
24210 this.cropData = false;
24211 this.notifyEl.dom.innerHTML = this.emptyText;
24215 resize : function()
24217 if(this.fireEvent('resize', this) != false){
24218 this.setThumbBoxPosition();
24219 this.setCanvasPosition();
24223 onFooterButtonClick : function(e, el, o, type)
24226 case 'rotate-left' :
24227 this.onRotateLeft(e);
24229 case 'rotate-right' :
24230 this.onRotateRight(e);
24233 this.beforeSelectFile(e);
24248 this.fireEvent('footerbuttonclick', this, type);
24251 beforeSelectFile : function(e)
24253 this.fireEvent('beforeselectfile', this);
24256 trash : function(e)
24258 this.fireEvent('trash', this);
24261 download : function(e)
24263 this.fireEvent('download', this);
24266 loadCanvas : function(src)
24268 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24272 this.imageEl = document.createElement('img');
24276 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24278 this.imageEl.src = src;
24282 onLoadCanvas : function()
24284 this.bodyEl.un('click', this.beforeSelectFile, this);
24286 this.notifyEl.hide();
24287 this.thumbEl.show();
24288 this.footerEl.show();
24290 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24291 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24293 this.setThumbBoxPosition();
24294 this.baseRotateLevel();
24295 this.baseScaleLevel();
24301 this.canvasLoaded = true;
24305 setCanvasPosition : function()
24307 if(!this.canvasEl){
24311 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24312 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24314 this.previewEl.setLeft(pw);
24315 this.previewEl.setTop(ph);
24319 onMouseDown : function(e)
24323 this.dragable = true;
24324 this.pinching = false;
24326 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24327 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24331 onMouseMove : function(e)
24335 if(!this.canvasLoaded){
24339 if (!this.dragable){
24343 var minX = Math.ceil(this.thumbEl.getLeft(true));
24344 var minY = Math.ceil(this.thumbEl.getTop(true));
24346 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24347 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24349 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24350 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24352 x = x - this.mouseX;
24353 y = y - this.mouseY;
24355 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24356 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24358 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24359 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24361 this.previewEl.setLeft(bgX);
24362 this.previewEl.setTop(bgY);
24364 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24365 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24368 onMouseUp : function(e)
24372 this.dragable = false;
24375 onMouseWheel : function(e)
24379 this.startScale = this.scale;
24381 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24383 if(!this.zoomable()){
24384 this.scale = this.startScale;
24393 zoomable : function()
24395 var minScale = this.thumbEl.getWidth() / this.minWidth;
24397 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24398 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24401 (this.rotate == 0 || this.rotate == 180) &&
24403 width / minScale < this.minWidth ||
24404 width / minScale > this.imageEl.OriginWidth ||
24405 height / minScale < this.minHeight ||
24406 height / minScale > this.imageEl.OriginHeight
24413 (this.rotate == 90 || this.rotate == 270) &&
24415 width / minScale < this.minHeight ||
24416 width / minScale > this.imageEl.OriginWidth ||
24417 height / minScale < this.minWidth ||
24418 height / minScale > this.imageEl.OriginHeight
24428 onRotateLeft : function(e)
24430 var minScale = this.thumbEl.getWidth() / this.minWidth;
24432 if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24434 var bw = this.canvasEl.width / this.getScaleLevel();
24435 var bh = this.canvasEl.height / this.getScaleLevel();
24437 this.startScale = this.scale;
24439 while (this.getScaleLevel() < minScale){
24441 this.scale = this.scale + 1;
24443 if(!this.zoomable()){
24448 bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24449 bh * this.getScaleLevel() < this.thumbEl.getWidth()
24454 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24461 this.scale = this.startScale;
24463 this.onRotateFail();
24468 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24474 onRotateRight : function(e)
24476 var minScale = this.thumbEl.getWidth() / this.minWidth;
24478 if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24480 var bw = this.canvasEl.width / this.getScaleLevel();
24481 var bh = this.canvasEl.height / this.getScaleLevel();
24483 this.startScale = this.scale;
24485 while (this.getScaleLevel() < minScale){
24487 this.scale = this.scale + 1;
24489 if(!this.zoomable()){
24494 bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24495 bh * this.getScaleLevel() < this.thumbEl.getWidth()
24500 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24507 this.scale = this.startScale;
24509 this.onRotateFail();
24514 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24519 onRotateFail : function()
24521 this.errorEl.show(true);
24525 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24530 this.previewEl.dom.innerHTML = '';
24532 var canvasEl = document.createElement("canvas");
24534 var contextEl = canvasEl.getContext("2d");
24536 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24537 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24538 var center = this.imageEl.OriginWidth / 2;
24540 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24541 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24542 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24543 center = this.imageEl.OriginHeight / 2;
24546 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24548 contextEl.translate(center, center);
24549 contextEl.rotate(this.rotate * Math.PI / 180);
24551 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24553 this.canvasEl = document.createElement("canvas");
24555 this.contextEl = this.canvasEl.getContext("2d");
24557 switch (this.rotate) {
24560 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24561 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24563 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24568 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24569 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24571 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24572 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);
24576 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24581 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24582 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24584 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24585 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);
24589 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);
24594 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24595 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24597 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24598 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24602 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);
24609 this.previewEl.appendChild(this.canvasEl);
24611 this.setCanvasPosition();
24616 if(!this.canvasLoaded){
24619 var canvas = document.createElement("canvas");
24621 var context = canvas.getContext("2d");
24623 canvas.width = this.minWidth;
24624 canvas.height = this.minHeight;
24626 var cropWidth = this.thumbEl.getWidth();
24627 var cropHeight = this.thumbEl.getHeight();
24629 var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24630 var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24632 if(this.canvasEl.width - cropWidth < x){
24633 x = this.canvasEl.width - cropWidth;
24636 if(this.canvasEl.height - cropHeight < y){
24637 y = this.canvasEl.height - cropHeight;
24643 context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24645 this.cropData = canvas.toDataURL(this.cropType);
24647 this.fireEvent('crop', this, this.cropData);
24651 setThumbBoxSize : function()
24654 var width = Math.ceil(this.minWidth * height / this.minHeight);
24656 if(this.minWidth > this.minHeight){
24658 height = Math.ceil(this.minHeight * width / this.minWidth);
24661 this.thumbEl.setStyle({
24662 width : width + 'px',
24663 height : height + 'px'
24670 setThumbBoxPosition : function()
24672 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24673 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24675 this.thumbEl.setLeft(x);
24676 this.thumbEl.setTop(y);
24680 baseRotateLevel : function()
24682 this.baseRotate = 1;
24685 typeof(this.exif) != 'undefined' &&
24686 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24687 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24689 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24692 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24696 baseScaleLevel : function()
24700 if(this.baseRotate == 6 || this.baseRotate == 8){
24702 width = this.thumbEl.getHeight();
24703 this.baseScale = height / this.imageEl.OriginHeight;
24705 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24706 height = this.thumbEl.getWidth();
24707 this.baseScale = height / this.imageEl.OriginHeight;
24710 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24711 height = this.thumbEl.getWidth();
24712 this.baseScale = height / this.imageEl.OriginHeight;
24714 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24715 width = this.thumbEl.getHeight();
24716 this.baseScale = width / this.imageEl.OriginWidth;
24723 width = this.thumbEl.getWidth();
24724 this.baseScale = width / this.imageEl.OriginWidth;
24726 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24727 height = this.thumbEl.getHeight();
24728 this.baseScale = height / this.imageEl.OriginHeight;
24732 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24734 height = this.thumbEl.getHeight();
24735 this.baseScale = height / this.imageEl.OriginHeight;
24737 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24738 width = this.thumbEl.getWidth();
24739 this.baseScale = width / this.imageEl.OriginWidth;
24747 getScaleLevel : function()
24749 return this.baseScale * Math.pow(1.1, this.scale);
24752 onTouchStart : function(e)
24754 if(!this.canvasLoaded){
24755 this.beforeSelectFile(e);
24759 var touches = e.browserEvent.touches;
24765 if(touches.length == 1){
24766 this.onMouseDown(e);
24770 if(touches.length != 2){
24776 for(var i = 0, finger; finger = touches[i]; i++){
24777 coords.push(finger.pageX, finger.pageY);
24780 var x = Math.pow(coords[0] - coords[2], 2);
24781 var y = Math.pow(coords[1] - coords[3], 2);
24783 this.startDistance = Math.sqrt(x + y);
24785 this.startScale = this.scale;
24787 this.pinching = true;
24788 this.dragable = false;
24792 onTouchMove : function(e)
24794 if(!this.pinching && !this.dragable){
24798 var touches = e.browserEvent.touches;
24805 this.onMouseMove(e);
24811 for(var i = 0, finger; finger = touches[i]; i++){
24812 coords.push(finger.pageX, finger.pageY);
24815 var x = Math.pow(coords[0] - coords[2], 2);
24816 var y = Math.pow(coords[1] - coords[3], 2);
24818 this.endDistance = Math.sqrt(x + y);
24820 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24822 if(!this.zoomable()){
24823 this.scale = this.startScale;
24831 onTouchEnd : function(e)
24833 this.pinching = false;
24834 this.dragable = false;
24838 prepare : function(input)
24843 if(typeof(input) === 'string'){
24844 this.loadCanvas(input);
24848 if(!input.files || !input.files[0] || !this.urlAPI){
24852 this.file = input.files[0];
24853 this.cropType = this.file.type;
24857 if(this.fireEvent('prepare', this, this.file) != false){
24859 var reader = new FileReader();
24861 reader.onload = function (e) {
24862 if (e.target.error) {
24863 Roo.log(e.target.error);
24867 var buffer = e.target.result,
24868 dataView = new DataView(buffer),
24870 maxOffset = dataView.byteLength - 4,
24874 if (dataView.getUint16(0) === 0xffd8) {
24875 while (offset < maxOffset) {
24876 markerBytes = dataView.getUint16(offset);
24878 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24879 markerLength = dataView.getUint16(offset + 2) + 2;
24880 if (offset + markerLength > dataView.byteLength) {
24881 Roo.log('Invalid meta data: Invalid segment size.');
24885 if(markerBytes == 0xffe1){
24886 _this.parseExifData(
24893 offset += markerLength;
24903 var url = _this.urlAPI.createObjectURL(_this.file);
24905 _this.loadCanvas(url);
24910 reader.readAsArrayBuffer(this.file);
24916 parseExifData : function(dataView, offset, length)
24918 var tiffOffset = offset + 10,
24922 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24923 // No Exif data, might be XMP data instead
24927 // Check for the ASCII code for "Exif" (0x45786966):
24928 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24929 // No Exif data, might be XMP data instead
24932 if (tiffOffset + 8 > dataView.byteLength) {
24933 Roo.log('Invalid Exif data: Invalid segment size.');
24936 // Check for the two null bytes:
24937 if (dataView.getUint16(offset + 8) !== 0x0000) {
24938 Roo.log('Invalid Exif data: Missing byte alignment offset.');
24941 // Check the byte alignment:
24942 switch (dataView.getUint16(tiffOffset)) {
24944 littleEndian = true;
24947 littleEndian = false;
24950 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24953 // Check for the TIFF tag marker (0x002A):
24954 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24955 Roo.log('Invalid Exif data: Missing TIFF marker.');
24958 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24959 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24961 this.parseExifTags(
24964 tiffOffset + dirOffset,
24969 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24974 if (dirOffset + 6 > dataView.byteLength) {
24975 Roo.log('Invalid Exif data: Invalid directory offset.');
24978 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24979 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24980 if (dirEndOffset + 4 > dataView.byteLength) {
24981 Roo.log('Invalid Exif data: Invalid directory size.');
24984 for (i = 0; i < tagsNumber; i += 1) {
24988 dirOffset + 2 + 12 * i, // tag offset
24992 // Return the offset to the next directory:
24993 return dataView.getUint32(dirEndOffset, littleEndian);
24996 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
24998 var tag = dataView.getUint16(offset, littleEndian);
25000 this.exif[tag] = this.getExifValue(
25004 dataView.getUint16(offset + 2, littleEndian), // tag type
25005 dataView.getUint32(offset + 4, littleEndian), // tag length
25010 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25012 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25021 Roo.log('Invalid Exif data: Invalid tag type.');
25025 tagSize = tagType.size * length;
25026 // Determine if the value is contained in the dataOffset bytes,
25027 // or if the value at the dataOffset is a pointer to the actual data:
25028 dataOffset = tagSize > 4 ?
25029 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25030 if (dataOffset + tagSize > dataView.byteLength) {
25031 Roo.log('Invalid Exif data: Invalid data offset.');
25034 if (length === 1) {
25035 return tagType.getValue(dataView, dataOffset, littleEndian);
25038 for (i = 0; i < length; i += 1) {
25039 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25042 if (tagType.ascii) {
25044 // Concatenate the chars:
25045 for (i = 0; i < values.length; i += 1) {
25047 // Ignore the terminating NULL byte(s):
25048 if (c === '\u0000') {
25060 Roo.apply(Roo.bootstrap.UploadCropbox, {
25062 'Orientation': 0x0112
25066 1: 0, //'top-left',
25068 3: 180, //'bottom-right',
25069 // 4: 'bottom-left',
25071 6: 90, //'right-top',
25072 // 7: 'right-bottom',
25073 8: 270 //'left-bottom'
25077 // byte, 8-bit unsigned int:
25079 getValue: function (dataView, dataOffset) {
25080 return dataView.getUint8(dataOffset);
25084 // ascii, 8-bit byte:
25086 getValue: function (dataView, dataOffset) {
25087 return String.fromCharCode(dataView.getUint8(dataOffset));
25092 // short, 16 bit int:
25094 getValue: function (dataView, dataOffset, littleEndian) {
25095 return dataView.getUint16(dataOffset, littleEndian);
25099 // long, 32 bit int:
25101 getValue: function (dataView, dataOffset, littleEndian) {
25102 return dataView.getUint32(dataOffset, littleEndian);
25106 // rational = two long values, first is numerator, second is denominator:
25108 getValue: function (dataView, dataOffset, littleEndian) {
25109 return dataView.getUint32(dataOffset, littleEndian) /
25110 dataView.getUint32(dataOffset + 4, littleEndian);
25114 // slong, 32 bit signed int:
25116 getValue: function (dataView, dataOffset, littleEndian) {
25117 return dataView.getInt32(dataOffset, littleEndian);
25121 // srational, two slongs, first is numerator, second is denominator:
25123 getValue: function (dataView, dataOffset, littleEndian) {
25124 return dataView.getInt32(dataOffset, littleEndian) /
25125 dataView.getInt32(dataOffset + 4, littleEndian);
25135 cls : 'btn-group roo-upload-cropbox-rotate-left',
25136 action : 'rotate-left',
25140 cls : 'btn btn-default',
25141 html : '<i class="fa fa-undo"></i>'
25147 cls : 'btn-group roo-upload-cropbox-picture',
25148 action : 'picture',
25152 cls : 'btn btn-default',
25153 html : '<i class="fa fa-picture-o"></i>'
25159 cls : 'btn-group roo-upload-cropbox-rotate-right',
25160 action : 'rotate-right',
25164 cls : 'btn btn-default',
25165 html : '<i class="fa fa-repeat"></i>'
25173 cls : 'btn-group roo-upload-cropbox-rotate-left',
25174 action : 'rotate-left',
25178 cls : 'btn btn-default',
25179 html : '<i class="fa fa-undo"></i>'
25185 cls : 'btn-group roo-upload-cropbox-download',
25186 action : 'download',
25190 cls : 'btn btn-default',
25191 html : '<i class="fa fa-download"></i>'
25197 cls : 'btn-group roo-upload-cropbox-crop',
25202 cls : 'btn btn-default',
25203 html : '<i class="fa fa-crop"></i>'
25209 cls : 'btn-group roo-upload-cropbox-trash',
25214 cls : 'btn btn-default',
25215 html : '<i class="fa fa-trash"></i>'
25221 cls : 'btn-group roo-upload-cropbox-rotate-right',
25222 action : 'rotate-right',
25226 cls : 'btn btn-default',
25227 html : '<i class="fa fa-repeat"></i>'
25240 * @class Roo.bootstrap.DocumentManager
25241 * @extends Roo.bootstrap.Component
25242 * Bootstrap DocumentManager class
25243 * @cfg {String} paramName default 'imageUpload'
25244 * @cfg {String} method default POST
25245 * @cfg {String} url action url
25246 * @cfg {Number} boxes number of boxes default 12
25247 * @cfg {Boolean} multiple multiple upload default true
25248 * @cfg {Number} minWidth default 300
25249 * @cfg {Number} minHeight default 300
25250 * @cfg {Number} thumbSize default 300
25251 * @cfg {String} fieldLabel
25252 * @cfg {Number} labelWidth default 4
25253 * @cfg {String} labelAlign (left|top) default left
25256 * Create a new DocumentManager
25257 * @param {Object} config The config object
25260 Roo.bootstrap.DocumentManager = function(config){
25261 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25266 * Fire when initial the DocumentManager
25267 * @param {Roo.bootstrap.DocumentManager} this
25272 * inspect selected file
25273 * @param {Roo.bootstrap.DocumentManager} this
25274 * @param {File} file
25279 * Fire when xhr load exception
25280 * @param {Roo.bootstrap.DocumentManager} this
25281 * @param {XMLHttpRequest} xhr
25283 "exception" : true,
25286 * prepare the form data
25287 * @param {Roo.bootstrap.DocumentManager} this
25288 * @param {Object} formData
25293 * Fire when remove the file
25294 * @param {Roo.bootstrap.DocumentManager} this
25295 * @param {Object} file
25300 * Fire after refresh the file
25301 * @param {Roo.bootstrap.DocumentManager} this
25306 * Fire after click the image
25307 * @param {Roo.bootstrap.DocumentManager} this
25308 * @param {Object} file
25315 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25326 paramName : 'imageUpload',
25329 labelAlign : 'left',
25331 getAutoCreate : function()
25333 var managerWidget = {
25335 cls : 'roo-document-manager',
25339 cls : 'roo-document-manager-selector',
25344 cls : 'roo-document-manager-uploader',
25348 cls : 'roo-document-manager-upload-btn',
25349 html : '<i class="fa fa-plus"></i>'
25360 cls : 'column col-md-12',
25365 if(this.fieldLabel.length){
25370 cls : 'column col-md-12',
25371 html : this.fieldLabel
25375 cls : 'column col-md-12',
25380 if(this.labelAlign == 'left'){
25384 cls : 'column col-md-' + this.labelWidth,
25385 html : this.fieldLabel
25389 cls : 'column col-md-' + (12 - this.labelWidth),
25399 cls : 'row clearfix',
25407 initEvents : function()
25409 this.managerEl = this.el.select('.roo-document-manager', true).first();
25410 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25412 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25413 this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25414 this.selectorEl.hide();
25417 this.selectorEl.attr('multiple', 'multiple');
25420 this.selectorEl.on('change', this.onSelect, this);
25422 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25423 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25425 this.uploader.on('click', this.onUpload, this);
25429 window.addEventListener("resize", function() { _this.refresh(); } );
25431 this.fireEvent('initial', this);
25434 onUpload : function(e)
25436 e.preventDefault();
25438 this.selectorEl.dom.click();
25442 onSelect : function(e)
25444 e.preventDefault();
25446 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25450 Roo.each(this.selectorEl.dom.files, function(file){
25451 if(this.fireEvent('inspect', this, file) != false){
25452 this.files.push(file);
25460 process : function()
25462 this.selectorEl.dom.value = '';
25464 if(!this.files.length){
25468 if(this.files.length > this.boxes){
25469 this.files = this.files.slice(0, this.boxes);
25472 var xhr = new XMLHttpRequest();
25474 Roo.each(this.files, function(file, index){
25475 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25481 this.managerEl.createChild({
25483 cls : 'roo-document-manager-loading',
25487 tooltip : file.name,
25488 cls : 'roo-document-manager-thumb',
25489 html : '<i class="fa fa-spinner fa-pulse"></i>'
25497 if(this.files.length > this.boxes - 1 ){
25498 this.uploader.hide();
25502 "Accept": "application/json",
25503 "Cache-Control": "no-cache",
25504 "X-Requested-With": "XMLHttpRequest"
25507 xhr.open(this.method, this.url, true);
25509 for (var headerName in headers) {
25510 var headerValue = headers[headerName];
25512 xhr.setRequestHeader(headerName, headerValue);
25518 xhr.onload = function()
25520 _this.xhrOnLoad(xhr);
25523 xhr.onerror = function()
25525 _this.xhrOnError(xhr);
25528 var formData = new FormData();
25530 formData.append('returnHTML', 'NO');
25532 Roo.each(this.files, function(file, index){
25534 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25538 formData.append(this.getParamName(index), file, file.name);
25542 if(this.fireEvent('prepare', this, formData) != false){
25543 xhr.send(formData);
25548 getParamName : function(i)
25550 if(!this.multiple){
25551 return this.paramName;
25554 return this.paramName + "_" + i;
25557 refresh : function()
25559 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25566 Roo.each(this.files, function(file){
25568 if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25577 var previewEl = this.managerEl.createChild({
25579 cls : 'roo-document-manager-preview',
25583 tooltip : file.filename,
25584 cls : 'roo-document-manager-thumb',
25585 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25595 var close = previewEl.select('button.close', true).first();
25597 close.on('click', this.onRemove, this, file);
25599 file.target = previewEl;
25601 var image = previewEl.select('img', true).first();
25603 image.on('click', this.onClick, this, file);
25611 this.files = files;
25613 this.uploader.show();
25615 if(this.files.length > this.boxes - 1){
25616 this.uploader.hide();
25619 Roo.isTouch ? this.closable(false) : this.closable(true);
25621 this.fireEvent('refresh', this);
25624 onRemove : function(e, el, o)
25626 e.preventDefault();
25628 this.fireEvent('remove', this, o);
25632 remove : function(o)
25636 Roo.each(this.files, function(file){
25637 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25646 this.files = files;
25651 onClick : function(e, el, o)
25653 e.preventDefault();
25655 this.fireEvent('click', this, o);
25659 closable : function(closable)
25661 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25663 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25675 xhrOnLoad : function(xhr)
25677 if (xhr.readyState !== 4) {
25679 this.fireEvent('exception', this, xhr);
25683 var response = Roo.decode(xhr.responseText);
25685 if(!response.success){
25687 this.fireEvent('exception', this, xhr);
25693 Roo.each(this.files, function(file, index){
25695 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25699 this.files[index] = response.data[i];
25710 xhrOnError : function()
25712 Roo.log('xhr on error');
25714 var response = Roo.decode(xhr.responseText);
25727 * @class Roo.bootstrap.DocumentViewer
25728 * @extends Roo.bootstrap.Component
25729 * Bootstrap DocumentViewer class
25732 * Create a new DocumentViewer
25733 * @param {Object} config The config object
25736 Roo.bootstrap.DocumentViewer = function(config){
25737 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25742 * Fire after initEvent
25743 * @param {Roo.bootstrap.DocumentViewer} this
25749 * @param {Roo.bootstrap.DocumentViewer} this
25754 * Fire after trash button
25755 * @param {Roo.bootstrap.DocumentViewer} this
25762 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
25764 getAutoCreate : function()
25768 cls : 'roo-document-viewer',
25772 cls : 'roo-document-viewer-body',
25776 cls : 'roo-document-viewer-thumb',
25780 cls : 'roo-document-viewer-image'
25788 cls : 'roo-document-viewer-footer',
25791 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25799 cls : 'btn btn-default roo-document-viewer-trash',
25800 html : '<i class="fa fa-trash"></i>'
25813 initEvents : function()
25816 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25817 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25819 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25820 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25822 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25823 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25825 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25826 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25828 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25829 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25831 this.bodyEl.on('click', this.onClick, this);
25833 this.trashBtn.on('click', this.onTrash, this);
25837 initial : function()
25839 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25842 this.fireEvent('initial', this);
25846 onClick : function(e)
25848 e.preventDefault();
25850 this.fireEvent('click', this);
25853 onTrash : function(e)
25855 e.preventDefault();
25857 this.fireEvent('trash', this);