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
7486 * Create a new Input
7487 * @param {Object} config The config object
7490 Roo.bootstrap.Input = function(config){
7491 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7496 * Fires when this field receives input focus.
7497 * @param {Roo.form.Field} this
7502 * Fires when this field loses input focus.
7503 * @param {Roo.form.Field} this
7508 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7509 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7510 * @param {Roo.form.Field} this
7511 * @param {Roo.EventObject} e The event object
7516 * Fires just before the field blurs if the field value has changed.
7517 * @param {Roo.form.Field} this
7518 * @param {Mixed} newValue The new value
7519 * @param {Mixed} oldValue The original value
7524 * Fires after the field has been marked as invalid.
7525 * @param {Roo.form.Field} this
7526 * @param {String} msg The validation message
7531 * Fires after the field has been validated with no errors.
7532 * @param {Roo.form.Field} this
7537 * Fires after the key up
7538 * @param {Roo.form.Field} this
7539 * @param {Roo.EventObject} e The event Object
7545 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7547 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7548 automatic validation (defaults to "keyup").
7550 validationEvent : "keyup",
7552 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7554 validateOnBlur : true,
7556 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7558 validationDelay : 250,
7560 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7562 focusClass : "x-form-focus", // not needed???
7566 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7568 invalidClass : "has-warning",
7571 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7573 validClass : "has-success",
7576 * @cfg {Boolean} hasFeedback (true|false) default true
7581 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7583 invalidFeedbackClass : "glyphicon-warning-sign",
7586 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7588 validFeedbackClass : "glyphicon-ok",
7591 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7593 selectOnFocus : false,
7596 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7600 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7605 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7607 disableKeyFilter : false,
7610 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7614 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7618 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7620 blankText : "This field is required",
7623 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7627 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7629 maxLength : Number.MAX_VALUE,
7631 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7633 minLengthText : "The minimum length for this field is {0}",
7635 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7637 maxLengthText : "The maximum length for this field is {0}",
7641 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7642 * If available, this function will be called only after the basic validators all return true, and will be passed the
7643 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7647 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7648 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7649 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7653 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7657 autocomplete: false,
7676 formatedValue : false,
7677 forceFeedback : false,
7679 parentLabelAlign : function()
7682 while (parent.parent()) {
7683 parent = parent.parent();
7684 if (typeof(parent.labelAlign) !='undefined') {
7685 return parent.labelAlign;
7692 getAutoCreate : function(){
7694 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7700 if(this.inputType != 'hidden'){
7701 cfg.cls = 'form-group' //input-group
7707 type : this.inputType,
7709 cls : 'form-control',
7710 placeholder : this.placeholder || '',
7711 autocomplete : this.autocomplete || 'new-password'
7716 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7719 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7720 input.maxLength = this.maxLength;
7723 if (this.disabled) {
7724 input.disabled=true;
7727 if (this.readOnly) {
7728 input.readonly=true;
7732 input.name = this.name;
7735 input.cls += ' input-' + this.size;
7738 ['xs','sm','md','lg'].map(function(size){
7739 if (settings[size]) {
7740 cfg.cls += ' col-' + size + '-' + settings[size];
7744 var inputblock = input;
7748 cls: 'glyphicon form-control-feedback'
7751 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7754 cls : 'has-feedback',
7762 if (this.before || this.after) {
7765 cls : 'input-group',
7769 if (this.before && typeof(this.before) == 'string') {
7771 inputblock.cn.push({
7773 cls : 'roo-input-before input-group-addon',
7777 if (this.before && typeof(this.before) == 'object') {
7778 this.before = Roo.factory(this.before);
7779 Roo.log(this.before);
7780 inputblock.cn.push({
7782 cls : 'roo-input-before input-group-' +
7783 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7787 inputblock.cn.push(input);
7789 if (this.after && typeof(this.after) == 'string') {
7790 inputblock.cn.push({
7792 cls : 'roo-input-after input-group-addon',
7796 if (this.after && typeof(this.after) == 'object') {
7797 this.after = Roo.factory(this.after);
7798 Roo.log(this.after);
7799 inputblock.cn.push({
7801 cls : 'roo-input-after input-group-' +
7802 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7806 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7807 inputblock.cls += ' has-feedback';
7808 inputblock.cn.push(feedback);
7812 if (align ==='left' && this.fieldLabel.length) {
7813 Roo.log("left and has label");
7819 cls : 'control-label col-sm-' + this.labelWidth,
7820 html : this.fieldLabel
7824 cls : "col-sm-" + (12 - this.labelWidth),
7831 } else if ( this.fieldLabel.length) {
7837 //cls : 'input-group-addon',
7838 html : this.fieldLabel
7848 Roo.log(" no label && no align");
7857 Roo.log('input-parentType: ' + this.parentType);
7859 if (this.parentType === 'Navbar' && this.parent().bar) {
7860 cfg.cls += ' navbar-form';
7868 * return the real input element.
7870 inputEl: function ()
7872 return this.el.select('input.form-control',true).first();
7875 tooltipEl : function()
7877 return this.inputEl();
7880 setDisabled : function(v)
7882 var i = this.inputEl().dom;
7884 i.removeAttribute('disabled');
7888 i.setAttribute('disabled','true');
7890 initEvents : function()
7893 this.inputEl().on("keydown" , this.fireKey, this);
7894 this.inputEl().on("focus", this.onFocus, this);
7895 this.inputEl().on("blur", this.onBlur, this);
7897 this.inputEl().relayEvent('keyup', this);
7899 // reference to original value for reset
7900 this.originalValue = this.getValue();
7901 //Roo.form.TextField.superclass.initEvents.call(this);
7902 if(this.validationEvent == 'keyup'){
7903 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7904 this.inputEl().on('keyup', this.filterValidation, this);
7906 else if(this.validationEvent !== false){
7907 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7910 if(this.selectOnFocus){
7911 this.on("focus", this.preFocus, this);
7914 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7915 this.inputEl().on("keypress", this.filterKeys, this);
7918 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7919 this.el.on("click", this.autoSize, this);
7922 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7923 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7926 if (typeof(this.before) == 'object') {
7927 this.before.render(this.el.select('.roo-input-before',true).first());
7929 if (typeof(this.after) == 'object') {
7930 this.after.render(this.el.select('.roo-input-after',true).first());
7935 filterValidation : function(e){
7936 if(!e.isNavKeyPress()){
7937 this.validationTask.delay(this.validationDelay);
7941 * Validates the field value
7942 * @return {Boolean} True if the value is valid, else false
7944 validate : function(){
7945 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7946 if(this.disabled || this.validateValue(this.getRawValue())){
7957 * Validates a value according to the field's validation rules and marks the field as invalid
7958 * if the validation fails
7959 * @param {Mixed} value The value to validate
7960 * @return {Boolean} True if the value is valid, else false
7962 validateValue : function(value){
7963 if(value.length < 1) { // if it's blank
7964 if(this.allowBlank){
7970 if(value.length < this.minLength){
7973 if(value.length > this.maxLength){
7977 var vt = Roo.form.VTypes;
7978 if(!vt[this.vtype](value, this)){
7982 if(typeof this.validator == "function"){
7983 var msg = this.validator(value);
7989 if(this.regex && !this.regex.test(value)){
7999 fireKey : function(e){
8000 //Roo.log('field ' + e.getKey());
8001 if(e.isNavKeyPress()){
8002 this.fireEvent("specialkey", this, e);
8005 focus : function (selectText){
8007 this.inputEl().focus();
8008 if(selectText === true){
8009 this.inputEl().dom.select();
8015 onFocus : function(){
8016 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8017 // this.el.addClass(this.focusClass);
8020 this.hasFocus = true;
8021 this.startValue = this.getValue();
8022 this.fireEvent("focus", this);
8026 beforeBlur : Roo.emptyFn,
8030 onBlur : function(){
8032 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8033 //this.el.removeClass(this.focusClass);
8035 this.hasFocus = false;
8036 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8039 var v = this.getValue();
8040 if(String(v) !== String(this.startValue)){
8041 this.fireEvent('change', this, v, this.startValue);
8043 this.fireEvent("blur", this);
8047 * Resets the current field value to the originally loaded value and clears any validation messages
8050 this.setValue(this.originalValue);
8054 * Returns the name of the field
8055 * @return {Mixed} name The name field
8057 getName: function(){
8061 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8062 * @return {Mixed} value The field value
8064 getValue : function(){
8066 var v = this.inputEl().getValue();
8071 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8072 * @return {Mixed} value The field value
8074 getRawValue : function(){
8075 var v = this.inputEl().getValue();
8081 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8082 * @param {Mixed} value The value to set
8084 setRawValue : function(v){
8085 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8088 selectText : function(start, end){
8089 var v = this.getRawValue();
8091 start = start === undefined ? 0 : start;
8092 end = end === undefined ? v.length : end;
8093 var d = this.inputEl().dom;
8094 if(d.setSelectionRange){
8095 d.setSelectionRange(start, end);
8096 }else if(d.createTextRange){
8097 var range = d.createTextRange();
8098 range.moveStart("character", start);
8099 range.moveEnd("character", v.length-end);
8106 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8107 * @param {Mixed} value The value to set
8109 setValue : function(v){
8112 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8118 processValue : function(value){
8119 if(this.stripCharsRe){
8120 var newValue = value.replace(this.stripCharsRe, '');
8121 if(newValue !== value){
8122 this.setRawValue(newValue);
8129 preFocus : function(){
8131 if(this.selectOnFocus){
8132 this.inputEl().dom.select();
8135 filterKeys : function(e){
8137 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8140 var c = e.getCharCode(), cc = String.fromCharCode(c);
8141 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8144 if(!this.maskRe.test(cc)){
8149 * Clear any invalid styles/messages for this field
8151 clearInvalid : function(){
8153 if(!this.el || this.preventMark){ // not rendered
8156 this.el.removeClass(this.invalidClass);
8158 this.fireEvent('valid', this);
8162 * Mark this field as valid
8164 markValid : function(){
8165 if(!this.el || this.preventMark){ // not rendered
8169 this.el.removeClass([this.invalidClass, this.validClass]);
8171 if(this.disabled || this.allowBlank){
8175 this.el.addClass(this.validClass);
8177 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8179 var feedback = this.el.select('.form-control-feedback', true).first();
8182 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8183 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8188 this.fireEvent('valid', this);
8192 * Mark this field as invalid
8193 * @param {String} msg The validation message
8195 markInvalid : function(msg){
8196 if(!this.el || this.preventMark){ // not rendered
8200 this.el.removeClass([this.invalidClass, this.validClass]);
8202 if(this.disabled || this.allowBlank){
8206 this.el.addClass(this.invalidClass);
8208 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8210 var feedback = this.el.select('.form-control-feedback', true).first();
8213 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8215 if(this.getValue().length || this.forceFeedback){
8216 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8223 this.fireEvent('invalid', this, msg);
8226 SafariOnKeyDown : function(event)
8228 // this is a workaround for a password hang bug on chrome/ webkit.
8230 var isSelectAll = false;
8232 if(this.inputEl().dom.selectionEnd > 0){
8233 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8235 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8236 event.preventDefault();
8241 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8243 event.preventDefault();
8244 // this is very hacky as keydown always get's upper case.
8246 var cc = String.fromCharCode(event.getCharCode());
8247 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8251 adjustWidth : function(tag, w){
8252 tag = tag.toLowerCase();
8253 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8254 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8258 if(tag == 'textarea'){
8261 }else if(Roo.isOpera){
8265 if(tag == 'textarea'){
8284 * @class Roo.bootstrap.TextArea
8285 * @extends Roo.bootstrap.Input
8286 * Bootstrap TextArea class
8287 * @cfg {Number} cols Specifies the visible width of a text area
8288 * @cfg {Number} rows Specifies the visible number of lines in a text area
8289 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8290 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8291 * @cfg {string} html text
8294 * Create a new TextArea
8295 * @param {Object} config The config object
8298 Roo.bootstrap.TextArea = function(config){
8299 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8303 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8313 getAutoCreate : function(){
8315 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8326 value : this.value || '',
8327 html: this.html || '',
8328 cls : 'form-control',
8329 placeholder : this.placeholder || ''
8333 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8334 input.maxLength = this.maxLength;
8338 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8342 input.cols = this.cols;
8345 if (this.readOnly) {
8346 input.readonly = true;
8350 input.name = this.name;
8354 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8358 ['xs','sm','md','lg'].map(function(size){
8359 if (settings[size]) {
8360 cfg.cls += ' col-' + size + '-' + settings[size];
8364 var inputblock = input;
8366 if(this.hasFeedback && !this.allowBlank){
8370 cls: 'glyphicon form-control-feedback'
8374 cls : 'has-feedback',
8383 if (this.before || this.after) {
8386 cls : 'input-group',
8390 inputblock.cn.push({
8392 cls : 'input-group-addon',
8397 inputblock.cn.push(input);
8399 if(this.hasFeedback && !this.allowBlank){
8400 inputblock.cls += ' has-feedback';
8401 inputblock.cn.push(feedback);
8405 inputblock.cn.push({
8407 cls : 'input-group-addon',
8414 if (align ==='left' && this.fieldLabel.length) {
8415 Roo.log("left and has label");
8421 cls : 'control-label col-sm-' + this.labelWidth,
8422 html : this.fieldLabel
8426 cls : "col-sm-" + (12 - this.labelWidth),
8433 } else if ( this.fieldLabel.length) {
8439 //cls : 'input-group-addon',
8440 html : this.fieldLabel
8450 Roo.log(" no label && no align");
8460 if (this.disabled) {
8461 input.disabled=true;
8468 * return the real textarea element.
8470 inputEl: function ()
8472 return this.el.select('textarea.form-control',true).first();
8480 * trigger field - base class for combo..
8485 * @class Roo.bootstrap.TriggerField
8486 * @extends Roo.bootstrap.Input
8487 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8488 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8489 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8490 * for which you can provide a custom implementation. For example:
8492 var trigger = new Roo.bootstrap.TriggerField();
8493 trigger.onTriggerClick = myTriggerFn;
8494 trigger.applyTo('my-field');
8497 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8498 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8499 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8500 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8501 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8504 * Create a new TriggerField.
8505 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8506 * to the base TextField)
8508 Roo.bootstrap.TriggerField = function(config){
8509 this.mimicing = false;
8510 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8513 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8515 * @cfg {String} triggerClass A CSS class to apply to the trigger
8518 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8523 * @cfg {Boolean} removable (true|false) special filter default false
8527 /** @cfg {Boolean} grow @hide */
8528 /** @cfg {Number} growMin @hide */
8529 /** @cfg {Number} growMax @hide */
8535 autoSize: Roo.emptyFn,
8542 actionMode : 'wrap',
8547 getAutoCreate : function(){
8549 var align = this.labelAlign || this.parentLabelAlign();
8554 cls: 'form-group' //input-group
8561 type : this.inputType,
8562 cls : 'form-control',
8563 autocomplete: 'new-password',
8564 placeholder : this.placeholder || ''
8568 input.name = this.name;
8571 input.cls += ' input-' + this.size;
8574 if (this.disabled) {
8575 input.disabled=true;
8578 var inputblock = input;
8580 if(this.hasFeedback && !this.allowBlank){
8584 cls: 'glyphicon form-control-feedback'
8587 if(this.removable && !this.editable && !this.tickable){
8589 cls : 'has-feedback',
8595 cls : 'roo-combo-removable-btn close'
8602 cls : 'has-feedback',
8611 if(this.removable && !this.editable && !this.tickable){
8613 cls : 'roo-removable',
8619 cls : 'roo-combo-removable-btn close'
8626 if (this.before || this.after) {
8629 cls : 'input-group',
8633 inputblock.cn.push({
8635 cls : 'input-group-addon',
8640 inputblock.cn.push(input);
8642 if(this.hasFeedback && !this.allowBlank){
8643 inputblock.cls += ' has-feedback';
8644 inputblock.cn.push(feedback);
8648 inputblock.cn.push({
8650 cls : 'input-group-addon',
8663 cls: 'form-hidden-field'
8671 Roo.log('multiple');
8679 cls: 'form-hidden-field'
8683 cls: 'select2-choices',
8687 cls: 'select2-search-field',
8700 cls: 'select2-container input-group',
8705 // cls: 'typeahead typeahead-long dropdown-menu',
8706 // style: 'display:none'
8711 if(!this.multiple && this.showToggleBtn){
8717 if (this.caret != false) {
8720 cls: 'fa fa-' + this.caret
8727 cls : 'input-group-addon btn dropdown-toggle',
8732 cls: 'combobox-clear',
8746 combobox.cls += ' select2-container-multi';
8749 if (align ==='left' && this.fieldLabel.length) {
8751 Roo.log("left and has label");
8757 cls : 'control-label col-sm-' + this.labelWidth,
8758 html : this.fieldLabel
8762 cls : "col-sm-" + (12 - this.labelWidth),
8769 } else if ( this.fieldLabel.length) {
8775 //cls : 'input-group-addon',
8776 html : this.fieldLabel
8786 Roo.log(" no label && no align");
8793 ['xs','sm','md','lg'].map(function(size){
8794 if (settings[size]) {
8795 cfg.cls += ' col-' + size + '-' + settings[size];
8806 onResize : function(w, h){
8807 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8808 // if(typeof w == 'number'){
8809 // var x = w - this.trigger.getWidth();
8810 // this.inputEl().setWidth(this.adjustWidth('input', x));
8811 // this.trigger.setStyle('left', x+'px');
8816 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8819 getResizeEl : function(){
8820 return this.inputEl();
8824 getPositionEl : function(){
8825 return this.inputEl();
8829 alignErrorIcon : function(){
8830 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8834 initEvents : function(){
8838 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8839 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8840 if(!this.multiple && this.showToggleBtn){
8841 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8842 if(this.hideTrigger){
8843 this.trigger.setDisplayed(false);
8845 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8849 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8852 if(this.removable && !this.editable && !this.tickable){
8853 var close = this.closeTriggerEl();
8856 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8857 close.on('click', this.removeBtnClick, this, close);
8861 //this.trigger.addClassOnOver('x-form-trigger-over');
8862 //this.trigger.addClassOnClick('x-form-trigger-click');
8865 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8869 closeTriggerEl : function()
8871 var close = this.el.select('.roo-combo-removable-btn', true).first();
8872 return close ? close : false;
8875 removeBtnClick : function(e, h, el)
8879 if(this.fireEvent("remove", this) !== false){
8884 createList : function()
8886 this.list = Roo.get(document.body).createChild({
8888 cls: 'typeahead typeahead-long dropdown-menu',
8889 style: 'display:none'
8892 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8897 initTrigger : function(){
8902 onDestroy : function(){
8904 this.trigger.removeAllListeners();
8905 // this.trigger.remove();
8908 // this.wrap.remove();
8910 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8914 onFocus : function(){
8915 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8918 this.wrap.addClass('x-trigger-wrap-focus');
8919 this.mimicing = true;
8920 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8921 if(this.monitorTab){
8922 this.el.on("keydown", this.checkTab, this);
8929 checkTab : function(e){
8930 if(e.getKey() == e.TAB){
8936 onBlur : function(){
8941 mimicBlur : function(e, t){
8943 if(!this.wrap.contains(t) && this.validateBlur()){
8950 triggerBlur : function(){
8951 this.mimicing = false;
8952 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8953 if(this.monitorTab){
8954 this.el.un("keydown", this.checkTab, this);
8956 //this.wrap.removeClass('x-trigger-wrap-focus');
8957 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8961 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8962 validateBlur : function(e, t){
8967 onDisable : function(){
8968 this.inputEl().dom.disabled = true;
8969 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8971 // this.wrap.addClass('x-item-disabled');
8976 onEnable : function(){
8977 this.inputEl().dom.disabled = false;
8978 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8980 // this.el.removeClass('x-item-disabled');
8985 onShow : function(){
8986 var ae = this.getActionEl();
8989 ae.dom.style.display = '';
8990 ae.dom.style.visibility = 'visible';
8996 onHide : function(){
8997 var ae = this.getActionEl();
8998 ae.dom.style.display = 'none';
9002 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9003 * by an implementing function.
9005 * @param {EventObject} e
9007 onTriggerClick : Roo.emptyFn
9011 * Ext JS Library 1.1.1
9012 * Copyright(c) 2006-2007, Ext JS, LLC.
9014 * Originally Released Under LGPL - original licence link has changed is not relivant.
9017 * <script type="text/javascript">
9022 * @class Roo.data.SortTypes
9024 * Defines the default sorting (casting?) comparison functions used when sorting data.
9026 Roo.data.SortTypes = {
9028 * Default sort that does nothing
9029 * @param {Mixed} s The value being converted
9030 * @return {Mixed} The comparison value
9037 * The regular expression used to strip tags
9041 stripTagsRE : /<\/?[^>]+>/gi,
9044 * Strips all HTML tags to sort on text only
9045 * @param {Mixed} s The value being converted
9046 * @return {String} The comparison value
9048 asText : function(s){
9049 return String(s).replace(this.stripTagsRE, "");
9053 * Strips all HTML tags to sort on text only - Case insensitive
9054 * @param {Mixed} s The value being converted
9055 * @return {String} The comparison value
9057 asUCText : function(s){
9058 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9062 * Case insensitive string
9063 * @param {Mixed} s The value being converted
9064 * @return {String} The comparison value
9066 asUCString : function(s) {
9067 return String(s).toUpperCase();
9072 * @param {Mixed} s The value being converted
9073 * @return {Number} The comparison value
9075 asDate : function(s) {
9079 if(s instanceof Date){
9082 return Date.parse(String(s));
9087 * @param {Mixed} s The value being converted
9088 * @return {Float} The comparison value
9090 asFloat : function(s) {
9091 var val = parseFloat(String(s).replace(/,/g, ""));
9092 if(isNaN(val)) val = 0;
9098 * @param {Mixed} s The value being converted
9099 * @return {Number} The comparison value
9101 asInt : function(s) {
9102 var val = parseInt(String(s).replace(/,/g, ""));
9103 if(isNaN(val)) val = 0;
9108 * Ext JS Library 1.1.1
9109 * Copyright(c) 2006-2007, Ext JS, LLC.
9111 * Originally Released Under LGPL - original licence link has changed is not relivant.
9114 * <script type="text/javascript">
9118 * @class Roo.data.Record
9119 * Instances of this class encapsulate both record <em>definition</em> information, and record
9120 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9121 * to access Records cached in an {@link Roo.data.Store} object.<br>
9123 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9124 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9127 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9129 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9130 * {@link #create}. The parameters are the same.
9131 * @param {Array} data An associative Array of data values keyed by the field name.
9132 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9133 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9134 * not specified an integer id is generated.
9136 Roo.data.Record = function(data, id){
9137 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9142 * Generate a constructor for a specific record layout.
9143 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9144 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9145 * Each field definition object may contain the following properties: <ul>
9146 * <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,
9147 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9148 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9149 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9150 * is being used, then this is a string containing the javascript expression to reference the data relative to
9151 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9152 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9153 * this may be omitted.</p></li>
9154 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9155 * <ul><li>auto (Default, implies no conversion)</li>
9160 * <li>date</li></ul></p></li>
9161 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9162 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9163 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9164 * by the Reader into an object that will be stored in the Record. It is passed the
9165 * following parameters:<ul>
9166 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9168 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9170 * <br>usage:<br><pre><code>
9171 var TopicRecord = Roo.data.Record.create(
9172 {name: 'title', mapping: 'topic_title'},
9173 {name: 'author', mapping: 'username'},
9174 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9175 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9176 {name: 'lastPoster', mapping: 'user2'},
9177 {name: 'excerpt', mapping: 'post_text'}
9180 var myNewRecord = new TopicRecord({
9181 title: 'Do my job please',
9184 lastPost: new Date(),
9185 lastPoster: 'Animal',
9186 excerpt: 'No way dude!'
9188 myStore.add(myNewRecord);
9193 Roo.data.Record.create = function(o){
9195 f.superclass.constructor.apply(this, arguments);
9197 Roo.extend(f, Roo.data.Record);
9198 var p = f.prototype;
9199 p.fields = new Roo.util.MixedCollection(false, function(field){
9202 for(var i = 0, len = o.length; i < len; i++){
9203 p.fields.add(new Roo.data.Field(o[i]));
9205 f.getField = function(name){
9206 return p.fields.get(name);
9211 Roo.data.Record.AUTO_ID = 1000;
9212 Roo.data.Record.EDIT = 'edit';
9213 Roo.data.Record.REJECT = 'reject';
9214 Roo.data.Record.COMMIT = 'commit';
9216 Roo.data.Record.prototype = {
9218 * Readonly flag - true if this record has been modified.
9227 join : function(store){
9232 * Set the named field to the specified value.
9233 * @param {String} name The name of the field to set.
9234 * @param {Object} value The value to set the field to.
9236 set : function(name, value){
9237 if(this.data[name] == value){
9244 if(typeof this.modified[name] == 'undefined'){
9245 this.modified[name] = this.data[name];
9247 this.data[name] = value;
9248 if(!this.editing && this.store){
9249 this.store.afterEdit(this);
9254 * Get the value of the named field.
9255 * @param {String} name The name of the field to get the value of.
9256 * @return {Object} The value of the field.
9258 get : function(name){
9259 return this.data[name];
9263 beginEdit : function(){
9264 this.editing = true;
9269 cancelEdit : function(){
9270 this.editing = false;
9271 delete this.modified;
9275 endEdit : function(){
9276 this.editing = false;
9277 if(this.dirty && this.store){
9278 this.store.afterEdit(this);
9283 * Usually called by the {@link Roo.data.Store} which owns the Record.
9284 * Rejects all changes made to the Record since either creation, or the last commit operation.
9285 * Modified fields are reverted to their original values.
9287 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9288 * of reject operations.
9290 reject : function(){
9291 var m = this.modified;
9293 if(typeof m[n] != "function"){
9294 this.data[n] = m[n];
9298 delete this.modified;
9299 this.editing = false;
9301 this.store.afterReject(this);
9306 * Usually called by the {@link Roo.data.Store} which owns the Record.
9307 * Commits all changes made to the Record since either creation, or the last commit operation.
9309 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9310 * of commit operations.
9312 commit : function(){
9314 delete this.modified;
9315 this.editing = false;
9317 this.store.afterCommit(this);
9322 hasError : function(){
9323 return this.error != null;
9327 clearError : function(){
9332 * Creates a copy of this record.
9333 * @param {String} id (optional) A new record id if you don't want to use this record's id
9336 copy : function(newId) {
9337 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9341 * Ext JS Library 1.1.1
9342 * Copyright(c) 2006-2007, Ext JS, LLC.
9344 * Originally Released Under LGPL - original licence link has changed is not relivant.
9347 * <script type="text/javascript">
9353 * @class Roo.data.Store
9354 * @extends Roo.util.Observable
9355 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9356 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9358 * 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
9359 * has no knowledge of the format of the data returned by the Proxy.<br>
9361 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9362 * instances from the data object. These records are cached and made available through accessor functions.
9364 * Creates a new Store.
9365 * @param {Object} config A config object containing the objects needed for the Store to access data,
9366 * and read the data into Records.
9368 Roo.data.Store = function(config){
9369 this.data = new Roo.util.MixedCollection(false);
9370 this.data.getKey = function(o){
9373 this.baseParams = {};
9380 "multisort" : "_multisort"
9383 if(config && config.data){
9384 this.inlineData = config.data;
9388 Roo.apply(this, config);
9390 if(this.reader){ // reader passed
9391 this.reader = Roo.factory(this.reader, Roo.data);
9392 this.reader.xmodule = this.xmodule || false;
9393 if(!this.recordType){
9394 this.recordType = this.reader.recordType;
9396 if(this.reader.onMetaChange){
9397 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9401 if(this.recordType){
9402 this.fields = this.recordType.prototype.fields;
9408 * @event datachanged
9409 * Fires when the data cache has changed, and a widget which is using this Store
9410 * as a Record cache should refresh its view.
9411 * @param {Store} this
9416 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9417 * @param {Store} this
9418 * @param {Object} meta The JSON metadata
9423 * Fires when Records have been added to the Store
9424 * @param {Store} this
9425 * @param {Roo.data.Record[]} records The array of Records added
9426 * @param {Number} index The index at which the record(s) were added
9431 * Fires when a Record has been removed from the Store
9432 * @param {Store} this
9433 * @param {Roo.data.Record} record The Record that was removed
9434 * @param {Number} index The index at which the record was removed
9439 * Fires when a Record has been updated
9440 * @param {Store} this
9441 * @param {Roo.data.Record} record The Record that was updated
9442 * @param {String} operation The update operation being performed. Value may be one of:
9444 Roo.data.Record.EDIT
9445 Roo.data.Record.REJECT
9446 Roo.data.Record.COMMIT
9452 * Fires when the data cache has been cleared.
9453 * @param {Store} this
9458 * Fires before a request is made for a new data object. If the beforeload handler returns false
9459 * the load action will be canceled.
9460 * @param {Store} this
9461 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9465 * @event beforeloadadd
9466 * Fires after a new set of Records has been loaded.
9467 * @param {Store} this
9468 * @param {Roo.data.Record[]} records The Records that were loaded
9469 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9471 beforeloadadd : true,
9474 * Fires after a new set of Records has been loaded, before they are added to the store.
9475 * @param {Store} this
9476 * @param {Roo.data.Record[]} records The Records that were loaded
9477 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9478 * @params {Object} return from reader
9482 * @event loadexception
9483 * Fires if an exception occurs in the Proxy during loading.
9484 * Called with the signature of the Proxy's "loadexception" event.
9485 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9488 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9489 * @param {Object} load options
9490 * @param {Object} jsonData from your request (normally this contains the Exception)
9492 loadexception : true
9496 this.proxy = Roo.factory(this.proxy, Roo.data);
9497 this.proxy.xmodule = this.xmodule || false;
9498 this.relayEvents(this.proxy, ["loadexception"]);
9500 this.sortToggle = {};
9501 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9503 Roo.data.Store.superclass.constructor.call(this);
9505 if(this.inlineData){
9506 this.loadData(this.inlineData);
9507 delete this.inlineData;
9511 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9513 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9514 * without a remote query - used by combo/forms at present.
9518 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9521 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9524 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9525 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9528 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9529 * on any HTTP request
9532 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9535 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9539 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9540 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9545 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9546 * loaded or when a record is removed. (defaults to false).
9548 pruneModifiedRecords : false,
9554 * Add Records to the Store and fires the add event.
9555 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9557 add : function(records){
9558 records = [].concat(records);
9559 for(var i = 0, len = records.length; i < len; i++){
9560 records[i].join(this);
9562 var index = this.data.length;
9563 this.data.addAll(records);
9564 this.fireEvent("add", this, records, index);
9568 * Remove a Record from the Store and fires the remove event.
9569 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9571 remove : function(record){
9572 var index = this.data.indexOf(record);
9573 this.data.removeAt(index);
9574 if(this.pruneModifiedRecords){
9575 this.modified.remove(record);
9577 this.fireEvent("remove", this, record, index);
9581 * Remove all Records from the Store and fires the clear event.
9583 removeAll : function(){
9585 if(this.pruneModifiedRecords){
9588 this.fireEvent("clear", this);
9592 * Inserts Records to the Store at the given index and fires the add event.
9593 * @param {Number} index The start index at which to insert the passed Records.
9594 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9596 insert : function(index, records){
9597 records = [].concat(records);
9598 for(var i = 0, len = records.length; i < len; i++){
9599 this.data.insert(index, records[i]);
9600 records[i].join(this);
9602 this.fireEvent("add", this, records, index);
9606 * Get the index within the cache of the passed Record.
9607 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9608 * @return {Number} The index of the passed Record. Returns -1 if not found.
9610 indexOf : function(record){
9611 return this.data.indexOf(record);
9615 * Get the index within the cache of the Record with the passed id.
9616 * @param {String} id The id of the Record to find.
9617 * @return {Number} The index of the Record. Returns -1 if not found.
9619 indexOfId : function(id){
9620 return this.data.indexOfKey(id);
9624 * Get the Record with the specified id.
9625 * @param {String} id The id of the Record to find.
9626 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9628 getById : function(id){
9629 return this.data.key(id);
9633 * Get the Record at the specified index.
9634 * @param {Number} index The index of the Record to find.
9635 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9637 getAt : function(index){
9638 return this.data.itemAt(index);
9642 * Returns a range of Records between specified indices.
9643 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9644 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9645 * @return {Roo.data.Record[]} An array of Records
9647 getRange : function(start, end){
9648 return this.data.getRange(start, end);
9652 storeOptions : function(o){
9653 o = Roo.apply({}, o);
9656 this.lastOptions = o;
9660 * Loads the Record cache from the configured Proxy using the configured Reader.
9662 * If using remote paging, then the first load call must specify the <em>start</em>
9663 * and <em>limit</em> properties in the options.params property to establish the initial
9664 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9666 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9667 * and this call will return before the new data has been loaded. Perform any post-processing
9668 * in a callback function, or in a "load" event handler.</strong>
9670 * @param {Object} options An object containing properties which control loading options:<ul>
9671 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9672 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9673 * passed the following arguments:<ul>
9674 * <li>r : Roo.data.Record[]</li>
9675 * <li>options: Options object from the load call</li>
9676 * <li>success: Boolean success indicator</li></ul></li>
9677 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9678 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9681 load : function(options){
9682 options = options || {};
9683 if(this.fireEvent("beforeload", this, options) !== false){
9684 this.storeOptions(options);
9685 var p = Roo.apply(options.params || {}, this.baseParams);
9686 // if meta was not loaded from remote source.. try requesting it.
9687 if (!this.reader.metaFromRemote) {
9690 if(this.sortInfo && this.remoteSort){
9691 var pn = this.paramNames;
9692 p[pn["sort"]] = this.sortInfo.field;
9693 p[pn["dir"]] = this.sortInfo.direction;
9695 if (this.multiSort) {
9696 var pn = this.paramNames;
9697 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9700 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9705 * Reloads the Record cache from the configured Proxy using the configured Reader and
9706 * the options from the last load operation performed.
9707 * @param {Object} options (optional) An object containing properties which may override the options
9708 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9709 * the most recently used options are reused).
9711 reload : function(options){
9712 this.load(Roo.applyIf(options||{}, this.lastOptions));
9716 // Called as a callback by the Reader during a load operation.
9717 loadRecords : function(o, options, success){
9718 if(!o || success === false){
9719 if(success !== false){
9720 this.fireEvent("load", this, [], options, o);
9722 if(options.callback){
9723 options.callback.call(options.scope || this, [], options, false);
9727 // if data returned failure - throw an exception.
9728 if (o.success === false) {
9729 // show a message if no listener is registered.
9730 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9731 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9733 // loadmask wil be hooked into this..
9734 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9737 var r = o.records, t = o.totalRecords || r.length;
9739 this.fireEvent("beforeloadadd", this, r, options, o);
9741 if(!options || options.add !== true){
9742 if(this.pruneModifiedRecords){
9745 for(var i = 0, len = r.length; i < len; i++){
9749 this.data = this.snapshot;
9750 delete this.snapshot;
9753 this.data.addAll(r);
9754 this.totalLength = t;
9756 this.fireEvent("datachanged", this);
9758 this.totalLength = Math.max(t, this.data.length+r.length);
9761 this.fireEvent("load", this, r, options, o);
9762 if(options.callback){
9763 options.callback.call(options.scope || this, r, options, true);
9769 * Loads data from a passed data block. A Reader which understands the format of the data
9770 * must have been configured in the constructor.
9771 * @param {Object} data The data block from which to read the Records. The format of the data expected
9772 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9773 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9775 loadData : function(o, append){
9776 var r = this.reader.readRecords(o);
9777 this.loadRecords(r, {add: append}, true);
9781 * Gets the number of cached records.
9783 * <em>If using paging, this may not be the total size of the dataset. If the data object
9784 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9785 * the data set size</em>
9787 getCount : function(){
9788 return this.data.length || 0;
9792 * Gets the total number of records in the dataset as returned by the server.
9794 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9795 * the dataset size</em>
9797 getTotalCount : function(){
9798 return this.totalLength || 0;
9802 * Returns the sort state of the Store as an object with two properties:
9804 field {String} The name of the field by which the Records are sorted
9805 direction {String} The sort order, "ASC" or "DESC"
9808 getSortState : function(){
9809 return this.sortInfo;
9813 applySort : function(){
9814 if(this.sortInfo && !this.remoteSort){
9815 var s = this.sortInfo, f = s.field;
9816 var st = this.fields.get(f).sortType;
9817 var fn = function(r1, r2){
9818 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9819 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9821 this.data.sort(s.direction, fn);
9822 if(this.snapshot && this.snapshot != this.data){
9823 this.snapshot.sort(s.direction, fn);
9829 * Sets the default sort column and order to be used by the next load operation.
9830 * @param {String} fieldName The name of the field to sort by.
9831 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9833 setDefaultSort : function(field, dir){
9834 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9839 * If remote sorting is used, the sort is performed on the server, and the cache is
9840 * reloaded. If local sorting is used, the cache is sorted internally.
9841 * @param {String} fieldName The name of the field to sort by.
9842 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9844 sort : function(fieldName, dir){
9845 var f = this.fields.get(fieldName);
9847 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9849 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9850 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9855 this.sortToggle[f.name] = dir;
9856 this.sortInfo = {field: f.name, direction: dir};
9857 if(!this.remoteSort){
9859 this.fireEvent("datachanged", this);
9861 this.load(this.lastOptions);
9866 * Calls the specified function for each of the Records in the cache.
9867 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9868 * Returning <em>false</em> aborts and exits the iteration.
9869 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9871 each : function(fn, scope){
9872 this.data.each(fn, scope);
9876 * Gets all records modified since the last commit. Modified records are persisted across load operations
9877 * (e.g., during paging).
9878 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9880 getModifiedRecords : function(){
9881 return this.modified;
9885 createFilterFn : function(property, value, anyMatch){
9886 if(!value.exec){ // not a regex
9887 value = String(value);
9888 if(value.length == 0){
9891 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9894 return value.test(r.data[property]);
9899 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9900 * @param {String} property A field on your records
9901 * @param {Number} start The record index to start at (defaults to 0)
9902 * @param {Number} end The last record index to include (defaults to length - 1)
9903 * @return {Number} The sum
9905 sum : function(property, start, end){
9906 var rs = this.data.items, v = 0;
9908 end = (end || end === 0) ? end : rs.length-1;
9910 for(var i = start; i <= end; i++){
9911 v += (rs[i].data[property] || 0);
9917 * Filter the records by a specified property.
9918 * @param {String} field A field on your records
9919 * @param {String/RegExp} value Either a string that the field
9920 * should start with or a RegExp to test against the field
9921 * @param {Boolean} anyMatch True to match any part not just the beginning
9923 filter : function(property, value, anyMatch){
9924 var fn = this.createFilterFn(property, value, anyMatch);
9925 return fn ? this.filterBy(fn) : this.clearFilter();
9929 * Filter by a function. The specified function will be called with each
9930 * record in this data source. If the function returns true the record is included,
9931 * otherwise it is filtered.
9932 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9933 * @param {Object} scope (optional) The scope of the function (defaults to this)
9935 filterBy : function(fn, scope){
9936 this.snapshot = this.snapshot || this.data;
9937 this.data = this.queryBy(fn, scope||this);
9938 this.fireEvent("datachanged", this);
9942 * Query the records by a specified property.
9943 * @param {String} field A field on your records
9944 * @param {String/RegExp} value Either a string that the field
9945 * should start with or a RegExp to test against the field
9946 * @param {Boolean} anyMatch True to match any part not just the beginning
9947 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9949 query : function(property, value, anyMatch){
9950 var fn = this.createFilterFn(property, value, anyMatch);
9951 return fn ? this.queryBy(fn) : this.data.clone();
9955 * Query by a function. The specified function will be called with each
9956 * record in this data source. If the function returns true the record is included
9958 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9959 * @param {Object} scope (optional) The scope of the function (defaults to this)
9960 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9962 queryBy : function(fn, scope){
9963 var data = this.snapshot || this.data;
9964 return data.filterBy(fn, scope||this);
9968 * Collects unique values for a particular dataIndex from this store.
9969 * @param {String} dataIndex The property to collect
9970 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9971 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9972 * @return {Array} An array of the unique values
9974 collect : function(dataIndex, allowNull, bypassFilter){
9975 var d = (bypassFilter === true && this.snapshot) ?
9976 this.snapshot.items : this.data.items;
9977 var v, sv, r = [], l = {};
9978 for(var i = 0, len = d.length; i < len; i++){
9979 v = d[i].data[dataIndex];
9981 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9990 * Revert to a view of the Record cache with no filtering applied.
9991 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9993 clearFilter : function(suppressEvent){
9994 if(this.snapshot && this.snapshot != this.data){
9995 this.data = this.snapshot;
9996 delete this.snapshot;
9997 if(suppressEvent !== true){
9998 this.fireEvent("datachanged", this);
10004 afterEdit : function(record){
10005 if(this.modified.indexOf(record) == -1){
10006 this.modified.push(record);
10008 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10012 afterReject : function(record){
10013 this.modified.remove(record);
10014 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10018 afterCommit : function(record){
10019 this.modified.remove(record);
10020 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10024 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10025 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10027 commitChanges : function(){
10028 var m = this.modified.slice(0);
10029 this.modified = [];
10030 for(var i = 0, len = m.length; i < len; i++){
10036 * Cancel outstanding changes on all changed records.
10038 rejectChanges : function(){
10039 var m = this.modified.slice(0);
10040 this.modified = [];
10041 for(var i = 0, len = m.length; i < len; i++){
10046 onMetaChange : function(meta, rtype, o){
10047 this.recordType = rtype;
10048 this.fields = rtype.prototype.fields;
10049 delete this.snapshot;
10050 this.sortInfo = meta.sortInfo || this.sortInfo;
10051 this.modified = [];
10052 this.fireEvent('metachange', this, this.reader.meta);
10055 moveIndex : function(data, type)
10057 var index = this.indexOf(data);
10059 var newIndex = index + type;
10063 this.insert(newIndex, data);
10068 * Ext JS Library 1.1.1
10069 * Copyright(c) 2006-2007, Ext JS, LLC.
10071 * Originally Released Under LGPL - original licence link has changed is not relivant.
10074 * <script type="text/javascript">
10078 * @class Roo.data.SimpleStore
10079 * @extends Roo.data.Store
10080 * Small helper class to make creating Stores from Array data easier.
10081 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10082 * @cfg {Array} fields An array of field definition objects, or field name strings.
10083 * @cfg {Array} data The multi-dimensional array of data
10085 * @param {Object} config
10087 Roo.data.SimpleStore = function(config){
10088 Roo.data.SimpleStore.superclass.constructor.call(this, {
10090 reader: new Roo.data.ArrayReader({
10093 Roo.data.Record.create(config.fields)
10095 proxy : new Roo.data.MemoryProxy(config.data)
10099 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10101 * Ext JS Library 1.1.1
10102 * Copyright(c) 2006-2007, Ext JS, LLC.
10104 * Originally Released Under LGPL - original licence link has changed is not relivant.
10107 * <script type="text/javascript">
10112 * @extends Roo.data.Store
10113 * @class Roo.data.JsonStore
10114 * Small helper class to make creating Stores for JSON data easier. <br/>
10116 var store = new Roo.data.JsonStore({
10117 url: 'get-images.php',
10119 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10122 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10123 * JsonReader and HttpProxy (unless inline data is provided).</b>
10124 * @cfg {Array} fields An array of field definition objects, or field name strings.
10126 * @param {Object} config
10128 Roo.data.JsonStore = function(c){
10129 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10130 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10131 reader: new Roo.data.JsonReader(c, c.fields)
10134 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10136 * Ext JS Library 1.1.1
10137 * Copyright(c) 2006-2007, Ext JS, LLC.
10139 * Originally Released Under LGPL - original licence link has changed is not relivant.
10142 * <script type="text/javascript">
10146 Roo.data.Field = function(config){
10147 if(typeof config == "string"){
10148 config = {name: config};
10150 Roo.apply(this, config);
10153 this.type = "auto";
10156 var st = Roo.data.SortTypes;
10157 // named sortTypes are supported, here we look them up
10158 if(typeof this.sortType == "string"){
10159 this.sortType = st[this.sortType];
10162 // set default sortType for strings and dates
10163 if(!this.sortType){
10166 this.sortType = st.asUCString;
10169 this.sortType = st.asDate;
10172 this.sortType = st.none;
10177 var stripRe = /[\$,%]/g;
10179 // prebuilt conversion function for this field, instead of
10180 // switching every time we're reading a value
10182 var cv, dateFormat = this.dateFormat;
10187 cv = function(v){ return v; };
10190 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10194 return v !== undefined && v !== null && v !== '' ?
10195 parseInt(String(v).replace(stripRe, ""), 10) : '';
10200 return v !== undefined && v !== null && v !== '' ?
10201 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10206 cv = function(v){ return v === true || v === "true" || v == 1; };
10213 if(v instanceof Date){
10217 if(dateFormat == "timestamp"){
10218 return new Date(v*1000);
10220 return Date.parseDate(v, dateFormat);
10222 var parsed = Date.parse(v);
10223 return parsed ? new Date(parsed) : null;
10232 Roo.data.Field.prototype = {
10240 * Ext JS Library 1.1.1
10241 * Copyright(c) 2006-2007, Ext JS, LLC.
10243 * Originally Released Under LGPL - original licence link has changed is not relivant.
10246 * <script type="text/javascript">
10249 // Base class for reading structured data from a data source. This class is intended to be
10250 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10253 * @class Roo.data.DataReader
10254 * Base class for reading structured data from a data source. This class is intended to be
10255 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10258 Roo.data.DataReader = function(meta, recordType){
10262 this.recordType = recordType instanceof Array ?
10263 Roo.data.Record.create(recordType) : recordType;
10266 Roo.data.DataReader.prototype = {
10268 * Create an empty record
10269 * @param {Object} data (optional) - overlay some values
10270 * @return {Roo.data.Record} record created.
10272 newRow : function(d) {
10274 this.recordType.prototype.fields.each(function(c) {
10276 case 'int' : da[c.name] = 0; break;
10277 case 'date' : da[c.name] = new Date(); break;
10278 case 'float' : da[c.name] = 0.0; break;
10279 case 'boolean' : da[c.name] = false; break;
10280 default : da[c.name] = ""; break;
10284 return new this.recordType(Roo.apply(da, d));
10289 * Ext JS Library 1.1.1
10290 * Copyright(c) 2006-2007, Ext JS, LLC.
10292 * Originally Released Under LGPL - original licence link has changed is not relivant.
10295 * <script type="text/javascript">
10299 * @class Roo.data.DataProxy
10300 * @extends Roo.data.Observable
10301 * This class is an abstract base class for implementations which provide retrieval of
10302 * unformatted data objects.<br>
10304 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10305 * (of the appropriate type which knows how to parse the data object) to provide a block of
10306 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10308 * Custom implementations must implement the load method as described in
10309 * {@link Roo.data.HttpProxy#load}.
10311 Roo.data.DataProxy = function(){
10314 * @event beforeload
10315 * Fires before a network request is made to retrieve a data object.
10316 * @param {Object} This DataProxy object.
10317 * @param {Object} params The params parameter to the load function.
10322 * Fires before the load method's callback is called.
10323 * @param {Object} This DataProxy object.
10324 * @param {Object} o The data object.
10325 * @param {Object} arg The callback argument object passed to the load function.
10329 * @event loadexception
10330 * Fires if an Exception occurs during data retrieval.
10331 * @param {Object} This DataProxy object.
10332 * @param {Object} o The data object.
10333 * @param {Object} arg The callback argument object passed to the load function.
10334 * @param {Object} e The Exception.
10336 loadexception : true
10338 Roo.data.DataProxy.superclass.constructor.call(this);
10341 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10344 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10348 * Ext JS Library 1.1.1
10349 * Copyright(c) 2006-2007, Ext JS, LLC.
10351 * Originally Released Under LGPL - original licence link has changed is not relivant.
10354 * <script type="text/javascript">
10357 * @class Roo.data.MemoryProxy
10358 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10359 * to the Reader when its load method is called.
10361 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10363 Roo.data.MemoryProxy = function(data){
10367 Roo.data.MemoryProxy.superclass.constructor.call(this);
10371 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10373 * Load data from the requested source (in this case an in-memory
10374 * data object passed to the constructor), read the data object into
10375 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10376 * process that block using the passed callback.
10377 * @param {Object} params This parameter is not used by the MemoryProxy class.
10378 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10379 * object into a block of Roo.data.Records.
10380 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10381 * The function must be passed <ul>
10382 * <li>The Record block object</li>
10383 * <li>The "arg" argument from the load function</li>
10384 * <li>A boolean success indicator</li>
10386 * @param {Object} scope The scope in which to call the callback
10387 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10389 load : function(params, reader, callback, scope, arg){
10390 params = params || {};
10393 result = reader.readRecords(this.data);
10395 this.fireEvent("loadexception", this, arg, null, e);
10396 callback.call(scope, null, arg, false);
10399 callback.call(scope, result, arg, true);
10403 update : function(params, records){
10408 * Ext JS Library 1.1.1
10409 * Copyright(c) 2006-2007, Ext JS, LLC.
10411 * Originally Released Under LGPL - original licence link has changed is not relivant.
10414 * <script type="text/javascript">
10417 * @class Roo.data.HttpProxy
10418 * @extends Roo.data.DataProxy
10419 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10420 * configured to reference a certain URL.<br><br>
10422 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10423 * from which the running page was served.<br><br>
10425 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10427 * Be aware that to enable the browser to parse an XML document, the server must set
10428 * the Content-Type header in the HTTP response to "text/xml".
10430 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10431 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10432 * will be used to make the request.
10434 Roo.data.HttpProxy = function(conn){
10435 Roo.data.HttpProxy.superclass.constructor.call(this);
10436 // is conn a conn config or a real conn?
10438 this.useAjax = !conn || !conn.events;
10442 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10443 // thse are take from connection...
10446 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10449 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10450 * extra parameters to each request made by this object. (defaults to undefined)
10453 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10454 * to each request made by this object. (defaults to undefined)
10457 * @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)
10460 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10463 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10469 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10473 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10474 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10475 * a finer-grained basis than the DataProxy events.
10477 getConnection : function(){
10478 return this.useAjax ? Roo.Ajax : this.conn;
10482 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10483 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10484 * process that block using the passed callback.
10485 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10486 * for the request to the remote server.
10487 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10488 * object into a block of Roo.data.Records.
10489 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10490 * The function must be passed <ul>
10491 * <li>The Record block object</li>
10492 * <li>The "arg" argument from the load function</li>
10493 * <li>A boolean success indicator</li>
10495 * @param {Object} scope The scope in which to call the callback
10496 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10498 load : function(params, reader, callback, scope, arg){
10499 if(this.fireEvent("beforeload", this, params) !== false){
10501 params : params || {},
10503 callback : callback,
10508 callback : this.loadResponse,
10512 Roo.applyIf(o, this.conn);
10513 if(this.activeRequest){
10514 Roo.Ajax.abort(this.activeRequest);
10516 this.activeRequest = Roo.Ajax.request(o);
10518 this.conn.request(o);
10521 callback.call(scope||this, null, arg, false);
10526 loadResponse : function(o, success, response){
10527 delete this.activeRequest;
10529 this.fireEvent("loadexception", this, o, response);
10530 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10535 result = o.reader.read(response);
10537 this.fireEvent("loadexception", this, o, response, e);
10538 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10542 this.fireEvent("load", this, o, o.request.arg);
10543 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10547 update : function(dataSet){
10552 updateResponse : function(dataSet){
10557 * Ext JS Library 1.1.1
10558 * Copyright(c) 2006-2007, Ext JS, LLC.
10560 * Originally Released Under LGPL - original licence link has changed is not relivant.
10563 * <script type="text/javascript">
10567 * @class Roo.data.ScriptTagProxy
10568 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10569 * other than the originating domain of the running page.<br><br>
10571 * <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
10572 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10574 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10575 * source code that is used as the source inside a <script> tag.<br><br>
10577 * In order for the browser to process the returned data, the server must wrap the data object
10578 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10579 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10580 * depending on whether the callback name was passed:
10583 boolean scriptTag = false;
10584 String cb = request.getParameter("callback");
10587 response.setContentType("text/javascript");
10589 response.setContentType("application/x-json");
10591 Writer out = response.getWriter();
10593 out.write(cb + "(");
10595 out.print(dataBlock.toJsonString());
10602 * @param {Object} config A configuration object.
10604 Roo.data.ScriptTagProxy = function(config){
10605 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10606 Roo.apply(this, config);
10607 this.head = document.getElementsByTagName("head")[0];
10610 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10612 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10614 * @cfg {String} url The URL from which to request the data object.
10617 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10621 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10622 * the server the name of the callback function set up by the load call to process the returned data object.
10623 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10624 * javascript output which calls this named function passing the data object as its only parameter.
10626 callbackParam : "callback",
10628 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10629 * name to the request.
10634 * Load data from the configured URL, read the data object into
10635 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10636 * process that block using the passed callback.
10637 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10638 * for the request to the remote server.
10639 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10640 * object into a block of Roo.data.Records.
10641 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10642 * The function must be passed <ul>
10643 * <li>The Record block object</li>
10644 * <li>The "arg" argument from the load function</li>
10645 * <li>A boolean success indicator</li>
10647 * @param {Object} scope The scope in which to call the callback
10648 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10650 load : function(params, reader, callback, scope, arg){
10651 if(this.fireEvent("beforeload", this, params) !== false){
10653 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10655 var url = this.url;
10656 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10658 url += "&_dc=" + (new Date().getTime());
10660 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10663 cb : "stcCallback"+transId,
10664 scriptId : "stcScript"+transId,
10668 callback : callback,
10674 window[trans.cb] = function(o){
10675 conn.handleResponse(o, trans);
10678 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10680 if(this.autoAbort !== false){
10684 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10686 var script = document.createElement("script");
10687 script.setAttribute("src", url);
10688 script.setAttribute("type", "text/javascript");
10689 script.setAttribute("id", trans.scriptId);
10690 this.head.appendChild(script);
10692 this.trans = trans;
10694 callback.call(scope||this, null, arg, false);
10699 isLoading : function(){
10700 return this.trans ? true : false;
10704 * Abort the current server request.
10706 abort : function(){
10707 if(this.isLoading()){
10708 this.destroyTrans(this.trans);
10713 destroyTrans : function(trans, isLoaded){
10714 this.head.removeChild(document.getElementById(trans.scriptId));
10715 clearTimeout(trans.timeoutId);
10717 window[trans.cb] = undefined;
10719 delete window[trans.cb];
10722 // if hasn't been loaded, wait for load to remove it to prevent script error
10723 window[trans.cb] = function(){
10724 window[trans.cb] = undefined;
10726 delete window[trans.cb];
10733 handleResponse : function(o, trans){
10734 this.trans = false;
10735 this.destroyTrans(trans, true);
10738 result = trans.reader.readRecords(o);
10740 this.fireEvent("loadexception", this, o, trans.arg, e);
10741 trans.callback.call(trans.scope||window, null, trans.arg, false);
10744 this.fireEvent("load", this, o, trans.arg);
10745 trans.callback.call(trans.scope||window, result, trans.arg, true);
10749 handleFailure : function(trans){
10750 this.trans = false;
10751 this.destroyTrans(trans, false);
10752 this.fireEvent("loadexception", this, null, trans.arg);
10753 trans.callback.call(trans.scope||window, null, trans.arg, false);
10757 * Ext JS Library 1.1.1
10758 * Copyright(c) 2006-2007, Ext JS, LLC.
10760 * Originally Released Under LGPL - original licence link has changed is not relivant.
10763 * <script type="text/javascript">
10767 * @class Roo.data.JsonReader
10768 * @extends Roo.data.DataReader
10769 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10770 * based on mappings in a provided Roo.data.Record constructor.
10772 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10773 * in the reply previously.
10778 var RecordDef = Roo.data.Record.create([
10779 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10780 {name: 'occupation'} // This field will use "occupation" as the mapping.
10782 var myReader = new Roo.data.JsonReader({
10783 totalProperty: "results", // The property which contains the total dataset size (optional)
10784 root: "rows", // The property which contains an Array of row objects
10785 id: "id" // The property within each row object that provides an ID for the record (optional)
10789 * This would consume a JSON file like this:
10791 { 'results': 2, 'rows': [
10792 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10793 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10796 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10797 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10798 * paged from the remote server.
10799 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10800 * @cfg {String} root name of the property which contains the Array of row objects.
10801 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10802 * @cfg {Array} fields Array of field definition objects
10804 * Create a new JsonReader
10805 * @param {Object} meta Metadata configuration options
10806 * @param {Object} recordType Either an Array of field definition objects,
10807 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10809 Roo.data.JsonReader = function(meta, recordType){
10812 // set some defaults:
10813 Roo.applyIf(meta, {
10814 totalProperty: 'total',
10815 successProperty : 'success',
10820 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10822 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10825 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10826 * Used by Store query builder to append _requestMeta to params.
10829 metaFromRemote : false,
10831 * This method is only used by a DataProxy which has retrieved data from a remote server.
10832 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10833 * @return {Object} data A data block which is used by an Roo.data.Store object as
10834 * a cache of Roo.data.Records.
10836 read : function(response){
10837 var json = response.responseText;
10839 var o = /* eval:var:o */ eval("("+json+")");
10841 throw {message: "JsonReader.read: Json object not found"};
10847 this.metaFromRemote = true;
10848 this.meta = o.metaData;
10849 this.recordType = Roo.data.Record.create(o.metaData.fields);
10850 this.onMetaChange(this.meta, this.recordType, o);
10852 return this.readRecords(o);
10855 // private function a store will implement
10856 onMetaChange : function(meta, recordType, o){
10863 simpleAccess: function(obj, subsc) {
10870 getJsonAccessor: function(){
10872 return function(expr) {
10874 return(re.test(expr))
10875 ? new Function("obj", "return obj." + expr)
10880 return Roo.emptyFn;
10885 * Create a data block containing Roo.data.Records from an XML document.
10886 * @param {Object} o An object which contains an Array of row objects in the property specified
10887 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10888 * which contains the total size of the dataset.
10889 * @return {Object} data A data block which is used by an Roo.data.Store object as
10890 * a cache of Roo.data.Records.
10892 readRecords : function(o){
10894 * After any data loads, the raw JSON data is available for further custom processing.
10898 var s = this.meta, Record = this.recordType,
10899 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10901 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10903 if(s.totalProperty) {
10904 this.getTotal = this.getJsonAccessor(s.totalProperty);
10906 if(s.successProperty) {
10907 this.getSuccess = this.getJsonAccessor(s.successProperty);
10909 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10911 var g = this.getJsonAccessor(s.id);
10912 this.getId = function(rec) {
10914 return (r === undefined || r === "") ? null : r;
10917 this.getId = function(){return null;};
10920 for(var jj = 0; jj < fl; jj++){
10922 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10923 this.ef[jj] = this.getJsonAccessor(map);
10927 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10928 if(s.totalProperty){
10929 var vt = parseInt(this.getTotal(o), 10);
10934 if(s.successProperty){
10935 var vs = this.getSuccess(o);
10936 if(vs === false || vs === 'false'){
10941 for(var i = 0; i < c; i++){
10944 var id = this.getId(n);
10945 for(var j = 0; j < fl; j++){
10947 var v = this.ef[j](n);
10949 Roo.log('missing convert for ' + f.name);
10953 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10955 var record = new Record(values, id);
10957 records[i] = record;
10963 totalRecords : totalRecords
10968 * Ext JS Library 1.1.1
10969 * Copyright(c) 2006-2007, Ext JS, LLC.
10971 * Originally Released Under LGPL - original licence link has changed is not relivant.
10974 * <script type="text/javascript">
10978 * @class Roo.data.ArrayReader
10979 * @extends Roo.data.DataReader
10980 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10981 * Each element of that Array represents a row of data fields. The
10982 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10983 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10987 var RecordDef = Roo.data.Record.create([
10988 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10989 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10991 var myReader = new Roo.data.ArrayReader({
10992 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10996 * This would consume an Array like this:
10998 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11000 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11002 * Create a new JsonReader
11003 * @param {Object} meta Metadata configuration options.
11004 * @param {Object} recordType Either an Array of field definition objects
11005 * as specified to {@link Roo.data.Record#create},
11006 * or an {@link Roo.data.Record} object
11007 * created using {@link Roo.data.Record#create}.
11009 Roo.data.ArrayReader = function(meta, recordType){
11010 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11013 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11015 * Create a data block containing Roo.data.Records from an XML document.
11016 * @param {Object} o An Array of row objects which represents the dataset.
11017 * @return {Object} data A data block which is used by an Roo.data.Store object as
11018 * a cache of Roo.data.Records.
11020 readRecords : function(o){
11021 var sid = this.meta ? this.meta.id : null;
11022 var recordType = this.recordType, fields = recordType.prototype.fields;
11025 for(var i = 0; i < root.length; i++){
11028 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11029 for(var j = 0, jlen = fields.length; j < jlen; j++){
11030 var f = fields.items[j];
11031 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11032 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11034 values[f.name] = v;
11036 var record = new recordType(values, id);
11038 records[records.length] = record;
11042 totalRecords : records.length
11051 * @class Roo.bootstrap.ComboBox
11052 * @extends Roo.bootstrap.TriggerField
11053 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11054 * @cfg {Boolean} append (true|false) default false
11055 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11056 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11057 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11058 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11059 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11060 * @cfg {Boolean} animate default true
11061 * @cfg {Boolean} emptyResultText only for touch device
11063 * Create a new ComboBox.
11064 * @param {Object} config Configuration options
11066 Roo.bootstrap.ComboBox = function(config){
11067 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11071 * Fires when the dropdown list is expanded
11072 * @param {Roo.bootstrap.ComboBox} combo This combo box
11077 * Fires when the dropdown list is collapsed
11078 * @param {Roo.bootstrap.ComboBox} combo This combo box
11082 * @event beforeselect
11083 * Fires before a list item is selected. Return false to cancel the selection.
11084 * @param {Roo.bootstrap.ComboBox} combo This combo box
11085 * @param {Roo.data.Record} record The data record returned from the underlying store
11086 * @param {Number} index The index of the selected item in the dropdown list
11088 'beforeselect' : true,
11091 * Fires when a list item is selected
11092 * @param {Roo.bootstrap.ComboBox} combo This combo box
11093 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11094 * @param {Number} index The index of the selected item in the dropdown list
11098 * @event beforequery
11099 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11100 * The event object passed has these properties:
11101 * @param {Roo.bootstrap.ComboBox} combo This combo box
11102 * @param {String} query The query
11103 * @param {Boolean} forceAll true to force "all" query
11104 * @param {Boolean} cancel true to cancel the query
11105 * @param {Object} e The query event object
11107 'beforequery': true,
11110 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11111 * @param {Roo.bootstrap.ComboBox} combo This combo box
11116 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11117 * @param {Roo.bootstrap.ComboBox} combo This combo box
11118 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11123 * Fires when the remove value from the combobox array
11124 * @param {Roo.bootstrap.ComboBox} combo This combo box
11128 * @event specialfilter
11129 * Fires when specialfilter
11130 * @param {Roo.bootstrap.ComboBox} combo This combo box
11132 'specialfilter' : true
11137 this.tickItems = [];
11139 this.selectedIndex = -1;
11140 if(this.mode == 'local'){
11141 if(config.queryDelay === undefined){
11142 this.queryDelay = 10;
11144 if(config.minChars === undefined){
11150 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11153 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11154 * rendering into an Roo.Editor, defaults to false)
11157 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11158 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11161 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11164 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11165 * the dropdown list (defaults to undefined, with no header element)
11169 * @cfg {String/Roo.Template} tpl The template to use to render the output
11173 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11175 listWidth: undefined,
11177 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11178 * mode = 'remote' or 'text' if mode = 'local')
11180 displayField: undefined,
11183 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11184 * mode = 'remote' or 'value' if mode = 'local').
11185 * Note: use of a valueField requires the user make a selection
11186 * in order for a value to be mapped.
11188 valueField: undefined,
11192 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11193 * field's data value (defaults to the underlying DOM element's name)
11195 hiddenName: undefined,
11197 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11201 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11203 selectedClass: 'active',
11206 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11210 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11211 * anchor positions (defaults to 'tl-bl')
11213 listAlign: 'tl-bl?',
11215 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11219 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11220 * query specified by the allQuery config option (defaults to 'query')
11222 triggerAction: 'query',
11224 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11225 * (defaults to 4, does not apply if editable = false)
11229 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11230 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11234 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11235 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11239 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11240 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11244 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11245 * when editable = true (defaults to false)
11247 selectOnFocus:false,
11249 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11251 queryParam: 'query',
11253 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11254 * when mode = 'remote' (defaults to 'Loading...')
11256 loadingText: 'Loading...',
11258 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11262 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11266 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11267 * traditional select (defaults to true)
11271 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11275 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11279 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11280 * listWidth has a higher value)
11284 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11285 * allow the user to set arbitrary text into the field (defaults to false)
11287 forceSelection:false,
11289 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11290 * if typeAhead = true (defaults to 250)
11292 typeAheadDelay : 250,
11294 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11295 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11297 valueNotFoundText : undefined,
11299 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11301 blockFocus : false,
11304 * @cfg {Boolean} disableClear Disable showing of clear button.
11306 disableClear : false,
11308 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11310 alwaysQuery : false,
11313 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11318 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11320 invalidClass : "has-warning",
11323 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11325 validClass : "has-success",
11328 * @cfg {Boolean} specialFilter (true|false) special filter default false
11330 specialFilter : false,
11333 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11335 mobileTouchView : true,
11347 btnPosition : 'right',
11348 triggerList : true,
11349 showToggleBtn : true,
11351 emptyResultText: 'Empty',
11352 // element that contains real text value.. (when hidden is used..)
11354 getAutoCreate : function()
11362 if(Roo.isTouch && this.mobileTouchView){
11363 cfg = this.getAutoCreateTouchView();
11370 if(!this.tickable){
11371 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11376 * ComboBox with tickable selections
11379 var align = this.labelAlign || this.parentLabelAlign();
11382 cls : 'form-group roo-combobox-tickable' //input-group
11387 cls : 'tickable-buttons',
11392 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11399 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11406 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11413 buttons.cn.unshift({
11415 cls: 'select2-search-field-input'
11421 Roo.each(buttons.cn, function(c){
11423 c.cls += ' btn-' + _this.size;
11426 if (_this.disabled) {
11437 cls: 'form-hidden-field'
11441 cls: 'select2-choices',
11445 cls: 'select2-search-field',
11457 cls: 'select2-container input-group select2-container-multi',
11462 // cls: 'typeahead typeahead-long dropdown-menu',
11463 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11468 if(this.hasFeedback && !this.allowBlank){
11472 cls: 'glyphicon form-control-feedback'
11475 combobox.cn.push(feedback);
11478 if (align ==='left' && this.fieldLabel.length) {
11480 Roo.log("left and has label");
11486 cls : 'control-label col-sm-' + this.labelWidth,
11487 html : this.fieldLabel
11491 cls : "col-sm-" + (12 - this.labelWidth),
11498 } else if ( this.fieldLabel.length) {
11504 //cls : 'input-group-addon',
11505 html : this.fieldLabel
11515 Roo.log(" no label && no align");
11522 ['xs','sm','md','lg'].map(function(size){
11523 if (settings[size]) {
11524 cfg.cls += ' col-' + size + '-' + settings[size];
11533 initEvents: function()
11537 throw "can not find store for combo";
11540 this.store = Roo.factory(this.store, Roo.data);
11546 if(Roo.isTouch && this.mobileTouchView){
11547 this.initTouchView();
11552 this.initTickableEvents();
11556 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11558 if(this.hiddenName){
11560 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11562 this.hiddenField.dom.value =
11563 this.hiddenValue !== undefined ? this.hiddenValue :
11564 this.value !== undefined ? this.value : '';
11566 // prevent input submission
11567 this.el.dom.removeAttribute('name');
11568 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11573 // this.el.dom.setAttribute('autocomplete', 'off');
11576 var cls = 'x-combo-list';
11578 //this.list = new Roo.Layer({
11579 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11585 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11586 _this.list.setWidth(lw);
11589 this.list.on('mouseover', this.onViewOver, this);
11590 this.list.on('mousemove', this.onViewMove, this);
11592 this.list.on('scroll', this.onViewScroll, this);
11595 this.list.swallowEvent('mousewheel');
11596 this.assetHeight = 0;
11599 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11600 this.assetHeight += this.header.getHeight();
11603 this.innerList = this.list.createChild({cls:cls+'-inner'});
11604 this.innerList.on('mouseover', this.onViewOver, this);
11605 this.innerList.on('mousemove', this.onViewMove, this);
11606 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11608 if(this.allowBlank && !this.pageSize && !this.disableClear){
11609 this.footer = this.list.createChild({cls:cls+'-ft'});
11610 this.pageTb = new Roo.Toolbar(this.footer);
11614 this.footer = this.list.createChild({cls:cls+'-ft'});
11615 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11616 {pageSize: this.pageSize});
11620 if (this.pageTb && this.allowBlank && !this.disableClear) {
11622 this.pageTb.add(new Roo.Toolbar.Fill(), {
11623 cls: 'x-btn-icon x-btn-clear',
11625 handler: function()
11628 _this.clearValue();
11629 _this.onSelect(false, -1);
11634 this.assetHeight += this.footer.getHeight();
11639 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11642 this.view = new Roo.View(this.list, this.tpl, {
11643 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11645 //this.view.wrapEl.setDisplayed(false);
11646 this.view.on('click', this.onViewClick, this);
11650 this.store.on('beforeload', this.onBeforeLoad, this);
11651 this.store.on('load', this.onLoad, this);
11652 this.store.on('loadexception', this.onLoadException, this);
11654 if(this.resizable){
11655 this.resizer = new Roo.Resizable(this.list, {
11656 pinned:true, handles:'se'
11658 this.resizer.on('resize', function(r, w, h){
11659 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11660 this.listWidth = w;
11661 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11662 this.restrictHeight();
11664 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11667 if(!this.editable){
11668 this.editable = true;
11669 this.setEditable(false);
11674 if (typeof(this.events.add.listeners) != 'undefined') {
11676 this.addicon = this.wrap.createChild(
11677 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11679 this.addicon.on('click', function(e) {
11680 this.fireEvent('add', this);
11683 if (typeof(this.events.edit.listeners) != 'undefined') {
11685 this.editicon = this.wrap.createChild(
11686 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11687 if (this.addicon) {
11688 this.editicon.setStyle('margin-left', '40px');
11690 this.editicon.on('click', function(e) {
11692 // we fire even if inothing is selected..
11693 this.fireEvent('edit', this, this.lastData );
11699 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11700 "up" : function(e){
11701 this.inKeyMode = true;
11705 "down" : function(e){
11706 if(!this.isExpanded()){
11707 this.onTriggerClick();
11709 this.inKeyMode = true;
11714 "enter" : function(e){
11715 // this.onViewClick();
11719 if(this.fireEvent("specialkey", this, e)){
11720 this.onViewClick(false);
11726 "esc" : function(e){
11730 "tab" : function(e){
11733 if(this.fireEvent("specialkey", this, e)){
11734 this.onViewClick(false);
11742 doRelay : function(foo, bar, hname){
11743 if(hname == 'down' || this.scope.isExpanded()){
11744 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11753 this.queryDelay = Math.max(this.queryDelay || 10,
11754 this.mode == 'local' ? 10 : 250);
11757 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11759 if(this.typeAhead){
11760 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11762 if(this.editable !== false){
11763 this.inputEl().on("keyup", this.onKeyUp, this);
11765 if(this.forceSelection){
11766 this.inputEl().on('blur', this.doForce, this);
11770 this.choices = this.el.select('ul.select2-choices', true).first();
11771 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11775 initTickableEvents: function()
11779 if(this.hiddenName){
11781 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11783 this.hiddenField.dom.value =
11784 this.hiddenValue !== undefined ? this.hiddenValue :
11785 this.value !== undefined ? this.value : '';
11787 // prevent input submission
11788 this.el.dom.removeAttribute('name');
11789 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11794 // this.list = this.el.select('ul.dropdown-menu',true).first();
11796 this.choices = this.el.select('ul.select2-choices', true).first();
11797 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11798 if(this.triggerList){
11799 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11802 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11803 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11805 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11806 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11808 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11809 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11811 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11812 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11813 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11816 this.cancelBtn.hide();
11821 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11822 _this.list.setWidth(lw);
11825 this.list.on('mouseover', this.onViewOver, this);
11826 this.list.on('mousemove', this.onViewMove, this);
11828 this.list.on('scroll', this.onViewScroll, this);
11831 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>';
11834 this.view = new Roo.View(this.list, this.tpl, {
11835 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11838 //this.view.wrapEl.setDisplayed(false);
11839 this.view.on('click', this.onViewClick, this);
11843 this.store.on('beforeload', this.onBeforeLoad, this);
11844 this.store.on('load', this.onLoad, this);
11845 this.store.on('loadexception', this.onLoadException, this);
11848 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11849 "up" : function(e){
11850 this.inKeyMode = true;
11854 "down" : function(e){
11855 this.inKeyMode = true;
11859 "enter" : function(e){
11860 if(this.fireEvent("specialkey", this, e)){
11861 this.onViewClick(false);
11867 "esc" : function(e){
11868 this.onTickableFooterButtonClick(e, false, false);
11871 "tab" : function(e){
11872 this.fireEvent("specialkey", this, e);
11874 this.onTickableFooterButtonClick(e, false, false);
11881 doRelay : function(e, fn, key){
11882 if(this.scope.isExpanded()){
11883 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11892 this.queryDelay = Math.max(this.queryDelay || 10,
11893 this.mode == 'local' ? 10 : 250);
11896 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11898 if(this.typeAhead){
11899 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11902 if(this.editable !== false){
11903 this.tickableInputEl().on("keyup", this.onKeyUp, this);
11908 onDestroy : function(){
11910 this.view.setStore(null);
11911 this.view.el.removeAllListeners();
11912 this.view.el.remove();
11913 this.view.purgeListeners();
11916 this.list.dom.innerHTML = '';
11920 this.store.un('beforeload', this.onBeforeLoad, this);
11921 this.store.un('load', this.onLoad, this);
11922 this.store.un('loadexception', this.onLoadException, this);
11924 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11928 fireKey : function(e){
11929 if(e.isNavKeyPress() && !this.list.isVisible()){
11930 this.fireEvent("specialkey", this, e);
11935 onResize: function(w, h){
11936 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11938 // if(typeof w != 'number'){
11939 // // we do not handle it!?!?
11942 // var tw = this.trigger.getWidth();
11943 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11944 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11946 // this.inputEl().setWidth( this.adjustWidth('input', x));
11948 // //this.trigger.setStyle('left', x+'px');
11950 // if(this.list && this.listWidth === undefined){
11951 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11952 // this.list.setWidth(lw);
11953 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11961 * Allow or prevent the user from directly editing the field text. If false is passed,
11962 * the user will only be able to select from the items defined in the dropdown list. This method
11963 * is the runtime equivalent of setting the 'editable' config option at config time.
11964 * @param {Boolean} value True to allow the user to directly edit the field text
11966 setEditable : function(value){
11967 if(value == this.editable){
11970 this.editable = value;
11972 this.inputEl().dom.setAttribute('readOnly', true);
11973 this.inputEl().on('mousedown', this.onTriggerClick, this);
11974 this.inputEl().addClass('x-combo-noedit');
11976 this.inputEl().dom.setAttribute('readOnly', false);
11977 this.inputEl().un('mousedown', this.onTriggerClick, this);
11978 this.inputEl().removeClass('x-combo-noedit');
11984 onBeforeLoad : function(combo,opts){
11985 if(!this.hasFocus){
11989 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11991 this.restrictHeight();
11992 this.selectedIndex = -1;
11996 onLoad : function(){
11998 this.hasQuery = false;
12000 if(!this.hasFocus){
12004 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12005 this.loading.hide();
12008 if(this.store.getCount() > 0){
12010 this.restrictHeight();
12011 if(this.lastQuery == this.allQuery){
12012 if(this.editable && !this.tickable){
12013 this.inputEl().dom.select();
12017 !this.selectByValue(this.value, true) &&
12020 !this.store.lastOptions ||
12021 typeof(this.store.lastOptions.add) == 'undefined' ||
12022 this.store.lastOptions.add != true
12025 this.select(0, true);
12028 if(this.autoFocus){
12031 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12032 this.taTask.delay(this.typeAheadDelay);
12036 this.onEmptyResults();
12042 onLoadException : function()
12044 this.hasQuery = false;
12046 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12047 this.loading.hide();
12050 if(this.tickable && this.editable){
12056 Roo.log(this.store.reader.jsonData);
12057 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12059 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12065 onTypeAhead : function(){
12066 if(this.store.getCount() > 0){
12067 var r = this.store.getAt(0);
12068 var newValue = r.data[this.displayField];
12069 var len = newValue.length;
12070 var selStart = this.getRawValue().length;
12072 if(selStart != len){
12073 this.setRawValue(newValue);
12074 this.selectText(selStart, newValue.length);
12080 onSelect : function(record, index){
12082 if(this.fireEvent('beforeselect', this, record, index) !== false){
12084 this.setFromData(index > -1 ? record.data : false);
12087 this.fireEvent('select', this, record, index);
12092 * Returns the currently selected field value or empty string if no value is set.
12093 * @return {String} value The selected value
12095 getValue : function(){
12098 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12101 if(this.valueField){
12102 return typeof this.value != 'undefined' ? this.value : '';
12104 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12109 * Clears any text/value currently set in the field
12111 clearValue : function(){
12112 if(this.hiddenField){
12113 this.hiddenField.dom.value = '';
12116 this.setRawValue('');
12117 this.lastSelectionText = '';
12118 this.lastData = false;
12120 var close = this.closeTriggerEl();
12129 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12130 * will be displayed in the field. If the value does not match the data value of an existing item,
12131 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12132 * Otherwise the field will be blank (although the value will still be set).
12133 * @param {String} value The value to match
12135 setValue : function(v){
12142 if(this.valueField){
12143 var r = this.findRecord(this.valueField, v);
12145 text = r.data[this.displayField];
12146 }else if(this.valueNotFoundText !== undefined){
12147 text = this.valueNotFoundText;
12150 this.lastSelectionText = text;
12151 if(this.hiddenField){
12152 this.hiddenField.dom.value = v;
12154 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12157 var close = this.closeTriggerEl();
12160 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12164 * @property {Object} the last set data for the element
12169 * Sets the value of the field based on a object which is related to the record format for the store.
12170 * @param {Object} value the value to set as. or false on reset?
12172 setFromData : function(o){
12179 var dv = ''; // display value
12180 var vv = ''; // value value..
12182 if (this.displayField) {
12183 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12185 // this is an error condition!!!
12186 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12189 if(this.valueField){
12190 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12193 var close = this.closeTriggerEl();
12196 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12199 if(this.hiddenField){
12200 this.hiddenField.dom.value = vv;
12202 this.lastSelectionText = dv;
12203 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12207 // no hidden field.. - we store the value in 'value', but still display
12208 // display field!!!!
12209 this.lastSelectionText = dv;
12210 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12217 reset : function(){
12218 // overridden so that last data is reset..
12225 this.setValue(this.originalValue);
12226 this.clearInvalid();
12227 this.lastData = false;
12229 this.view.clearSelections();
12233 findRecord : function(prop, value){
12235 if(this.store.getCount() > 0){
12236 this.store.each(function(r){
12237 if(r.data[prop] == value){
12247 getName: function()
12249 // returns hidden if it's set..
12250 if (!this.rendered) {return ''};
12251 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12255 onViewMove : function(e, t){
12256 this.inKeyMode = false;
12260 onViewOver : function(e, t){
12261 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12264 var item = this.view.findItemFromChild(t);
12267 var index = this.view.indexOf(item);
12268 this.select(index, false);
12273 onViewClick : function(view, doFocus, el, e)
12275 var index = this.view.getSelectedIndexes()[0];
12277 var r = this.store.getAt(index);
12281 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12288 Roo.each(this.tickItems, function(v,k){
12290 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12291 _this.tickItems.splice(k, 1);
12293 if(typeof(e) == 'undefined' && view == false){
12294 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12306 this.tickItems.push(r.data);
12308 if(typeof(e) == 'undefined' && view == false){
12309 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12316 this.onSelect(r, index);
12318 if(doFocus !== false && !this.blockFocus){
12319 this.inputEl().focus();
12324 restrictHeight : function(){
12325 //this.innerList.dom.style.height = '';
12326 //var inner = this.innerList.dom;
12327 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12328 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12329 //this.list.beginUpdate();
12330 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12331 this.list.alignTo(this.inputEl(), this.listAlign);
12332 this.list.alignTo(this.inputEl(), this.listAlign);
12333 //this.list.endUpdate();
12337 onEmptyResults : function(){
12339 if(this.tickable && this.editable){
12340 this.restrictHeight();
12348 * Returns true if the dropdown list is expanded, else false.
12350 isExpanded : function(){
12351 return this.list.isVisible();
12355 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12356 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12357 * @param {String} value The data value of the item to select
12358 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12359 * selected item if it is not currently in view (defaults to true)
12360 * @return {Boolean} True if the value matched an item in the list, else false
12362 selectByValue : function(v, scrollIntoView){
12363 if(v !== undefined && v !== null){
12364 var r = this.findRecord(this.valueField || this.displayField, v);
12366 this.select(this.store.indexOf(r), scrollIntoView);
12374 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12375 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12376 * @param {Number} index The zero-based index of the list item to select
12377 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12378 * selected item if it is not currently in view (defaults to true)
12380 select : function(index, scrollIntoView){
12381 this.selectedIndex = index;
12382 this.view.select(index);
12383 if(scrollIntoView !== false){
12384 var el = this.view.getNode(index);
12386 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12389 this.list.scrollChildIntoView(el, false);
12395 selectNext : function(){
12396 var ct = this.store.getCount();
12398 if(this.selectedIndex == -1){
12400 }else if(this.selectedIndex < ct-1){
12401 this.select(this.selectedIndex+1);
12407 selectPrev : function(){
12408 var ct = this.store.getCount();
12410 if(this.selectedIndex == -1){
12412 }else if(this.selectedIndex != 0){
12413 this.select(this.selectedIndex-1);
12419 onKeyUp : function(e){
12420 if(this.editable !== false && !e.isSpecialKey()){
12421 this.lastKey = e.getKey();
12422 this.dqTask.delay(this.queryDelay);
12427 validateBlur : function(){
12428 return !this.list || !this.list.isVisible();
12432 initQuery : function(){
12434 var v = this.getRawValue();
12436 if(this.tickable && this.editable){
12437 v = this.tickableInputEl().getValue();
12444 doForce : function(){
12445 if(this.inputEl().dom.value.length > 0){
12446 this.inputEl().dom.value =
12447 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12453 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12454 * query allowing the query action to be canceled if needed.
12455 * @param {String} query The SQL query to execute
12456 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12457 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12458 * saved in the current store (defaults to false)
12460 doQuery : function(q, forceAll){
12462 if(q === undefined || q === null){
12467 forceAll: forceAll,
12471 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12476 forceAll = qe.forceAll;
12477 if(forceAll === true || (q.length >= this.minChars)){
12479 this.hasQuery = true;
12481 if(this.lastQuery != q || this.alwaysQuery){
12482 this.lastQuery = q;
12483 if(this.mode == 'local'){
12484 this.selectedIndex = -1;
12486 this.store.clearFilter();
12489 if(this.specialFilter){
12490 this.fireEvent('specialfilter', this);
12495 this.store.filter(this.displayField, q);
12498 this.store.fireEvent("datachanged", this.store);
12505 this.store.baseParams[this.queryParam] = q;
12507 var options = {params : this.getParams(q)};
12510 options.add = true;
12511 options.params.start = this.page * this.pageSize;
12514 this.store.load(options);
12517 * this code will make the page width larger, at the beginning, the list not align correctly,
12518 * we should expand the list on onLoad
12519 * so command out it
12524 this.selectedIndex = -1;
12529 this.loadNext = false;
12533 getParams : function(q){
12535 //p[this.queryParam] = q;
12539 p.limit = this.pageSize;
12545 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12547 collapse : function(){
12548 if(!this.isExpanded()){
12555 this.hasFocus = false;
12557 this.cancelBtn.hide();
12558 this.trigger.show();
12561 this.tickableInputEl().dom.value = '';
12562 this.tickableInputEl().blur();
12567 Roo.get(document).un('mousedown', this.collapseIf, this);
12568 Roo.get(document).un('mousewheel', this.collapseIf, this);
12569 if (!this.editable) {
12570 Roo.get(document).un('keydown', this.listKeyPress, this);
12572 this.fireEvent('collapse', this);
12576 collapseIf : function(e){
12577 var in_combo = e.within(this.el);
12578 var in_list = e.within(this.list);
12579 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12581 if (in_combo || in_list || is_list) {
12582 //e.stopPropagation();
12587 this.onTickableFooterButtonClick(e, false, false);
12595 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12597 expand : function(){
12599 if(this.isExpanded() || !this.hasFocus){
12603 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12604 this.list.setWidth(lw);
12611 this.restrictHeight();
12615 this.tickItems = Roo.apply([], this.item);
12618 this.cancelBtn.show();
12619 this.trigger.hide();
12622 this.tickableInputEl().focus();
12627 Roo.get(document).on('mousedown', this.collapseIf, this);
12628 Roo.get(document).on('mousewheel', this.collapseIf, this);
12629 if (!this.editable) {
12630 Roo.get(document).on('keydown', this.listKeyPress, this);
12633 this.fireEvent('expand', this);
12637 // Implements the default empty TriggerField.onTriggerClick function
12638 onTriggerClick : function(e)
12640 Roo.log('trigger click');
12642 if(this.disabled || !this.triggerList){
12647 this.loadNext = false;
12649 if(this.isExpanded()){
12651 if (!this.blockFocus) {
12652 this.inputEl().focus();
12656 this.hasFocus = true;
12657 if(this.triggerAction == 'all') {
12658 this.doQuery(this.allQuery, true);
12660 this.doQuery(this.getRawValue());
12662 if (!this.blockFocus) {
12663 this.inputEl().focus();
12668 onTickableTriggerClick : function(e)
12675 this.loadNext = false;
12676 this.hasFocus = true;
12678 if(this.triggerAction == 'all') {
12679 this.doQuery(this.allQuery, true);
12681 this.doQuery(this.getRawValue());
12685 onSearchFieldClick : function(e)
12687 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12688 this.onTickableFooterButtonClick(e, false, false);
12692 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12697 this.loadNext = false;
12698 this.hasFocus = true;
12700 if(this.triggerAction == 'all') {
12701 this.doQuery(this.allQuery, true);
12703 this.doQuery(this.getRawValue());
12707 listKeyPress : function(e)
12709 //Roo.log('listkeypress');
12710 // scroll to first matching element based on key pres..
12711 if (e.isSpecialKey()) {
12714 var k = String.fromCharCode(e.getKey()).toUpperCase();
12717 var csel = this.view.getSelectedNodes();
12718 var cselitem = false;
12720 var ix = this.view.indexOf(csel[0]);
12721 cselitem = this.store.getAt(ix);
12722 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12728 this.store.each(function(v) {
12730 // start at existing selection.
12731 if (cselitem.id == v.id) {
12737 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12738 match = this.store.indexOf(v);
12744 if (match === false) {
12745 return true; // no more action?
12748 this.view.select(match);
12749 var sn = Roo.get(this.view.getSelectedNodes()[0])
12750 sn.scrollIntoView(sn.dom.parentNode, false);
12753 onViewScroll : function(e, t){
12755 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){
12759 this.hasQuery = true;
12761 this.loading = this.list.select('.loading', true).first();
12763 if(this.loading === null){
12764 this.list.createChild({
12766 cls: 'loading select2-more-results select2-active',
12767 html: 'Loading more results...'
12770 this.loading = this.list.select('.loading', true).first();
12772 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12774 this.loading.hide();
12777 this.loading.show();
12782 this.loadNext = true;
12784 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12789 addItem : function(o)
12791 var dv = ''; // display value
12793 if (this.displayField) {
12794 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12796 // this is an error condition!!!
12797 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12804 var choice = this.choices.createChild({
12806 cls: 'select2-search-choice',
12815 cls: 'select2-search-choice-close',
12820 }, this.searchField);
12822 var close = choice.select('a.select2-search-choice-close', true).first()
12824 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12832 this.inputEl().dom.value = '';
12837 onRemoveItem : function(e, _self, o)
12839 e.preventDefault();
12841 this.lastItem = Roo.apply([], this.item);
12843 var index = this.item.indexOf(o.data) * 1;
12846 Roo.log('not this item?!');
12850 this.item.splice(index, 1);
12855 this.fireEvent('remove', this, e);
12861 syncValue : function()
12863 if(!this.item.length){
12870 Roo.each(this.item, function(i){
12871 if(_this.valueField){
12872 value.push(i[_this.valueField]);
12879 this.value = value.join(',');
12881 if(this.hiddenField){
12882 this.hiddenField.dom.value = this.value;
12885 this.store.fireEvent("datachanged", this.store);
12888 clearItem : function()
12890 if(!this.multiple){
12896 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12905 inputEl: function ()
12907 if(Roo.isTouch && this.mobileTouchView){
12908 return this.el.select('input.form-control',true).first();
12912 return this.searchField;
12915 return this.el.select('input.form-control',true).first();
12919 onTickableFooterButtonClick : function(e, btn, el)
12921 e.preventDefault();
12923 this.lastItem = Roo.apply([], this.item);
12925 if(btn && btn.name == 'cancel'){
12926 this.tickItems = Roo.apply([], this.item);
12935 Roo.each(this.tickItems, function(o){
12943 validate : function()
12945 var v = this.getRawValue();
12948 v = this.getValue();
12951 if(this.disabled || this.allowBlank || v.length){
12956 this.markInvalid();
12960 tickableInputEl : function()
12962 if(!this.tickable || !this.editable){
12963 return this.inputEl();
12966 return this.inputEl().select('.select2-search-field-input', true).first();
12970 getAutoCreateTouchView : function()
12975 cls: 'form-group' //input-group
12981 type : this.inputType,
12982 cls : 'form-control x-combo-noedit',
12983 autocomplete: 'new-password',
12984 placeholder : this.placeholder || '',
12989 input.name = this.name;
12993 input.cls += ' input-' + this.size;
12996 if (this.disabled) {
12997 input.disabled = true;
13008 inputblock.cls += ' input-group';
13010 inputblock.cn.unshift({
13012 cls : 'input-group-addon',
13017 if(this.removable && !this.multiple){
13018 inputblock.cls += ' roo-removable';
13020 inputblock.cn.push({
13023 cls : 'roo-combo-removable-btn close'
13027 if(this.hasFeedback && !this.allowBlank){
13029 inputblock.cls += ' has-feedback';
13031 inputblock.cn.push({
13033 cls: 'glyphicon form-control-feedback'
13040 inputblock.cls += (this.before) ? '' : ' input-group';
13042 inputblock.cn.push({
13044 cls : 'input-group-addon',
13055 cls: 'form-hidden-field'
13069 cls: 'form-hidden-field'
13073 cls: 'select2-choices',
13077 cls: 'select2-search-field',
13090 cls: 'select2-container input-group',
13097 combobox.cls += ' select2-container-multi';
13100 var align = this.labelAlign || this.parentLabelAlign();
13104 if(this.fieldLabel.length){
13106 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13107 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13112 cls : 'control-label ' + lw,
13113 html : this.fieldLabel
13125 var settings = this;
13127 ['xs','sm','md','lg'].map(function(size){
13128 if (settings[size]) {
13129 cfg.cls += ' col-' + size + '-' + settings[size];
13136 initTouchView : function()
13138 this.renderTouchView();
13140 this.touchViewEl.on('scroll', function(){
13141 this.el.dom.scrollTop = 0;
13144 this.inputEl().on("click", this.showTouchView, this);
13145 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13146 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13148 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13150 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13151 this.store.on('load', this.onTouchViewLoad, this);
13152 this.store.on('loadexception', this.onTouchViewLoadException, this);
13154 if(this.hiddenName){
13156 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13158 this.hiddenField.dom.value =
13159 this.hiddenValue !== undefined ? this.hiddenValue :
13160 this.value !== undefined ? this.value : '';
13162 this.el.dom.removeAttribute('name');
13163 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13167 this.choices = this.el.select('ul.select2-choices', true).first();
13168 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13171 if(this.removable && !this.multiple){
13172 var close = this.closeTriggerEl();
13174 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13175 close.on('click', this.removeBtnClick, this, close);
13184 renderTouchView : function()
13186 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13187 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13189 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13190 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13192 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13193 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13194 this.touchViewBodyEl.setStyle('overflow', 'auto');
13196 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13197 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13199 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13200 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13204 showTouchView : function()
13206 this.touchViewHeaderEl.hide();
13208 if(this.fieldLabel.length){
13209 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13210 this.touchViewHeaderEl.show();
13213 this.touchViewEl.show();
13215 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13216 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13218 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13220 if(this.fieldLabel.length){
13221 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13224 this.touchViewBodyEl.setHeight(bodyHeight);
13228 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13230 this.touchViewEl.addClass('in');
13233 this.doTouchViewQuery();
13237 hideTouchView : function()
13239 this.touchViewEl.removeClass('in');
13243 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13245 this.touchViewEl.setStyle('display', 'none');
13250 setTouchViewValue : function()
13257 Roo.each(this.tickItems, function(o){
13262 this.hideTouchView();
13265 doTouchViewQuery : function()
13274 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13278 if(!this.alwaysQuery || this.mode == 'local'){
13279 this.onTouchViewLoad();
13286 onTouchViewBeforeLoad : function(combo,opts)
13292 onTouchViewLoad : function()
13294 if(this.store.getCount() < 1){
13295 this.onTouchViewEmptyResults();
13299 this.clearTouchView();
13301 var rawValue = this.getRawValue();
13303 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13305 this.tickItems = [];
13307 this.store.data.each(function(d, rowIndex){
13308 var row = this.touchViewListGroup.createChild(template);
13310 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13311 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13314 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13315 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13318 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13319 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13320 this.tickItems.push(d.data);
13323 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13327 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13329 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13331 if(this.fieldLabel.length){
13332 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13335 var listHeight = this.touchViewListGroup.getHeight();
13337 if(firstChecked && listHeight > bodyHeight){
13338 (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13343 onTouchViewLoadException : function()
13345 this.hideTouchView();
13348 onTouchViewEmptyResults : function()
13350 this.clearTouchView();
13352 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13354 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13358 clearTouchView : function()
13360 this.touchViewListGroup.dom.innerHTML = '';
13363 onTouchViewClick : function(e, el, o)
13365 e.preventDefault();
13368 var rowIndex = o.rowIndex;
13370 var r = this.store.getAt(rowIndex);
13372 if(!this.multiple){
13373 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13374 c.dom.removeAttribute('checked');
13377 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13379 this.setFromData(r.data);
13381 var close = this.closeTriggerEl();
13387 this.hideTouchView();
13389 this.fireEvent('select', this, r, rowIndex);
13394 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13395 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13396 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13400 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13401 this.addItem(r.data);
13402 this.tickItems.push(r.data);
13408 * @cfg {Boolean} grow
13412 * @cfg {Number} growMin
13416 * @cfg {Number} growMax
13425 Roo.apply(Roo.bootstrap.ComboBox, {
13429 cls: 'modal-header',
13451 cls: 'list-group-item',
13455 cls: 'roo-combobox-list-group-item-value'
13459 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13473 listItemCheckbox : {
13475 cls: 'list-group-item',
13479 cls: 'roo-combobox-list-group-item-value'
13483 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13499 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13504 cls: 'modal-footer',
13512 cls: 'col-xs-6 text-left',
13515 cls: 'btn btn-danger roo-touch-view-cancel',
13521 cls: 'col-xs-6 text-right',
13524 cls: 'btn btn-success roo-touch-view-ok',
13535 Roo.apply(Roo.bootstrap.ComboBox, {
13537 touchViewTemplate : {
13539 cls: 'modal fade roo-combobox-touch-view',
13543 cls: 'modal-dialog',
13547 cls: 'modal-content',
13549 Roo.bootstrap.ComboBox.header,
13550 Roo.bootstrap.ComboBox.body,
13551 Roo.bootstrap.ComboBox.footer
13560 * Ext JS Library 1.1.1
13561 * Copyright(c) 2006-2007, Ext JS, LLC.
13563 * Originally Released Under LGPL - original licence link has changed is not relivant.
13566 * <script type="text/javascript">
13571 * @extends Roo.util.Observable
13572 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13573 * This class also supports single and multi selection modes. <br>
13574 * Create a data model bound view:
13576 var store = new Roo.data.Store(...);
13578 var view = new Roo.View({
13580 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13582 singleSelect: true,
13583 selectedClass: "ydataview-selected",
13587 // listen for node click?
13588 view.on("click", function(vw, index, node, e){
13589 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13593 dataModel.load("foobar.xml");
13595 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13597 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13598 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13600 * Note: old style constructor is still suported (container, template, config)
13603 * Create a new View
13604 * @param {Object} config The config object
13607 Roo.View = function(config, depreciated_tpl, depreciated_config){
13609 this.parent = false;
13611 if (typeof(depreciated_tpl) == 'undefined') {
13612 // new way.. - universal constructor.
13613 Roo.apply(this, config);
13614 this.el = Roo.get(this.el);
13617 this.el = Roo.get(config);
13618 this.tpl = depreciated_tpl;
13619 Roo.apply(this, depreciated_config);
13621 this.wrapEl = this.el.wrap().wrap();
13622 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13625 if(typeof(this.tpl) == "string"){
13626 this.tpl = new Roo.Template(this.tpl);
13628 // support xtype ctors..
13629 this.tpl = new Roo.factory(this.tpl, Roo);
13633 this.tpl.compile();
13638 * @event beforeclick
13639 * Fires before a click is processed. Returns false to cancel the default action.
13640 * @param {Roo.View} this
13641 * @param {Number} index The index of the target node
13642 * @param {HTMLElement} node The target node
13643 * @param {Roo.EventObject} e The raw event object
13645 "beforeclick" : true,
13648 * Fires when a template node is clicked.
13649 * @param {Roo.View} this
13650 * @param {Number} index The index of the target node
13651 * @param {HTMLElement} node The target node
13652 * @param {Roo.EventObject} e The raw event object
13657 * Fires when a template node is double clicked.
13658 * @param {Roo.View} this
13659 * @param {Number} index The index of the target node
13660 * @param {HTMLElement} node The target node
13661 * @param {Roo.EventObject} e The raw event object
13665 * @event contextmenu
13666 * Fires when a template node is right clicked.
13667 * @param {Roo.View} this
13668 * @param {Number} index The index of the target node
13669 * @param {HTMLElement} node The target node
13670 * @param {Roo.EventObject} e The raw event object
13672 "contextmenu" : true,
13674 * @event selectionchange
13675 * Fires when the selected nodes change.
13676 * @param {Roo.View} this
13677 * @param {Array} selections Array of the selected nodes
13679 "selectionchange" : true,
13682 * @event beforeselect
13683 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13684 * @param {Roo.View} this
13685 * @param {HTMLElement} node The node to be selected
13686 * @param {Array} selections Array of currently selected nodes
13688 "beforeselect" : true,
13690 * @event preparedata
13691 * Fires on every row to render, to allow you to change the data.
13692 * @param {Roo.View} this
13693 * @param {Object} data to be rendered (change this)
13695 "preparedata" : true
13703 "click": this.onClick,
13704 "dblclick": this.onDblClick,
13705 "contextmenu": this.onContextMenu,
13709 this.selections = [];
13711 this.cmp = new Roo.CompositeElementLite([]);
13713 this.store = Roo.factory(this.store, Roo.data);
13714 this.setStore(this.store, true);
13717 if ( this.footer && this.footer.xtype) {
13719 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13721 this.footer.dataSource = this.store
13722 this.footer.container = fctr;
13723 this.footer = Roo.factory(this.footer, Roo);
13724 fctr.insertFirst(this.el);
13726 // this is a bit insane - as the paging toolbar seems to detach the el..
13727 // dom.parentNode.parentNode.parentNode
13728 // they get detached?
13732 Roo.View.superclass.constructor.call(this);
13737 Roo.extend(Roo.View, Roo.util.Observable, {
13740 * @cfg {Roo.data.Store} store Data store to load data from.
13745 * @cfg {String|Roo.Element} el The container element.
13750 * @cfg {String|Roo.Template} tpl The template used by this View
13754 * @cfg {String} dataName the named area of the template to use as the data area
13755 * Works with domtemplates roo-name="name"
13759 * @cfg {String} selectedClass The css class to add to selected nodes
13761 selectedClass : "x-view-selected",
13763 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13768 * @cfg {String} text to display on mask (default Loading)
13772 * @cfg {Boolean} multiSelect Allow multiple selection
13774 multiSelect : false,
13776 * @cfg {Boolean} singleSelect Allow single selection
13778 singleSelect: false,
13781 * @cfg {Boolean} toggleSelect - selecting
13783 toggleSelect : false,
13786 * @cfg {Boolean} tickable - selecting
13791 * Returns the element this view is bound to.
13792 * @return {Roo.Element}
13794 getEl : function(){
13795 return this.wrapEl;
13801 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13803 refresh : function(){
13804 //Roo.log('refresh');
13807 // if we are using something like 'domtemplate', then
13808 // the what gets used is:
13809 // t.applySubtemplate(NAME, data, wrapping data..)
13810 // the outer template then get' applied with
13811 // the store 'extra data'
13812 // and the body get's added to the
13813 // roo-name="data" node?
13814 // <span class='roo-tpl-{name}'></span> ?????
13818 this.clearSelections();
13819 this.el.update("");
13821 var records = this.store.getRange();
13822 if(records.length < 1) {
13824 // is this valid?? = should it render a template??
13826 this.el.update(this.emptyText);
13830 if (this.dataName) {
13831 this.el.update(t.apply(this.store.meta)); //????
13832 el = this.el.child('.roo-tpl-' + this.dataName);
13835 for(var i = 0, len = records.length; i < len; i++){
13836 var data = this.prepareData(records[i].data, i, records[i]);
13837 this.fireEvent("preparedata", this, data, i, records[i]);
13839 var d = Roo.apply({}, data);
13842 Roo.apply(d, {'roo-id' : Roo.id()});
13846 Roo.each(this.parent.item, function(item){
13847 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13850 Roo.apply(d, {'roo-data-checked' : 'checked'});
13854 html[html.length] = Roo.util.Format.trim(
13856 t.applySubtemplate(this.dataName, d, this.store.meta) :
13863 el.update(html.join(""));
13864 this.nodes = el.dom.childNodes;
13865 this.updateIndexes(0);
13870 * Function to override to reformat the data that is sent to
13871 * the template for each node.
13872 * DEPRICATED - use the preparedata event handler.
13873 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13874 * a JSON object for an UpdateManager bound view).
13876 prepareData : function(data, index, record)
13878 this.fireEvent("preparedata", this, data, index, record);
13882 onUpdate : function(ds, record){
13883 // Roo.log('on update');
13884 this.clearSelections();
13885 var index = this.store.indexOf(record);
13886 var n = this.nodes[index];
13887 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13888 n.parentNode.removeChild(n);
13889 this.updateIndexes(index, index);
13895 onAdd : function(ds, records, index)
13897 //Roo.log(['on Add', ds, records, index] );
13898 this.clearSelections();
13899 if(this.nodes.length == 0){
13903 var n = this.nodes[index];
13904 for(var i = 0, len = records.length; i < len; i++){
13905 var d = this.prepareData(records[i].data, i, records[i]);
13907 this.tpl.insertBefore(n, d);
13910 this.tpl.append(this.el, d);
13913 this.updateIndexes(index);
13916 onRemove : function(ds, record, index){
13917 // Roo.log('onRemove');
13918 this.clearSelections();
13919 var el = this.dataName ?
13920 this.el.child('.roo-tpl-' + this.dataName) :
13923 el.dom.removeChild(this.nodes[index]);
13924 this.updateIndexes(index);
13928 * Refresh an individual node.
13929 * @param {Number} index
13931 refreshNode : function(index){
13932 this.onUpdate(this.store, this.store.getAt(index));
13935 updateIndexes : function(startIndex, endIndex){
13936 var ns = this.nodes;
13937 startIndex = startIndex || 0;
13938 endIndex = endIndex || ns.length - 1;
13939 for(var i = startIndex; i <= endIndex; i++){
13940 ns[i].nodeIndex = i;
13945 * Changes the data store this view uses and refresh the view.
13946 * @param {Store} store
13948 setStore : function(store, initial){
13949 if(!initial && this.store){
13950 this.store.un("datachanged", this.refresh);
13951 this.store.un("add", this.onAdd);
13952 this.store.un("remove", this.onRemove);
13953 this.store.un("update", this.onUpdate);
13954 this.store.un("clear", this.refresh);
13955 this.store.un("beforeload", this.onBeforeLoad);
13956 this.store.un("load", this.onLoad);
13957 this.store.un("loadexception", this.onLoad);
13961 store.on("datachanged", this.refresh, this);
13962 store.on("add", this.onAdd, this);
13963 store.on("remove", this.onRemove, this);
13964 store.on("update", this.onUpdate, this);
13965 store.on("clear", this.refresh, this);
13966 store.on("beforeload", this.onBeforeLoad, this);
13967 store.on("load", this.onLoad, this);
13968 store.on("loadexception", this.onLoad, this);
13976 * onbeforeLoad - masks the loading area.
13979 onBeforeLoad : function(store,opts)
13981 //Roo.log('onBeforeLoad');
13983 this.el.update("");
13985 this.el.mask(this.mask ? this.mask : "Loading" );
13987 onLoad : function ()
13994 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13995 * @param {HTMLElement} node
13996 * @return {HTMLElement} The template node
13998 findItemFromChild : function(node){
13999 var el = this.dataName ?
14000 this.el.child('.roo-tpl-' + this.dataName,true) :
14003 if(!node || node.parentNode == el){
14006 var p = node.parentNode;
14007 while(p && p != el){
14008 if(p.parentNode == el){
14017 onClick : function(e){
14018 var item = this.findItemFromChild(e.getTarget());
14020 var index = this.indexOf(item);
14021 if(this.onItemClick(item, index, e) !== false){
14022 this.fireEvent("click", this, index, item, e);
14025 this.clearSelections();
14030 onContextMenu : function(e){
14031 var item = this.findItemFromChild(e.getTarget());
14033 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14038 onDblClick : function(e){
14039 var item = this.findItemFromChild(e.getTarget());
14041 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14045 onItemClick : function(item, index, e)
14047 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14050 if (this.toggleSelect) {
14051 var m = this.isSelected(item) ? 'unselect' : 'select';
14054 _t[m](item, true, false);
14057 if(this.multiSelect || this.singleSelect){
14058 if(this.multiSelect && e.shiftKey && this.lastSelection){
14059 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14061 this.select(item, this.multiSelect && e.ctrlKey);
14062 this.lastSelection = item;
14065 if(!this.tickable){
14066 e.preventDefault();
14074 * Get the number of selected nodes.
14077 getSelectionCount : function(){
14078 return this.selections.length;
14082 * Get the currently selected nodes.
14083 * @return {Array} An array of HTMLElements
14085 getSelectedNodes : function(){
14086 return this.selections;
14090 * Get the indexes of the selected nodes.
14093 getSelectedIndexes : function(){
14094 var indexes = [], s = this.selections;
14095 for(var i = 0, len = s.length; i < len; i++){
14096 indexes.push(s[i].nodeIndex);
14102 * Clear all selections
14103 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14105 clearSelections : function(suppressEvent){
14106 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14107 this.cmp.elements = this.selections;
14108 this.cmp.removeClass(this.selectedClass);
14109 this.selections = [];
14110 if(!suppressEvent){
14111 this.fireEvent("selectionchange", this, this.selections);
14117 * Returns true if the passed node is selected
14118 * @param {HTMLElement/Number} node The node or node index
14119 * @return {Boolean}
14121 isSelected : function(node){
14122 var s = this.selections;
14126 node = this.getNode(node);
14127 return s.indexOf(node) !== -1;
14132 * @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
14133 * @param {Boolean} keepExisting (optional) true to keep existing selections
14134 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14136 select : function(nodeInfo, keepExisting, suppressEvent){
14137 if(nodeInfo instanceof Array){
14139 this.clearSelections(true);
14141 for(var i = 0, len = nodeInfo.length; i < len; i++){
14142 this.select(nodeInfo[i], true, true);
14146 var node = this.getNode(nodeInfo);
14147 if(!node || this.isSelected(node)){
14148 return; // already selected.
14151 this.clearSelections(true);
14154 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14155 Roo.fly(node).addClass(this.selectedClass);
14156 this.selections.push(node);
14157 if(!suppressEvent){
14158 this.fireEvent("selectionchange", this, this.selections);
14166 * @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
14167 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14168 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14170 unselect : function(nodeInfo, keepExisting, suppressEvent)
14172 if(nodeInfo instanceof Array){
14173 Roo.each(this.selections, function(s) {
14174 this.unselect(s, nodeInfo);
14178 var node = this.getNode(nodeInfo);
14179 if(!node || !this.isSelected(node)){
14180 //Roo.log("not selected");
14181 return; // not selected.
14185 Roo.each(this.selections, function(s) {
14187 Roo.fly(node).removeClass(this.selectedClass);
14194 this.selections= ns;
14195 this.fireEvent("selectionchange", this, this.selections);
14199 * Gets a template node.
14200 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14201 * @return {HTMLElement} The node or null if it wasn't found
14203 getNode : function(nodeInfo){
14204 if(typeof nodeInfo == "string"){
14205 return document.getElementById(nodeInfo);
14206 }else if(typeof nodeInfo == "number"){
14207 return this.nodes[nodeInfo];
14213 * Gets a range template nodes.
14214 * @param {Number} startIndex
14215 * @param {Number} endIndex
14216 * @return {Array} An array of nodes
14218 getNodes : function(start, end){
14219 var ns = this.nodes;
14220 start = start || 0;
14221 end = typeof end == "undefined" ? ns.length - 1 : end;
14224 for(var i = start; i <= end; i++){
14228 for(var i = start; i >= end; i--){
14236 * Finds the index of the passed node
14237 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14238 * @return {Number} The index of the node or -1
14240 indexOf : function(node){
14241 node = this.getNode(node);
14242 if(typeof node.nodeIndex == "number"){
14243 return node.nodeIndex;
14245 var ns = this.nodes;
14246 for(var i = 0, len = ns.length; i < len; i++){
14257 * based on jquery fullcalendar
14261 Roo.bootstrap = Roo.bootstrap || {};
14263 * @class Roo.bootstrap.Calendar
14264 * @extends Roo.bootstrap.Component
14265 * Bootstrap Calendar class
14266 * @cfg {Boolean} loadMask (true|false) default false
14267 * @cfg {Object} header generate the user specific header of the calendar, default false
14270 * Create a new Container
14271 * @param {Object} config The config object
14276 Roo.bootstrap.Calendar = function(config){
14277 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14281 * Fires when a date is selected
14282 * @param {DatePicker} this
14283 * @param {Date} date The selected date
14287 * @event monthchange
14288 * Fires when the displayed month changes
14289 * @param {DatePicker} this
14290 * @param {Date} date The selected month
14292 'monthchange': true,
14294 * @event evententer
14295 * Fires when mouse over an event
14296 * @param {Calendar} this
14297 * @param {event} Event
14299 'evententer': true,
14301 * @event eventleave
14302 * Fires when the mouse leaves an
14303 * @param {Calendar} this
14306 'eventleave': true,
14308 * @event eventclick
14309 * Fires when the mouse click an
14310 * @param {Calendar} this
14319 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14322 * @cfg {Number} startDay
14323 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14331 getAutoCreate : function(){
14334 var fc_button = function(name, corner, style, content ) {
14335 return Roo.apply({},{
14337 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14339 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14342 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14353 style : 'width:100%',
14360 cls : 'fc-header-left',
14362 fc_button('prev', 'left', 'arrow', '‹' ),
14363 fc_button('next', 'right', 'arrow', '›' ),
14364 { tag: 'span', cls: 'fc-header-space' },
14365 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14373 cls : 'fc-header-center',
14377 cls: 'fc-header-title',
14380 html : 'month / year'
14388 cls : 'fc-header-right',
14390 /* fc_button('month', 'left', '', 'month' ),
14391 fc_button('week', '', '', 'week' ),
14392 fc_button('day', 'right', '', 'day' )
14404 header = this.header;
14407 var cal_heads = function() {
14409 // fixme - handle this.
14411 for (var i =0; i < Date.dayNames.length; i++) {
14412 var d = Date.dayNames[i];
14415 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14416 html : d.substring(0,3)
14420 ret[0].cls += ' fc-first';
14421 ret[6].cls += ' fc-last';
14424 var cal_cell = function(n) {
14427 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14432 cls: 'fc-day-number',
14436 cls: 'fc-day-content',
14440 style: 'position: relative;' // height: 17px;
14452 var cal_rows = function() {
14455 for (var r = 0; r < 6; r++) {
14462 for (var i =0; i < Date.dayNames.length; i++) {
14463 var d = Date.dayNames[i];
14464 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14467 row.cn[0].cls+=' fc-first';
14468 row.cn[0].cn[0].style = 'min-height:90px';
14469 row.cn[6].cls+=' fc-last';
14473 ret[0].cls += ' fc-first';
14474 ret[4].cls += ' fc-prev-last';
14475 ret[5].cls += ' fc-last';
14482 cls: 'fc-border-separate',
14483 style : 'width:100%',
14491 cls : 'fc-first fc-last',
14509 cls : 'fc-content',
14510 style : "position: relative;",
14513 cls : 'fc-view fc-view-month fc-grid',
14514 style : 'position: relative',
14515 unselectable : 'on',
14518 cls : 'fc-event-container',
14519 style : 'position:absolute;z-index:8;top:0;left:0;'
14537 initEvents : function()
14540 throw "can not find store for calendar";
14546 style: "text-align:center",
14550 style: "background-color:white;width:50%;margin:250 auto",
14554 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14565 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14567 var size = this.el.select('.fc-content', true).first().getSize();
14568 this.maskEl.setSize(size.width, size.height);
14569 this.maskEl.enableDisplayMode("block");
14570 if(!this.loadMask){
14571 this.maskEl.hide();
14574 this.store = Roo.factory(this.store, Roo.data);
14575 this.store.on('load', this.onLoad, this);
14576 this.store.on('beforeload', this.onBeforeLoad, this);
14580 this.cells = this.el.select('.fc-day',true);
14581 //Roo.log(this.cells);
14582 this.textNodes = this.el.query('.fc-day-number');
14583 this.cells.addClassOnOver('fc-state-hover');
14585 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14586 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14587 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14588 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14590 this.on('monthchange', this.onMonthChange, this);
14592 this.update(new Date().clearTime());
14595 resize : function() {
14596 var sz = this.el.getSize();
14598 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14599 this.el.select('.fc-day-content div',true).setHeight(34);
14604 showPrevMonth : function(e){
14605 this.update(this.activeDate.add("mo", -1));
14607 showToday : function(e){
14608 this.update(new Date().clearTime());
14611 showNextMonth : function(e){
14612 this.update(this.activeDate.add("mo", 1));
14616 showPrevYear : function(){
14617 this.update(this.activeDate.add("y", -1));
14621 showNextYear : function(){
14622 this.update(this.activeDate.add("y", 1));
14627 update : function(date)
14629 var vd = this.activeDate;
14630 this.activeDate = date;
14631 // if(vd && this.el){
14632 // var t = date.getTime();
14633 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14634 // Roo.log('using add remove');
14636 // this.fireEvent('monthchange', this, date);
14638 // this.cells.removeClass("fc-state-highlight");
14639 // this.cells.each(function(c){
14640 // if(c.dateValue == t){
14641 // c.addClass("fc-state-highlight");
14642 // setTimeout(function(){
14643 // try{c.dom.firstChild.focus();}catch(e){}
14653 var days = date.getDaysInMonth();
14655 var firstOfMonth = date.getFirstDateOfMonth();
14656 var startingPos = firstOfMonth.getDay()-this.startDay;
14658 if(startingPos < this.startDay){
14662 var pm = date.add(Date.MONTH, -1);
14663 var prevStart = pm.getDaysInMonth()-startingPos;
14665 this.cells = this.el.select('.fc-day',true);
14666 this.textNodes = this.el.query('.fc-day-number');
14667 this.cells.addClassOnOver('fc-state-hover');
14669 var cells = this.cells.elements;
14670 var textEls = this.textNodes;
14672 Roo.each(cells, function(cell){
14673 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14676 days += startingPos;
14678 // convert everything to numbers so it's fast
14679 var day = 86400000;
14680 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14683 //Roo.log(prevStart);
14685 var today = new Date().clearTime().getTime();
14686 var sel = date.clearTime().getTime();
14687 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14688 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14689 var ddMatch = this.disabledDatesRE;
14690 var ddText = this.disabledDatesText;
14691 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14692 var ddaysText = this.disabledDaysText;
14693 var format = this.format;
14695 var setCellClass = function(cal, cell){
14699 //Roo.log('set Cell Class');
14701 var t = d.getTime();
14705 cell.dateValue = t;
14707 cell.className += " fc-today";
14708 cell.className += " fc-state-highlight";
14709 cell.title = cal.todayText;
14712 // disable highlight in other month..
14713 //cell.className += " fc-state-highlight";
14718 cell.className = " fc-state-disabled";
14719 cell.title = cal.minText;
14723 cell.className = " fc-state-disabled";
14724 cell.title = cal.maxText;
14728 if(ddays.indexOf(d.getDay()) != -1){
14729 cell.title = ddaysText;
14730 cell.className = " fc-state-disabled";
14733 if(ddMatch && format){
14734 var fvalue = d.dateFormat(format);
14735 if(ddMatch.test(fvalue)){
14736 cell.title = ddText.replace("%0", fvalue);
14737 cell.className = " fc-state-disabled";
14741 if (!cell.initialClassName) {
14742 cell.initialClassName = cell.dom.className;
14745 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14750 for(; i < startingPos; i++) {
14751 textEls[i].innerHTML = (++prevStart);
14752 d.setDate(d.getDate()+1);
14754 cells[i].className = "fc-past fc-other-month";
14755 setCellClass(this, cells[i]);
14760 for(; i < days; i++){
14761 intDay = i - startingPos + 1;
14762 textEls[i].innerHTML = (intDay);
14763 d.setDate(d.getDate()+1);
14765 cells[i].className = ''; // "x-date-active";
14766 setCellClass(this, cells[i]);
14770 for(; i < 42; i++) {
14771 textEls[i].innerHTML = (++extraDays);
14772 d.setDate(d.getDate()+1);
14774 cells[i].className = "fc-future fc-other-month";
14775 setCellClass(this, cells[i]);
14778 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14780 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14782 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14783 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14785 if(totalRows != 6){
14786 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14787 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14790 this.fireEvent('monthchange', this, date);
14794 if(!this.internalRender){
14795 var main = this.el.dom.firstChild;
14796 var w = main.offsetWidth;
14797 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14798 Roo.fly(main).setWidth(w);
14799 this.internalRender = true;
14800 // opera does not respect the auto grow header center column
14801 // then, after it gets a width opera refuses to recalculate
14802 // without a second pass
14803 if(Roo.isOpera && !this.secondPass){
14804 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14805 this.secondPass = true;
14806 this.update.defer(10, this, [date]);
14813 findCell : function(dt) {
14814 dt = dt.clearTime().getTime();
14816 this.cells.each(function(c){
14817 //Roo.log("check " +c.dateValue + '?=' + dt);
14818 if(c.dateValue == dt){
14828 findCells : function(ev) {
14829 var s = ev.start.clone().clearTime().getTime();
14831 var e= ev.end.clone().clearTime().getTime();
14834 this.cells.each(function(c){
14835 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14837 if(c.dateValue > e){
14840 if(c.dateValue < s){
14849 // findBestRow: function(cells)
14853 // for (var i =0 ; i < cells.length;i++) {
14854 // ret = Math.max(cells[i].rows || 0,ret);
14861 addItem : function(ev)
14863 // look for vertical location slot in
14864 var cells = this.findCells(ev);
14866 // ev.row = this.findBestRow(cells);
14868 // work out the location.
14872 for(var i =0; i < cells.length; i++) {
14874 cells[i].row = cells[0].row;
14877 cells[i].row = cells[i].row + 1;
14887 if (crow.start.getY() == cells[i].getY()) {
14889 crow.end = cells[i];
14906 cells[0].events.push(ev);
14908 this.calevents.push(ev);
14911 clearEvents: function() {
14913 if(!this.calevents){
14917 Roo.each(this.cells.elements, function(c){
14923 Roo.each(this.calevents, function(e) {
14924 Roo.each(e.els, function(el) {
14925 el.un('mouseenter' ,this.onEventEnter, this);
14926 el.un('mouseleave' ,this.onEventLeave, this);
14931 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14937 renderEvents: function()
14941 this.cells.each(function(c) {
14950 if(c.row != c.events.length){
14951 r = 4 - (4 - (c.row - c.events.length));
14954 c.events = ev.slice(0, r);
14955 c.more = ev.slice(r);
14957 if(c.more.length && c.more.length == 1){
14958 c.events.push(c.more.pop());
14961 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14965 this.cells.each(function(c) {
14967 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14970 for (var e = 0; e < c.events.length; e++){
14971 var ev = c.events[e];
14972 var rows = ev.rows;
14974 for(var i = 0; i < rows.length; i++) {
14976 // how many rows should it span..
14979 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14980 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14982 unselectable : "on",
14985 cls: 'fc-event-inner',
14989 // cls: 'fc-event-time',
14990 // html : cells.length > 1 ? '' : ev.time
14994 cls: 'fc-event-title',
14995 html : String.format('{0}', ev.title)
15002 cls: 'ui-resizable-handle ui-resizable-e',
15003 html : '  '
15010 cfg.cls += ' fc-event-start';
15012 if ((i+1) == rows.length) {
15013 cfg.cls += ' fc-event-end';
15016 var ctr = _this.el.select('.fc-event-container',true).first();
15017 var cg = ctr.createChild(cfg);
15019 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15020 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15022 var r = (c.more.length) ? 1 : 0;
15023 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15024 cg.setWidth(ebox.right - sbox.x -2);
15026 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15027 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15028 cg.on('click', _this.onEventClick, _this, ev);
15039 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15040 style : 'position: absolute',
15041 unselectable : "on",
15044 cls: 'fc-event-inner',
15048 cls: 'fc-event-title',
15056 cls: 'ui-resizable-handle ui-resizable-e',
15057 html : '  '
15063 var ctr = _this.el.select('.fc-event-container',true).first();
15064 var cg = ctr.createChild(cfg);
15066 var sbox = c.select('.fc-day-content',true).first().getBox();
15067 var ebox = c.select('.fc-day-content',true).first().getBox();
15069 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15070 cg.setWidth(ebox.right - sbox.x -2);
15072 cg.on('click', _this.onMoreEventClick, _this, c.more);
15082 onEventEnter: function (e, el,event,d) {
15083 this.fireEvent('evententer', this, el, event);
15086 onEventLeave: function (e, el,event,d) {
15087 this.fireEvent('eventleave', this, el, event);
15090 onEventClick: function (e, el,event,d) {
15091 this.fireEvent('eventclick', this, el, event);
15094 onMonthChange: function () {
15098 onMoreEventClick: function(e, el, more)
15102 this.calpopover.placement = 'right';
15103 this.calpopover.setTitle('More');
15105 this.calpopover.setContent('');
15107 var ctr = this.calpopover.el.select('.popover-content', true).first();
15109 Roo.each(more, function(m){
15111 cls : 'fc-event-hori fc-event-draggable',
15114 var cg = ctr.createChild(cfg);
15116 cg.on('click', _this.onEventClick, _this, m);
15119 this.calpopover.show(el);
15124 onLoad: function ()
15126 this.calevents = [];
15129 if(this.store.getCount() > 0){
15130 this.store.data.each(function(d){
15133 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15134 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15135 time : d.data.start_time,
15136 title : d.data.title,
15137 description : d.data.description,
15138 venue : d.data.venue
15143 this.renderEvents();
15145 if(this.calevents.length && this.loadMask){
15146 this.maskEl.hide();
15150 onBeforeLoad: function()
15152 this.clearEvents();
15154 this.maskEl.show();
15168 * @class Roo.bootstrap.Popover
15169 * @extends Roo.bootstrap.Component
15170 * Bootstrap Popover class
15171 * @cfg {String} html contents of the popover (or false to use children..)
15172 * @cfg {String} title of popover (or false to hide)
15173 * @cfg {String} placement how it is placed
15174 * @cfg {String} trigger click || hover (or false to trigger manually)
15175 * @cfg {String} over what (parent or false to trigger manually.)
15176 * @cfg {Number} delay - delay before showing
15179 * Create a new Popover
15180 * @param {Object} config The config object
15183 Roo.bootstrap.Popover = function(config){
15184 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15187 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15189 title: 'Fill in a title',
15192 placement : 'right',
15193 trigger : 'hover', // hover
15199 can_build_overlaid : false,
15201 getChildContainer : function()
15203 return this.el.select('.popover-content',true).first();
15206 getAutoCreate : function(){
15207 Roo.log('make popover?');
15209 cls : 'popover roo-dynamic',
15210 style: 'display:block',
15216 cls : 'popover-inner',
15220 cls: 'popover-title',
15224 cls : 'popover-content',
15235 setTitle: function(str)
15238 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15240 setContent: function(str)
15243 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15245 // as it get's added to the bottom of the page.
15246 onRender : function(ct, position)
15248 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15250 var cfg = Roo.apply({}, this.getAutoCreate());
15254 cfg.cls += ' ' + this.cls;
15257 cfg.style = this.style;
15259 Roo.log("adding to ")
15260 this.el = Roo.get(document.body).createChild(cfg, position);
15266 initEvents : function()
15268 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15269 this.el.enableDisplayMode('block');
15271 if (this.over === false) {
15274 if (this.triggers === false) {
15277 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15278 var triggers = this.trigger ? this.trigger.split(' ') : [];
15279 Roo.each(triggers, function(trigger) {
15281 if (trigger == 'click') {
15282 on_el.on('click', this.toggle, this);
15283 } else if (trigger != 'manual') {
15284 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15285 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15287 on_el.on(eventIn ,this.enter, this);
15288 on_el.on(eventOut, this.leave, this);
15299 toggle : function () {
15300 this.hoverState == 'in' ? this.leave() : this.enter();
15303 enter : function () {
15306 clearTimeout(this.timeout);
15308 this.hoverState = 'in';
15310 if (!this.delay || !this.delay.show) {
15315 this.timeout = setTimeout(function () {
15316 if (_t.hoverState == 'in') {
15319 }, this.delay.show)
15321 leave : function() {
15322 clearTimeout(this.timeout);
15324 this.hoverState = 'out';
15326 if (!this.delay || !this.delay.hide) {
15331 this.timeout = setTimeout(function () {
15332 if (_t.hoverState == 'out') {
15335 }, this.delay.hide)
15338 show : function (on_el)
15341 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15344 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15345 if (this.html !== false) {
15346 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15348 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15349 if (!this.title.length) {
15350 this.el.select('.popover-title',true).hide();
15353 var placement = typeof this.placement == 'function' ?
15354 this.placement.call(this, this.el, on_el) :
15357 var autoToken = /\s?auto?\s?/i;
15358 var autoPlace = autoToken.test(placement);
15360 placement = placement.replace(autoToken, '') || 'top';
15364 //this.el.setXY([0,0]);
15366 this.el.dom.style.display='block';
15367 this.el.addClass(placement);
15369 //this.el.appendTo(on_el);
15371 var p = this.getPosition();
15372 var box = this.el.getBox();
15377 var align = Roo.bootstrap.Popover.alignment[placement];
15378 this.el.alignTo(on_el, align[0],align[1]);
15379 //var arrow = this.el.select('.arrow',true).first();
15380 //arrow.set(align[2],
15382 this.el.addClass('in');
15385 if (this.el.hasClass('fade')) {
15392 this.el.setXY([0,0]);
15393 this.el.removeClass('in');
15395 this.hoverState = null;
15401 Roo.bootstrap.Popover.alignment = {
15402 'left' : ['r-l', [-10,0], 'right'],
15403 'right' : ['l-r', [10,0], 'left'],
15404 'bottom' : ['t-b', [0,10], 'top'],
15405 'top' : [ 'b-t', [0,-10], 'bottom']
15416 * @class Roo.bootstrap.Progress
15417 * @extends Roo.bootstrap.Component
15418 * Bootstrap Progress class
15419 * @cfg {Boolean} striped striped of the progress bar
15420 * @cfg {Boolean} active animated of the progress bar
15424 * Create a new Progress
15425 * @param {Object} config The config object
15428 Roo.bootstrap.Progress = function(config){
15429 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15432 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15437 getAutoCreate : function(){
15445 cfg.cls += ' progress-striped';
15449 cfg.cls += ' active';
15468 * @class Roo.bootstrap.ProgressBar
15469 * @extends Roo.bootstrap.Component
15470 * Bootstrap ProgressBar class
15471 * @cfg {Number} aria_valuenow aria-value now
15472 * @cfg {Number} aria_valuemin aria-value min
15473 * @cfg {Number} aria_valuemax aria-value max
15474 * @cfg {String} label label for the progress bar
15475 * @cfg {String} panel (success | info | warning | danger )
15476 * @cfg {String} role role of the progress bar
15477 * @cfg {String} sr_only text
15481 * Create a new ProgressBar
15482 * @param {Object} config The config object
15485 Roo.bootstrap.ProgressBar = function(config){
15486 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15489 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15493 aria_valuemax : 100,
15499 getAutoCreate : function()
15504 cls: 'progress-bar',
15505 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15517 cfg.role = this.role;
15520 if(this.aria_valuenow){
15521 cfg['aria-valuenow'] = this.aria_valuenow;
15524 if(this.aria_valuemin){
15525 cfg['aria-valuemin'] = this.aria_valuemin;
15528 if(this.aria_valuemax){
15529 cfg['aria-valuemax'] = this.aria_valuemax;
15532 if(this.label && !this.sr_only){
15533 cfg.html = this.label;
15537 cfg.cls += ' progress-bar-' + this.panel;
15543 update : function(aria_valuenow)
15545 this.aria_valuenow = aria_valuenow;
15547 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15562 * @class Roo.bootstrap.TabGroup
15563 * @extends Roo.bootstrap.Column
15564 * Bootstrap Column class
15565 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15566 * @cfg {Boolean} carousel true to make the group behave like a carousel
15567 * @cfg {Number} bullets show the panel pointer.. default 0
15568 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15569 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15570 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15573 * Create a new TabGroup
15574 * @param {Object} config The config object
15577 Roo.bootstrap.TabGroup = function(config){
15578 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15580 this.navId = Roo.id();
15583 Roo.bootstrap.TabGroup.register(this);
15587 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15590 transition : false,
15595 slideOnTouch : false,
15597 getAutoCreate : function()
15599 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15601 cfg.cls += ' tab-content';
15603 Roo.log('get auto create...............');
15605 if (this.carousel) {
15606 cfg.cls += ' carousel slide';
15609 cls : 'carousel-inner'
15612 if(this.bullets > 0 && !Roo.isTouch){
15615 cls : 'carousel-bullets',
15619 if(this.bullets_cls){
15620 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15623 for (var i = 0; i < this.bullets; i++){
15625 cls : 'bullet bullet-' + i
15633 cfg.cn[0].cn = bullets;
15640 initEvents: function()
15642 Roo.log('-------- init events on tab group ---------');
15644 if(this.bullets > 0 && !Roo.isTouch){
15650 if(Roo.isTouch && this.slideOnTouch){
15651 this.el.on("touchstart", this.onTouchStart, this);
15654 if(this.autoslide){
15657 this.slideFn = window.setInterval(function() {
15658 _this.showPanelNext();
15664 onTouchStart : function(e, el, o)
15666 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15670 this.showPanelNext();
15673 getChildContainer : function()
15675 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15679 * register a Navigation item
15680 * @param {Roo.bootstrap.NavItem} the navitem to add
15682 register : function(item)
15684 this.tabs.push( item);
15685 item.navId = this.navId; // not really needed..
15689 getActivePanel : function()
15692 Roo.each(this.tabs, function(t) {
15702 getPanelByName : function(n)
15705 Roo.each(this.tabs, function(t) {
15706 if (t.tabId == n) {
15714 indexOfPanel : function(p)
15717 Roo.each(this.tabs, function(t,i) {
15718 if (t.tabId == p.tabId) {
15727 * show a specific panel
15728 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15729 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15731 showPanel : function (pan)
15733 if(this.transition){
15734 Roo.log("waiting for the transitionend");
15738 if (typeof(pan) == 'number') {
15739 pan = this.tabs[pan];
15741 if (typeof(pan) == 'string') {
15742 pan = this.getPanelByName(pan);
15744 if (pan.tabId == this.getActivePanel().tabId) {
15747 var cur = this.getActivePanel();
15749 if (false === cur.fireEvent('beforedeactivate')) {
15753 if(this.bullets > 0 && !Roo.isTouch){
15754 this.setActiveBullet(this.indexOfPanel(pan));
15757 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15759 this.transition = true;
15760 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15761 var lr = dir == 'next' ? 'left' : 'right';
15762 pan.el.addClass(dir); // or prev
15763 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15764 cur.el.addClass(lr); // or right
15765 pan.el.addClass(lr);
15768 cur.el.on('transitionend', function() {
15769 Roo.log("trans end?");
15771 pan.el.removeClass([lr,dir]);
15772 pan.setActive(true);
15774 cur.el.removeClass([lr]);
15775 cur.setActive(false);
15777 _this.transition = false;
15779 }, this, { single: true } );
15784 cur.setActive(false);
15785 pan.setActive(true);
15790 showPanelNext : function()
15792 var i = this.indexOfPanel(this.getActivePanel());
15794 if (i >= this.tabs.length - 1 && !this.autoslide) {
15798 if (i >= this.tabs.length - 1 && this.autoslide) {
15802 this.showPanel(this.tabs[i+1]);
15805 showPanelPrev : function()
15807 var i = this.indexOfPanel(this.getActivePanel());
15809 if (i < 1 && !this.autoslide) {
15813 if (i < 1 && this.autoslide) {
15814 i = this.tabs.length;
15817 this.showPanel(this.tabs[i-1]);
15820 initBullet : function()
15828 for (var i = 0; i < this.bullets; i++){
15829 var bullet = this.el.select('.bullet-' + i, true).first();
15835 bullet.on('click', (function(e, el, o, ii, t){
15837 e.preventDefault();
15839 _this.showPanel(ii);
15841 if(_this.autoslide && _this.slideFn){
15842 clearInterval(_this.slideFn);
15843 _this.slideFn = window.setInterval(function() {
15844 _this.showPanelNext();
15848 }).createDelegate(this, [i, bullet], true));
15852 setActiveBullet : function(i)
15858 Roo.each(this.el.select('.bullet', true).elements, function(el){
15859 el.removeClass('selected');
15862 var bullet = this.el.select('.bullet-' + i, true).first();
15868 bullet.addClass('selected');
15879 Roo.apply(Roo.bootstrap.TabGroup, {
15883 * register a Navigation Group
15884 * @param {Roo.bootstrap.NavGroup} the navgroup to add
15886 register : function(navgrp)
15888 this.groups[navgrp.navId] = navgrp;
15892 * fetch a Navigation Group based on the navigation ID
15893 * if one does not exist , it will get created.
15894 * @param {string} the navgroup to add
15895 * @returns {Roo.bootstrap.NavGroup} the navgroup
15897 get: function(navId) {
15898 if (typeof(this.groups[navId]) == 'undefined') {
15899 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15901 return this.groups[navId] ;
15916 * @class Roo.bootstrap.TabPanel
15917 * @extends Roo.bootstrap.Component
15918 * Bootstrap TabPanel class
15919 * @cfg {Boolean} active panel active
15920 * @cfg {String} html panel content
15921 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15922 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15926 * Create a new TabPanel
15927 * @param {Object} config The config object
15930 Roo.bootstrap.TabPanel = function(config){
15931 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15935 * Fires when the active status changes
15936 * @param {Roo.bootstrap.TabPanel} this
15937 * @param {Boolean} state the new state
15942 * @event beforedeactivate
15943 * Fires before a tab is de-activated - can be used to do validation on a form.
15944 * @param {Roo.bootstrap.TabPanel} this
15945 * @return {Boolean} false if there is an error
15948 'beforedeactivate': true
15951 this.tabId = this.tabId || Roo.id();
15955 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
15962 getAutoCreate : function(){
15965 // item is needed for carousel - not sure if it has any effect otherwise
15966 cls: 'tab-pane item',
15967 html: this.html || ''
15971 cfg.cls += ' active';
15975 cfg.tabId = this.tabId;
15982 initEvents: function()
15984 Roo.log('-------- init events on tab panel ---------');
15986 var p = this.parent();
15987 this.navId = this.navId || p.navId;
15989 if (typeof(this.navId) != 'undefined') {
15990 // not really needed.. but just in case.. parent should be a NavGroup.
15991 var tg = Roo.bootstrap.TabGroup.get(this.navId);
15992 Roo.log(['register', tg, this]);
15995 var i = tg.tabs.length - 1;
15997 if(this.active && tg.bullets > 0 && i < tg.bullets){
15998 tg.setActiveBullet(i);
16005 onRender : function(ct, position)
16007 // Roo.log("Call onRender: " + this.xtype);
16009 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16017 setActive: function(state)
16019 Roo.log("panel - set active " + this.tabId + "=" + state);
16021 this.active = state;
16023 this.el.removeClass('active');
16025 } else if (!this.el.hasClass('active')) {
16026 this.el.addClass('active');
16029 this.fireEvent('changed', this, state);
16046 * @class Roo.bootstrap.DateField
16047 * @extends Roo.bootstrap.Input
16048 * Bootstrap DateField class
16049 * @cfg {Number} weekStart default 0
16050 * @cfg {String} viewMode default empty, (months|years)
16051 * @cfg {String} minViewMode default empty, (months|years)
16052 * @cfg {Number} startDate default -Infinity
16053 * @cfg {Number} endDate default Infinity
16054 * @cfg {Boolean} todayHighlight default false
16055 * @cfg {Boolean} todayBtn default false
16056 * @cfg {Boolean} calendarWeeks default false
16057 * @cfg {Object} daysOfWeekDisabled default empty
16058 * @cfg {Boolean} singleMode default false (true | false)
16060 * @cfg {Boolean} keyboardNavigation default true
16061 * @cfg {String} language default en
16064 * Create a new DateField
16065 * @param {Object} config The config object
16068 Roo.bootstrap.DateField = function(config){
16069 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16073 * Fires when this field show.
16074 * @param {Roo.bootstrap.DateField} this
16075 * @param {Mixed} date The date value
16080 * Fires when this field hide.
16081 * @param {Roo.bootstrap.DateField} this
16082 * @param {Mixed} date The date value
16087 * Fires when select a date.
16088 * @param {Roo.bootstrap.DateField} this
16089 * @param {Mixed} date The date value
16095 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16098 * @cfg {String} format
16099 * The default date format string which can be overriden for localization support. The format must be
16100 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16104 * @cfg {String} altFormats
16105 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16106 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16108 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16116 todayHighlight : false,
16122 keyboardNavigation: true,
16124 calendarWeeks: false,
16126 startDate: -Infinity,
16130 daysOfWeekDisabled: [],
16134 singleMode : false,
16136 UTCDate: function()
16138 return new Date(Date.UTC.apply(Date, arguments));
16141 UTCToday: function()
16143 var today = new Date();
16144 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16147 getDate: function() {
16148 var d = this.getUTCDate();
16149 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16152 getUTCDate: function() {
16156 setDate: function(d) {
16157 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16160 setUTCDate: function(d) {
16162 this.setValue(this.formatDate(this.date));
16165 onRender: function(ct, position)
16168 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16170 this.language = this.language || 'en';
16171 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16172 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16174 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16175 this.format = this.format || 'm/d/y';
16176 this.isInline = false;
16177 this.isInput = true;
16178 this.component = this.el.select('.add-on', true).first() || false;
16179 this.component = (this.component && this.component.length === 0) ? false : this.component;
16180 this.hasInput = this.component && this.inputEL().length;
16182 if (typeof(this.minViewMode === 'string')) {
16183 switch (this.minViewMode) {
16185 this.minViewMode = 1;
16188 this.minViewMode = 2;
16191 this.minViewMode = 0;
16196 if (typeof(this.viewMode === 'string')) {
16197 switch (this.viewMode) {
16210 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16212 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16214 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16216 this.picker().on('mousedown', this.onMousedown, this);
16217 this.picker().on('click', this.onClick, this);
16219 this.picker().addClass('datepicker-dropdown');
16221 this.startViewMode = this.viewMode;
16223 if(this.singleMode){
16224 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16225 v.setVisibilityMode(Roo.Element.DISPLAY)
16229 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16230 v.setStyle('width', '189px');
16234 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16235 if(!this.calendarWeeks){
16240 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16241 v.attr('colspan', function(i, val){
16242 return parseInt(val) + 1;
16247 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16249 this.setStartDate(this.startDate);
16250 this.setEndDate(this.endDate);
16252 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16259 if(this.isInline) {
16264 picker : function()
16266 return this.pickerEl;
16267 // return this.el.select('.datepicker', true).first();
16270 fillDow: function()
16272 var dowCnt = this.weekStart;
16281 if(this.calendarWeeks){
16289 while (dowCnt < this.weekStart + 7) {
16293 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16297 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16300 fillMonths: function()
16303 var months = this.picker().select('>.datepicker-months td', true).first();
16305 months.dom.innerHTML = '';
16311 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16314 months.createChild(month);
16321 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;
16323 if (this.date < this.startDate) {
16324 this.viewDate = new Date(this.startDate);
16325 } else if (this.date > this.endDate) {
16326 this.viewDate = new Date(this.endDate);
16328 this.viewDate = new Date(this.date);
16336 var d = new Date(this.viewDate),
16337 year = d.getUTCFullYear(),
16338 month = d.getUTCMonth(),
16339 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16340 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16341 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16342 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16343 currentDate = this.date && this.date.valueOf(),
16344 today = this.UTCToday();
16346 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16348 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16350 // this.picker.select('>tfoot th.today').
16351 // .text(dates[this.language].today)
16352 // .toggle(this.todayBtn !== false);
16354 this.updateNavArrows();
16357 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16359 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16361 prevMonth.setUTCDate(day);
16363 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16365 var nextMonth = new Date(prevMonth);
16367 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16369 nextMonth = nextMonth.valueOf();
16371 var fillMonths = false;
16373 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16375 while(prevMonth.valueOf() < nextMonth) {
16378 if (prevMonth.getUTCDay() === this.weekStart) {
16380 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16388 if(this.calendarWeeks){
16389 // ISO 8601: First week contains first thursday.
16390 // ISO also states week starts on Monday, but we can be more abstract here.
16392 // Start of current week: based on weekstart/current date
16393 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16394 // Thursday of this week
16395 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16396 // First Thursday of year, year from thursday
16397 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16398 // Calendar week: ms between thursdays, div ms per day, div 7 days
16399 calWeek = (th - yth) / 864e5 / 7 + 1;
16401 fillMonths.cn.push({
16409 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16411 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16414 if (this.todayHighlight &&
16415 prevMonth.getUTCFullYear() == today.getFullYear() &&
16416 prevMonth.getUTCMonth() == today.getMonth() &&
16417 prevMonth.getUTCDate() == today.getDate()) {
16418 clsName += ' today';
16421 if (currentDate && prevMonth.valueOf() === currentDate) {
16422 clsName += ' active';
16425 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16426 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16427 clsName += ' disabled';
16430 fillMonths.cn.push({
16432 cls: 'day ' + clsName,
16433 html: prevMonth.getDate()
16436 prevMonth.setDate(prevMonth.getDate()+1);
16439 var currentYear = this.date && this.date.getUTCFullYear();
16440 var currentMonth = this.date && this.date.getUTCMonth();
16442 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16444 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16445 v.removeClass('active');
16447 if(currentYear === year && k === currentMonth){
16448 v.addClass('active');
16451 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16452 v.addClass('disabled');
16458 year = parseInt(year/10, 10) * 10;
16460 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16462 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16465 for (var i = -1; i < 11; i++) {
16466 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16468 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16476 showMode: function(dir)
16479 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16482 Roo.each(this.picker().select('>div',true).elements, function(v){
16483 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16486 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16491 if(this.isInline) return;
16493 this.picker().removeClass(['bottom', 'top']);
16495 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16497 * place to the top of element!
16501 this.picker().addClass('top');
16502 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16507 this.picker().addClass('bottom');
16509 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16512 parseDate : function(value)
16514 if(!value || value instanceof Date){
16517 var v = Date.parseDate(value, this.format);
16518 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16519 v = Date.parseDate(value, 'Y-m-d');
16521 if(!v && this.altFormats){
16522 if(!this.altFormatsArray){
16523 this.altFormatsArray = this.altFormats.split("|");
16525 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16526 v = Date.parseDate(value, this.altFormatsArray[i]);
16532 formatDate : function(date, fmt)
16534 return (!date || !(date instanceof Date)) ?
16535 date : date.dateFormat(fmt || this.format);
16538 onFocus : function()
16540 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16544 onBlur : function()
16546 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16548 var d = this.inputEl().getValue();
16557 this.picker().show();
16561 this.fireEvent('show', this, this.date);
16566 if(this.isInline) return;
16567 this.picker().hide();
16568 this.viewMode = this.startViewMode;
16571 this.fireEvent('hide', this, this.date);
16575 onMousedown: function(e)
16577 e.stopPropagation();
16578 e.preventDefault();
16583 Roo.bootstrap.DateField.superclass.keyup.call(this);
16587 setValue: function(v)
16590 // v can be a string or a date..
16593 var d = new Date(this.parseDate(v) ).clearTime();
16595 if(isNaN(d.getTime())){
16596 this.date = this.viewDate = '';
16597 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16601 v = this.formatDate(d);
16603 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16605 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16609 this.fireEvent('select', this, this.date);
16613 getValue: function()
16615 return this.formatDate(this.date);
16618 fireKey: function(e)
16620 if (!this.picker().isVisible()){
16621 if (e.keyCode == 27) // allow escape to hide and re-show picker
16626 var dateChanged = false,
16628 newDate, newViewDate;
16633 e.preventDefault();
16637 if (!this.keyboardNavigation) break;
16638 dir = e.keyCode == 37 ? -1 : 1;
16641 newDate = this.moveYear(this.date, dir);
16642 newViewDate = this.moveYear(this.viewDate, dir);
16643 } else if (e.shiftKey){
16644 newDate = this.moveMonth(this.date, dir);
16645 newViewDate = this.moveMonth(this.viewDate, dir);
16647 newDate = new Date(this.date);
16648 newDate.setUTCDate(this.date.getUTCDate() + dir);
16649 newViewDate = new Date(this.viewDate);
16650 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16652 if (this.dateWithinRange(newDate)){
16653 this.date = newDate;
16654 this.viewDate = newViewDate;
16655 this.setValue(this.formatDate(this.date));
16657 e.preventDefault();
16658 dateChanged = true;
16663 if (!this.keyboardNavigation) break;
16664 dir = e.keyCode == 38 ? -1 : 1;
16666 newDate = this.moveYear(this.date, dir);
16667 newViewDate = this.moveYear(this.viewDate, dir);
16668 } else if (e.shiftKey){
16669 newDate = this.moveMonth(this.date, dir);
16670 newViewDate = this.moveMonth(this.viewDate, dir);
16672 newDate = new Date(this.date);
16673 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16674 newViewDate = new Date(this.viewDate);
16675 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16677 if (this.dateWithinRange(newDate)){
16678 this.date = newDate;
16679 this.viewDate = newViewDate;
16680 this.setValue(this.formatDate(this.date));
16682 e.preventDefault();
16683 dateChanged = true;
16687 this.setValue(this.formatDate(this.date));
16689 e.preventDefault();
16692 this.setValue(this.formatDate(this.date));
16706 onClick: function(e)
16708 e.stopPropagation();
16709 e.preventDefault();
16711 var target = e.getTarget();
16713 if(target.nodeName.toLowerCase() === 'i'){
16714 target = Roo.get(target).dom.parentNode;
16717 var nodeName = target.nodeName;
16718 var className = target.className;
16719 var html = target.innerHTML;
16720 //Roo.log(nodeName);
16722 switch(nodeName.toLowerCase()) {
16724 switch(className) {
16730 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16731 switch(this.viewMode){
16733 this.viewDate = this.moveMonth(this.viewDate, dir);
16737 this.viewDate = this.moveYear(this.viewDate, dir);
16743 var date = new Date();
16744 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16746 this.setValue(this.formatDate(this.date));
16753 if (className.indexOf('disabled') < 0) {
16754 this.viewDate.setUTCDate(1);
16755 if (className.indexOf('month') > -1) {
16756 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16758 var year = parseInt(html, 10) || 0;
16759 this.viewDate.setUTCFullYear(year);
16763 if(this.singleMode){
16764 this.setValue(this.formatDate(this.viewDate));
16775 //Roo.log(className);
16776 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16777 var day = parseInt(html, 10) || 1;
16778 var year = this.viewDate.getUTCFullYear(),
16779 month = this.viewDate.getUTCMonth();
16781 if (className.indexOf('old') > -1) {
16788 } else if (className.indexOf('new') > -1) {
16796 //Roo.log([year,month,day]);
16797 this.date = this.UTCDate(year, month, day,0,0,0,0);
16798 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16800 //Roo.log(this.formatDate(this.date));
16801 this.setValue(this.formatDate(this.date));
16808 setStartDate: function(startDate)
16810 this.startDate = startDate || -Infinity;
16811 if (this.startDate !== -Infinity) {
16812 this.startDate = this.parseDate(this.startDate);
16815 this.updateNavArrows();
16818 setEndDate: function(endDate)
16820 this.endDate = endDate || Infinity;
16821 if (this.endDate !== Infinity) {
16822 this.endDate = this.parseDate(this.endDate);
16825 this.updateNavArrows();
16828 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16830 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16831 if (typeof(this.daysOfWeekDisabled) !== 'object') {
16832 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16834 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16835 return parseInt(d, 10);
16838 this.updateNavArrows();
16841 updateNavArrows: function()
16843 if(this.singleMode){
16847 var d = new Date(this.viewDate),
16848 year = d.getUTCFullYear(),
16849 month = d.getUTCMonth();
16851 Roo.each(this.picker().select('.prev', true).elements, function(v){
16853 switch (this.viewMode) {
16856 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16862 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16869 Roo.each(this.picker().select('.next', true).elements, function(v){
16871 switch (this.viewMode) {
16874 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16880 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16888 moveMonth: function(date, dir)
16890 if (!dir) return date;
16891 var new_date = new Date(date.valueOf()),
16892 day = new_date.getUTCDate(),
16893 month = new_date.getUTCMonth(),
16894 mag = Math.abs(dir),
16896 dir = dir > 0 ? 1 : -1;
16899 // If going back one month, make sure month is not current month
16900 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16902 return new_date.getUTCMonth() == month;
16904 // If going forward one month, make sure month is as expected
16905 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16907 return new_date.getUTCMonth() != new_month;
16909 new_month = month + dir;
16910 new_date.setUTCMonth(new_month);
16911 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16912 if (new_month < 0 || new_month > 11)
16913 new_month = (new_month + 12) % 12;
16915 // For magnitudes >1, move one month at a time...
16916 for (var i=0; i<mag; i++)
16917 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16918 new_date = this.moveMonth(new_date, dir);
16919 // ...then reset the day, keeping it in the new month
16920 new_month = new_date.getUTCMonth();
16921 new_date.setUTCDate(day);
16923 return new_month != new_date.getUTCMonth();
16926 // Common date-resetting loop -- if date is beyond end of month, make it
16929 new_date.setUTCDate(--day);
16930 new_date.setUTCMonth(new_month);
16935 moveYear: function(date, dir)
16937 return this.moveMonth(date, dir*12);
16940 dateWithinRange: function(date)
16942 return date >= this.startDate && date <= this.endDate;
16948 this.picker().remove();
16953 Roo.apply(Roo.bootstrap.DateField, {
16964 html: '<i class="fa fa-arrow-left"/>'
16974 html: '<i class="fa fa-arrow-right"/>'
17016 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17017 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17018 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17019 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17020 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17033 navFnc: 'FullYear',
17038 navFnc: 'FullYear',
17043 Roo.apply(Roo.bootstrap.DateField, {
17047 cls: 'datepicker dropdown-menu roo-dynamic',
17051 cls: 'datepicker-days',
17055 cls: 'table-condensed',
17057 Roo.bootstrap.DateField.head,
17061 Roo.bootstrap.DateField.footer
17068 cls: 'datepicker-months',
17072 cls: 'table-condensed',
17074 Roo.bootstrap.DateField.head,
17075 Roo.bootstrap.DateField.content,
17076 Roo.bootstrap.DateField.footer
17083 cls: 'datepicker-years',
17087 cls: 'table-condensed',
17089 Roo.bootstrap.DateField.head,
17090 Roo.bootstrap.DateField.content,
17091 Roo.bootstrap.DateField.footer
17110 * @class Roo.bootstrap.TimeField
17111 * @extends Roo.bootstrap.Input
17112 * Bootstrap DateField class
17116 * Create a new TimeField
17117 * @param {Object} config The config object
17120 Roo.bootstrap.TimeField = function(config){
17121 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17125 * Fires when this field show.
17126 * @param {Roo.bootstrap.DateField} thisthis
17127 * @param {Mixed} date The date value
17132 * Fires when this field hide.
17133 * @param {Roo.bootstrap.DateField} this
17134 * @param {Mixed} date The date value
17139 * Fires when select a date.
17140 * @param {Roo.bootstrap.DateField} this
17141 * @param {Mixed} date The date value
17147 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17150 * @cfg {String} format
17151 * The default time format string which can be overriden for localization support. The format must be
17152 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17156 onRender: function(ct, position)
17159 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17161 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17163 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17165 this.pop = this.picker().select('>.datepicker-time',true).first();
17166 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17168 this.picker().on('mousedown', this.onMousedown, this);
17169 this.picker().on('click', this.onClick, this);
17171 this.picker().addClass('datepicker-dropdown');
17176 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17177 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17178 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17179 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17180 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17181 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17185 fireKey: function(e){
17186 if (!this.picker().isVisible()){
17187 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17193 e.preventDefault();
17201 this.onTogglePeriod();
17204 this.onIncrementMinutes();
17207 this.onDecrementMinutes();
17216 onClick: function(e) {
17217 e.stopPropagation();
17218 e.preventDefault();
17221 picker : function()
17223 return this.el.select('.datepicker', true).first();
17226 fillTime: function()
17228 var time = this.pop.select('tbody', true).first();
17230 time.dom.innerHTML = '';
17245 cls: 'hours-up glyphicon glyphicon-chevron-up'
17265 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17286 cls: 'timepicker-hour',
17301 cls: 'timepicker-minute',
17316 cls: 'btn btn-primary period',
17338 cls: 'hours-down glyphicon glyphicon-chevron-down'
17358 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17376 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17383 var hours = this.time.getHours();
17384 var minutes = this.time.getMinutes();
17397 hours = hours - 12;
17401 hours = '0' + hours;
17405 minutes = '0' + minutes;
17408 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17409 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17410 this.pop.select('button', true).first().dom.innerHTML = period;
17416 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17418 var cls = ['bottom'];
17420 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17427 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17432 this.picker().addClass(cls.join('-'));
17436 Roo.each(cls, function(c){
17438 _this.picker().setTop(_this.inputEl().getHeight());
17442 _this.picker().setTop(0 - _this.picker().getHeight());
17447 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17451 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17458 onFocus : function()
17460 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17464 onBlur : function()
17466 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17472 this.picker().show();
17477 this.fireEvent('show', this, this.date);
17482 this.picker().hide();
17485 this.fireEvent('hide', this, this.date);
17488 setTime : function()
17491 this.setValue(this.time.format(this.format));
17493 this.fireEvent('select', this, this.date);
17498 onMousedown: function(e){
17499 e.stopPropagation();
17500 e.preventDefault();
17503 onIncrementHours: function()
17505 Roo.log('onIncrementHours');
17506 this.time = this.time.add(Date.HOUR, 1);
17511 onDecrementHours: function()
17513 Roo.log('onDecrementHours');
17514 this.time = this.time.add(Date.HOUR, -1);
17518 onIncrementMinutes: function()
17520 Roo.log('onIncrementMinutes');
17521 this.time = this.time.add(Date.MINUTE, 1);
17525 onDecrementMinutes: function()
17527 Roo.log('onDecrementMinutes');
17528 this.time = this.time.add(Date.MINUTE, -1);
17532 onTogglePeriod: function()
17534 Roo.log('onTogglePeriod');
17535 this.time = this.time.add(Date.HOUR, 12);
17542 Roo.apply(Roo.bootstrap.TimeField, {
17572 cls: 'btn btn-info ok',
17584 Roo.apply(Roo.bootstrap.TimeField, {
17588 cls: 'datepicker dropdown-menu',
17592 cls: 'datepicker-time',
17596 cls: 'table-condensed',
17598 Roo.bootstrap.TimeField.content,
17599 Roo.bootstrap.TimeField.footer
17618 * @class Roo.bootstrap.MonthField
17619 * @extends Roo.bootstrap.Input
17620 * Bootstrap MonthField class
17622 * @cfg {String} language default en
17625 * Create a new MonthField
17626 * @param {Object} config The config object
17629 Roo.bootstrap.MonthField = function(config){
17630 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17635 * Fires when this field show.
17636 * @param {Roo.bootstrap.MonthField} this
17637 * @param {Mixed} date The date value
17642 * Fires when this field hide.
17643 * @param {Roo.bootstrap.MonthField} this
17644 * @param {Mixed} date The date value
17649 * Fires when select a date.
17650 * @param {Roo.bootstrap.MonthField} this
17651 * @param {String} oldvalue The old value
17652 * @param {String} newvalue The new value
17658 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17660 onRender: function(ct, position)
17663 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17665 this.language = this.language || 'en';
17666 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17667 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17669 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17670 this.isInline = false;
17671 this.isInput = true;
17672 this.component = this.el.select('.add-on', true).first() || false;
17673 this.component = (this.component && this.component.length === 0) ? false : this.component;
17674 this.hasInput = this.component && this.inputEL().length;
17676 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17678 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17680 this.picker().on('mousedown', this.onMousedown, this);
17681 this.picker().on('click', this.onClick, this);
17683 this.picker().addClass('datepicker-dropdown');
17685 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17686 v.setStyle('width', '189px');
17693 if(this.isInline) {
17699 setValue: function(v, suppressEvent)
17701 var o = this.getValue();
17703 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17707 if(suppressEvent !== true){
17708 this.fireEvent('select', this, o, v);
17713 getValue: function()
17718 onClick: function(e)
17720 e.stopPropagation();
17721 e.preventDefault();
17723 var target = e.getTarget();
17725 if(target.nodeName.toLowerCase() === 'i'){
17726 target = Roo.get(target).dom.parentNode;
17729 var nodeName = target.nodeName;
17730 var className = target.className;
17731 var html = target.innerHTML;
17733 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17737 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17739 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17745 picker : function()
17747 return this.pickerEl;
17750 fillMonths: function()
17753 var months = this.picker().select('>.datepicker-months td', true).first();
17755 months.dom.innerHTML = '';
17761 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17764 months.createChild(month);
17773 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17774 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17777 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17778 e.removeClass('active');
17780 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17781 e.addClass('active');
17788 if(this.isInline) return;
17790 this.picker().removeClass(['bottom', 'top']);
17792 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17794 * place to the top of element!
17798 this.picker().addClass('top');
17799 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17804 this.picker().addClass('bottom');
17806 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17809 onFocus : function()
17811 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17815 onBlur : function()
17817 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17819 var d = this.inputEl().getValue();
17828 this.picker().show();
17829 this.picker().select('>.datepicker-months', true).first().show();
17833 this.fireEvent('show', this, this.date);
17838 if(this.isInline) return;
17839 this.picker().hide();
17840 this.fireEvent('hide', this, this.date);
17844 onMousedown: function(e)
17846 e.stopPropagation();
17847 e.preventDefault();
17852 Roo.bootstrap.MonthField.superclass.keyup.call(this);
17856 fireKey: function(e)
17858 if (!this.picker().isVisible()){
17859 if (e.keyCode == 27) // allow escape to hide and re-show picker
17869 e.preventDefault();
17873 dir = e.keyCode == 37 ? -1 : 1;
17875 this.vIndex = this.vIndex + dir;
17877 if(this.vIndex < 0){
17881 if(this.vIndex > 11){
17885 if(isNaN(this.vIndex)){
17889 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17895 dir = e.keyCode == 38 ? -1 : 1;
17897 this.vIndex = this.vIndex + dir * 4;
17899 if(this.vIndex < 0){
17903 if(this.vIndex > 11){
17907 if(isNaN(this.vIndex)){
17911 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17916 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17917 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17921 e.preventDefault();
17924 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17925 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17941 this.picker().remove();
17946 Roo.apply(Roo.bootstrap.MonthField, {
17965 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17966 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17971 Roo.apply(Roo.bootstrap.MonthField, {
17975 cls: 'datepicker dropdown-menu roo-dynamic',
17979 cls: 'datepicker-months',
17983 cls: 'table-condensed',
17985 Roo.bootstrap.DateField.content
18005 * @class Roo.bootstrap.CheckBox
18006 * @extends Roo.bootstrap.Input
18007 * Bootstrap CheckBox class
18009 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18010 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18011 * @cfg {String} boxLabel The text that appears beside the checkbox
18012 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18013 * @cfg {Boolean} checked initnal the element
18014 * @cfg {Boolean} inline inline the element (default false)
18015 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18018 * Create a new CheckBox
18019 * @param {Object} config The config object
18022 Roo.bootstrap.CheckBox = function(config){
18023 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18028 * Fires when the element is checked or unchecked.
18029 * @param {Roo.bootstrap.CheckBox} this This input
18030 * @param {Boolean} checked The new checked value
18037 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18039 inputType: 'checkbox',
18047 getAutoCreate : function()
18049 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18055 cfg.cls = 'form-group ' + this.inputType; //input-group
18058 cfg.cls += ' ' + this.inputType + '-inline';
18064 type : this.inputType,
18065 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18066 cls : 'roo-' + this.inputType, //'form-box',
18067 placeholder : this.placeholder || ''
18071 if (this.weight) { // Validity check?
18072 cfg.cls += " " + this.inputType + "-" + this.weight;
18075 if (this.disabled) {
18076 input.disabled=true;
18080 input.checked = this.checked;
18084 input.name = this.name;
18088 input.cls += ' input-' + this.size;
18093 ['xs','sm','md','lg'].map(function(size){
18094 if (settings[size]) {
18095 cfg.cls += ' col-' + size + '-' + settings[size];
18099 var inputblock = input;
18101 if (this.before || this.after) {
18104 cls : 'input-group',
18109 inputblock.cn.push({
18111 cls : 'input-group-addon',
18116 inputblock.cn.push(input);
18119 inputblock.cn.push({
18121 cls : 'input-group-addon',
18128 if (align ==='left' && this.fieldLabel.length) {
18129 Roo.log("left and has label");
18135 cls : 'control-label col-md-' + this.labelWidth,
18136 html : this.fieldLabel
18140 cls : "col-md-" + (12 - this.labelWidth),
18147 } else if ( this.fieldLabel.length) {
18152 tag: this.boxLabel ? 'span' : 'label',
18154 cls: 'control-label box-input-label',
18155 //cls : 'input-group-addon',
18156 html : this.fieldLabel
18166 Roo.log(" no label && no align");
18167 cfg.cn = [ inputblock ] ;
18172 var boxLabelCfg = {
18174 //'for': id, // box label is handled by onclick - so no for...
18176 html: this.boxLabel
18180 boxLabelCfg.tooltip = this.tooltip;
18183 cfg.cn.push(boxLabelCfg);
18193 * return the real input element.
18195 inputEl: function ()
18197 return this.el.select('input.roo-' + this.inputType,true).first();
18200 labelEl: function()
18202 return this.el.select('label.control-label',true).first();
18204 /* depricated... */
18208 return this.labelEl();
18211 initEvents : function()
18213 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18215 this.inputEl().on('click', this.onClick, this);
18217 if (this.boxLabel) {
18218 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18221 this.startValue = this.getValue();
18224 Roo.bootstrap.CheckBox.register(this);
18228 onClick : function()
18230 this.setChecked(!this.checked);
18233 setChecked : function(state,suppressEvent)
18235 this.startValue = this.getValue();
18237 if(this.inputType == 'radio'){
18239 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18240 e.dom.checked = false;
18243 this.inputEl().dom.checked = true;
18245 this.inputEl().dom.value = this.inputValue;
18247 if(suppressEvent !== true){
18248 this.fireEvent('check', this, true);
18256 this.checked = state;
18258 this.inputEl().dom.checked = state;
18260 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18262 if(suppressEvent !== true){
18263 this.fireEvent('check', this, state);
18269 getValue : function()
18271 if(this.inputType == 'radio'){
18272 return this.getGroupValue();
18275 return this.inputEl().getValue();
18279 getGroupValue : function()
18281 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18285 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18288 setValue : function(v,suppressEvent)
18290 if(this.inputType == 'radio'){
18291 this.setGroupValue(v, suppressEvent);
18295 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18300 setGroupValue : function(v, suppressEvent)
18302 this.startValue = this.getValue();
18304 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18305 e.dom.checked = false;
18307 if(e.dom.value == v){
18308 e.dom.checked = true;
18312 if(suppressEvent !== true){
18313 this.fireEvent('check', this, true);
18321 validate : function()
18325 (this.inputType == 'radio' && this.validateRadio()) ||
18326 (this.inputType == 'checkbox' && this.validateCheckbox())
18332 this.markInvalid();
18336 validateRadio : function()
18340 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18341 if(!e.dom.checked){
18353 validateCheckbox : function()
18356 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18359 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18367 for(var i in group){
18372 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18379 * Mark this field as valid
18381 markValid : function()
18383 if(this.allowBlank){
18389 this.fireEvent('valid', this);
18391 if(this.inputType == 'radio'){
18392 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18393 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18394 e.findParent('.form-group', false, true).addClass(_this.validClass);
18401 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18402 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18406 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18412 for(var i in group){
18413 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18414 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18419 * Mark this field as invalid
18420 * @param {String} msg The validation message
18422 markInvalid : function(msg)
18424 if(this.allowBlank){
18430 this.fireEvent('invalid', this, msg);
18432 if(this.inputType == 'radio'){
18433 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18434 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18435 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18442 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18443 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18447 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18453 for(var i in group){
18454 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18455 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18462 Roo.apply(Roo.bootstrap.CheckBox, {
18467 * register a CheckBox Group
18468 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18470 register : function(checkbox)
18472 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18473 this.groups[checkbox.groupId] = {};
18476 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18480 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18484 * fetch a CheckBox Group based on the group ID
18485 * @param {string} the group ID
18486 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18488 get: function(groupId) {
18489 if (typeof(this.groups[groupId]) == 'undefined') {
18493 return this.groups[groupId] ;
18505 *<div class="radio">
18507 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18508 Option one is this and that—be sure to include why it's great
18515 *<label class="radio-inline">fieldLabel</label>
18516 *<label class="radio-inline">
18517 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18525 * @class Roo.bootstrap.Radio
18526 * @extends Roo.bootstrap.CheckBox
18527 * Bootstrap Radio class
18530 * Create a new Radio
18531 * @param {Object} config The config object
18534 Roo.bootstrap.Radio = function(config){
18535 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18539 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18541 inputType: 'radio',
18545 getAutoCreate : function()
18547 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18548 align = align || 'left'; // default...
18555 tag : this.inline ? 'span' : 'div',
18560 var inline = this.inline ? ' radio-inline' : '';
18564 // does not need for, as we wrap the input with it..
18566 cls : 'control-label box-label' + inline,
18569 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18573 //cls : 'control-label' + inline,
18574 html : this.fieldLabel,
18575 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18584 type : this.inputType,
18585 //value : (!this.checked) ? this.valueOff : this.inputValue,
18586 value : this.inputValue,
18588 placeholder : this.placeholder || '' // ?? needed????
18591 if (this.weight) { // Validity check?
18592 input.cls += " radio-" + this.weight;
18594 if (this.disabled) {
18595 input.disabled=true;
18599 input.checked = this.checked;
18603 input.name = this.name;
18607 input.cls += ' input-' + this.size;
18610 //?? can span's inline have a width??
18613 ['xs','sm','md','lg'].map(function(size){
18614 if (settings[size]) {
18615 cfg.cls += ' col-' + size + '-' + settings[size];
18619 var inputblock = input;
18621 if (this.before || this.after) {
18624 cls : 'input-group',
18629 inputblock.cn.push({
18631 cls : 'input-group-addon',
18635 inputblock.cn.push(input);
18637 inputblock.cn.push({
18639 cls : 'input-group-addon',
18647 if (this.fieldLabel && this.fieldLabel.length) {
18648 cfg.cn.push(fieldLabel);
18651 // normal bootstrap puts the input inside the label.
18652 // however with our styled version - it has to go after the input.
18654 //lbl.cn.push(inputblock);
18658 cls: 'radio' + inline,
18665 cfg.cn.push( lblwrap);
18670 html: this.boxLabel
18679 initEvents : function()
18681 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18683 this.inputEl().on('click', this.onClick, this);
18684 if (this.boxLabel) {
18685 Roo.log('find label')
18686 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18691 inputEl: function ()
18693 return this.el.select('input.roo-radio',true).first();
18695 onClick : function()
18698 this.setChecked(true);
18701 setChecked : function(state,suppressEvent)
18704 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18705 v.dom.checked = false;
18708 Roo.log(this.inputEl().dom);
18709 this.checked = state;
18710 this.inputEl().dom.checked = state;
18712 if(suppressEvent !== true){
18713 this.fireEvent('check', this, state);
18716 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18720 getGroupValue : function()
18723 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18724 if(v.dom.checked == true){
18725 value = v.dom.value;
18733 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18734 * @return {Mixed} value The field value
18736 getValue : function(){
18737 return this.getGroupValue();
18743 //<script type="text/javascript">
18746 * Based Ext JS Library 1.1.1
18747 * Copyright(c) 2006-2007, Ext JS, LLC.
18753 * @class Roo.HtmlEditorCore
18754 * @extends Roo.Component
18755 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18757 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18760 Roo.HtmlEditorCore = function(config){
18763 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18768 * @event initialize
18769 * Fires when the editor is fully initialized (including the iframe)
18770 * @param {Roo.HtmlEditorCore} this
18775 * Fires when the editor is first receives the focus. Any insertion must wait
18776 * until after this event.
18777 * @param {Roo.HtmlEditorCore} this
18781 * @event beforesync
18782 * Fires before the textarea is updated with content from the editor iframe. Return false
18783 * to cancel the sync.
18784 * @param {Roo.HtmlEditorCore} this
18785 * @param {String} html
18789 * @event beforepush
18790 * Fires before the iframe editor is updated with content from the textarea. Return false
18791 * to cancel the push.
18792 * @param {Roo.HtmlEditorCore} this
18793 * @param {String} html
18798 * Fires when the textarea is updated with content from the editor iframe.
18799 * @param {Roo.HtmlEditorCore} this
18800 * @param {String} html
18805 * Fires when the iframe editor is updated with content from the textarea.
18806 * @param {Roo.HtmlEditorCore} this
18807 * @param {String} html
18812 * @event editorevent
18813 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18814 * @param {Roo.HtmlEditorCore} this
18820 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18822 // defaults : white / black...
18823 this.applyBlacklists();
18830 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
18834 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
18840 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18845 * @cfg {Number} height (in pixels)
18849 * @cfg {Number} width (in pixels)
18854 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18857 stylesheets: false,
18862 // private properties
18863 validationEvent : false,
18865 initialized : false,
18867 sourceEditMode : false,
18868 onFocus : Roo.emptyFn,
18870 hideMode:'offsets',
18874 // blacklist + whitelisted elements..
18881 * Protected method that will not generally be called directly. It
18882 * is called when the editor initializes the iframe with HTML contents. Override this method if you
18883 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18885 getDocMarkup : function(){
18889 // inherit styels from page...??
18890 if (this.stylesheets === false) {
18892 Roo.get(document.head).select('style').each(function(node) {
18893 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18896 Roo.get(document.head).select('link').each(function(node) {
18897 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18900 } else if (!this.stylesheets.length) {
18902 st = '<style type="text/css">' +
18903 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18909 st += '<style type="text/css">' +
18910 'IMG { cursor: pointer } ' +
18914 return '<html><head>' + st +
18915 //<style type="text/css">' +
18916 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18918 ' </head><body class="roo-htmleditor-body"></body></html>';
18922 onRender : function(ct, position)
18925 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18926 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18929 this.el.dom.style.border = '0 none';
18930 this.el.dom.setAttribute('tabIndex', -1);
18931 this.el.addClass('x-hidden hide');
18935 if(Roo.isIE){ // fix IE 1px bogus margin
18936 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18940 this.frameId = Roo.id();
18944 var iframe = this.owner.wrap.createChild({
18946 cls: 'form-control', // bootstrap..
18948 name: this.frameId,
18949 frameBorder : 'no',
18950 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
18955 this.iframe = iframe.dom;
18957 this.assignDocWin();
18959 this.doc.designMode = 'on';
18962 this.doc.write(this.getDocMarkup());
18966 var task = { // must defer to wait for browser to be ready
18968 //console.log("run task?" + this.doc.readyState);
18969 this.assignDocWin();
18970 if(this.doc.body || this.doc.readyState == 'complete'){
18972 this.doc.designMode="on";
18976 Roo.TaskMgr.stop(task);
18977 this.initEditor.defer(10, this);
18984 Roo.TaskMgr.start(task);
18989 onResize : function(w, h)
18991 Roo.log('resize: ' +w + ',' + h );
18992 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18996 if(typeof w == 'number'){
18998 this.iframe.style.width = w + 'px';
19000 if(typeof h == 'number'){
19002 this.iframe.style.height = h + 'px';
19004 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19011 * Toggles the editor between standard and source edit mode.
19012 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19014 toggleSourceEdit : function(sourceEditMode){
19016 this.sourceEditMode = sourceEditMode === true;
19018 if(this.sourceEditMode){
19020 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19023 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19024 //this.iframe.className = '';
19027 //this.setSize(this.owner.wrap.getSize());
19028 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19035 * Protected method that will not generally be called directly. If you need/want
19036 * custom HTML cleanup, this is the method you should override.
19037 * @param {String} html The HTML to be cleaned
19038 * return {String} The cleaned HTML
19040 cleanHtml : function(html){
19041 html = String(html);
19042 if(html.length > 5){
19043 if(Roo.isSafari){ // strip safari nonsense
19044 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19047 if(html == ' '){
19054 * HTML Editor -> Textarea
19055 * Protected method that will not generally be called directly. Syncs the contents
19056 * of the editor iframe with the textarea.
19058 syncValue : function(){
19059 if(this.initialized){
19060 var bd = (this.doc.body || this.doc.documentElement);
19061 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19062 var html = bd.innerHTML;
19064 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19065 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19067 html = '<div style="'+m[0]+'">' + html + '</div>';
19070 html = this.cleanHtml(html);
19071 // fix up the special chars.. normaly like back quotes in word...
19072 // however we do not want to do this with chinese..
19073 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19074 var cc = b.charCodeAt();
19076 (cc >= 0x4E00 && cc < 0xA000 ) ||
19077 (cc >= 0x3400 && cc < 0x4E00 ) ||
19078 (cc >= 0xf900 && cc < 0xfb00 )
19084 if(this.owner.fireEvent('beforesync', this, html) !== false){
19085 this.el.dom.value = html;
19086 this.owner.fireEvent('sync', this, html);
19092 * Protected method that will not generally be called directly. Pushes the value of the textarea
19093 * into the iframe editor.
19095 pushValue : function(){
19096 if(this.initialized){
19097 var v = this.el.dom.value.trim();
19099 // if(v.length < 1){
19103 if(this.owner.fireEvent('beforepush', this, v) !== false){
19104 var d = (this.doc.body || this.doc.documentElement);
19106 this.cleanUpPaste();
19107 this.el.dom.value = d.innerHTML;
19108 this.owner.fireEvent('push', this, v);
19114 deferFocus : function(){
19115 this.focus.defer(10, this);
19119 focus : function(){
19120 if(this.win && !this.sourceEditMode){
19127 assignDocWin: function()
19129 var iframe = this.iframe;
19132 this.doc = iframe.contentWindow.document;
19133 this.win = iframe.contentWindow;
19135 // if (!Roo.get(this.frameId)) {
19138 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19139 // this.win = Roo.get(this.frameId).dom.contentWindow;
19141 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19145 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19146 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19151 initEditor : function(){
19152 //console.log("INIT EDITOR");
19153 this.assignDocWin();
19157 this.doc.designMode="on";
19159 this.doc.write(this.getDocMarkup());
19162 var dbody = (this.doc.body || this.doc.documentElement);
19163 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19164 // this copies styles from the containing element into thsi one..
19165 // not sure why we need all of this..
19166 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19168 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19169 //ss['background-attachment'] = 'fixed'; // w3c
19170 dbody.bgProperties = 'fixed'; // ie
19171 //Roo.DomHelper.applyStyles(dbody, ss);
19172 Roo.EventManager.on(this.doc, {
19173 //'mousedown': this.onEditorEvent,
19174 'mouseup': this.onEditorEvent,
19175 'dblclick': this.onEditorEvent,
19176 'click': this.onEditorEvent,
19177 'keyup': this.onEditorEvent,
19182 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19184 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19185 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19187 this.initialized = true;
19189 this.owner.fireEvent('initialize', this);
19194 onDestroy : function(){
19200 //for (var i =0; i < this.toolbars.length;i++) {
19201 // // fixme - ask toolbars for heights?
19202 // this.toolbars[i].onDestroy();
19205 //this.wrap.dom.innerHTML = '';
19206 //this.wrap.remove();
19211 onFirstFocus : function(){
19213 this.assignDocWin();
19216 this.activated = true;
19219 if(Roo.isGecko){ // prevent silly gecko errors
19221 var s = this.win.getSelection();
19222 if(!s.focusNode || s.focusNode.nodeType != 3){
19223 var r = s.getRangeAt(0);
19224 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19229 this.execCmd('useCSS', true);
19230 this.execCmd('styleWithCSS', false);
19233 this.owner.fireEvent('activate', this);
19237 adjustFont: function(btn){
19238 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19239 //if(Roo.isSafari){ // safari
19242 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19243 if(Roo.isSafari){ // safari
19244 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19245 v = (v < 10) ? 10 : v;
19246 v = (v > 48) ? 48 : v;
19247 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19252 v = Math.max(1, v+adjust);
19254 this.execCmd('FontSize', v );
19257 onEditorEvent : function(e)
19259 this.owner.fireEvent('editorevent', this, e);
19260 // this.updateToolbar();
19261 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19264 insertTag : function(tg)
19266 // could be a bit smarter... -> wrap the current selected tRoo..
19267 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19269 range = this.createRange(this.getSelection());
19270 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19271 wrappingNode.appendChild(range.extractContents());
19272 range.insertNode(wrappingNode);
19279 this.execCmd("formatblock", tg);
19283 insertText : function(txt)
19287 var range = this.createRange();
19288 range.deleteContents();
19289 //alert(Sender.getAttribute('label'));
19291 range.insertNode(this.doc.createTextNode(txt));
19297 * Executes a Midas editor command on the editor document and performs necessary focus and
19298 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19299 * @param {String} cmd The Midas command
19300 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19302 relayCmd : function(cmd, value){
19304 this.execCmd(cmd, value);
19305 this.owner.fireEvent('editorevent', this);
19306 //this.updateToolbar();
19307 this.owner.deferFocus();
19311 * Executes a Midas editor command directly on the editor document.
19312 * For visual commands, you should use {@link #relayCmd} instead.
19313 * <b>This should only be called after the editor is initialized.</b>
19314 * @param {String} cmd The Midas command
19315 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19317 execCmd : function(cmd, value){
19318 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19325 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19327 * @param {String} text | dom node..
19329 insertAtCursor : function(text)
19334 if(!this.activated){
19340 var r = this.doc.selection.createRange();
19351 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19355 // from jquery ui (MIT licenced)
19357 var win = this.win;
19359 if (win.getSelection && win.getSelection().getRangeAt) {
19360 range = win.getSelection().getRangeAt(0);
19361 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19362 range.insertNode(node);
19363 } else if (win.document.selection && win.document.selection.createRange) {
19364 // no firefox support
19365 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19366 win.document.selection.createRange().pasteHTML(txt);
19368 // no firefox support
19369 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19370 this.execCmd('InsertHTML', txt);
19379 mozKeyPress : function(e){
19381 var c = e.getCharCode(), cmd;
19384 c = String.fromCharCode(c).toLowerCase();
19398 this.cleanUpPaste.defer(100, this);
19406 e.preventDefault();
19414 fixKeys : function(){ // load time branching for fastest keydown performance
19416 return function(e){
19417 var k = e.getKey(), r;
19420 r = this.doc.selection.createRange();
19423 r.pasteHTML('    ');
19430 r = this.doc.selection.createRange();
19432 var target = r.parentElement();
19433 if(!target || target.tagName.toLowerCase() != 'li'){
19435 r.pasteHTML('<br />');
19441 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19442 this.cleanUpPaste.defer(100, this);
19448 }else if(Roo.isOpera){
19449 return function(e){
19450 var k = e.getKey();
19454 this.execCmd('InsertHTML','    ');
19457 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19458 this.cleanUpPaste.defer(100, this);
19463 }else if(Roo.isSafari){
19464 return function(e){
19465 var k = e.getKey();
19469 this.execCmd('InsertText','\t');
19473 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19474 this.cleanUpPaste.defer(100, this);
19482 getAllAncestors: function()
19484 var p = this.getSelectedNode();
19487 a.push(p); // push blank onto stack..
19488 p = this.getParentElement();
19492 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19496 a.push(this.doc.body);
19500 lastSelNode : false,
19503 getSelection : function()
19505 this.assignDocWin();
19506 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19509 getSelectedNode: function()
19511 // this may only work on Gecko!!!
19513 // should we cache this!!!!
19518 var range = this.createRange(this.getSelection()).cloneRange();
19521 var parent = range.parentElement();
19523 var testRange = range.duplicate();
19524 testRange.moveToElementText(parent);
19525 if (testRange.inRange(range)) {
19528 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19531 parent = parent.parentElement;
19536 // is ancestor a text element.
19537 var ac = range.commonAncestorContainer;
19538 if (ac.nodeType == 3) {
19539 ac = ac.parentNode;
19542 var ar = ac.childNodes;
19545 var other_nodes = [];
19546 var has_other_nodes = false;
19547 for (var i=0;i<ar.length;i++) {
19548 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19551 // fullly contained node.
19553 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19558 // probably selected..
19559 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19560 other_nodes.push(ar[i]);
19564 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19569 has_other_nodes = true;
19571 if (!nodes.length && other_nodes.length) {
19572 nodes= other_nodes;
19574 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19580 createRange: function(sel)
19582 // this has strange effects when using with
19583 // top toolbar - not sure if it's a great idea.
19584 //this.editor.contentWindow.focus();
19585 if (typeof sel != "undefined") {
19587 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19589 return this.doc.createRange();
19592 return this.doc.createRange();
19595 getParentElement: function()
19598 this.assignDocWin();
19599 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19601 var range = this.createRange(sel);
19604 var p = range.commonAncestorContainer;
19605 while (p.nodeType == 3) { // text node
19616 * Range intersection.. the hard stuff...
19620 * [ -- selected range --- ]
19624 * if end is before start or hits it. fail.
19625 * if start is after end or hits it fail.
19627 * if either hits (but other is outside. - then it's not
19633 // @see http://www.thismuchiknow.co.uk/?p=64.
19634 rangeIntersectsNode : function(range, node)
19636 var nodeRange = node.ownerDocument.createRange();
19638 nodeRange.selectNode(node);
19640 nodeRange.selectNodeContents(node);
19643 var rangeStartRange = range.cloneRange();
19644 rangeStartRange.collapse(true);
19646 var rangeEndRange = range.cloneRange();
19647 rangeEndRange.collapse(false);
19649 var nodeStartRange = nodeRange.cloneRange();
19650 nodeStartRange.collapse(true);
19652 var nodeEndRange = nodeRange.cloneRange();
19653 nodeEndRange.collapse(false);
19655 return rangeStartRange.compareBoundaryPoints(
19656 Range.START_TO_START, nodeEndRange) == -1 &&
19657 rangeEndRange.compareBoundaryPoints(
19658 Range.START_TO_START, nodeStartRange) == 1;
19662 rangeCompareNode : function(range, node)
19664 var nodeRange = node.ownerDocument.createRange();
19666 nodeRange.selectNode(node);
19668 nodeRange.selectNodeContents(node);
19672 range.collapse(true);
19674 nodeRange.collapse(true);
19676 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19677 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19679 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19681 var nodeIsBefore = ss == 1;
19682 var nodeIsAfter = ee == -1;
19684 if (nodeIsBefore && nodeIsAfter)
19686 if (!nodeIsBefore && nodeIsAfter)
19687 return 1; //right trailed.
19689 if (nodeIsBefore && !nodeIsAfter)
19690 return 2; // left trailed.
19695 // private? - in a new class?
19696 cleanUpPaste : function()
19698 // cleans up the whole document..
19699 Roo.log('cleanuppaste');
19701 this.cleanUpChildren(this.doc.body);
19702 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19703 if (clean != this.doc.body.innerHTML) {
19704 this.doc.body.innerHTML = clean;
19709 cleanWordChars : function(input) {// change the chars to hex code
19710 var he = Roo.HtmlEditorCore;
19712 var output = input;
19713 Roo.each(he.swapCodes, function(sw) {
19714 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19716 output = output.replace(swapper, sw[1]);
19723 cleanUpChildren : function (n)
19725 if (!n.childNodes.length) {
19728 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19729 this.cleanUpChild(n.childNodes[i]);
19736 cleanUpChild : function (node)
19739 //console.log(node);
19740 if (node.nodeName == "#text") {
19741 // clean up silly Windows -- stuff?
19744 if (node.nodeName == "#comment") {
19745 node.parentNode.removeChild(node);
19746 // clean up silly Windows -- stuff?
19749 var lcname = node.tagName.toLowerCase();
19750 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19751 // whitelist of tags..
19753 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19755 node.parentNode.removeChild(node);
19760 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19762 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19763 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19765 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19766 // remove_keep_children = true;
19769 if (remove_keep_children) {
19770 this.cleanUpChildren(node);
19771 // inserts everything just before this node...
19772 while (node.childNodes.length) {
19773 var cn = node.childNodes[0];
19774 node.removeChild(cn);
19775 node.parentNode.insertBefore(cn, node);
19777 node.parentNode.removeChild(node);
19781 if (!node.attributes || !node.attributes.length) {
19782 this.cleanUpChildren(node);
19786 function cleanAttr(n,v)
19789 if (v.match(/^\./) || v.match(/^\//)) {
19792 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19795 if (v.match(/^#/)) {
19798 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19799 node.removeAttribute(n);
19803 var cwhite = this.cwhite;
19804 var cblack = this.cblack;
19806 function cleanStyle(n,v)
19808 if (v.match(/expression/)) { //XSS?? should we even bother..
19809 node.removeAttribute(n);
19813 var parts = v.split(/;/);
19816 Roo.each(parts, function(p) {
19817 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19821 var l = p.split(':').shift().replace(/\s+/g,'');
19822 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19824 if ( cwhite.length && cblack.indexOf(l) > -1) {
19825 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19826 //node.removeAttribute(n);
19830 // only allow 'c whitelisted system attributes'
19831 if ( cwhite.length && cwhite.indexOf(l) < 0) {
19832 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19833 //node.removeAttribute(n);
19843 if (clean.length) {
19844 node.setAttribute(n, clean.join(';'));
19846 node.removeAttribute(n);
19852 for (var i = node.attributes.length-1; i > -1 ; i--) {
19853 var a = node.attributes[i];
19856 if (a.name.toLowerCase().substr(0,2)=='on') {
19857 node.removeAttribute(a.name);
19860 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19861 node.removeAttribute(a.name);
19864 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19865 cleanAttr(a.name,a.value); // fixme..
19868 if (a.name == 'style') {
19869 cleanStyle(a.name,a.value);
19872 /// clean up MS crap..
19873 // tecnically this should be a list of valid class'es..
19876 if (a.name == 'class') {
19877 if (a.value.match(/^Mso/)) {
19878 node.className = '';
19881 if (a.value.match(/body/)) {
19882 node.className = '';
19893 this.cleanUpChildren(node);
19899 * Clean up MS wordisms...
19901 cleanWord : function(node)
19906 this.cleanWord(this.doc.body);
19909 if (node.nodeName == "#text") {
19910 // clean up silly Windows -- stuff?
19913 if (node.nodeName == "#comment") {
19914 node.parentNode.removeChild(node);
19915 // clean up silly Windows -- stuff?
19919 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19920 node.parentNode.removeChild(node);
19924 // remove - but keep children..
19925 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19926 while (node.childNodes.length) {
19927 var cn = node.childNodes[0];
19928 node.removeChild(cn);
19929 node.parentNode.insertBefore(cn, node);
19931 node.parentNode.removeChild(node);
19932 this.iterateChildren(node, this.cleanWord);
19936 if (node.className.length) {
19938 var cn = node.className.split(/\W+/);
19940 Roo.each(cn, function(cls) {
19941 if (cls.match(/Mso[a-zA-Z]+/)) {
19946 node.className = cna.length ? cna.join(' ') : '';
19948 node.removeAttribute("class");
19952 if (node.hasAttribute("lang")) {
19953 node.removeAttribute("lang");
19956 if (node.hasAttribute("style")) {
19958 var styles = node.getAttribute("style").split(";");
19960 Roo.each(styles, function(s) {
19961 if (!s.match(/:/)) {
19964 var kv = s.split(":");
19965 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19968 // what ever is left... we allow.
19971 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19972 if (!nstyle.length) {
19973 node.removeAttribute('style');
19976 this.iterateChildren(node, this.cleanWord);
19982 * iterateChildren of a Node, calling fn each time, using this as the scole..
19983 * @param {DomNode} node node to iterate children of.
19984 * @param {Function} fn method of this class to call on each item.
19986 iterateChildren : function(node, fn)
19988 if (!node.childNodes.length) {
19991 for (var i = node.childNodes.length-1; i > -1 ; i--) {
19992 fn.call(this, node.childNodes[i])
19998 * cleanTableWidths.
20000 * Quite often pasting from word etc.. results in tables with column and widths.
20001 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20004 cleanTableWidths : function(node)
20009 this.cleanTableWidths(this.doc.body);
20014 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20017 Roo.log(node.tagName);
20018 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20019 this.iterateChildren(node, this.cleanTableWidths);
20022 if (node.hasAttribute('width')) {
20023 node.removeAttribute('width');
20027 if (node.hasAttribute("style")) {
20030 var styles = node.getAttribute("style").split(";");
20032 Roo.each(styles, function(s) {
20033 if (!s.match(/:/)) {
20036 var kv = s.split(":");
20037 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20040 // what ever is left... we allow.
20043 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20044 if (!nstyle.length) {
20045 node.removeAttribute('style');
20049 this.iterateChildren(node, this.cleanTableWidths);
20057 domToHTML : function(currentElement, depth, nopadtext) {
20059 depth = depth || 0;
20060 nopadtext = nopadtext || false;
20062 if (!currentElement) {
20063 return this.domToHTML(this.doc.body);
20066 //Roo.log(currentElement);
20068 var allText = false;
20069 var nodeName = currentElement.nodeName;
20070 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20072 if (nodeName == '#text') {
20074 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20079 if (nodeName != 'BODY') {
20082 // Prints the node tagName, such as <A>, <IMG>, etc
20085 for(i = 0; i < currentElement.attributes.length;i++) {
20087 var aname = currentElement.attributes.item(i).name;
20088 if (!currentElement.attributes.item(i).value.length) {
20091 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20094 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20103 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20106 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20111 // Traverse the tree
20113 var currentElementChild = currentElement.childNodes.item(i);
20114 var allText = true;
20115 var innerHTML = '';
20117 while (currentElementChild) {
20118 // Formatting code (indent the tree so it looks nice on the screen)
20119 var nopad = nopadtext;
20120 if (lastnode == 'SPAN') {
20124 if (currentElementChild.nodeName == '#text') {
20125 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20126 toadd = nopadtext ? toadd : toadd.trim();
20127 if (!nopad && toadd.length > 80) {
20128 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20130 innerHTML += toadd;
20133 currentElementChild = currentElement.childNodes.item(i);
20139 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20141 // Recursively traverse the tree structure of the child node
20142 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20143 lastnode = currentElementChild.nodeName;
20145 currentElementChild=currentElement.childNodes.item(i);
20151 // The remaining code is mostly for formatting the tree
20152 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20157 ret+= "</"+tagName+">";
20163 applyBlacklists : function()
20165 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20166 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20170 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20171 if (b.indexOf(tag) > -1) {
20174 this.white.push(tag);
20178 Roo.each(w, function(tag) {
20179 if (b.indexOf(tag) > -1) {
20182 if (this.white.indexOf(tag) > -1) {
20185 this.white.push(tag);
20190 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20191 if (w.indexOf(tag) > -1) {
20194 this.black.push(tag);
20198 Roo.each(b, function(tag) {
20199 if (w.indexOf(tag) > -1) {
20202 if (this.black.indexOf(tag) > -1) {
20205 this.black.push(tag);
20210 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20211 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20215 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20216 if (b.indexOf(tag) > -1) {
20219 this.cwhite.push(tag);
20223 Roo.each(w, function(tag) {
20224 if (b.indexOf(tag) > -1) {
20227 if (this.cwhite.indexOf(tag) > -1) {
20230 this.cwhite.push(tag);
20235 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20236 if (w.indexOf(tag) > -1) {
20239 this.cblack.push(tag);
20243 Roo.each(b, function(tag) {
20244 if (w.indexOf(tag) > -1) {
20247 if (this.cblack.indexOf(tag) > -1) {
20250 this.cblack.push(tag);
20255 setStylesheets : function(stylesheets)
20257 if(typeof(stylesheets) == 'string'){
20258 Roo.get(this.iframe.contentDocument.head).createChild({
20260 rel : 'stylesheet',
20269 Roo.each(stylesheets, function(s) {
20274 Roo.get(_this.iframe.contentDocument.head).createChild({
20276 rel : 'stylesheet',
20285 removeStylesheets : function()
20289 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20294 // hide stuff that is not compatible
20308 * @event specialkey
20312 * @cfg {String} fieldClass @hide
20315 * @cfg {String} focusClass @hide
20318 * @cfg {String} autoCreate @hide
20321 * @cfg {String} inputType @hide
20324 * @cfg {String} invalidClass @hide
20327 * @cfg {String} invalidText @hide
20330 * @cfg {String} msgFx @hide
20333 * @cfg {String} validateOnBlur @hide
20337 Roo.HtmlEditorCore.white = [
20338 'area', 'br', 'img', 'input', 'hr', 'wbr',
20340 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20341 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20342 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20343 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20344 'table', 'ul', 'xmp',
20346 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20349 'dir', 'menu', 'ol', 'ul', 'dl',
20355 Roo.HtmlEditorCore.black = [
20356 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20358 'base', 'basefont', 'bgsound', 'blink', 'body',
20359 'frame', 'frameset', 'head', 'html', 'ilayer',
20360 'iframe', 'layer', 'link', 'meta', 'object',
20361 'script', 'style' ,'title', 'xml' // clean later..
20363 Roo.HtmlEditorCore.clean = [
20364 'script', 'style', 'title', 'xml'
20366 Roo.HtmlEditorCore.remove = [
20371 Roo.HtmlEditorCore.ablack = [
20375 Roo.HtmlEditorCore.aclean = [
20376 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20380 Roo.HtmlEditorCore.pwhite= [
20381 'http', 'https', 'mailto'
20384 // white listed style attributes.
20385 Roo.HtmlEditorCore.cwhite= [
20386 // 'text-align', /// default is to allow most things..
20392 // black listed style attributes.
20393 Roo.HtmlEditorCore.cblack= [
20394 // 'font-size' -- this can be set by the project
20398 Roo.HtmlEditorCore.swapCodes =[
20417 * @class Roo.bootstrap.HtmlEditor
20418 * @extends Roo.bootstrap.TextArea
20419 * Bootstrap HtmlEditor class
20422 * Create a new HtmlEditor
20423 * @param {Object} config The config object
20426 Roo.bootstrap.HtmlEditor = function(config){
20427 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20428 if (!this.toolbars) {
20429 this.toolbars = [];
20431 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20434 * @event initialize
20435 * Fires when the editor is fully initialized (including the iframe)
20436 * @param {HtmlEditor} this
20441 * Fires when the editor is first receives the focus. Any insertion must wait
20442 * until after this event.
20443 * @param {HtmlEditor} this
20447 * @event beforesync
20448 * Fires before the textarea is updated with content from the editor iframe. Return false
20449 * to cancel the sync.
20450 * @param {HtmlEditor} this
20451 * @param {String} html
20455 * @event beforepush
20456 * Fires before the iframe editor is updated with content from the textarea. Return false
20457 * to cancel the push.
20458 * @param {HtmlEditor} this
20459 * @param {String} html
20464 * Fires when the textarea is updated with content from the editor iframe.
20465 * @param {HtmlEditor} this
20466 * @param {String} html
20471 * Fires when the iframe editor is updated with content from the textarea.
20472 * @param {HtmlEditor} this
20473 * @param {String} html
20477 * @event editmodechange
20478 * Fires when the editor switches edit modes
20479 * @param {HtmlEditor} this
20480 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20482 editmodechange: true,
20484 * @event editorevent
20485 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20486 * @param {HtmlEditor} this
20490 * @event firstfocus
20491 * Fires when on first focus - needed by toolbars..
20492 * @param {HtmlEditor} this
20497 * Auto save the htmlEditor value as a file into Events
20498 * @param {HtmlEditor} this
20502 * @event savedpreview
20503 * preview the saved version of htmlEditor
20504 * @param {HtmlEditor} this
20511 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20515 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20520 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20525 * @cfg {Number} height (in pixels)
20529 * @cfg {Number} width (in pixels)
20534 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20537 stylesheets: false,
20542 // private properties
20543 validationEvent : false,
20545 initialized : false,
20548 onFocus : Roo.emptyFn,
20550 hideMode:'offsets',
20553 tbContainer : false,
20555 toolbarContainer :function() {
20556 return this.wrap.select('.x-html-editor-tb',true).first();
20560 * Protected method that will not generally be called directly. It
20561 * is called when the editor creates its toolbar. Override this method if you need to
20562 * add custom toolbar buttons.
20563 * @param {HtmlEditor} editor
20565 createToolbar : function(){
20567 Roo.log("create toolbars");
20569 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20570 this.toolbars[0].render(this.toolbarContainer());
20574 // if (!editor.toolbars || !editor.toolbars.length) {
20575 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20578 // for (var i =0 ; i < editor.toolbars.length;i++) {
20579 // editor.toolbars[i] = Roo.factory(
20580 // typeof(editor.toolbars[i]) == 'string' ?
20581 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20582 // Roo.bootstrap.HtmlEditor);
20583 // editor.toolbars[i].init(editor);
20589 onRender : function(ct, position)
20591 // Roo.log("Call onRender: " + this.xtype);
20593 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20595 this.wrap = this.inputEl().wrap({
20596 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20599 this.editorcore.onRender(ct, position);
20601 if (this.resizable) {
20602 this.resizeEl = new Roo.Resizable(this.wrap, {
20606 minHeight : this.height,
20607 height: this.height,
20608 handles : this.resizable,
20611 resize : function(r, w, h) {
20612 _t.onResize(w,h); // -something
20618 this.createToolbar(this);
20621 if(!this.width && this.resizable){
20622 this.setSize(this.wrap.getSize());
20624 if (this.resizeEl) {
20625 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20626 // should trigger onReize..
20632 onResize : function(w, h)
20634 Roo.log('resize: ' +w + ',' + h );
20635 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20639 if(this.inputEl() ){
20640 if(typeof w == 'number'){
20641 var aw = w - this.wrap.getFrameWidth('lr');
20642 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20645 if(typeof h == 'number'){
20646 var tbh = -11; // fixme it needs to tool bar size!
20647 for (var i =0; i < this.toolbars.length;i++) {
20648 // fixme - ask toolbars for heights?
20649 tbh += this.toolbars[i].el.getHeight();
20650 //if (this.toolbars[i].footer) {
20651 // tbh += this.toolbars[i].footer.el.getHeight();
20659 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20660 ah -= 5; // knock a few pixes off for look..
20661 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20665 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20666 this.editorcore.onResize(ew,eh);
20671 * Toggles the editor between standard and source edit mode.
20672 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20674 toggleSourceEdit : function(sourceEditMode)
20676 this.editorcore.toggleSourceEdit(sourceEditMode);
20678 if(this.editorcore.sourceEditMode){
20679 Roo.log('editor - showing textarea');
20682 // Roo.log(this.syncValue());
20684 this.inputEl().removeClass(['hide', 'x-hidden']);
20685 this.inputEl().dom.removeAttribute('tabIndex');
20686 this.inputEl().focus();
20688 Roo.log('editor - hiding textarea');
20690 // Roo.log(this.pushValue());
20693 this.inputEl().addClass(['hide', 'x-hidden']);
20694 this.inputEl().dom.setAttribute('tabIndex', -1);
20695 //this.deferFocus();
20698 if(this.resizable){
20699 this.setSize(this.wrap.getSize());
20702 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20705 // private (for BoxComponent)
20706 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20708 // private (for BoxComponent)
20709 getResizeEl : function(){
20713 // private (for BoxComponent)
20714 getPositionEl : function(){
20719 initEvents : function(){
20720 this.originalValue = this.getValue();
20724 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20727 // markInvalid : Roo.emptyFn,
20729 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20732 // clearInvalid : Roo.emptyFn,
20734 setValue : function(v){
20735 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20736 this.editorcore.pushValue();
20741 deferFocus : function(){
20742 this.focus.defer(10, this);
20746 focus : function(){
20747 this.editorcore.focus();
20753 onDestroy : function(){
20759 for (var i =0; i < this.toolbars.length;i++) {
20760 // fixme - ask toolbars for heights?
20761 this.toolbars[i].onDestroy();
20764 this.wrap.dom.innerHTML = '';
20765 this.wrap.remove();
20770 onFirstFocus : function(){
20771 //Roo.log("onFirstFocus");
20772 this.editorcore.onFirstFocus();
20773 for (var i =0; i < this.toolbars.length;i++) {
20774 this.toolbars[i].onFirstFocus();
20780 syncValue : function()
20782 this.editorcore.syncValue();
20785 pushValue : function()
20787 this.editorcore.pushValue();
20791 // hide stuff that is not compatible
20805 * @event specialkey
20809 * @cfg {String} fieldClass @hide
20812 * @cfg {String} focusClass @hide
20815 * @cfg {String} autoCreate @hide
20818 * @cfg {String} inputType @hide
20821 * @cfg {String} invalidClass @hide
20824 * @cfg {String} invalidText @hide
20827 * @cfg {String} msgFx @hide
20830 * @cfg {String} validateOnBlur @hide
20839 Roo.namespace('Roo.bootstrap.htmleditor');
20841 * @class Roo.bootstrap.HtmlEditorToolbar1
20846 new Roo.bootstrap.HtmlEditor({
20849 new Roo.bootstrap.HtmlEditorToolbar1({
20850 disable : { fonts: 1 , format: 1, ..., ... , ...],
20856 * @cfg {Object} disable List of elements to disable..
20857 * @cfg {Array} btns List of additional buttons.
20861 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20864 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20867 Roo.apply(this, config);
20869 // default disabled, based on 'good practice'..
20870 this.disable = this.disable || {};
20871 Roo.applyIf(this.disable, {
20874 specialElements : true
20876 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20878 this.editor = config.editor;
20879 this.editorcore = config.editor.editorcore;
20881 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20883 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20884 // dont call parent... till later.
20886 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
20891 editorcore : false,
20896 "h1","h2","h3","h4","h5","h6",
20898 "abbr", "acronym", "address", "cite", "samp", "var",
20902 onRender : function(ct, position)
20904 // Roo.log("Call onRender: " + this.xtype);
20906 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20908 this.el.dom.style.marginBottom = '0';
20910 var editorcore = this.editorcore;
20911 var editor= this.editor;
20914 var btn = function(id,cmd , toggle, handler){
20916 var event = toggle ? 'toggle' : 'click';
20921 xns: Roo.bootstrap,
20924 enableToggle:toggle !== false,
20926 pressed : toggle ? false : null,
20929 a.listeners[toggle ? 'toggle' : 'click'] = function() {
20930 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
20939 xns: Roo.bootstrap,
20940 glyphicon : 'font',
20944 xns: Roo.bootstrap,
20948 Roo.each(this.formats, function(f) {
20949 style.menu.items.push({
20951 xns: Roo.bootstrap,
20952 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20957 editorcore.insertTag(this.tagname);
20964 children.push(style);
20967 btn('bold',false,true);
20968 btn('italic',false,true);
20969 btn('align-left', 'justifyleft',true);
20970 btn('align-center', 'justifycenter',true);
20971 btn('align-right' , 'justifyright',true);
20972 btn('link', false, false, function(btn) {
20973 //Roo.log("create link?");
20974 var url = prompt(this.createLinkText, this.defaultLinkValue);
20975 if(url && url != 'http:/'+'/'){
20976 this.editorcore.relayCmd('createlink', url);
20979 btn('list','insertunorderedlist',true);
20980 btn('pencil', false,true, function(btn){
20983 this.toggleSourceEdit(btn.pressed);
20989 xns: Roo.bootstrap,
20994 xns: Roo.bootstrap,
20999 cog.menu.items.push({
21001 xns: Roo.bootstrap,
21002 html : Clean styles,
21007 editorcore.insertTag(this.tagname);
21016 this.xtype = 'NavSimplebar';
21018 for(var i=0;i< children.length;i++) {
21020 this.buttons.add(this.addxtypeChild(children[i]));
21024 editor.on('editorevent', this.updateToolbar, this);
21026 onBtnClick : function(id)
21028 this.editorcore.relayCmd(id);
21029 this.editorcore.focus();
21033 * Protected method that will not generally be called directly. It triggers
21034 * a toolbar update by reading the markup state of the current selection in the editor.
21036 updateToolbar: function(){
21038 if(!this.editorcore.activated){
21039 this.editor.onFirstFocus(); // is this neeed?
21043 var btns = this.buttons;
21044 var doc = this.editorcore.doc;
21045 btns.get('bold').setActive(doc.queryCommandState('bold'));
21046 btns.get('italic').setActive(doc.queryCommandState('italic'));
21047 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21049 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21050 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21051 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21053 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21054 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21057 var ans = this.editorcore.getAllAncestors();
21058 if (this.formatCombo) {
21061 var store = this.formatCombo.store;
21062 this.formatCombo.setValue("");
21063 for (var i =0; i < ans.length;i++) {
21064 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21066 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21074 // hides menus... - so this cant be on a menu...
21075 Roo.bootstrap.MenuMgr.hideAll();
21077 Roo.bootstrap.MenuMgr.hideAll();
21078 //this.editorsyncValue();
21080 onFirstFocus: function() {
21081 this.buttons.each(function(item){
21085 toggleSourceEdit : function(sourceEditMode){
21088 if(sourceEditMode){
21089 Roo.log("disabling buttons");
21090 this.buttons.each( function(item){
21091 if(item.cmd != 'pencil'){
21097 Roo.log("enabling buttons");
21098 if(this.editorcore.initialized){
21099 this.buttons.each( function(item){
21105 Roo.log("calling toggole on editor");
21106 // tell the editor that it's been pressed..
21107 this.editor.toggleSourceEdit(sourceEditMode);
21117 * @class Roo.bootstrap.Table.AbstractSelectionModel
21118 * @extends Roo.util.Observable
21119 * Abstract base class for grid SelectionModels. It provides the interface that should be
21120 * implemented by descendant classes. This class should not be directly instantiated.
21123 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21124 this.locked = false;
21125 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21129 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21130 /** @ignore Called by the grid automatically. Do not call directly. */
21131 init : function(grid){
21137 * Locks the selections.
21140 this.locked = true;
21144 * Unlocks the selections.
21146 unlock : function(){
21147 this.locked = false;
21151 * Returns true if the selections are locked.
21152 * @return {Boolean}
21154 isLocked : function(){
21155 return this.locked;
21159 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21160 * @class Roo.bootstrap.Table.RowSelectionModel
21161 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21162 * It supports multiple selections and keyboard selection/navigation.
21164 * @param {Object} config
21167 Roo.bootstrap.Table.RowSelectionModel = function(config){
21168 Roo.apply(this, config);
21169 this.selections = new Roo.util.MixedCollection(false, function(o){
21174 this.lastActive = false;
21178 * @event selectionchange
21179 * Fires when the selection changes
21180 * @param {SelectionModel} this
21182 "selectionchange" : true,
21184 * @event afterselectionchange
21185 * Fires after the selection changes (eg. by key press or clicking)
21186 * @param {SelectionModel} this
21188 "afterselectionchange" : true,
21190 * @event beforerowselect
21191 * Fires when a row is selected being selected, return false to cancel.
21192 * @param {SelectionModel} this
21193 * @param {Number} rowIndex The selected index
21194 * @param {Boolean} keepExisting False if other selections will be cleared
21196 "beforerowselect" : true,
21199 * Fires when a row is selected.
21200 * @param {SelectionModel} this
21201 * @param {Number} rowIndex The selected index
21202 * @param {Roo.data.Record} r The record
21204 "rowselect" : true,
21206 * @event rowdeselect
21207 * Fires when a row is deselected.
21208 * @param {SelectionModel} this
21209 * @param {Number} rowIndex The selected index
21211 "rowdeselect" : true
21213 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21214 this.locked = false;
21217 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21219 * @cfg {Boolean} singleSelect
21220 * True to allow selection of only one row at a time (defaults to false)
21222 singleSelect : false,
21225 initEvents : function(){
21227 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21228 this.grid.on("mousedown", this.handleMouseDown, this);
21229 }else{ // allow click to work like normal
21230 this.grid.on("rowclick", this.handleDragableRowClick, this);
21233 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21234 "up" : function(e){
21236 this.selectPrevious(e.shiftKey);
21237 }else if(this.last !== false && this.lastActive !== false){
21238 var last = this.last;
21239 this.selectRange(this.last, this.lastActive-1);
21240 this.grid.getView().focusRow(this.lastActive);
21241 if(last !== false){
21245 this.selectFirstRow();
21247 this.fireEvent("afterselectionchange", this);
21249 "down" : function(e){
21251 this.selectNext(e.shiftKey);
21252 }else if(this.last !== false && this.lastActive !== false){
21253 var last = this.last;
21254 this.selectRange(this.last, this.lastActive+1);
21255 this.grid.getView().focusRow(this.lastActive);
21256 if(last !== false){
21260 this.selectFirstRow();
21262 this.fireEvent("afterselectionchange", this);
21267 var view = this.grid.view;
21268 view.on("refresh", this.onRefresh, this);
21269 view.on("rowupdated", this.onRowUpdated, this);
21270 view.on("rowremoved", this.onRemove, this);
21274 onRefresh : function(){
21275 var ds = this.grid.dataSource, i, v = this.grid.view;
21276 var s = this.selections;
21277 s.each(function(r){
21278 if((i = ds.indexOfId(r.id)) != -1){
21287 onRemove : function(v, index, r){
21288 this.selections.remove(r);
21292 onRowUpdated : function(v, index, r){
21293 if(this.isSelected(r)){
21294 v.onRowSelect(index);
21300 * @param {Array} records The records to select
21301 * @param {Boolean} keepExisting (optional) True to keep existing selections
21303 selectRecords : function(records, keepExisting){
21305 this.clearSelections();
21307 var ds = this.grid.dataSource;
21308 for(var i = 0, len = records.length; i < len; i++){
21309 this.selectRow(ds.indexOf(records[i]), true);
21314 * Gets the number of selected rows.
21317 getCount : function(){
21318 return this.selections.length;
21322 * Selects the first row in the grid.
21324 selectFirstRow : function(){
21329 * Select the last row.
21330 * @param {Boolean} keepExisting (optional) True to keep existing selections
21332 selectLastRow : function(keepExisting){
21333 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21337 * Selects the row immediately following the last selected row.
21338 * @param {Boolean} keepExisting (optional) True to keep existing selections
21340 selectNext : function(keepExisting){
21341 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21342 this.selectRow(this.last+1, keepExisting);
21343 this.grid.getView().focusRow(this.last);
21348 * Selects the row that precedes the last selected row.
21349 * @param {Boolean} keepExisting (optional) True to keep existing selections
21351 selectPrevious : function(keepExisting){
21353 this.selectRow(this.last-1, keepExisting);
21354 this.grid.getView().focusRow(this.last);
21359 * Returns the selected records
21360 * @return {Array} Array of selected records
21362 getSelections : function(){
21363 return [].concat(this.selections.items);
21367 * Returns the first selected record.
21370 getSelected : function(){
21371 return this.selections.itemAt(0);
21376 * Clears all selections.
21378 clearSelections : function(fast){
21379 if(this.locked) return;
21381 var ds = this.grid.dataSource;
21382 var s = this.selections;
21383 s.each(function(r){
21384 this.deselectRow(ds.indexOfId(r.id));
21388 this.selections.clear();
21395 * Selects all rows.
21397 selectAll : function(){
21398 if(this.locked) return;
21399 this.selections.clear();
21400 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21401 this.selectRow(i, true);
21406 * Returns True if there is a selection.
21407 * @return {Boolean}
21409 hasSelection : function(){
21410 return this.selections.length > 0;
21414 * Returns True if the specified row is selected.
21415 * @param {Number/Record} record The record or index of the record to check
21416 * @return {Boolean}
21418 isSelected : function(index){
21419 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21420 return (r && this.selections.key(r.id) ? true : false);
21424 * Returns True if the specified record id is selected.
21425 * @param {String} id The id of record to check
21426 * @return {Boolean}
21428 isIdSelected : function(id){
21429 return (this.selections.key(id) ? true : false);
21433 handleMouseDown : function(e, t){
21434 var view = this.grid.getView(), rowIndex;
21435 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21438 if(e.shiftKey && this.last !== false){
21439 var last = this.last;
21440 this.selectRange(last, rowIndex, e.ctrlKey);
21441 this.last = last; // reset the last
21442 view.focusRow(rowIndex);
21444 var isSelected = this.isSelected(rowIndex);
21445 if(e.button !== 0 && isSelected){
21446 view.focusRow(rowIndex);
21447 }else if(e.ctrlKey && isSelected){
21448 this.deselectRow(rowIndex);
21449 }else if(!isSelected){
21450 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21451 view.focusRow(rowIndex);
21454 this.fireEvent("afterselectionchange", this);
21457 handleDragableRowClick : function(grid, rowIndex, e)
21459 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21460 this.selectRow(rowIndex, false);
21461 grid.view.focusRow(rowIndex);
21462 this.fireEvent("afterselectionchange", this);
21467 * Selects multiple rows.
21468 * @param {Array} rows Array of the indexes of the row to select
21469 * @param {Boolean} keepExisting (optional) True to keep existing selections
21471 selectRows : function(rows, keepExisting){
21473 this.clearSelections();
21475 for(var i = 0, len = rows.length; i < len; i++){
21476 this.selectRow(rows[i], true);
21481 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21482 * @param {Number} startRow The index of the first row in the range
21483 * @param {Number} endRow The index of the last row in the range
21484 * @param {Boolean} keepExisting (optional) True to retain existing selections
21486 selectRange : function(startRow, endRow, keepExisting){
21487 if(this.locked) return;
21489 this.clearSelections();
21491 if(startRow <= endRow){
21492 for(var i = startRow; i <= endRow; i++){
21493 this.selectRow(i, true);
21496 for(var i = startRow; i >= endRow; i--){
21497 this.selectRow(i, true);
21503 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21504 * @param {Number} startRow The index of the first row in the range
21505 * @param {Number} endRow The index of the last row in the range
21507 deselectRange : function(startRow, endRow, preventViewNotify){
21508 if(this.locked) return;
21509 for(var i = startRow; i <= endRow; i++){
21510 this.deselectRow(i, preventViewNotify);
21516 * @param {Number} row The index of the row to select
21517 * @param {Boolean} keepExisting (optional) True to keep existing selections
21519 selectRow : function(index, keepExisting, preventViewNotify){
21520 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21521 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21522 if(!keepExisting || this.singleSelect){
21523 this.clearSelections();
21525 var r = this.grid.dataSource.getAt(index);
21526 this.selections.add(r);
21527 this.last = this.lastActive = index;
21528 if(!preventViewNotify){
21529 this.grid.getView().onRowSelect(index);
21531 this.fireEvent("rowselect", this, index, r);
21532 this.fireEvent("selectionchange", this);
21538 * @param {Number} row The index of the row to deselect
21540 deselectRow : function(index, preventViewNotify){
21541 if(this.locked) return;
21542 if(this.last == index){
21545 if(this.lastActive == index){
21546 this.lastActive = false;
21548 var r = this.grid.dataSource.getAt(index);
21549 this.selections.remove(r);
21550 if(!preventViewNotify){
21551 this.grid.getView().onRowDeselect(index);
21553 this.fireEvent("rowdeselect", this, index);
21554 this.fireEvent("selectionchange", this);
21558 restoreLast : function(){
21560 this.last = this._last;
21565 acceptsNav : function(row, col, cm){
21566 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21570 onEditorKey : function(field, e){
21571 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21576 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21578 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21580 }else if(k == e.ENTER && !e.ctrlKey){
21584 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21586 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21588 }else if(k == e.ESC){
21592 g.startEditing(newCell[0], newCell[1]);
21597 * Ext JS Library 1.1.1
21598 * Copyright(c) 2006-2007, Ext JS, LLC.
21600 * Originally Released Under LGPL - original licence link has changed is not relivant.
21603 * <script type="text/javascript">
21607 * @class Roo.bootstrap.PagingToolbar
21609 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21611 * Create a new PagingToolbar
21612 * @param {Object} config The config object
21614 Roo.bootstrap.PagingToolbar = function(config)
21616 // old args format still supported... - xtype is prefered..
21617 // created from xtype...
21618 var ds = config.dataSource;
21619 this.toolbarItems = [];
21620 if (config.items) {
21621 this.toolbarItems = config.items;
21622 // config.items = [];
21625 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21632 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21636 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21638 * @cfg {Roo.data.Store} dataSource
21639 * The underlying data store providing the paged data
21642 * @cfg {String/HTMLElement/Element} container
21643 * container The id or element that will contain the toolbar
21646 * @cfg {Boolean} displayInfo
21647 * True to display the displayMsg (defaults to false)
21650 * @cfg {Number} pageSize
21651 * The number of records to display per page (defaults to 20)
21655 * @cfg {String} displayMsg
21656 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21658 displayMsg : 'Displaying {0} - {1} of {2}',
21660 * @cfg {String} emptyMsg
21661 * The message to display when no records are found (defaults to "No data to display")
21663 emptyMsg : 'No data to display',
21665 * Customizable piece of the default paging text (defaults to "Page")
21668 beforePageText : "Page",
21670 * Customizable piece of the default paging text (defaults to "of %0")
21673 afterPageText : "of {0}",
21675 * Customizable piece of the default paging text (defaults to "First Page")
21678 firstText : "First Page",
21680 * Customizable piece of the default paging text (defaults to "Previous Page")
21683 prevText : "Previous Page",
21685 * Customizable piece of the default paging text (defaults to "Next Page")
21688 nextText : "Next Page",
21690 * Customizable piece of the default paging text (defaults to "Last Page")
21693 lastText : "Last Page",
21695 * Customizable piece of the default paging text (defaults to "Refresh")
21698 refreshText : "Refresh",
21702 onRender : function(ct, position)
21704 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21705 this.navgroup.parentId = this.id;
21706 this.navgroup.onRender(this.el, null);
21707 // add the buttons to the navgroup
21709 if(this.displayInfo){
21710 Roo.log(this.el.select('ul.navbar-nav',true).first());
21711 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21712 this.displayEl = this.el.select('.x-paging-info', true).first();
21713 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21714 // this.displayEl = navel.el.select('span',true).first();
21720 Roo.each(_this.buttons, function(e){
21721 Roo.factory(e).onRender(_this.el, null);
21725 Roo.each(_this.toolbarItems, function(e) {
21726 _this.navgroup.addItem(e);
21730 this.first = this.navgroup.addItem({
21731 tooltip: this.firstText,
21733 icon : 'fa fa-backward',
21735 preventDefault: true,
21736 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21739 this.prev = this.navgroup.addItem({
21740 tooltip: this.prevText,
21742 icon : 'fa fa-step-backward',
21744 preventDefault: true,
21745 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21747 //this.addSeparator();
21750 var field = this.navgroup.addItem( {
21752 cls : 'x-paging-position',
21754 html : this.beforePageText +
21755 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21756 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21759 this.field = field.el.select('input', true).first();
21760 this.field.on("keydown", this.onPagingKeydown, this);
21761 this.field.on("focus", function(){this.dom.select();});
21764 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21765 //this.field.setHeight(18);
21766 //this.addSeparator();
21767 this.next = this.navgroup.addItem({
21768 tooltip: this.nextText,
21770 html : ' <i class="fa fa-step-forward">',
21772 preventDefault: true,
21773 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21775 this.last = this.navgroup.addItem({
21776 tooltip: this.lastText,
21777 icon : 'fa fa-forward',
21780 preventDefault: true,
21781 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21783 //this.addSeparator();
21784 this.loading = this.navgroup.addItem({
21785 tooltip: this.refreshText,
21786 icon: 'fa fa-refresh',
21787 preventDefault: true,
21788 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21794 updateInfo : function(){
21795 if(this.displayEl){
21796 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21797 var msg = count == 0 ?
21801 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21803 this.displayEl.update(msg);
21808 onLoad : function(ds, r, o){
21809 this.cursor = o.params ? o.params.start : 0;
21810 var d = this.getPageData(),
21814 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21815 this.field.dom.value = ap;
21816 this.first.setDisabled(ap == 1);
21817 this.prev.setDisabled(ap == 1);
21818 this.next.setDisabled(ap == ps);
21819 this.last.setDisabled(ap == ps);
21820 this.loading.enable();
21825 getPageData : function(){
21826 var total = this.ds.getTotalCount();
21829 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21830 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21835 onLoadError : function(){
21836 this.loading.enable();
21840 onPagingKeydown : function(e){
21841 var k = e.getKey();
21842 var d = this.getPageData();
21844 var v = this.field.dom.value, pageNum;
21845 if(!v || isNaN(pageNum = parseInt(v, 10))){
21846 this.field.dom.value = d.activePage;
21849 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21850 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21853 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))
21855 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21856 this.field.dom.value = pageNum;
21857 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21860 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21862 var v = this.field.dom.value, pageNum;
21863 var increment = (e.shiftKey) ? 10 : 1;
21864 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21866 if(!v || isNaN(pageNum = parseInt(v, 10))) {
21867 this.field.dom.value = d.activePage;
21870 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21872 this.field.dom.value = parseInt(v, 10) + increment;
21873 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21874 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21881 beforeLoad : function(){
21883 this.loading.disable();
21888 onClick : function(which){
21897 ds.load({params:{start: 0, limit: this.pageSize}});
21900 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21903 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21906 var total = ds.getTotalCount();
21907 var extra = total % this.pageSize;
21908 var lastStart = extra ? (total - extra) : total-this.pageSize;
21909 ds.load({params:{start: lastStart, limit: this.pageSize}});
21912 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21918 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21919 * @param {Roo.data.Store} store The data store to unbind
21921 unbind : function(ds){
21922 ds.un("beforeload", this.beforeLoad, this);
21923 ds.un("load", this.onLoad, this);
21924 ds.un("loadexception", this.onLoadError, this);
21925 ds.un("remove", this.updateInfo, this);
21926 ds.un("add", this.updateInfo, this);
21927 this.ds = undefined;
21931 * Binds the paging toolbar to the specified {@link Roo.data.Store}
21932 * @param {Roo.data.Store} store The data store to bind
21934 bind : function(ds){
21935 ds.on("beforeload", this.beforeLoad, this);
21936 ds.on("load", this.onLoad, this);
21937 ds.on("loadexception", this.onLoadError, this);
21938 ds.on("remove", this.updateInfo, this);
21939 ds.on("add", this.updateInfo, this);
21950 * @class Roo.bootstrap.MessageBar
21951 * @extends Roo.bootstrap.Component
21952 * Bootstrap MessageBar class
21953 * @cfg {String} html contents of the MessageBar
21954 * @cfg {String} weight (info | success | warning | danger) default info
21955 * @cfg {String} beforeClass insert the bar before the given class
21956 * @cfg {Boolean} closable (true | false) default false
21957 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21960 * Create a new Element
21961 * @param {Object} config The config object
21964 Roo.bootstrap.MessageBar = function(config){
21965 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21968 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
21974 beforeClass: 'bootstrap-sticky-wrap',
21976 getAutoCreate : function(){
21980 cls: 'alert alert-dismissable alert-' + this.weight,
21985 html: this.html || ''
21991 cfg.cls += ' alert-messages-fixed';
22005 onRender : function(ct, position)
22007 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22010 var cfg = Roo.apply({}, this.getAutoCreate());
22014 cfg.cls += ' ' + this.cls;
22017 cfg.style = this.style;
22019 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22021 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22024 this.el.select('>button.close').on('click', this.hide, this);
22030 if (!this.rendered) {
22036 this.fireEvent('show', this);
22042 if (!this.rendered) {
22048 this.fireEvent('hide', this);
22051 update : function()
22053 // var e = this.el.dom.firstChild;
22055 // if(this.closable){
22056 // e = e.nextSibling;
22059 // e.data = this.html || '';
22061 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22077 * @class Roo.bootstrap.Graph
22078 * @extends Roo.bootstrap.Component
22079 * Bootstrap Graph class
22083 @cfg {String} graphtype bar | vbar | pie
22084 @cfg {number} g_x coodinator | centre x (pie)
22085 @cfg {number} g_y coodinator | centre y (pie)
22086 @cfg {number} g_r radius (pie)
22087 @cfg {number} g_height height of the chart (respected by all elements in the set)
22088 @cfg {number} g_width width of the chart (respected by all elements in the set)
22089 @cfg {Object} title The title of the chart
22092 -opts (object) options for the chart
22094 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22095 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22097 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.
22098 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22100 o stretch (boolean)
22102 -opts (object) options for the pie
22105 o startAngle (number)
22106 o endAngle (number)
22110 * Create a new Input
22111 * @param {Object} config The config object
22114 Roo.bootstrap.Graph = function(config){
22115 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22121 * The img click event for the img.
22122 * @param {Roo.EventObject} e
22128 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22139 //g_colors: this.colors,
22146 getAutoCreate : function(){
22157 onRender : function(ct,position){
22158 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22159 this.raphael = Raphael(this.el.dom);
22161 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22162 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22163 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22164 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22166 r.text(160, 10, "Single Series Chart").attr(txtattr);
22167 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22168 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22169 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22171 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22172 r.barchart(330, 10, 300, 220, data1);
22173 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22174 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22177 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22178 // r.barchart(30, 30, 560, 250, xdata, {
22179 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22180 // axis : "0 0 1 1",
22181 // axisxlabels : xdata
22182 // //yvalues : cols,
22185 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22187 // this.load(null,xdata,{
22188 // axis : "0 0 1 1",
22189 // axisxlabels : xdata
22194 load : function(graphtype,xdata,opts){
22195 this.raphael.clear();
22197 graphtype = this.graphtype;
22202 var r = this.raphael,
22203 fin = function () {
22204 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22206 fout = function () {
22207 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22209 pfin = function() {
22210 this.sector.stop();
22211 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22214 this.label[0].stop();
22215 this.label[0].attr({ r: 7.5 });
22216 this.label[1].attr({ "font-weight": 800 });
22219 pfout = function() {
22220 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22223 this.label[0].animate({ r: 5 }, 500, "bounce");
22224 this.label[1].attr({ "font-weight": 400 });
22230 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22233 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22236 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22237 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22239 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22246 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22251 setTitle: function(o)
22256 initEvents: function() {
22259 this.el.on('click', this.onClick, this);
22263 onClick : function(e)
22265 Roo.log('img onclick');
22266 this.fireEvent('click', this, e);
22278 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22281 * @class Roo.bootstrap.dash.NumberBox
22282 * @extends Roo.bootstrap.Component
22283 * Bootstrap NumberBox class
22284 * @cfg {String} headline Box headline
22285 * @cfg {String} content Box content
22286 * @cfg {String} icon Box icon
22287 * @cfg {String} footer Footer text
22288 * @cfg {String} fhref Footer href
22291 * Create a new NumberBox
22292 * @param {Object} config The config object
22296 Roo.bootstrap.dash.NumberBox = function(config){
22297 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22301 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22310 getAutoCreate : function(){
22314 cls : 'small-box ',
22322 cls : 'roo-headline',
22323 html : this.headline
22327 cls : 'roo-content',
22328 html : this.content
22342 cls : 'ion ' + this.icon
22351 cls : 'small-box-footer',
22352 href : this.fhref || '#',
22356 cfg.cn.push(footer);
22363 onRender : function(ct,position){
22364 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22371 setHeadline: function (value)
22373 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22376 setFooter: function (value, href)
22378 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22381 this.el.select('a.small-box-footer',true).first().attr('href', href);
22386 setContent: function (value)
22388 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22391 initEvents: function()
22405 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22408 * @class Roo.bootstrap.dash.TabBox
22409 * @extends Roo.bootstrap.Component
22410 * Bootstrap TabBox class
22411 * @cfg {String} title Title of the TabBox
22412 * @cfg {String} icon Icon of the TabBox
22413 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22414 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22417 * Create a new TabBox
22418 * @param {Object} config The config object
22422 Roo.bootstrap.dash.TabBox = function(config){
22423 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22428 * When a pane is added
22429 * @param {Roo.bootstrap.dash.TabPane} pane
22433 * @event activatepane
22434 * When a pane is activated
22435 * @param {Roo.bootstrap.dash.TabPane} pane
22437 "activatepane" : true
22445 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22450 tabScrollable : false,
22452 getChildContainer : function()
22454 return this.el.select('.tab-content', true).first();
22457 getAutoCreate : function(){
22461 cls: 'pull-left header',
22469 cls: 'fa ' + this.icon
22475 cls: 'nav nav-tabs pull-right',
22481 if(this.tabScrollable){
22488 cls: 'nav nav-tabs pull-right',
22499 cls: 'nav-tabs-custom',
22504 cls: 'tab-content no-padding',
22512 initEvents : function()
22514 //Roo.log('add add pane handler');
22515 this.on('addpane', this.onAddPane, this);
22518 * Updates the box title
22519 * @param {String} html to set the title to.
22521 setTitle : function(value)
22523 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22525 onAddPane : function(pane)
22527 this.panes.push(pane);
22528 //Roo.log('addpane');
22530 // tabs are rendere left to right..
22531 if(!this.showtabs){
22535 var ctr = this.el.select('.nav-tabs', true).first();
22538 var existing = ctr.select('.nav-tab',true);
22539 var qty = existing.getCount();;
22542 var tab = ctr.createChild({
22544 cls : 'nav-tab' + (qty ? '' : ' active'),
22552 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22555 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22557 pane.el.addClass('active');
22562 onTabClick : function(ev,un,ob,pane)
22564 //Roo.log('tab - prev default');
22565 ev.preventDefault();
22568 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22569 pane.tab.addClass('active');
22570 //Roo.log(pane.title);
22571 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22572 // technically we should have a deactivate event.. but maybe add later.
22573 // and it should not de-activate the selected tab...
22574 this.fireEvent('activatepane', pane);
22575 pane.el.addClass('active');
22576 pane.fireEvent('activate');
22581 getActivePane : function()
22584 Roo.each(this.panes, function(p) {
22585 if(p.el.hasClass('active')){
22606 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22608 * @class Roo.bootstrap.TabPane
22609 * @extends Roo.bootstrap.Component
22610 * Bootstrap TabPane class
22611 * @cfg {Boolean} active (false | true) Default false
22612 * @cfg {String} title title of panel
22616 * Create a new TabPane
22617 * @param {Object} config The config object
22620 Roo.bootstrap.dash.TabPane = function(config){
22621 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22627 * When a pane is activated
22628 * @param {Roo.bootstrap.dash.TabPane} pane
22635 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22640 // the tabBox that this is attached to.
22643 getAutoCreate : function()
22651 cfg.cls += ' active';
22656 initEvents : function()
22658 //Roo.log('trigger add pane handler');
22659 this.parent().fireEvent('addpane', this)
22663 * Updates the tab title
22664 * @param {String} html to set the title to.
22666 setTitle: function(str)
22672 this.tab.select('a', true).first().dom.innerHTML = str;
22689 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22692 * @class Roo.bootstrap.menu.Menu
22693 * @extends Roo.bootstrap.Component
22694 * Bootstrap Menu class - container for Menu
22695 * @cfg {String} html Text of the menu
22696 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22697 * @cfg {String} icon Font awesome icon
22698 * @cfg {String} pos Menu align to (top | bottom) default bottom
22702 * Create a new Menu
22703 * @param {Object} config The config object
22707 Roo.bootstrap.menu.Menu = function(config){
22708 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22712 * @event beforeshow
22713 * Fires before this menu is displayed
22714 * @param {Roo.bootstrap.menu.Menu} this
22718 * @event beforehide
22719 * Fires before this menu is hidden
22720 * @param {Roo.bootstrap.menu.Menu} this
22725 * Fires after this menu is displayed
22726 * @param {Roo.bootstrap.menu.Menu} this
22731 * Fires after this menu is hidden
22732 * @param {Roo.bootstrap.menu.Menu} this
22737 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22738 * @param {Roo.bootstrap.menu.Menu} this
22739 * @param {Roo.EventObject} e
22746 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22750 weight : 'default',
22755 getChildContainer : function() {
22756 if(this.isSubMenu){
22760 return this.el.select('ul.dropdown-menu', true).first();
22763 getAutoCreate : function()
22768 cls : 'roo-menu-text',
22776 cls : 'fa ' + this.icon
22787 cls : 'dropdown-button btn btn-' + this.weight,
22792 cls : 'dropdown-toggle btn btn-' + this.weight,
22802 cls : 'dropdown-menu'
22808 if(this.pos == 'top'){
22809 cfg.cls += ' dropup';
22812 if(this.isSubMenu){
22815 cls : 'dropdown-menu'
22822 onRender : function(ct, position)
22824 this.isSubMenu = ct.hasClass('dropdown-submenu');
22826 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22829 initEvents : function()
22831 if(this.isSubMenu){
22835 this.hidden = true;
22837 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22838 this.triggerEl.on('click', this.onTriggerPress, this);
22840 this.buttonEl = this.el.select('button.dropdown-button', true).first();
22841 this.buttonEl.on('click', this.onClick, this);
22847 if(this.isSubMenu){
22851 return this.el.select('ul.dropdown-menu', true).first();
22854 onClick : function(e)
22856 this.fireEvent("click", this, e);
22859 onTriggerPress : function(e)
22861 if (this.isVisible()) {
22868 isVisible : function(){
22869 return !this.hidden;
22874 this.fireEvent("beforeshow", this);
22876 this.hidden = false;
22877 this.el.addClass('open');
22879 Roo.get(document).on("mouseup", this.onMouseUp, this);
22881 this.fireEvent("show", this);
22888 this.fireEvent("beforehide", this);
22890 this.hidden = true;
22891 this.el.removeClass('open');
22893 Roo.get(document).un("mouseup", this.onMouseUp);
22895 this.fireEvent("hide", this);
22898 onMouseUp : function()
22912 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22915 * @class Roo.bootstrap.menu.Item
22916 * @extends Roo.bootstrap.Component
22917 * Bootstrap MenuItem class
22918 * @cfg {Boolean} submenu (true | false) default false
22919 * @cfg {String} html text of the item
22920 * @cfg {String} href the link
22921 * @cfg {Boolean} disable (true | false) default false
22922 * @cfg {Boolean} preventDefault (true | false) default true
22923 * @cfg {String} icon Font awesome icon
22924 * @cfg {String} pos Submenu align to (left | right) default right
22928 * Create a new Item
22929 * @param {Object} config The config object
22933 Roo.bootstrap.menu.Item = function(config){
22934 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22938 * Fires when the mouse is hovering over this menu
22939 * @param {Roo.bootstrap.menu.Item} this
22940 * @param {Roo.EventObject} e
22945 * Fires when the mouse exits this menu
22946 * @param {Roo.bootstrap.menu.Item} this
22947 * @param {Roo.EventObject} e
22953 * The raw click event for the entire grid.
22954 * @param {Roo.EventObject} e
22960 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
22965 preventDefault: true,
22970 getAutoCreate : function()
22975 cls : 'roo-menu-item-text',
22983 cls : 'fa ' + this.icon
22992 href : this.href || '#',
22999 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23003 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23005 if(this.pos == 'left'){
23006 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23013 initEvents : function()
23015 this.el.on('mouseover', this.onMouseOver, this);
23016 this.el.on('mouseout', this.onMouseOut, this);
23018 this.el.select('a', true).first().on('click', this.onClick, this);
23022 onClick : function(e)
23024 if(this.preventDefault){
23025 e.preventDefault();
23028 this.fireEvent("click", this, e);
23031 onMouseOver : function(e)
23033 if(this.submenu && this.pos == 'left'){
23034 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23037 this.fireEvent("mouseover", this, e);
23040 onMouseOut : function(e)
23042 this.fireEvent("mouseout", this, e);
23054 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23057 * @class Roo.bootstrap.menu.Separator
23058 * @extends Roo.bootstrap.Component
23059 * Bootstrap Separator class
23062 * Create a new Separator
23063 * @param {Object} config The config object
23067 Roo.bootstrap.menu.Separator = function(config){
23068 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23071 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23073 getAutoCreate : function(){
23094 * @class Roo.bootstrap.Tooltip
23095 * Bootstrap Tooltip class
23096 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23097 * to determine which dom element triggers the tooltip.
23099 * It needs to add support for additional attributes like tooltip-position
23102 * Create a new Toolti
23103 * @param {Object} config The config object
23106 Roo.bootstrap.Tooltip = function(config){
23107 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23110 Roo.apply(Roo.bootstrap.Tooltip, {
23112 * @function init initialize tooltip monitoring.
23116 currentTip : false,
23117 currentRegion : false,
23123 Roo.get(document).on('mouseover', this.enter ,this);
23124 Roo.get(document).on('mouseout', this.leave, this);
23127 this.currentTip = new Roo.bootstrap.Tooltip();
23130 enter : function(ev)
23132 var dom = ev.getTarget();
23134 //Roo.log(['enter',dom]);
23135 var el = Roo.fly(dom);
23136 if (this.currentEl) {
23138 //Roo.log(this.currentEl);
23139 //Roo.log(this.currentEl.contains(dom));
23140 if (this.currentEl == el) {
23143 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23151 if (this.currentTip.el) {
23152 this.currentTip.el.hide(); // force hiding...
23157 // you can not look for children, as if el is the body.. then everythign is the child..
23158 if (!el.attr('tooltip')) { //
23159 if (!el.select("[tooltip]").elements.length) {
23162 // is the mouse over this child...?
23163 bindEl = el.select("[tooltip]").first();
23164 var xy = ev.getXY();
23165 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23166 //Roo.log("not in region.");
23169 //Roo.log("child element over..");
23172 this.currentEl = bindEl;
23173 this.currentTip.bind(bindEl);
23174 this.currentRegion = Roo.lib.Region.getRegion(dom);
23175 this.currentTip.enter();
23178 leave : function(ev)
23180 var dom = ev.getTarget();
23181 //Roo.log(['leave',dom]);
23182 if (!this.currentEl) {
23187 if (dom != this.currentEl.dom) {
23190 var xy = ev.getXY();
23191 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23194 // only activate leave if mouse cursor is outside... bounding box..
23199 if (this.currentTip) {
23200 this.currentTip.leave();
23202 //Roo.log('clear currentEl');
23203 this.currentEl = false;
23208 'left' : ['r-l', [-2,0], 'right'],
23209 'right' : ['l-r', [2,0], 'left'],
23210 'bottom' : ['t-b', [0,2], 'top'],
23211 'top' : [ 'b-t', [0,-2], 'bottom']
23217 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23222 delay : null, // can be { show : 300 , hide: 500}
23226 hoverState : null, //???
23228 placement : 'bottom',
23230 getAutoCreate : function(){
23237 cls : 'tooltip-arrow'
23240 cls : 'tooltip-inner'
23247 bind : function(el)
23253 enter : function () {
23255 if (this.timeout != null) {
23256 clearTimeout(this.timeout);
23259 this.hoverState = 'in';
23260 //Roo.log("enter - show");
23261 if (!this.delay || !this.delay.show) {
23266 this.timeout = setTimeout(function () {
23267 if (_t.hoverState == 'in') {
23270 }, this.delay.show);
23274 clearTimeout(this.timeout);
23276 this.hoverState = 'out';
23277 if (!this.delay || !this.delay.hide) {
23283 this.timeout = setTimeout(function () {
23284 //Roo.log("leave - timeout");
23286 if (_t.hoverState == 'out') {
23288 Roo.bootstrap.Tooltip.currentEl = false;
23296 this.render(document.body);
23299 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23301 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23303 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23305 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23307 var placement = typeof this.placement == 'function' ?
23308 this.placement.call(this, this.el, on_el) :
23311 var autoToken = /\s?auto?\s?/i;
23312 var autoPlace = autoToken.test(placement);
23314 placement = placement.replace(autoToken, '') || 'top';
23318 //this.el.setXY([0,0]);
23320 //this.el.dom.style.display='block';
23321 this.el.addClass(placement);
23323 //this.el.appendTo(on_el);
23325 var p = this.getPosition();
23326 var box = this.el.getBox();
23331 var align = Roo.bootstrap.Tooltip.alignment[placement];
23332 this.el.alignTo(this.bindEl, align[0],align[1]);
23333 //var arrow = this.el.select('.arrow',true).first();
23334 //arrow.set(align[2],
23336 this.el.addClass('in fade');
23337 this.hoverState = null;
23339 if (this.el.hasClass('fade')) {
23350 //this.el.setXY([0,0]);
23351 this.el.removeClass('in');
23367 * @class Roo.bootstrap.LocationPicker
23368 * @extends Roo.bootstrap.Component
23369 * Bootstrap LocationPicker class
23370 * @cfg {Number} latitude Position when init default 0
23371 * @cfg {Number} longitude Position when init default 0
23372 * @cfg {Number} zoom default 15
23373 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23374 * @cfg {Boolean} mapTypeControl default false
23375 * @cfg {Boolean} disableDoubleClickZoom default false
23376 * @cfg {Boolean} scrollwheel default true
23377 * @cfg {Boolean} streetViewControl default false
23378 * @cfg {Number} radius default 0
23379 * @cfg {String} locationName
23380 * @cfg {Boolean} draggable default true
23381 * @cfg {Boolean} enableAutocomplete default false
23382 * @cfg {Boolean} enableReverseGeocode default true
23383 * @cfg {String} markerTitle
23386 * Create a new LocationPicker
23387 * @param {Object} config The config object
23391 Roo.bootstrap.LocationPicker = function(config){
23393 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23398 * Fires when the picker initialized.
23399 * @param {Roo.bootstrap.LocationPicker} this
23400 * @param {Google Location} location
23404 * @event positionchanged
23405 * Fires when the picker position changed.
23406 * @param {Roo.bootstrap.LocationPicker} this
23407 * @param {Google Location} location
23409 positionchanged : true,
23412 * Fires when the map resize.
23413 * @param {Roo.bootstrap.LocationPicker} this
23418 * Fires when the map show.
23419 * @param {Roo.bootstrap.LocationPicker} this
23424 * Fires when the map hide.
23425 * @param {Roo.bootstrap.LocationPicker} this
23430 * Fires when click the map.
23431 * @param {Roo.bootstrap.LocationPicker} this
23432 * @param {Map event} e
23436 * @event mapRightClick
23437 * Fires when right click the map.
23438 * @param {Roo.bootstrap.LocationPicker} this
23439 * @param {Map event} e
23441 mapRightClick : true,
23443 * @event markerClick
23444 * Fires when click the marker.
23445 * @param {Roo.bootstrap.LocationPicker} this
23446 * @param {Map event} e
23448 markerClick : true,
23450 * @event markerRightClick
23451 * Fires when right click the marker.
23452 * @param {Roo.bootstrap.LocationPicker} this
23453 * @param {Map event} e
23455 markerRightClick : true,
23457 * @event OverlayViewDraw
23458 * Fires when OverlayView Draw
23459 * @param {Roo.bootstrap.LocationPicker} this
23461 OverlayViewDraw : true,
23463 * @event OverlayViewOnAdd
23464 * Fires when OverlayView Draw
23465 * @param {Roo.bootstrap.LocationPicker} this
23467 OverlayViewOnAdd : true,
23469 * @event OverlayViewOnRemove
23470 * Fires when OverlayView Draw
23471 * @param {Roo.bootstrap.LocationPicker} this
23473 OverlayViewOnRemove : true,
23475 * @event OverlayViewShow
23476 * Fires when OverlayView Draw
23477 * @param {Roo.bootstrap.LocationPicker} this
23478 * @param {Pixel} cpx
23480 OverlayViewShow : true,
23482 * @event OverlayViewHide
23483 * Fires when OverlayView Draw
23484 * @param {Roo.bootstrap.LocationPicker} this
23486 OverlayViewHide : true
23491 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23493 gMapContext: false,
23499 mapTypeControl: false,
23500 disableDoubleClickZoom: false,
23502 streetViewControl: false,
23506 enableAutocomplete: false,
23507 enableReverseGeocode: true,
23510 getAutoCreate: function()
23515 cls: 'roo-location-picker'
23521 initEvents: function(ct, position)
23523 if(!this.el.getWidth() || this.isApplied()){
23527 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23532 initial: function()
23534 if(!this.mapTypeId){
23535 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23538 this.gMapContext = this.GMapContext();
23540 this.initOverlayView();
23542 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23546 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23547 _this.setPosition(_this.gMapContext.marker.position);
23550 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23551 _this.fireEvent('mapClick', this, event);
23555 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23556 _this.fireEvent('mapRightClick', this, event);
23560 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23561 _this.fireEvent('markerClick', this, event);
23565 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23566 _this.fireEvent('markerRightClick', this, event);
23570 this.setPosition(this.gMapContext.location);
23572 this.fireEvent('initial', this, this.gMapContext.location);
23575 initOverlayView: function()
23579 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23583 _this.fireEvent('OverlayViewDraw', _this);
23588 _this.fireEvent('OverlayViewOnAdd', _this);
23591 onRemove: function()
23593 _this.fireEvent('OverlayViewOnRemove', _this);
23596 show: function(cpx)
23598 _this.fireEvent('OverlayViewShow', _this, cpx);
23603 _this.fireEvent('OverlayViewHide', _this);
23609 fromLatLngToContainerPixel: function(event)
23611 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23614 isApplied: function()
23616 return this.getGmapContext() == false ? false : true;
23619 getGmapContext: function()
23621 return this.gMapContext
23624 GMapContext: function()
23626 var position = new google.maps.LatLng(this.latitude, this.longitude);
23628 var _map = new google.maps.Map(this.el.dom, {
23631 mapTypeId: this.mapTypeId,
23632 mapTypeControl: this.mapTypeControl,
23633 disableDoubleClickZoom: this.disableDoubleClickZoom,
23634 scrollwheel: this.scrollwheel,
23635 streetViewControl: this.streetViewControl,
23636 locationName: this.locationName,
23637 draggable: this.draggable,
23638 enableAutocomplete: this.enableAutocomplete,
23639 enableReverseGeocode: this.enableReverseGeocode
23642 var _marker = new google.maps.Marker({
23643 position: position,
23645 title: this.markerTitle,
23646 draggable: this.draggable
23653 location: position,
23654 radius: this.radius,
23655 locationName: this.locationName,
23656 addressComponents: {
23657 formatted_address: null,
23658 addressLine1: null,
23659 addressLine2: null,
23661 streetNumber: null,
23665 stateOrProvince: null
23668 domContainer: this.el.dom,
23669 geodecoder: new google.maps.Geocoder()
23673 drawCircle: function(center, radius, options)
23675 if (this.gMapContext.circle != null) {
23676 this.gMapContext.circle.setMap(null);
23680 options = Roo.apply({}, options, {
23681 strokeColor: "#0000FF",
23682 strokeOpacity: .35,
23684 fillColor: "#0000FF",
23688 options.map = this.gMapContext.map;
23689 options.radius = radius;
23690 options.center = center;
23691 this.gMapContext.circle = new google.maps.Circle(options);
23692 return this.gMapContext.circle;
23698 setPosition: function(location)
23700 this.gMapContext.location = location;
23701 this.gMapContext.marker.setPosition(location);
23702 this.gMapContext.map.panTo(location);
23703 this.drawCircle(location, this.gMapContext.radius, {});
23707 if (this.gMapContext.settings.enableReverseGeocode) {
23708 this.gMapContext.geodecoder.geocode({
23709 latLng: this.gMapContext.location
23710 }, function(results, status) {
23712 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23713 _this.gMapContext.locationName = results[0].formatted_address;
23714 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23716 _this.fireEvent('positionchanged', this, location);
23723 this.fireEvent('positionchanged', this, location);
23728 google.maps.event.trigger(this.gMapContext.map, "resize");
23730 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23732 this.fireEvent('resize', this);
23735 setPositionByLatLng: function(latitude, longitude)
23737 this.setPosition(new google.maps.LatLng(latitude, longitude));
23740 getCurrentPosition: function()
23743 latitude: this.gMapContext.location.lat(),
23744 longitude: this.gMapContext.location.lng()
23748 getAddressName: function()
23750 return this.gMapContext.locationName;
23753 getAddressComponents: function()
23755 return this.gMapContext.addressComponents;
23758 address_component_from_google_geocode: function(address_components)
23762 for (var i = 0; i < address_components.length; i++) {
23763 var component = address_components[i];
23764 if (component.types.indexOf("postal_code") >= 0) {
23765 result.postalCode = component.short_name;
23766 } else if (component.types.indexOf("street_number") >= 0) {
23767 result.streetNumber = component.short_name;
23768 } else if (component.types.indexOf("route") >= 0) {
23769 result.streetName = component.short_name;
23770 } else if (component.types.indexOf("neighborhood") >= 0) {
23771 result.city = component.short_name;
23772 } else if (component.types.indexOf("locality") >= 0) {
23773 result.city = component.short_name;
23774 } else if (component.types.indexOf("sublocality") >= 0) {
23775 result.district = component.short_name;
23776 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23777 result.stateOrProvince = component.short_name;
23778 } else if (component.types.indexOf("country") >= 0) {
23779 result.country = component.short_name;
23783 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23784 result.addressLine2 = "";
23788 setZoomLevel: function(zoom)
23790 this.gMapContext.map.setZoom(zoom);
23803 this.fireEvent('show', this);
23814 this.fireEvent('hide', this);
23819 Roo.apply(Roo.bootstrap.LocationPicker, {
23821 OverlayView : function(map, options)
23823 options = options || {};
23837 * @class Roo.bootstrap.Alert
23838 * @extends Roo.bootstrap.Component
23839 * Bootstrap Alert class
23840 * @cfg {String} title The title of alert
23841 * @cfg {String} html The content of alert
23842 * @cfg {String} weight ( success | info | warning | danger )
23843 * @cfg {String} faicon font-awesomeicon
23846 * Create a new alert
23847 * @param {Object} config The config object
23851 Roo.bootstrap.Alert = function(config){
23852 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23856 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
23863 getAutoCreate : function()
23872 cls : 'roo-alert-icon'
23877 cls : 'roo-alert-title',
23882 cls : 'roo-alert-text',
23889 cfg.cn[0].cls += ' fa ' + this.faicon;
23893 cfg.cls += ' alert-' + this.weight;
23899 initEvents: function()
23901 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23904 setTitle : function(str)
23906 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23909 setText : function(str)
23911 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23914 setWeight : function(weight)
23917 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23920 this.weight = weight;
23922 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23925 setIcon : function(icon)
23928 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23933 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23954 * @class Roo.bootstrap.UploadCropbox
23955 * @extends Roo.bootstrap.Component
23956 * Bootstrap UploadCropbox class
23957 * @cfg {String} emptyText show when image has been loaded
23958 * @cfg {String} rotateNotify show when image too small to rotate
23959 * @cfg {Number} errorTimeout default 3000
23960 * @cfg {Number} minWidth default 300
23961 * @cfg {Number} minHeight default 300
23962 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23965 * Create a new UploadCropbox
23966 * @param {Object} config The config object
23969 Roo.bootstrap.UploadCropbox = function(config){
23970 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23974 * @event beforeselectfile
23975 * Fire before select file
23976 * @param {Roo.bootstrap.UploadCropbox} this
23978 "beforeselectfile" : true,
23981 * Fire after initEvent
23982 * @param {Roo.bootstrap.UploadCropbox} this
23987 * Fire after initEvent
23988 * @param {Roo.bootstrap.UploadCropbox} this
23989 * @param {String} data
23994 * Fire when preparing the file data
23995 * @param {Roo.bootstrap.UploadCropbox} this
23996 * @param {Object} file
24001 * Fire when get exception
24002 * @param {Roo.bootstrap.UploadCropbox} this
24003 * @param {Object} options
24005 "exception" : true,
24007 * @event beforeloadcanvas
24008 * Fire before load the canvas
24009 * @param {Roo.bootstrap.UploadCropbox} this
24010 * @param {String} src
24012 "beforeloadcanvas" : true,
24015 * Fire when trash image
24016 * @param {Roo.bootstrap.UploadCropbox} this
24021 * Fire when download the image
24022 * @param {Roo.bootstrap.UploadCropbox} this
24026 * @event footerbuttonclick
24027 * Fire when footerbuttonclick
24028 * @param {Roo.bootstrap.UploadCropbox} this
24029 * @param {String} type
24031 "footerbuttonclick" : true,
24035 * @param {Roo.bootstrap.UploadCropbox} this
24041 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24044 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24046 emptyText : 'Click to upload image',
24047 rotateNotify : 'Image is too small to rotate',
24048 errorTimeout : 3000,
24062 cropType : 'image/jpeg',
24064 canvasLoaded : false,
24066 getAutoCreate : function()
24070 cls : 'roo-upload-cropbox',
24074 cls : 'roo-upload-cropbox-body',
24075 style : 'cursor:pointer',
24079 cls : 'roo-upload-cropbox-preview'
24083 cls : 'roo-upload-cropbox-thumb'
24087 cls : 'roo-upload-cropbox-empty-notify',
24088 html : this.emptyText
24092 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24093 html : this.rotateNotify
24099 cls : 'roo-upload-cropbox-footer',
24102 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24112 onRender : function(ct, position)
24114 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24116 if (this.buttons.length) {
24118 Roo.each(this.buttons, function(bb) {
24120 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24122 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24128 initEvents : function()
24130 this.urlAPI = (window.createObjectURL && window) ||
24131 (window.URL && URL.revokeObjectURL && URL) ||
24132 (window.webkitURL && webkitURL);
24134 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24135 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24137 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24138 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24140 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24141 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24142 this.thumbEl.hide();
24144 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24145 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24147 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24148 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24149 this.errorEl.hide();
24151 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24152 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24153 this.footerEl.hide();
24155 this.setThumbBoxSize();
24161 this.fireEvent('initial', this);
24168 window.addEventListener("resize", function() { _this.resize(); } );
24170 this.bodyEl.on('click', this.beforeSelectFile, this);
24173 this.bodyEl.on('touchstart', this.onTouchStart, this);
24174 this.bodyEl.on('touchmove', this.onTouchMove, this);
24175 this.bodyEl.on('touchend', this.onTouchEnd, this);
24179 this.bodyEl.on('mousedown', this.onMouseDown, this);
24180 this.bodyEl.on('mousemove', this.onMouseMove, this);
24181 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24182 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24183 Roo.get(document).on('mouseup', this.onMouseUp, this);
24190 this.baseScale = 1;
24192 this.baseRotate = 1;
24193 this.dragable = false;
24194 this.pinching = false;
24197 this.cropData = false;
24198 this.notifyEl.dom.innerHTML = this.emptyText;
24202 resize : function()
24204 if(this.fireEvent('resize', this) != false){
24205 this.setThumbBoxPosition();
24206 this.setCanvasPosition();
24210 onFooterButtonClick : function(e, el, o, type)
24213 case 'rotate-left' :
24214 this.onRotateLeft(e);
24216 case 'rotate-right' :
24217 this.onRotateRight(e);
24220 this.beforeSelectFile(e);
24235 this.fireEvent('footerbuttonclick', this, type);
24238 beforeSelectFile : function(e)
24240 this.fireEvent('beforeselectfile', this);
24243 trash : function(e)
24245 this.fireEvent('trash', this);
24248 download : function(e)
24250 this.fireEvent('download', this);
24253 loadCanvas : function(src)
24255 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24259 this.imageEl = document.createElement('img');
24263 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24265 this.imageEl.src = src;
24269 onLoadCanvas : function()
24271 this.bodyEl.un('click', this.beforeSelectFile, this);
24273 this.notifyEl.hide();
24274 this.thumbEl.show();
24275 this.footerEl.show();
24277 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24278 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24280 this.setThumbBoxPosition();
24281 this.baseRotateLevel();
24282 this.baseScaleLevel();
24288 this.canvasLoaded = true;
24292 setCanvasPosition : function()
24294 if(!this.canvasEl){
24298 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24299 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24301 this.previewEl.setLeft(pw);
24302 this.previewEl.setTop(ph);
24306 onMouseDown : function(e)
24310 this.dragable = true;
24311 this.pinching = false;
24313 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24314 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24318 onMouseMove : function(e)
24322 if(!this.canvasLoaded){
24326 if (!this.dragable){
24330 var minX = Math.ceil(this.thumbEl.getLeft(true));
24331 var minY = Math.ceil(this.thumbEl.getTop(true));
24333 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24334 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24336 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24337 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24339 x = x - this.mouseX;
24340 y = y - this.mouseY;
24342 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24343 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24345 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24346 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24348 this.previewEl.setLeft(bgX);
24349 this.previewEl.setTop(bgY);
24351 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24352 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24355 onMouseUp : function(e)
24359 this.dragable = false;
24362 onMouseWheel : function(e)
24366 this.startScale = this.scale;
24368 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24370 if(!this.zoomable()){
24371 this.scale = this.startScale;
24380 zoomable : function()
24382 var minScale = this.thumbEl.getWidth() / this.minWidth;
24384 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24385 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24388 (this.rotate == 0 || this.rotate == 180) &&
24390 width / minScale < this.minWidth ||
24391 width / minScale > this.imageEl.OriginWidth ||
24392 height / minScale < this.minHeight ||
24393 height / minScale > this.imageEl.OriginHeight
24400 (this.rotate == 90 || this.rotate == 270) &&
24402 width / minScale < this.minHeight ||
24403 width / minScale > this.imageEl.OriginWidth ||
24404 height / minScale < this.minWidth ||
24405 height / minScale > this.imageEl.OriginHeight
24415 onRotateLeft : function(e)
24417 var minScale = this.thumbEl.getWidth() / this.minWidth;
24419 if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24421 var bw = this.canvasEl.width / this.getScaleLevel();
24422 var bh = this.canvasEl.height / this.getScaleLevel();
24424 this.startScale = this.scale;
24426 while (this.getScaleLevel() < minScale){
24428 this.scale = this.scale + 1;
24430 if(!this.zoomable()){
24435 bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24436 bh * this.getScaleLevel() < this.thumbEl.getWidth()
24441 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24448 this.scale = this.startScale;
24450 this.onRotateFail();
24455 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24461 onRotateRight : function(e)
24463 var minScale = this.thumbEl.getWidth() / this.minWidth;
24465 if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24467 var bw = this.canvasEl.width / this.getScaleLevel();
24468 var bh = this.canvasEl.height / this.getScaleLevel();
24470 this.startScale = this.scale;
24472 while (this.getScaleLevel() < minScale){
24474 this.scale = this.scale + 1;
24476 if(!this.zoomable()){
24481 bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24482 bh * this.getScaleLevel() < this.thumbEl.getWidth()
24487 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24494 this.scale = this.startScale;
24496 this.onRotateFail();
24501 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24506 onRotateFail : function()
24508 this.errorEl.show(true);
24512 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24517 this.previewEl.dom.innerHTML = '';
24519 var canvasEl = document.createElement("canvas");
24521 var contextEl = canvasEl.getContext("2d");
24523 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24524 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24525 var center = this.imageEl.OriginWidth / 2;
24527 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24528 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24529 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24530 center = this.imageEl.OriginHeight / 2;
24533 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24535 contextEl.translate(center, center);
24536 contextEl.rotate(this.rotate * Math.PI / 180);
24538 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24540 this.canvasEl = document.createElement("canvas");
24542 this.contextEl = this.canvasEl.getContext("2d");
24544 switch (this.rotate) {
24547 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24548 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24550 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24555 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24556 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24558 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24559 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);
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.OriginWidth * this.getScaleLevel();
24569 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24571 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24572 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);
24576 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);
24581 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24582 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24584 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24585 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24589 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);
24596 this.previewEl.appendChild(this.canvasEl);
24598 this.setCanvasPosition();
24603 if(!this.canvasLoaded){
24606 var canvas = document.createElement("canvas");
24608 var context = canvas.getContext("2d");
24610 canvas.width = this.minWidth;
24611 canvas.height = this.minHeight;
24613 var cropWidth = this.thumbEl.getWidth();
24614 var cropHeight = this.thumbEl.getHeight();
24616 var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24617 var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24619 if(this.canvasEl.width - cropWidth < x){
24620 x = this.canvasEl.width - cropWidth;
24623 if(this.canvasEl.height - cropHeight < y){
24624 y = this.canvasEl.height - cropHeight;
24630 context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24632 this.cropData = canvas.toDataURL(this.cropType);
24634 this.fireEvent('crop', this, this.cropData);
24638 setThumbBoxSize : function()
24641 var width = Math.ceil(this.minWidth * height / this.minHeight);
24643 if(this.minWidth > this.minHeight){
24645 height = Math.ceil(this.minHeight * width / this.minWidth);
24648 this.thumbEl.setStyle({
24649 width : width + 'px',
24650 height : height + 'px'
24657 setThumbBoxPosition : function()
24659 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24660 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24662 this.thumbEl.setLeft(x);
24663 this.thumbEl.setTop(y);
24667 baseRotateLevel : function()
24669 this.baseRotate = 1;
24672 typeof(this.exif) != 'undefined' &&
24673 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24674 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24676 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24679 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24683 baseScaleLevel : function()
24687 if(this.baseRotate == 6 || this.baseRotate == 8){
24689 width = this.thumbEl.getHeight();
24690 this.baseScale = height / this.imageEl.OriginHeight;
24692 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24693 height = this.thumbEl.getWidth();
24694 this.baseScale = height / this.imageEl.OriginHeight;
24697 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24698 height = this.thumbEl.getWidth();
24699 this.baseScale = height / this.imageEl.OriginHeight;
24701 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24702 width = this.thumbEl.getHeight();
24703 this.baseScale = width / this.imageEl.OriginWidth;
24710 width = this.thumbEl.getWidth();
24711 this.baseScale = width / this.imageEl.OriginWidth;
24713 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24714 height = this.thumbEl.getHeight();
24715 this.baseScale = height / this.imageEl.OriginHeight;
24719 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24721 height = this.thumbEl.getHeight();
24722 this.baseScale = height / this.imageEl.OriginHeight;
24724 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24725 width = this.thumbEl.getWidth();
24726 this.baseScale = width / this.imageEl.OriginWidth;
24734 getScaleLevel : function()
24736 return this.baseScale * Math.pow(1.1, this.scale);
24739 onTouchStart : function(e)
24741 if(!this.canvasLoaded){
24742 this.beforeSelectFile(e);
24746 var touches = e.browserEvent.touches;
24752 if(touches.length == 1){
24753 this.onMouseDown(e);
24757 if(touches.length != 2){
24763 for(var i = 0, finger; finger = touches[i]; i++){
24764 coords.push(finger.pageX, finger.pageY);
24767 var x = Math.pow(coords[0] - coords[2], 2);
24768 var y = Math.pow(coords[1] - coords[3], 2);
24770 this.startDistance = Math.sqrt(x + y);
24772 this.startScale = this.scale;
24774 this.pinching = true;
24775 this.dragable = false;
24779 onTouchMove : function(e)
24781 if(!this.pinching && !this.dragable){
24785 var touches = e.browserEvent.touches;
24792 this.onMouseMove(e);
24798 for(var i = 0, finger; finger = touches[i]; i++){
24799 coords.push(finger.pageX, finger.pageY);
24802 var x = Math.pow(coords[0] - coords[2], 2);
24803 var y = Math.pow(coords[1] - coords[3], 2);
24805 this.endDistance = Math.sqrt(x + y);
24807 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24809 if(!this.zoomable()){
24810 this.scale = this.startScale;
24818 onTouchEnd : function(e)
24820 this.pinching = false;
24821 this.dragable = false;
24825 prepare : function(input)
24830 if(typeof(input) === 'string'){
24831 this.loadCanvas(input);
24835 if(!input.files || !input.files[0] || !this.urlAPI){
24839 this.file = input.files[0];
24840 this.cropType = this.file.type;
24844 if(this.fireEvent('prepare', this, this.file) != false){
24846 var reader = new FileReader();
24848 reader.onload = function (e) {
24849 if (e.target.error) {
24850 Roo.log(e.target.error);
24854 var buffer = e.target.result,
24855 dataView = new DataView(buffer),
24857 maxOffset = dataView.byteLength - 4,
24861 if (dataView.getUint16(0) === 0xffd8) {
24862 while (offset < maxOffset) {
24863 markerBytes = dataView.getUint16(offset);
24865 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24866 markerLength = dataView.getUint16(offset + 2) + 2;
24867 if (offset + markerLength > dataView.byteLength) {
24868 Roo.log('Invalid meta data: Invalid segment size.');
24872 if(markerBytes == 0xffe1){
24873 _this.parseExifData(
24880 offset += markerLength;
24890 var url = _this.urlAPI.createObjectURL(_this.file);
24892 _this.loadCanvas(url);
24897 reader.readAsArrayBuffer(this.file);
24903 parseExifData : function(dataView, offset, length)
24905 var tiffOffset = offset + 10,
24909 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24910 // No Exif data, might be XMP data instead
24914 // Check for the ASCII code for "Exif" (0x45786966):
24915 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24916 // No Exif data, might be XMP data instead
24919 if (tiffOffset + 8 > dataView.byteLength) {
24920 Roo.log('Invalid Exif data: Invalid segment size.');
24923 // Check for the two null bytes:
24924 if (dataView.getUint16(offset + 8) !== 0x0000) {
24925 Roo.log('Invalid Exif data: Missing byte alignment offset.');
24928 // Check the byte alignment:
24929 switch (dataView.getUint16(tiffOffset)) {
24931 littleEndian = true;
24934 littleEndian = false;
24937 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24940 // Check for the TIFF tag marker (0x002A):
24941 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24942 Roo.log('Invalid Exif data: Missing TIFF marker.');
24945 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24946 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24948 this.parseExifTags(
24951 tiffOffset + dirOffset,
24956 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24961 if (dirOffset + 6 > dataView.byteLength) {
24962 Roo.log('Invalid Exif data: Invalid directory offset.');
24965 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24966 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24967 if (dirEndOffset + 4 > dataView.byteLength) {
24968 Roo.log('Invalid Exif data: Invalid directory size.');
24971 for (i = 0; i < tagsNumber; i += 1) {
24975 dirOffset + 2 + 12 * i, // tag offset
24979 // Return the offset to the next directory:
24980 return dataView.getUint32(dirEndOffset, littleEndian);
24983 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
24985 var tag = dataView.getUint16(offset, littleEndian);
24987 this.exif[tag] = this.getExifValue(
24991 dataView.getUint16(offset + 2, littleEndian), // tag type
24992 dataView.getUint32(offset + 4, littleEndian), // tag length
24997 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
24999 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25008 Roo.log('Invalid Exif data: Invalid tag type.');
25012 tagSize = tagType.size * length;
25013 // Determine if the value is contained in the dataOffset bytes,
25014 // or if the value at the dataOffset is a pointer to the actual data:
25015 dataOffset = tagSize > 4 ?
25016 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25017 if (dataOffset + tagSize > dataView.byteLength) {
25018 Roo.log('Invalid Exif data: Invalid data offset.');
25021 if (length === 1) {
25022 return tagType.getValue(dataView, dataOffset, littleEndian);
25025 for (i = 0; i < length; i += 1) {
25026 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25029 if (tagType.ascii) {
25031 // Concatenate the chars:
25032 for (i = 0; i < values.length; i += 1) {
25034 // Ignore the terminating NULL byte(s):
25035 if (c === '\u0000') {
25047 Roo.apply(Roo.bootstrap.UploadCropbox, {
25049 'Orientation': 0x0112
25053 1: 0, //'top-left',
25055 3: 180, //'bottom-right',
25056 // 4: 'bottom-left',
25058 6: 90, //'right-top',
25059 // 7: 'right-bottom',
25060 8: 270 //'left-bottom'
25064 // byte, 8-bit unsigned int:
25066 getValue: function (dataView, dataOffset) {
25067 return dataView.getUint8(dataOffset);
25071 // ascii, 8-bit byte:
25073 getValue: function (dataView, dataOffset) {
25074 return String.fromCharCode(dataView.getUint8(dataOffset));
25079 // short, 16 bit int:
25081 getValue: function (dataView, dataOffset, littleEndian) {
25082 return dataView.getUint16(dataOffset, littleEndian);
25086 // long, 32 bit int:
25088 getValue: function (dataView, dataOffset, littleEndian) {
25089 return dataView.getUint32(dataOffset, littleEndian);
25093 // rational = two long values, first is numerator, second is denominator:
25095 getValue: function (dataView, dataOffset, littleEndian) {
25096 return dataView.getUint32(dataOffset, littleEndian) /
25097 dataView.getUint32(dataOffset + 4, littleEndian);
25101 // slong, 32 bit signed int:
25103 getValue: function (dataView, dataOffset, littleEndian) {
25104 return dataView.getInt32(dataOffset, littleEndian);
25108 // srational, two slongs, first is numerator, second is denominator:
25110 getValue: function (dataView, dataOffset, littleEndian) {
25111 return dataView.getInt32(dataOffset, littleEndian) /
25112 dataView.getInt32(dataOffset + 4, littleEndian);
25122 cls : 'btn-group roo-upload-cropbox-rotate-left',
25123 action : 'rotate-left',
25127 cls : 'btn btn-default',
25128 html : '<i class="fa fa-undo"></i>'
25134 cls : 'btn-group roo-upload-cropbox-picture',
25135 action : 'picture',
25139 cls : 'btn btn-default',
25140 html : '<i class="fa fa-picture-o"></i>'
25146 cls : 'btn-group roo-upload-cropbox-rotate-right',
25147 action : 'rotate-right',
25151 cls : 'btn btn-default',
25152 html : '<i class="fa fa-repeat"></i>'
25160 cls : 'btn-group roo-upload-cropbox-rotate-left',
25161 action : 'rotate-left',
25165 cls : 'btn btn-default',
25166 html : '<i class="fa fa-undo"></i>'
25172 cls : 'btn-group roo-upload-cropbox-download',
25173 action : 'download',
25177 cls : 'btn btn-default',
25178 html : '<i class="fa fa-download"></i>'
25184 cls : 'btn-group roo-upload-cropbox-crop',
25189 cls : 'btn btn-default',
25190 html : '<i class="fa fa-crop"></i>'
25196 cls : 'btn-group roo-upload-cropbox-trash',
25201 cls : 'btn btn-default',
25202 html : '<i class="fa fa-trash"></i>'
25208 cls : 'btn-group roo-upload-cropbox-rotate-right',
25209 action : 'rotate-right',
25213 cls : 'btn btn-default',
25214 html : '<i class="fa fa-repeat"></i>'
25227 * @class Roo.bootstrap.DocumentManager
25228 * @extends Roo.bootstrap.Component
25229 * Bootstrap DocumentManager class
25230 * @cfg {String} paramName default 'imageUpload'
25231 * @cfg {String} method default POST
25232 * @cfg {String} url action url
25233 * @cfg {Number} boxes number of boxes default 12
25234 * @cfg {Boolean} multiple multiple upload default true
25235 * @cfg {Number} minWidth default 300
25236 * @cfg {Number} minHeight default 300
25237 * @cfg {Number} thumbSize default 300
25238 * @cfg {String} fieldLabel
25239 * @cfg {Number} labelWidth default 4
25240 * @cfg {String} labelAlign (left|top) default left
25243 * Create a new DocumentManager
25244 * @param {Object} config The config object
25247 Roo.bootstrap.DocumentManager = function(config){
25248 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25253 * Fire when initial the DocumentManager
25254 * @param {Roo.bootstrap.DocumentManager} this
25259 * inspect selected file
25260 * @param {Roo.bootstrap.DocumentManager} this
25261 * @param {File} file
25266 * Fire when xhr load exception
25267 * @param {Roo.bootstrap.DocumentManager} this
25268 * @param {XMLHttpRequest} xhr
25270 "exception" : true,
25273 * prepare the form data
25274 * @param {Roo.bootstrap.DocumentManager} this
25275 * @param {Object} formData
25280 * Fire when remove the file
25281 * @param {Roo.bootstrap.DocumentManager} this
25282 * @param {Object} file
25287 * Fire after refresh the file
25288 * @param {Roo.bootstrap.DocumentManager} this
25293 * Fire after click the image
25294 * @param {Roo.bootstrap.DocumentManager} this
25295 * @param {Object} file
25302 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25313 paramName : 'imageUpload',
25316 labelAlign : 'left',
25318 getAutoCreate : function()
25320 var managerWidget = {
25322 cls : 'roo-document-manager',
25326 cls : 'roo-document-manager-selector',
25331 cls : 'roo-document-manager-uploader',
25335 cls : 'roo-document-manager-upload-btn',
25336 html : '<i class="fa fa-plus"></i>'
25347 cls : 'column col-md-12',
25352 if(this.fieldLabel.length){
25357 cls : 'column col-md-12',
25358 html : this.fieldLabel
25362 cls : 'column col-md-12',
25367 if(this.labelAlign == 'left'){
25371 cls : 'column col-md-' + this.labelWidth,
25372 html : this.fieldLabel
25376 cls : 'column col-md-' + (12 - this.labelWidth),
25386 cls : 'row clearfix',
25394 initEvents : function()
25396 this.managerEl = this.el.select('.roo-document-manager', true).first();
25397 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25399 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25400 this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25401 this.selectorEl.hide();
25404 this.selectorEl.attr('multiple', 'multiple');
25407 this.selectorEl.on('change', this.onSelect, this);
25409 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25410 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25412 this.uploader.on('click', this.onUpload, this);
25416 window.addEventListener("resize", function() { _this.refresh(); } );
25418 this.fireEvent('initial', this);
25421 onUpload : function(e)
25423 e.preventDefault();
25425 this.selectorEl.dom.click();
25429 onSelect : function(e)
25431 e.preventDefault();
25433 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25437 Roo.each(this.selectorEl.dom.files, function(file){
25438 if(this.fireEvent('inspect', this, file) != false){
25439 this.files.push(file);
25447 process : function()
25449 this.selectorEl.dom.value = '';
25451 if(!this.files.length){
25455 if(this.files.length > this.boxes){
25456 this.files = this.files.slice(0, this.boxes);
25459 var xhr = new XMLHttpRequest();
25461 Roo.each(this.files, function(file, index){
25462 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25468 this.managerEl.createChild({
25470 cls : 'roo-document-manager-loading',
25474 tooltip : file.name,
25475 cls : 'roo-document-manager-thumb',
25476 html : '<i class="fa fa-spinner fa-pulse"></i>'
25484 if(this.files.length > this.boxes - 1 ){
25485 this.uploader.hide();
25489 "Accept": "application/json",
25490 "Cache-Control": "no-cache",
25491 "X-Requested-With": "XMLHttpRequest"
25494 xhr.open(this.method, this.url, true);
25496 for (var headerName in headers) {
25497 var headerValue = headers[headerName];
25499 xhr.setRequestHeader(headerName, headerValue);
25505 xhr.onload = function()
25507 _this.xhrOnLoad(xhr);
25510 xhr.onerror = function()
25512 _this.xhrOnError(xhr);
25515 var formData = new FormData();
25517 formData.append('returnHTML', 'NO');
25519 Roo.each(this.files, function(file, index){
25521 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25525 formData.append(this.getParamName(index), file, file.name);
25529 if(this.fireEvent('prepare', this, formData) != false){
25530 xhr.send(formData);
25535 getParamName : function(i)
25537 if(!this.multiple){
25538 return this.paramName;
25541 return this.paramName + "_" + i;
25544 refresh : function()
25546 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25553 Roo.each(this.files, function(file){
25555 if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25564 var previewEl = this.managerEl.createChild({
25566 cls : 'roo-document-manager-preview',
25570 tooltip : file.filename,
25571 cls : 'roo-document-manager-thumb',
25572 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25582 var close = previewEl.select('button.close', true).first();
25584 close.on('click', this.onRemove, this, file);
25586 file.target = previewEl;
25588 var image = previewEl.select('img', true).first();
25590 image.on('click', this.onClick, this, file);
25598 this.files = files;
25600 this.uploader.show();
25602 if(this.files.length > this.boxes - 1){
25603 this.uploader.hide();
25606 Roo.isTouch ? this.closable(false) : this.closable(true);
25608 this.fireEvent('refresh', this);
25611 onRemove : function(e, el, o)
25613 e.preventDefault();
25615 this.fireEvent('remove', this, o);
25619 remove : function(o)
25623 Roo.each(this.files, function(file){
25624 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25633 this.files = files;
25638 onClick : function(e, el, o)
25640 e.preventDefault();
25642 this.fireEvent('click', this, o);
25646 closable : function(closable)
25648 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25650 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25662 xhrOnLoad : function(xhr)
25664 if (xhr.readyState !== 4) {
25666 this.fireEvent('exception', this, xhr);
25670 var response = Roo.decode(xhr.responseText);
25672 if(!response.success){
25674 this.fireEvent('exception', this, xhr);
25680 Roo.each(this.files, function(file, index){
25682 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25686 this.files[index] = response.data[i];
25697 xhrOnError : function()
25699 Roo.log('xhr on error');
25701 var response = Roo.decode(xhr.responseText);
25714 * @class Roo.bootstrap.DocumentViewer
25715 * @extends Roo.bootstrap.Component
25716 * Bootstrap DocumentViewer class
25719 * Create a new DocumentViewer
25720 * @param {Object} config The config object
25723 Roo.bootstrap.DocumentViewer = function(config){
25724 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25729 * Fire after initEvent
25730 * @param {Roo.bootstrap.DocumentViewer} this
25736 * @param {Roo.bootstrap.DocumentViewer} this
25741 * Fire after trash button
25742 * @param {Roo.bootstrap.DocumentViewer} this
25749 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
25751 getAutoCreate : function()
25755 cls : 'roo-document-viewer',
25759 cls : 'roo-document-viewer-body',
25763 cls : 'roo-document-viewer-thumb',
25767 cls : 'roo-document-viewer-image'
25775 cls : 'roo-document-viewer-footer',
25778 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25786 cls : 'btn btn-default roo-document-viewer-trash',
25787 html : '<i class="fa fa-trash"></i>'
25800 initEvents : function()
25803 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25804 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25806 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25807 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25809 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25810 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25812 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25813 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25815 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25816 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25818 this.bodyEl.on('click', this.onClick, this);
25820 this.trashBtn.on('click', this.onTrash, this);
25824 initial : function()
25826 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25829 this.fireEvent('initial', this);
25833 onClick : function(e)
25835 e.preventDefault();
25837 this.fireEvent('click', this);
25840 onTrash : function(e)
25842 e.preventDefault();
25844 this.fireEvent('trash', this);