4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa (ban|check|...) font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1968 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1970 this.el.on("mouseover", this.onMouseOver, this);
1971 this.el.on("mouseout", this.onMouseOut, this);
1975 findTargetItem : function(e){
1976 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1980 //Roo.log(t); Roo.log(t.id);
1982 //Roo.log(this.menuitems);
1983 return this.menuitems.get(t.id);
1985 //return this.items.get(t.menuItemId);
1990 onClick : function(e){
1991 Roo.log("menu.onClick");
1992 var t = this.findTargetItem(e);
1993 if(!t || t.isContainer){
1998 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1999 if(t == this.activeItem && t.shouldDeactivate(e)){
2000 this.activeItem.deactivate();
2001 delete this.activeItem;
2005 this.setActiveItem(t, true);
2013 Roo.log('pass click event');
2017 this.fireEvent("click", this, t, e);
2021 onMouseOver : function(e){
2022 var t = this.findTargetItem(e);
2025 // if(t.canActivate && !t.disabled){
2026 // this.setActiveItem(t, true);
2030 this.fireEvent("mouseover", this, e, t);
2032 isVisible : function(){
2033 return !this.hidden;
2035 onMouseOut : function(e){
2036 var t = this.findTargetItem(e);
2039 // if(t == this.activeItem && t.shouldDeactivate(e)){
2040 // this.activeItem.deactivate();
2041 // delete this.activeItem;
2044 this.fireEvent("mouseout", this, e, t);
2049 * Displays this menu relative to another element
2050 * @param {String/HTMLElement/Roo.Element} element The element to align to
2051 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052 * the element (defaults to this.defaultAlign)
2053 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2055 show : function(el, pos, parentMenu){
2056 this.parentMenu = parentMenu;
2060 this.fireEvent("beforeshow", this);
2061 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2064 * Displays this menu at a specific xy position
2065 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 showAt : function(xy, parentMenu, /* private: */_e){
2069 this.parentMenu = parentMenu;
2074 this.fireEvent("beforeshow", this);
2075 //xy = this.el.adjustForConstraints(xy);
2079 this.hideMenuItems();
2080 this.hidden = false;
2081 this.triggerEl.addClass('open');
2083 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2089 this.fireEvent("show", this);
2095 this.doFocus.defer(50, this);
2099 doFocus : function(){
2101 this.focusEl.focus();
2106 * Hides this menu and optionally all parent menus
2107 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2109 hide : function(deep){
2111 this.hideMenuItems();
2112 if(this.el && this.isVisible()){
2113 this.fireEvent("beforehide", this);
2114 if(this.activeItem){
2115 this.activeItem.deactivate();
2116 this.activeItem = null;
2118 this.triggerEl.removeClass('open');;
2120 this.fireEvent("hide", this);
2122 if(deep === true && this.parentMenu){
2123 this.parentMenu.hide(true);
2127 onTriggerPress : function(e)
2130 Roo.log('trigger press');
2131 //Roo.log(e.getTarget());
2132 // Roo.log(this.triggerEl.dom);
2133 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2137 if (this.isVisible()) {
2142 this.show(this.triggerEl, false, false);
2151 hideMenuItems : function()
2153 //$(backdrop).remove()
2154 Roo.select('.open',true).each(function(aa) {
2156 aa.removeClass('open');
2157 //var parent = getParent($(this))
2158 //var relatedTarget = { relatedTarget: this }
2160 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161 //if (e.isDefaultPrevented()) return
2162 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2165 addxtypeChild : function (tree, cntr) {
2166 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2168 this.menuitems.add(comp);
2189 * @class Roo.bootstrap.MenuItem
2190 * @extends Roo.bootstrap.Component
2191 * Bootstrap MenuItem class
2192 * @cfg {String} html the menu label
2193 * @cfg {String} href the link
2194 * @cfg {Boolean} preventDefault (true | false) default true
2195 * @cfg {Boolean} isContainer (true | false) default false
2199 * Create a new MenuItem
2200 * @param {Object} config The config object
2204 Roo.bootstrap.MenuItem = function(config){
2205 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2210 * The raw click event for the entire grid.
2211 * @param {Roo.bootstrap.MenuItem} this
2212 * @param {Roo.EventObject} e
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2222 preventDefault: true,
2223 isContainer : false,
2225 getAutoCreate : function(){
2227 if(this.isContainer){
2230 cls: 'dropdown-menu-item'
2236 cls: 'dropdown-menu-item',
2245 if (this.parent().type == 'treeview') {
2246 cfg.cls = 'treeview-menu';
2249 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2254 initEvents: function() {
2256 //this.el.select('a').on('click', this.onClick, this);
2259 onClick : function(e)
2261 Roo.log('item on click ');
2262 //if(this.preventDefault){
2263 // e.preventDefault();
2265 //this.parent().hideMenuItems();
2267 this.fireEvent('click', this, e);
2286 * @class Roo.bootstrap.MenuSeparator
2287 * @extends Roo.bootstrap.Component
2288 * Bootstrap MenuSeparator class
2291 * Create a new MenuItem
2292 * @param {Object} config The config object
2296 Roo.bootstrap.MenuSeparator = function(config){
2297 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2302 getAutoCreate : function(){
2321 * @class Roo.bootstrap.Modal
2322 * @extends Roo.bootstrap.Component
2323 * Bootstrap Modal class
2324 * @cfg {String} title Title of dialog
2325 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2327 * @cfg {Boolean} specificTitle default false
2328 * @cfg {Array} buttons Array of buttons or standard button set..
2329 * @cfg {String} buttonPosition (left|right|center) default right
2330 * @cfg {Boolean} animate default true
2331 * @cfg {Boolean} allow_close default true
2334 * Create a new Modal Dialog
2335 * @param {Object} config The config object
2338 Roo.bootstrap.Modal = function(config){
2339 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2344 * The raw btnclick event for the button
2345 * @param {Roo.EventObject} e
2349 this.buttons = this.buttons || [];
2352 this.tmpl = Roo.factory(this.tmpl);
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2359 title : 'test dialog',
2369 specificTitle: false,
2371 buttonPosition: 'right',
2385 onRender : function(ct, position)
2387 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2390 var cfg = Roo.apply({}, this.getAutoCreate());
2393 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2395 //if (!cfg.name.length) {
2399 cfg.cls += ' ' + this.cls;
2402 cfg.style = this.style;
2404 this.el = Roo.get(document.body).createChild(cfg, position);
2406 //var type = this.el.dom.type;
2411 if(this.tabIndex !== undefined){
2412 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2416 this.bodyEl = this.el.select('.modal-body',true).first();
2417 this.closeEl = this.el.select('.modal-header .close', true).first();
2418 this.footerEl = this.el.select('.modal-footer',true).first();
2419 this.titleEl = this.el.select('.modal-title',true).first();
2423 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424 this.maskEl.enableDisplayMode("block");
2426 //this.el.addClass("x-dlg-modal");
2428 if (this.buttons.length) {
2429 Roo.each(this.buttons, function(bb) {
2430 var b = Roo.apply({}, bb);
2431 b.xns = b.xns || Roo.bootstrap;
2432 b.xtype = b.xtype || 'Button';
2433 if (typeof(b.listeners) == 'undefined') {
2434 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2437 var btn = Roo.factory(b);
2439 btn.onRender(this.el.select('.modal-footer div').first());
2443 // render the children.
2446 if(typeof(this.items) != 'undefined'){
2447 var items = this.items;
2450 for(var i =0;i < items.length;i++) {
2451 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2455 this.items = nitems;
2457 // where are these used - they used to be body/close/footer
2461 //this.el.addClass([this.fieldClass, this.cls]);
2465 getAutoCreate : function(){
2470 html : this.html || ''
2475 cls : 'modal-title',
2479 if(this.specificTitle){
2485 if (this.allow_close) {
2496 style : 'display: none',
2499 cls: "modal-dialog",
2502 cls : "modal-content",
2505 cls : 'modal-header',
2510 cls : 'modal-footer',
2514 cls: 'btn-' + this.buttonPosition
2531 modal.cls += ' fade';
2537 getChildContainer : function() {
2542 getButtonContainer : function() {
2543 return this.el.select('.modal-footer div',true).first();
2546 initEvents : function()
2548 if (this.allow_close) {
2549 this.closeEl.on('click', this.hide, this);
2554 window.addEventListener("resize", function() { _this.resize(); } );
2560 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2565 if (!this.rendered) {
2569 this.el.setStyle('display', 'block');
2573 (function(){ _this.el.addClass('in'); }).defer(50);
2575 this.el.addClass('in');
2578 // not sure how we can show data in here..
2580 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2583 Roo.get(document.body).addClass("x-body-masked");
2584 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2586 this.el.setStyle('zIndex', '10001');
2588 this.fireEvent('show', this);
2595 Roo.get(document.body).removeClass("x-body-masked");
2596 this.el.removeClass('in');
2600 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2602 this.el.setStyle('display', 'none');
2605 this.fireEvent('hide', this);
2608 addButton : function(str, cb)
2612 var b = Roo.apply({}, { html : str } );
2613 b.xns = b.xns || Roo.bootstrap;
2614 b.xtype = b.xtype || 'Button';
2615 if (typeof(b.listeners) == 'undefined') {
2616 b.listeners = { click : cb.createDelegate(this) };
2619 var btn = Roo.factory(b);
2621 btn.onRender(this.el.select('.modal-footer div').first());
2627 setDefaultButton : function(btn)
2629 //this.el.select('.modal-footer').()
2631 resizeTo: function(w,h)
2635 setContentSize : function(w, h)
2639 onButtonClick: function(btn,e)
2642 this.fireEvent('btnclick', btn.name, e);
2645 * Set the title of the Dialog
2646 * @param {String} str new Title
2648 setTitle: function(str) {
2649 this.titleEl.dom.innerHTML = str;
2652 * Set the body of the Dialog
2653 * @param {String} str new Title
2655 setBody: function(str) {
2656 this.bodyEl.dom.innerHTML = str;
2659 * Set the body of the Dialog using the template
2660 * @param {Obj} data - apply this data to the template and replace the body contents.
2662 applyBody: function(obj)
2665 Roo.log("Error - using apply Body without a template");
2668 this.tmpl.overwrite(this.bodyEl, obj);
2674 Roo.apply(Roo.bootstrap.Modal, {
2676 * Button config that displays a single OK button
2685 * Button config that displays Yes and No buttons
2701 * Button config that displays OK and Cancel buttons
2716 * Button config that displays Yes, No and Cancel buttons
2739 * messagebox - can be used as a replace
2743 * @class Roo.MessageBox
2744 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2753 // process text value...
2757 // Show a dialog using config options:
2759 title:'Save Changes?',
2760 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761 buttons: Roo.Msg.YESNOCANCEL,
2768 Roo.bootstrap.MessageBox = function(){
2769 var dlg, opt, mask, waitTimer;
2770 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771 var buttons, activeTextEl, bwidth;
2775 var handleButton = function(button){
2777 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2781 var handleHide = function(){
2783 dlg.el.removeClass(opt.cls);
2786 // Roo.TaskMgr.stop(waitTimer);
2787 // waitTimer = null;
2792 var updateButtons = function(b){
2795 buttons["ok"].hide();
2796 buttons["cancel"].hide();
2797 buttons["yes"].hide();
2798 buttons["no"].hide();
2799 //dlg.footer.dom.style.display = 'none';
2802 dlg.footerEl.dom.style.display = '';
2803 for(var k in buttons){
2804 if(typeof buttons[k] != "function"){
2807 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808 width += buttons[k].el.getWidth()+15;
2818 var handleEsc = function(d, k, e){
2819 if(opt && opt.closable !== false){
2829 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830 * @return {Roo.BasicDialog} The BasicDialog element
2832 getDialog : function(){
2834 dlg = new Roo.bootstrap.Modal( {
2837 //constraintoviewport:false,
2839 //collapsible : false,
2844 //buttonAlign:"center",
2845 closeClick : function(){
2846 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2849 handleButton("cancel");
2854 dlg.on("hide", handleHide);
2856 //dlg.addKeyListener(27, handleEsc);
2858 this.buttons = buttons;
2859 var bt = this.buttonText;
2860 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2865 bodyEl = dlg.bodyEl.createChild({
2867 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868 '<textarea class="roo-mb-textarea"></textarea>' +
2869 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2871 msgEl = bodyEl.dom.firstChild;
2872 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873 textboxEl.enableDisplayMode();
2874 textboxEl.addKeyListener([10,13], function(){
2875 if(dlg.isVisible() && opt && opt.buttons){
2878 }else if(opt.buttons.yes){
2879 handleButton("yes");
2883 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884 textareaEl.enableDisplayMode();
2885 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886 progressEl.enableDisplayMode();
2887 var pf = progressEl.dom.firstChild;
2889 pp = Roo.get(pf.firstChild);
2890 pp.setHeight(pf.offsetHeight);
2898 * Updates the message box body text
2899 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900 * the XHTML-compliant non-breaking space character '&#160;')
2901 * @return {Roo.MessageBox} This message box
2903 updateText : function(text){
2904 if(!dlg.isVisible() && !opt.width){
2905 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2907 msgEl.innerHTML = text || ' ';
2909 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2912 Math.min(opt.width || cw , this.maxWidth),
2913 Math.max(opt.minWidth || this.minWidth, bwidth)
2916 activeTextEl.setWidth(w);
2918 if(dlg.isVisible()){
2919 dlg.fixedcenter = false;
2921 // to big, make it scroll. = But as usual stupid IE does not support
2924 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2928 bodyEl.dom.style.height = '';
2929 bodyEl.dom.style.overflowY = '';
2932 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2934 bodyEl.dom.style.overflowX = '';
2937 dlg.setContentSize(w, bodyEl.getHeight());
2938 if(dlg.isVisible()){
2939 dlg.fixedcenter = true;
2945 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2946 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949 * @return {Roo.MessageBox} This message box
2951 updateProgress : function(value, text){
2953 this.updateText(text);
2955 if (pp) { // weird bug on my firefox - for some reason this is not defined
2956 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2962 * Returns true if the message box is currently displayed
2963 * @return {Boolean} True if the message box is visible, else false
2965 isVisible : function(){
2966 return dlg && dlg.isVisible();
2970 * Hides the message box if it is displayed
2973 if(this.isVisible()){
2979 * Displays a new message box, or reinitializes an existing message box, based on the config options
2980 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981 * The following config object properties are supported:
2983 Property Type Description
2984 ---------- --------------- ------------------------------------------------------------------------------------
2985 animEl String/Element An id or Element from which the message box should animate as it opens and
2986 closes (defaults to undefined)
2987 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable Boolean False to hide the top-right close button (defaults to true). Note that
2990 progress and wait dialogs will ignore this property and always hide the
2991 close button as they can only be closed programmatically.
2992 cls String A custom CSS class to apply to the message box element
2993 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2994 displayed (defaults to 75)
2995 fn Function A callback function to execute after closing the dialog. The arguments to the
2996 function will be btn (the name of the button that was clicked, if applicable,
2997 e.g. "ok"), and text (the value of the active text field, if applicable).
2998 Progress and wait dialogs will ignore this option since they do not respond to
2999 user actions and can only be closed programmatically, so any required function
3000 should be called by the same code after it closes the dialog.
3001 icon String A CSS class that provides a background image to be used as an icon for
3002 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3004 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3005 modal Boolean False to allow user interaction with the page while the message box is
3006 displayed (defaults to true)
3007 msg String A string that will replace the existing message box body text (defaults
3008 to the XHTML-compliant non-breaking space character ' ')
3009 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3010 progress Boolean True to display a progress bar (defaults to false)
3011 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3014 title String The title text
3015 value String The string value to set into the active textbox element if displayed
3016 wait Boolean True to display a progress bar (defaults to false)
3017 width Number The width of the dialog in pixels
3024 msg: 'Please enter your address:',
3026 buttons: Roo.MessageBox.OKCANCEL,
3029 animEl: 'addAddressBtn'
3032 * @param {Object} config Configuration options
3033 * @return {Roo.MessageBox} This message box
3035 show : function(options)
3038 // this causes nightmares if you show one dialog after another
3039 // especially on callbacks..
3041 if(this.isVisible()){
3044 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3046 Roo.log("New Dialog Message:" + options.msg )
3047 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3051 var d = this.getDialog();
3053 d.setTitle(opt.title || " ");
3054 d.closeEl.setDisplayed(opt.closable !== false);
3055 activeTextEl = textboxEl;
3056 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3061 textareaEl.setHeight(typeof opt.multiline == "number" ?
3062 opt.multiline : this.defaultTextHeight);
3063 activeTextEl = textareaEl;
3072 progressEl.setDisplayed(opt.progress === true);
3073 this.updateProgress(0);
3074 activeTextEl.dom.value = opt.value || "";
3076 dlg.setDefaultButton(activeTextEl);
3078 var bs = opt.buttons;
3082 }else if(bs && bs.yes){
3083 db = buttons["yes"];
3085 dlg.setDefaultButton(db);
3087 bwidth = updateButtons(opt.buttons);
3088 this.updateText(opt.msg);
3090 d.el.addClass(opt.cls);
3092 d.proxyDrag = opt.proxyDrag === true;
3093 d.modal = opt.modal !== false;
3094 d.mask = opt.modal !== false ? mask : false;
3096 // force it to the end of the z-index stack so it gets a cursor in FF
3097 document.body.appendChild(dlg.el.dom);
3098 d.animateTarget = null;
3099 d.show(options.animEl);
3105 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3106 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107 * and closing the message box when the process is complete.
3108 * @param {String} title The title bar text
3109 * @param {String} msg The message box body text
3110 * @return {Roo.MessageBox} This message box
3112 progress : function(title, msg){
3119 minWidth: this.minProgressWidth,
3126 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127 * If a callback function is passed it will be called after the user clicks the button, and the
3128 * id of the button that was clicked will be passed as the only parameter to the callback
3129 * (could also be the top-right close button).
3130 * @param {String} title The title bar text
3131 * @param {String} msg The message box body text
3132 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133 * @param {Object} scope (optional) The scope of the callback function
3134 * @return {Roo.MessageBox} This message box
3136 alert : function(title, msg, fn, scope){
3149 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3150 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151 * You are responsible for closing the message box when the process is complete.
3152 * @param {String} msg The message box body text
3153 * @param {String} title (optional) The title bar text
3154 * @return {Roo.MessageBox} This message box
3156 wait : function(msg, title){
3167 waitTimer = Roo.TaskMgr.start({
3169 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3177 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180 * @param {String} title The title bar text
3181 * @param {String} msg The message box body text
3182 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183 * @param {Object} scope (optional) The scope of the callback function
3184 * @return {Roo.MessageBox} This message box
3186 confirm : function(title, msg, fn, scope){
3190 buttons: this.YESNO,
3199 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3201 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202 * (could also be the top-right close button) and the text that was entered will be passed as the two
3203 * parameters to the callback.
3204 * @param {String} title The title bar text
3205 * @param {String} msg The message box body text
3206 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207 * @param {Object} scope (optional) The scope of the callback function
3208 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210 * @return {Roo.MessageBox} This message box
3212 prompt : function(title, msg, fn, scope, multiline){
3216 buttons: this.OKCANCEL,
3221 multiline: multiline,
3228 * Button config that displays a single OK button
3233 * Button config that displays Yes and No buttons
3236 YESNO : {yes:true, no:true},
3238 * Button config that displays OK and Cancel buttons
3241 OKCANCEL : {ok:true, cancel:true},
3243 * Button config that displays Yes, No and Cancel buttons
3246 YESNOCANCEL : {yes:true, no:true, cancel:true},
3249 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3252 defaultTextHeight : 75,
3254 * The maximum width in pixels of the message box (defaults to 600)
3259 * The minimum width in pixels of the message box (defaults to 100)
3264 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3265 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3268 minProgressWidth : 250,
3270 * An object containing the default button text strings that can be overriden for localized language support.
3271 * Supported properties are: ok, cancel, yes and no.
3272 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3285 * Shorthand for {@link Roo.MessageBox}
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3297 * @class Roo.bootstrap.Navbar
3298 * @extends Roo.bootstrap.Component
3299 * Bootstrap Navbar class
3302 * Create a new Navbar
3303 * @param {Object} config The config object
3307 Roo.bootstrap.Navbar = function(config){
3308 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3321 getAutoCreate : function(){
3324 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3328 initEvents :function ()
3330 //Roo.log(this.el.select('.navbar-toggle',true));
3331 this.el.select('.navbar-toggle',true).on('click', function() {
3332 // Roo.log('click');
3333 this.el.select('.navbar-collapse',true).toggleClass('in');
3341 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3343 var size = this.el.getSize();
3344 this.maskEl.setSize(size.width, size.height);
3345 this.maskEl.enableDisplayMode("block");
3354 getChildContainer : function()
3356 if (this.el.select('.collapse').getCount()) {
3357 return this.el.select('.collapse',true).first();
3390 * @class Roo.bootstrap.NavSimplebar
3391 * @extends Roo.bootstrap.Navbar
3392 * Bootstrap Sidebar class
3394 * @cfg {Boolean} inverse is inverted color
3396 * @cfg {String} type (nav | pills | tabs)
3397 * @cfg {Boolean} arrangement stacked | justified
3398 * @cfg {String} align (left | right) alignment
3400 * @cfg {Boolean} main (true|false) main nav bar? default false
3401 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3403 * @cfg {String} tag (header|footer|nav|div) default is nav
3409 * Create a new Sidebar
3410 * @param {Object} config The config object
3414 Roo.bootstrap.NavSimplebar = function(config){
3415 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3434 getAutoCreate : function(){
3438 tag : this.tag || 'div',
3451 this.type = this.type || 'nav';
3452 if (['tabs','pills'].indexOf(this.type)!==-1) {
3453 cfg.cn[0].cls += ' nav-' + this.type
3457 if (this.type!=='nav') {
3458 Roo.log('nav type must be nav/tabs/pills')
3460 cfg.cn[0].cls += ' navbar-nav'
3466 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467 cfg.cn[0].cls += ' nav-' + this.arrangement;
3471 if (this.align === 'right') {
3472 cfg.cn[0].cls += ' navbar-right';
3476 cfg.cls += ' navbar-inverse';
3503 * @class Roo.bootstrap.NavHeaderbar
3504 * @extends Roo.bootstrap.NavSimplebar
3505 * Bootstrap Sidebar class
3507 * @cfg {String} brand what is brand
3508 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509 * @cfg {String} brand_href href of the brand
3510 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3511 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3516 * Create a new Sidebar
3517 * @param {Object} config The config object
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3533 desktopCenter : false,
3536 getAutoCreate : function(){
3539 tag: this.nav || 'nav',
3546 if (this.desktopCenter) {
3547 cn.push({cls : 'container', cn : []});
3554 cls: 'navbar-header',
3559 cls: 'navbar-toggle',
3560 'data-toggle': 'collapse',
3565 html: 'Toggle navigation'
3587 cls: 'collapse navbar-collapse',
3591 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3593 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594 cfg.cls += ' navbar-' + this.position;
3596 // tag can override this..
3598 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3601 if (this.brand !== '') {
3604 href: this.brand_href ? this.brand_href : '#',
3605 cls: 'navbar-brand',
3613 cfg.cls += ' main-nav';
3621 getHeaderChildContainer : function()
3623 if (this.el.select('.navbar-header').getCount()) {
3624 return this.el.select('.navbar-header',true).first();
3627 return this.getChildContainer();
3631 initEvents : function()
3633 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3635 if (this.autohide) {
3640 Roo.get(document).on('scroll',function(e) {
3641 var ns = Roo.get(document).getScroll().top;
3642 var os = prevScroll;
3646 ft.removeClass('slideDown');
3647 ft.addClass('slideUp');
3650 ft.removeClass('slideUp');
3651 ft.addClass('slideDown');
3672 * @class Roo.bootstrap.NavSidebar
3673 * @extends Roo.bootstrap.Navbar
3674 * Bootstrap Sidebar class
3677 * Create a new Sidebar
3678 * @param {Object} config The config object
3682 Roo.bootstrap.NavSidebar = function(config){
3683 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3688 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3690 getAutoCreate : function(){
3695 cls: 'sidebar sidebar-nav'
3717 * @class Roo.bootstrap.NavGroup
3718 * @extends Roo.bootstrap.Component
3719 * Bootstrap NavGroup class
3720 * @cfg {String} align (left|right)
3721 * @cfg {Boolean} inverse
3722 * @cfg {String} type (nav|pills|tab) default nav
3723 * @cfg {String} navId - reference Id for navbar.
3727 * Create a new nav group
3728 * @param {Object} config The config object
3731 Roo.bootstrap.NavGroup = function(config){
3732 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3735 Roo.bootstrap.NavGroup.register(this);
3739 * Fires when the active item changes
3740 * @param {Roo.bootstrap.NavGroup} this
3741 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3760 getAutoCreate : function()
3762 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3769 if (['tabs','pills'].indexOf(this.type)!==-1) {
3770 cfg.cls += ' nav-' + this.type
3772 if (this.type!=='nav') {
3773 Roo.log('nav type must be nav/tabs/pills')
3775 cfg.cls += ' navbar-nav'
3778 if (this.parent().sidebar) {
3781 cls: 'dashboard-menu sidebar-menu'
3787 if (this.form === true) {
3793 if (this.align === 'right') {
3794 cfg.cls += ' navbar-right';
3796 cfg.cls += ' navbar-left';
3800 if (this.align === 'right') {
3801 cfg.cls += ' navbar-right';
3805 cfg.cls += ' navbar-inverse';
3813 * sets the active Navigation item
3814 * @param {Roo.bootstrap.NavItem} the new current navitem
3816 setActiveItem : function(item)
3819 Roo.each(this.navItems, function(v){
3824 v.setActive(false, true);
3831 item.setActive(true, true);
3832 this.fireEvent('changed', this, item, prev);
3837 * gets the active Navigation item
3838 * @return {Roo.bootstrap.NavItem} the current navitem
3840 getActive : function()
3844 Roo.each(this.navItems, function(v){
3855 indexOfNav : function()
3859 Roo.each(this.navItems, function(v,i){
3870 * adds a Navigation item
3871 * @param {Roo.bootstrap.NavItem} the navitem to add
3873 addItem : function(cfg)
3875 var cn = new Roo.bootstrap.NavItem(cfg);
3877 cn.parentId = this.id;
3878 cn.onRender(this.el, null);
3882 * register a Navigation item
3883 * @param {Roo.bootstrap.NavItem} the navitem to add
3885 register : function(item)
3887 this.navItems.push( item);
3888 item.navId = this.navId;
3893 * clear all the Navigation item
3896 clearAll : function()
3899 this.el.dom.innerHTML = '';
3902 getNavItem: function(tabId)
3905 Roo.each(this.navItems, function(e) {
3906 if (e.tabId == tabId) {
3916 setActiveNext : function()
3918 var i = this.indexOfNav(this.getActive());
3919 if (i > this.navItems.length) {
3922 this.setActiveItem(this.navItems[i+1]);
3924 setActivePrev : function()
3926 var i = this.indexOfNav(this.getActive());
3930 this.setActiveItem(this.navItems[i-1]);
3932 clearWasActive : function(except) {
3933 Roo.each(this.navItems, function(e) {
3934 if (e.tabId != except.tabId && e.was_active) {
3935 e.was_active = false;
3942 getWasActive : function ()
3945 Roo.each(this.navItems, function(e) {
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3964 * register a Navigation Group
3965 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3967 register : function(navgrp)
3969 this.groups[navgrp.navId] = navgrp;
3973 * fetch a Navigation Group based on the navigation ID
3974 * @param {string} the navgroup to add
3975 * @returns {Roo.bootstrap.NavGroup} the navgroup
3977 get: function(navId) {
3978 if (typeof(this.groups[navId]) == 'undefined') {
3980 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3982 return this.groups[navId] ;
3997 * @class Roo.bootstrap.NavItem
3998 * @extends Roo.bootstrap.Component
3999 * Bootstrap Navbar.NavItem class
4000 * @cfg {String} href link to
4001 * @cfg {String} html content of button
4002 * @cfg {String} badge text inside badge
4003 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004 * @cfg {String} glyphicon name of glyphicon
4005 * @cfg {String} icon name of font awesome icon
4006 * @cfg {Boolean} active Is item active
4007 * @cfg {Boolean} disabled Is item disabled
4009 * @cfg {Boolean} preventDefault (true | false) default false
4010 * @cfg {String} tabId the tab that this item activates.
4011 * @cfg {String} tagtype (a|span) render as a href or span?
4012 * @cfg {Boolean} animateRef (true|false) link to element default false
4015 * Create a new Navbar Item
4016 * @param {Object} config The config object
4018 Roo.bootstrap.NavItem = function(config){
4019 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4024 * The raw click event for the entire grid.
4025 * @param {Roo.EventObject} e
4030 * Fires when the active item active state changes
4031 * @param {Roo.bootstrap.NavItem} this
4032 * @param {boolean} state the new state
4038 * Fires when scroll to element
4039 * @param {Roo.bootstrap.NavItem} this
4040 * @param {Object} options
4041 * @param {Roo.EventObject} e
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4057 preventDefault : false,
4064 getAutoCreate : function(){
4072 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4074 if (this.disabled) {
4075 cfg.cls += ' disabled';
4078 if (this.href || this.html || this.glyphicon || this.icon) {
4082 href : this.href || "#",
4083 html: this.html || ''
4088 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4091 if(this.glyphicon) {
4092 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4097 cfg.cn[0].html += " <span class='caret'></span>";
4101 if (this.badge !== '') {
4103 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4111 initEvents: function()
4113 if (typeof (this.menu) != 'undefined') {
4114 this.menu.parentType = this.xtype;
4115 this.menu.triggerEl = this.el;
4116 this.menu = this.addxtype(Roo.apply({}, this.menu));
4119 this.el.select('a',true).on('click', this.onClick, this);
4121 if(this.tagtype == 'span'){
4122 this.el.select('span',true).on('click', this.onClick, this);
4125 // at this point parent should be available..
4126 this.parent().register(this);
4129 onClick : function(e)
4132 this.preventDefault ||
4139 if (this.disabled) {
4143 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144 if (tg && tg.transition) {
4145 Roo.log("waiting for the transitionend");
4151 //Roo.log("fire event clicked");
4152 if(this.fireEvent('click', this, e) === false){
4156 if(this.tagtype == 'span'){
4160 //Roo.log(this.href);
4161 var ael = this.el.select('a',true).first();
4164 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167 return; // ignore... - it's a 'hash' to another page.
4171 this.scrollToElement(e);
4175 var p = this.parent();
4177 if (['tabs','pills'].indexOf(p.type)!==-1) {
4178 if (typeof(p.setActiveItem) !== 'undefined') {
4179 p.setActiveItem(this);
4183 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185 // remove the collapsed menu expand...
4186 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4190 isActive: function () {
4193 setActive : function(state, fire, is_was_active)
4195 if (this.active && !state & this.navId) {
4196 this.was_active = true;
4197 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4199 nv.clearWasActive(this);
4203 this.active = state;
4206 this.el.removeClass('active');
4207 } else if (!this.el.hasClass('active')) {
4208 this.el.addClass('active');
4211 this.fireEvent('changed', this, state);
4214 // show a panel if it's registered and related..
4216 if (!this.navId || !this.tabId || !state || is_was_active) {
4220 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4224 var pan = tg.getPanelByName(this.tabId);
4228 // if we can not flip to new panel - go back to old nav highlight..
4229 if (false == tg.showPanel(pan)) {
4230 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4232 var onav = nv.getWasActive();
4234 onav.setActive(true, false, true);
4243 // this should not be here...
4244 setDisabled : function(state)
4246 this.disabled = state;
4248 this.el.removeClass('disabled');
4249 } else if (!this.el.hasClass('disabled')) {
4250 this.el.addClass('disabled');
4256 * Fetch the element to display the tooltip on.
4257 * @return {Roo.Element} defaults to this.el
4259 tooltipEl : function()
4261 return this.el.select('' + this.tagtype + '', true).first();
4264 scrollToElement : function(e)
4266 var c = document.body;
4269 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4271 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272 c = document.documentElement;
4275 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4281 var o = target.calcOffsetsTo(c);
4288 this.fireEvent('scrollto', this, options, e);
4290 Roo.get(c).scrollTo('top', options.value, true);
4303 * <span> icon </span>
4304 * <span> text </span>
4305 * <span>badge </span>
4309 * @class Roo.bootstrap.NavSidebarItem
4310 * @extends Roo.bootstrap.NavItem
4311 * Bootstrap Navbar.NavSidebarItem class
4313 * Create a new Navbar Button
4314 * @param {Object} config The config object
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4322 * The raw click event for the entire grid.
4323 * @param {Roo.EventObject} e
4328 * Fires when the active item active state changes
4329 * @param {Roo.bootstrap.NavSidebarItem} this
4330 * @param {boolean} state the new state
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4341 getAutoCreate : function(){
4346 href : this.href || '#',
4358 html : this.html || ''
4363 cfg.cls += ' active';
4367 if (this.glyphicon || this.icon) {
4368 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4369 a.cn.push({ tag : 'i', cls : c }) ;
4374 if (this.badge !== '') {
4375 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
4379 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380 a.cls += 'dropdown-toggle treeview' ;
4404 * @class Roo.bootstrap.Row
4405 * @extends Roo.bootstrap.Component
4406 * Bootstrap Row class (contains columns...)
4410 * @param {Object} config The config object
4413 Roo.bootstrap.Row = function(config){
4414 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4419 getAutoCreate : function(){
4438 * @class Roo.bootstrap.Element
4439 * @extends Roo.bootstrap.Component
4440 * Bootstrap Element class
4441 * @cfg {String} html contents of the element
4442 * @cfg {String} tag tag of the element
4443 * @cfg {String} cls class of the element
4444 * @cfg {Boolean} preventDefault (true|false) default false
4445 * @cfg {Boolean} clickable (true|false) default false
4448 * Create a new Element
4449 * @param {Object} config The config object
4452 Roo.bootstrap.Element = function(config){
4453 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4459 * When a element is chick
4460 * @param {Roo.bootstrap.Element} this
4461 * @param {Roo.EventObject} e
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4472 preventDefault: false,
4475 getAutoCreate : function(){
4486 initEvents: function()
4488 Roo.bootstrap.Element.superclass.initEvents.call(this);
4491 this.el.on('click', this.onClick, this);
4496 onClick : function(e)
4498 if(this.preventDefault){
4502 this.fireEvent('click', this, e);
4505 getValue : function()
4507 return this.el.dom.innerHTML;
4510 setValue : function(value)
4512 this.el.dom.innerHTML = value;
4527 * @class Roo.bootstrap.Pagination
4528 * @extends Roo.bootstrap.Component
4529 * Bootstrap Pagination class
4530 * @cfg {String} size xs | sm | md | lg
4531 * @cfg {Boolean} inverse false | true
4534 * Create a new Pagination
4535 * @param {Object} config The config object
4538 Roo.bootstrap.Pagination = function(config){
4539 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4548 getAutoCreate : function(){
4554 cfg.cls += ' inverse';
4560 cfg.cls += " " + this.cls;
4578 * @class Roo.bootstrap.PaginationItem
4579 * @extends Roo.bootstrap.Component
4580 * Bootstrap PaginationItem class
4581 * @cfg {String} html text
4582 * @cfg {String} href the link
4583 * @cfg {Boolean} preventDefault (true | false) default true
4584 * @cfg {Boolean} active (true | false) default false
4585 * @cfg {Boolean} disabled default false
4589 * Create a new PaginationItem
4590 * @param {Object} config The config object
4594 Roo.bootstrap.PaginationItem = function(config){
4595 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4600 * The raw click event for the entire grid.
4601 * @param {Roo.EventObject} e
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4611 preventDefault: true,
4616 getAutoCreate : function(){
4622 href : this.href ? this.href : '#',
4623 html : this.html ? this.html : ''
4633 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4637 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4643 initEvents: function() {
4645 this.el.on('click', this.onClick, this);
4648 onClick : function(e)
4650 Roo.log('PaginationItem on click ');
4651 if(this.preventDefault){
4659 this.fireEvent('click', this, e);
4675 * @class Roo.bootstrap.Slider
4676 * @extends Roo.bootstrap.Component
4677 * Bootstrap Slider class
4680 * Create a new Slider
4681 * @param {Object} config The config object
4684 Roo.bootstrap.Slider = function(config){
4685 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4690 getAutoCreate : function(){
4694 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4698 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4710 * Ext JS Library 1.1.1
4711 * Copyright(c) 2006-2007, Ext JS, LLC.
4713 * Originally Released Under LGPL - original licence link has changed is not relivant.
4716 * <script type="text/javascript">
4721 * @class Roo.grid.ColumnModel
4722 * @extends Roo.util.Observable
4723 * This is the default implementation of a ColumnModel used by the Grid. It defines
4724 * the columns in the grid.
4727 var colModel = new Roo.grid.ColumnModel([
4728 {header: "Ticker", width: 60, sortable: true, locked: true},
4729 {header: "Company Name", width: 150, sortable: true},
4730 {header: "Market Cap.", width: 100, sortable: true},
4731 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732 {header: "Employees", width: 100, sortable: true, resizable: false}
4737 * The config options listed for this class are options which may appear in each
4738 * individual column definition.
4739 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4741 * @param {Object} config An Array of column config objects. See this class's
4742 * config objects for details.
4744 Roo.grid.ColumnModel = function(config){
4746 * The config passed into the constructor
4748 this.config = config;
4751 // if no id, create one
4752 // if the column does not have a dataIndex mapping,
4753 // map it to the order it is in the config
4754 for(var i = 0, len = config.length; i < len; i++){
4756 if(typeof c.dataIndex == "undefined"){
4759 if(typeof c.renderer == "string"){
4760 c.renderer = Roo.util.Format[c.renderer];
4762 if(typeof c.id == "undefined"){
4765 if(c.editor && c.editor.xtype){
4766 c.editor = Roo.factory(c.editor, Roo.grid);
4768 if(c.editor && c.editor.isFormField){
4769 c.editor = new Roo.grid.GridEditor(c.editor);
4771 this.lookup[c.id] = c;
4775 * The width of columns which have no width specified (defaults to 100)
4778 this.defaultWidth = 100;
4781 * Default sortable of columns which have no sortable specified (defaults to false)
4784 this.defaultSortable = false;
4788 * @event widthchange
4789 * Fires when the width of a column changes.
4790 * @param {ColumnModel} this
4791 * @param {Number} columnIndex The column index
4792 * @param {Number} newWidth The new width
4794 "widthchange": true,
4796 * @event headerchange
4797 * Fires when the text of a header changes.
4798 * @param {ColumnModel} this
4799 * @param {Number} columnIndex The column index
4800 * @param {Number} newText The new header text
4802 "headerchange": true,
4804 * @event hiddenchange
4805 * Fires when a column is hidden or "unhidden".
4806 * @param {ColumnModel} this
4807 * @param {Number} columnIndex The column index
4808 * @param {Boolean} hidden true if hidden, false otherwise
4810 "hiddenchange": true,
4812 * @event columnmoved
4813 * Fires when a column is moved.
4814 * @param {ColumnModel} this
4815 * @param {Number} oldIndex
4816 * @param {Number} newIndex
4818 "columnmoved" : true,
4820 * @event columlockchange
4821 * Fires when a column's locked state is changed
4822 * @param {ColumnModel} this
4823 * @param {Number} colIndex
4824 * @param {Boolean} locked true if locked
4826 "columnlockchange" : true
4828 Roo.grid.ColumnModel.superclass.constructor.call(this);
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4832 * @cfg {String} header The header text to display in the Grid view.
4835 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837 * specified, the column's index is used as an index into the Record's data Array.
4840 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4844 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845 * Defaults to the value of the {@link #defaultSortable} property.
4846 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4849 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4852 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4855 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4858 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4861 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4867 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4870 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4873 * @cfg {String} cursor (Optional)
4876 * @cfg {String} tooltip (Optional)
4879 * Returns the id of the column at the specified index.
4880 * @param {Number} index The column index
4881 * @return {String} the id
4883 getColumnId : function(index){
4884 return this.config[index].id;
4888 * Returns the column for a specified id.
4889 * @param {String} id The column id
4890 * @return {Object} the column
4892 getColumnById : function(id){
4893 return this.lookup[id];
4898 * Returns the column for a specified dataIndex.
4899 * @param {String} dataIndex The column dataIndex
4900 * @return {Object|Boolean} the column or false if not found
4902 getColumnByDataIndex: function(dataIndex){
4903 var index = this.findColumnIndex(dataIndex);
4904 return index > -1 ? this.config[index] : false;
4908 * Returns the index for a specified column id.
4909 * @param {String} id The column id
4910 * @return {Number} the index, or -1 if not found
4912 getIndexById : function(id){
4913 for(var i = 0, len = this.config.length; i < len; i++){
4914 if(this.config[i].id == id){
4922 * Returns the index for a specified column dataIndex.
4923 * @param {String} dataIndex The column dataIndex
4924 * @return {Number} the index, or -1 if not found
4927 findColumnIndex : function(dataIndex){
4928 for(var i = 0, len = this.config.length; i < len; i++){
4929 if(this.config[i].dataIndex == dataIndex){
4937 moveColumn : function(oldIndex, newIndex){
4938 var c = this.config[oldIndex];
4939 this.config.splice(oldIndex, 1);
4940 this.config.splice(newIndex, 0, c);
4941 this.dataMap = null;
4942 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4945 isLocked : function(colIndex){
4946 return this.config[colIndex].locked === true;
4949 setLocked : function(colIndex, value, suppressEvent){
4950 if(this.isLocked(colIndex) == value){
4953 this.config[colIndex].locked = value;
4955 this.fireEvent("columnlockchange", this, colIndex, value);
4959 getTotalLockedWidth : function(){
4961 for(var i = 0; i < this.config.length; i++){
4962 if(this.isLocked(i) && !this.isHidden(i)){
4963 this.totalWidth += this.getColumnWidth(i);
4969 getLockedCount : function(){
4970 for(var i = 0, len = this.config.length; i < len; i++){
4971 if(!this.isLocked(i)){
4978 * Returns the number of columns.
4981 getColumnCount : function(visibleOnly){
4982 if(visibleOnly === true){
4984 for(var i = 0, len = this.config.length; i < len; i++){
4985 if(!this.isHidden(i)){
4991 return this.config.length;
4995 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996 * @param {Function} fn
4997 * @param {Object} scope (optional)
4998 * @return {Array} result
5000 getColumnsBy : function(fn, scope){
5002 for(var i = 0, len = this.config.length; i < len; i++){
5003 var c = this.config[i];
5004 if(fn.call(scope||this, c, i) === true){
5012 * Returns true if the specified column is sortable.
5013 * @param {Number} col The column index
5016 isSortable : function(col){
5017 if(typeof this.config[col].sortable == "undefined"){
5018 return this.defaultSortable;
5020 return this.config[col].sortable;
5024 * Returns the rendering (formatting) function defined for the column.
5025 * @param {Number} col The column index.
5026 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5028 getRenderer : function(col){
5029 if(!this.config[col].renderer){
5030 return Roo.grid.ColumnModel.defaultRenderer;
5032 return this.config[col].renderer;
5036 * Sets the rendering (formatting) function for a column.
5037 * @param {Number} col The column index
5038 * @param {Function} fn The function to use to process the cell's raw data
5039 * to return HTML markup for the grid view. The render function is called with
5040 * the following parameters:<ul>
5041 * <li>Data value.</li>
5042 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043 * <li>css A CSS style string to apply to the table cell.</li>
5044 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046 * <li>Row index</li>
5047 * <li>Column index</li>
5048 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5050 setRenderer : function(col, fn){
5051 this.config[col].renderer = fn;
5055 * Returns the width for the specified column.
5056 * @param {Number} col The column index
5059 getColumnWidth : function(col){
5060 return this.config[col].width * 1 || this.defaultWidth;
5064 * Sets the width for a column.
5065 * @param {Number} col The column index
5066 * @param {Number} width The new width
5068 setColumnWidth : function(col, width, suppressEvent){
5069 this.config[col].width = width;
5070 this.totalWidth = null;
5072 this.fireEvent("widthchange", this, col, width);
5077 * Returns the total width of all columns.
5078 * @param {Boolean} includeHidden True to include hidden column widths
5081 getTotalWidth : function(includeHidden){
5082 if(!this.totalWidth){
5083 this.totalWidth = 0;
5084 for(var i = 0, len = this.config.length; i < len; i++){
5085 if(includeHidden || !this.isHidden(i)){
5086 this.totalWidth += this.getColumnWidth(i);
5090 return this.totalWidth;
5094 * Returns the header for the specified column.
5095 * @param {Number} col The column index
5098 getColumnHeader : function(col){
5099 return this.config[col].header;
5103 * Sets the header for a column.
5104 * @param {Number} col The column index
5105 * @param {String} header The new header
5107 setColumnHeader : function(col, header){
5108 this.config[col].header = header;
5109 this.fireEvent("headerchange", this, col, header);
5113 * Returns the tooltip for the specified column.
5114 * @param {Number} col The column index
5117 getColumnTooltip : function(col){
5118 return this.config[col].tooltip;
5121 * Sets the tooltip for a column.
5122 * @param {Number} col The column index
5123 * @param {String} tooltip The new tooltip
5125 setColumnTooltip : function(col, tooltip){
5126 this.config[col].tooltip = tooltip;
5130 * Returns the dataIndex for the specified column.
5131 * @param {Number} col The column index
5134 getDataIndex : function(col){
5135 return this.config[col].dataIndex;
5139 * Sets the dataIndex for a column.
5140 * @param {Number} col The column index
5141 * @param {Number} dataIndex The new dataIndex
5143 setDataIndex : function(col, dataIndex){
5144 this.config[col].dataIndex = dataIndex;
5150 * Returns true if the cell is editable.
5151 * @param {Number} colIndex The column index
5152 * @param {Number} rowIndex The row index
5155 isCellEditable : function(colIndex, rowIndex){
5156 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5160 * Returns the editor defined for the cell/column.
5161 * return false or null to disable editing.
5162 * @param {Number} colIndex The column index
5163 * @param {Number} rowIndex The row index
5166 getCellEditor : function(colIndex, rowIndex){
5167 return this.config[colIndex].editor;
5171 * Sets if a column is editable.
5172 * @param {Number} col The column index
5173 * @param {Boolean} editable True if the column is editable
5175 setEditable : function(col, editable){
5176 this.config[col].editable = editable;
5181 * Returns true if the column is hidden.
5182 * @param {Number} colIndex The column index
5185 isHidden : function(colIndex){
5186 return this.config[colIndex].hidden;
5191 * Returns true if the column width cannot be changed
5193 isFixed : function(colIndex){
5194 return this.config[colIndex].fixed;
5198 * Returns true if the column can be resized
5201 isResizable : function(colIndex){
5202 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5205 * Sets if a column is hidden.
5206 * @param {Number} colIndex The column index
5207 * @param {Boolean} hidden True if the column is hidden
5209 setHidden : function(colIndex, hidden){
5210 this.config[colIndex].hidden = hidden;
5211 this.totalWidth = null;
5212 this.fireEvent("hiddenchange", this, colIndex, hidden);
5216 * Sets the editor for a column.
5217 * @param {Number} col The column index
5218 * @param {Object} editor The editor object
5220 setEditor : function(col, editor){
5221 this.config[col].editor = editor;
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226 if(typeof value == "string" && value.length < 1){
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5236 * Ext JS Library 1.1.1
5237 * Copyright(c) 2006-2007, Ext JS, LLC.
5239 * Originally Released Under LGPL - original licence link has changed is not relivant.
5242 * <script type="text/javascript">
5246 * @class Roo.LoadMask
5247 * A simple utility class for generically masking elements while loading data. If the element being masked has
5248 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5250 * element's UpdateManager load indicator and will be destroyed after the initial load.
5252 * Create a new LoadMask
5253 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254 * @param {Object} config The config object
5256 Roo.LoadMask = function(el, config){
5257 this.el = Roo.get(el);
5258 Roo.apply(this, config);
5260 this.store.on('beforeload', this.onBeforeLoad, this);
5261 this.store.on('load', this.onLoad, this);
5262 this.store.on('loadexception', this.onLoadException, this);
5263 this.removeMask = false;
5265 var um = this.el.getUpdateManager();
5266 um.showLoadIndicator = false; // disable the default indicator
5267 um.on('beforeupdate', this.onBeforeLoad, this);
5268 um.on('update', this.onLoad, this);
5269 um.on('failure', this.onLoad, this);
5270 this.removeMask = true;
5274 Roo.LoadMask.prototype = {
5276 * @cfg {Boolean} removeMask
5277 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5282 * The text to display in a centered loading message box (defaults to 'Loading...')
5286 * @cfg {String} msgCls
5287 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5289 msgCls : 'x-mask-loading',
5292 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5298 * Disables the mask to prevent it from being displayed
5300 disable : function(){
5301 this.disabled = true;
5305 * Enables the mask so that it can be displayed
5307 enable : function(){
5308 this.disabled = false;
5311 onLoadException : function()
5315 if (typeof(arguments[3]) != 'undefined') {
5316 Roo.MessageBox.alert("Error loading",arguments[3]);
5320 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5330 this.el.unmask(this.removeMask);
5335 this.el.unmask(this.removeMask);
5339 onBeforeLoad : function(){
5341 this.el.mask(this.msg, this.msgCls);
5346 destroy : function(){
5348 this.store.un('beforeload', this.onBeforeLoad, this);
5349 this.store.un('load', this.onLoad, this);
5350 this.store.un('loadexception', this.onLoadException, this);
5352 var um = this.el.getUpdateManager();
5353 um.un('beforeupdate', this.onBeforeLoad, this);
5354 um.un('update', this.onLoad, this);
5355 um.un('failure', this.onLoad, this);
5366 * @class Roo.bootstrap.Table
5367 * @extends Roo.bootstrap.Component
5368 * Bootstrap Table class
5369 * @cfg {String} cls table class
5370 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371 * @cfg {String} bgcolor Specifies the background color for a table
5372 * @cfg {Number} border Specifies whether the table cells should have borders or not
5373 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374 * @cfg {Number} cellspacing Specifies the space between cells
5375 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377 * @cfg {String} sortable Specifies that the table should be sortable
5378 * @cfg {String} summary Specifies a summary of the content of a table
5379 * @cfg {Number} width Specifies the width of a table
5380 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5382 * @cfg {boolean} striped Should the rows be alternative striped
5383 * @cfg {boolean} bordered Add borders to the table
5384 * @cfg {boolean} hover Add hover highlighting
5385 * @cfg {boolean} condensed Format condensed
5386 * @cfg {boolean} responsive Format condensed
5387 * @cfg {Boolean} loadMask (true|false) default false
5388 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5389 * @cfg {Boolean} thead (true|false) generate thead, default true
5390 * @cfg {Boolean} RowSelection (true|false) default false
5391 * @cfg {Boolean} CellSelection (true|false) default false
5392 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5396 * Create a new Table
5397 * @param {Object} config The config object
5400 Roo.bootstrap.Table = function(config){
5401 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5404 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5405 this.sm = this.selModel;
5406 this.sm.xmodule = this.xmodule || false;
5408 if (this.cm && typeof(this.cm.config) == 'undefined') {
5409 this.colModel = new Roo.grid.ColumnModel(this.cm);
5410 this.cm = this.colModel;
5411 this.cm.xmodule = this.xmodule || false;
5414 this.store= Roo.factory(this.store, Roo.data);
5415 this.ds = this.store;
5416 this.ds.xmodule = this.xmodule || false;
5419 if (this.footer && this.store) {
5420 this.footer.dataSource = this.ds;
5421 this.footer = Roo.factory(this.footer);
5428 * Fires when a cell is clicked
5429 * @param {Roo.bootstrap.Table} this
5430 * @param {Roo.Element} el
5431 * @param {Number} rowIndex
5432 * @param {Number} columnIndex
5433 * @param {Roo.EventObject} e
5437 * @event celldblclick
5438 * Fires when a cell is double clicked
5439 * @param {Roo.bootstrap.Table} this
5440 * @param {Roo.Element} el
5441 * @param {Number} rowIndex
5442 * @param {Number} columnIndex
5443 * @param {Roo.EventObject} e
5445 "celldblclick" : true,
5448 * Fires when a row is clicked
5449 * @param {Roo.bootstrap.Table} this
5450 * @param {Roo.Element} el
5451 * @param {Number} rowIndex
5452 * @param {Roo.EventObject} e
5456 * @event rowdblclick
5457 * Fires when a row is double clicked
5458 * @param {Roo.bootstrap.Table} this
5459 * @param {Roo.Element} el
5460 * @param {Number} rowIndex
5461 * @param {Roo.EventObject} e
5463 "rowdblclick" : true,
5466 * Fires when a mouseover occur
5467 * @param {Roo.bootstrap.Table} this
5468 * @param {Roo.Element} el
5469 * @param {Number} rowIndex
5470 * @param {Number} columnIndex
5471 * @param {Roo.EventObject} e
5476 * Fires when a mouseout occur
5477 * @param {Roo.bootstrap.Table} this
5478 * @param {Roo.Element} el
5479 * @param {Number} rowIndex
5480 * @param {Number} columnIndex
5481 * @param {Roo.EventObject} e
5486 * Fires when a row is rendered, so you can change add a style to it.
5487 * @param {Roo.bootstrap.Table} this
5488 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5492 * @event rowsrendered
5493 * Fires when all the rows have been rendered
5494 * @param {Roo.bootstrap.Table} this
5496 'rowsrendered' : true
5501 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5525 RowSelection : false,
5526 CellSelection : false,
5529 // Roo.Element - the tbody
5532 getAutoCreate : function(){
5533 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5542 cfg.cls += ' table-striped';
5546 cfg.cls += ' table-hover';
5548 if (this.bordered) {
5549 cfg.cls += ' table-bordered';
5551 if (this.condensed) {
5552 cfg.cls += ' table-condensed';
5554 if (this.responsive) {
5555 cfg.cls += ' table-responsive';
5559 cfg.cls+= ' ' +this.cls;
5562 // this lot should be simplifed...
5565 cfg.align=this.align;
5568 cfg.bgcolor=this.bgcolor;
5571 cfg.border=this.border;
5573 if (this.cellpadding) {
5574 cfg.cellpadding=this.cellpadding;
5576 if (this.cellspacing) {
5577 cfg.cellspacing=this.cellspacing;
5580 cfg.frame=this.frame;
5583 cfg.rules=this.rules;
5585 if (this.sortable) {
5586 cfg.sortable=this.sortable;
5589 cfg.summary=this.summary;
5592 cfg.width=this.width;
5595 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5598 if(this.store || this.cm){
5600 cfg.cn.push(this.renderHeader());
5603 cfg.cn.push(this.renderBody());
5606 cfg.cn.push(this.renderFooter());
5609 cfg.cls+= ' TableGrid';
5612 return { cn : [ cfg ] };
5615 initEvents : function()
5617 if(!this.store || !this.cm){
5621 //Roo.log('initEvents with ds!!!!');
5623 this.mainBody = this.el.select('tbody', true).first();
5628 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5629 e.on('click', _this.sort, _this);
5632 this.el.on("click", this.onClick, this);
5633 this.el.on("dblclick", this.onDblClick, this);
5635 // why is this done????? = it breaks dialogs??
5636 //this.parent().el.setStyle('position', 'relative');
5640 this.footer.parentId = this.id;
5641 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5644 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5646 this.store.on('load', this.onLoad, this);
5647 this.store.on('beforeload', this.onBeforeLoad, this);
5648 this.store.on('update', this.onUpdate, this);
5649 this.store.on('add', this.onAdd, this);
5653 onMouseover : function(e, el)
5655 var cell = Roo.get(el);
5661 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5662 cell = cell.findParent('td', false, true);
5665 var row = cell.findParent('tr', false, true);
5666 var cellIndex = cell.dom.cellIndex;
5667 var rowIndex = row.dom.rowIndex - 1; // start from 0
5669 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5673 onMouseout : function(e, el)
5675 var cell = Roo.get(el);
5681 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5682 cell = cell.findParent('td', false, true);
5685 var row = cell.findParent('tr', false, true);
5686 var cellIndex = cell.dom.cellIndex;
5687 var rowIndex = row.dom.rowIndex - 1; // start from 0
5689 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5693 onClick : function(e, el)
5695 var cell = Roo.get(el);
5697 if(!cell || (!this.CellSelection && !this.RowSelection)){
5701 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5702 cell = cell.findParent('td', false, true);
5705 if(!cell || typeof(cell) == 'undefined'){
5709 var row = cell.findParent('tr', false, true);
5711 if(!row || typeof(row) == 'undefined'){
5715 var cellIndex = cell.dom.cellIndex;
5716 var rowIndex = this.getRowIndex(row);
5718 if(this.CellSelection){
5719 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5722 if(this.RowSelection){
5723 this.fireEvent('rowclick', this, row, rowIndex, e);
5729 onDblClick : function(e,el)
5731 var cell = Roo.get(el);
5733 if(!cell || (!this.CellSelection && !this.RowSelection)){
5737 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5738 cell = cell.findParent('td', false, true);
5741 if(!cell || typeof(cell) == 'undefined'){
5745 var row = cell.findParent('tr', false, true);
5747 if(!row || typeof(row) == 'undefined'){
5751 var cellIndex = cell.dom.cellIndex;
5752 var rowIndex = this.getRowIndex(row);
5754 if(this.CellSelection){
5755 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5758 if(this.RowSelection){
5759 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5763 sort : function(e,el)
5765 var col = Roo.get(el);
5767 if(!col.hasClass('sortable')){
5771 var sort = col.attr('sort');
5774 if(col.hasClass('glyphicon-arrow-up')){
5778 this.store.sortInfo = {field : sort, direction : dir};
5781 Roo.log("calling footer first");
5782 this.footer.onClick('first');
5785 this.store.load({ params : { start : 0 } });
5789 renderHeader : function()
5798 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5800 var config = cm.config[i];
5805 html: cm.getColumnHeader(i)
5808 if(typeof(config.tooltip) != 'undefined'){
5809 c.tooltip = config.tooltip;
5812 if(typeof(config.colspan) != 'undefined'){
5813 c.colspan = config.colspan;
5816 if(typeof(config.hidden) != 'undefined' && config.hidden){
5817 c.style += ' display:none;';
5820 if(typeof(config.dataIndex) != 'undefined'){
5821 c.sort = config.dataIndex;
5824 if(typeof(config.sortable) != 'undefined' && config.sortable){
5828 if(typeof(config.align) != 'undefined' && config.align.length){
5829 c.style += ' text-align:' + config.align + ';';
5832 if(typeof(config.width) != 'undefined'){
5833 c.style += ' width:' + config.width + 'px;';
5836 if(typeof(config.cls) != 'undefined'){
5837 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5846 renderBody : function()
5856 colspan : this.cm.getColumnCount()
5866 renderFooter : function()
5876 colspan : this.cm.getColumnCount()
5890 Roo.log('ds onload');
5895 var ds = this.store;
5897 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5898 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5900 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5901 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5904 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5905 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5909 var tbody = this.mainBody;
5911 if(ds.getCount() > 0){
5912 ds.data.each(function(d,rowIndex){
5913 var row = this.renderRow(cm, ds, rowIndex);
5915 tbody.createChild(row);
5919 if(row.cellObjects.length){
5920 Roo.each(row.cellObjects, function(r){
5921 _this.renderCellObject(r);
5928 Roo.each(this.el.select('tbody td', true).elements, function(e){
5929 e.on('mouseover', _this.onMouseover, _this);
5932 Roo.each(this.el.select('tbody td', true).elements, function(e){
5933 e.on('mouseout', _this.onMouseout, _this);
5935 this.fireEvent('rowsrendered', this);
5936 //if(this.loadMask){
5937 // this.maskEl.hide();
5942 onUpdate : function(ds,record)
5944 this.refreshRow(record);
5947 onRemove : function(ds, record, index, isUpdate){
5948 if(isUpdate !== true){
5949 this.fireEvent("beforerowremoved", this, index, record);
5951 var bt = this.mainBody.dom;
5953 var rows = this.el.select('tbody > tr', true).elements;
5955 if(typeof(rows[index]) != 'undefined'){
5956 bt.removeChild(rows[index].dom);
5959 // if(bt.rows[index]){
5960 // bt.removeChild(bt.rows[index]);
5963 if(isUpdate !== true){
5964 //this.stripeRows(index);
5965 //this.syncRowHeights(index, index);
5967 this.fireEvent("rowremoved", this, index, record);
5971 onAdd : function(ds, records, rowIndex)
5973 //Roo.log('on Add called');
5974 // - note this does not handle multiple adding very well..
5975 var bt = this.mainBody.dom;
5976 for (var i =0 ; i < records.length;i++) {
5977 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5978 //Roo.log(records[i]);
5979 //Roo.log(this.store.getAt(rowIndex+i));
5980 this.insertRow(this.store, rowIndex + i, false);
5987 refreshRow : function(record){
5988 var ds = this.store, index;
5989 if(typeof record == 'number'){
5991 record = ds.getAt(index);
5993 index = ds.indexOf(record);
5995 this.insertRow(ds, index, true);
5996 this.onRemove(ds, record, index+1, true);
5997 //this.syncRowHeights(index, index);
5999 this.fireEvent("rowupdated", this, index, record);
6002 insertRow : function(dm, rowIndex, isUpdate){
6005 this.fireEvent("beforerowsinserted", this, rowIndex);
6007 //var s = this.getScrollState();
6008 var row = this.renderRow(this.cm, this.store, rowIndex);
6009 // insert before rowIndex..
6010 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6014 if(row.cellObjects.length){
6015 Roo.each(row.cellObjects, function(r){
6016 _this.renderCellObject(r);
6021 this.fireEvent("rowsinserted", this, rowIndex);
6022 //this.syncRowHeights(firstRow, lastRow);
6023 //this.stripeRows(firstRow);
6030 getRowDom : function(rowIndex)
6032 var rows = this.el.select('tbody > tr', true).elements;
6034 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6037 // returns the object tree for a tr..
6040 renderRow : function(cm, ds, rowIndex)
6043 var d = ds.getAt(rowIndex);
6050 var cellObjects = [];
6052 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6053 var config = cm.config[i];
6055 var renderer = cm.getRenderer(i);
6059 if(typeof(renderer) !== 'undefined'){
6060 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6062 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6063 // and are rendered into the cells after the row is rendered - using the id for the element.
6065 if(typeof(value) === 'object'){
6075 rowIndex : rowIndex,
6080 this.fireEvent('rowclass', this, rowcfg);
6084 cls : rowcfg.rowClass,
6086 html: (typeof(value) === 'object') ? '' : value
6093 if(typeof(config.colspan) != 'undefined'){
6094 td.colspan = config.colspan;
6097 if(typeof(config.hidden) != 'undefined' && config.hidden){
6098 td.style += ' display:none;';
6101 if(typeof(config.align) != 'undefined' && config.align.length){
6102 td.style += ' text-align:' + config.align + ';';
6105 if(typeof(config.width) != 'undefined'){
6106 td.style += ' width:' + config.width + 'px;';
6109 if(typeof(config.cursor) != 'undefined'){
6110 td.style += ' cursor:' + config.cursor + ';';
6113 if(typeof(config.cls) != 'undefined'){
6114 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6121 row.cellObjects = cellObjects;
6129 onBeforeLoad : function()
6131 //Roo.log('ds onBeforeLoad');
6135 //if(this.loadMask){
6136 // this.maskEl.show();
6144 this.el.select('tbody', true).first().dom.innerHTML = '';
6147 * Show or hide a row.
6148 * @param {Number} rowIndex to show or hide
6149 * @param {Boolean} state hide
6151 setRowVisibility : function(rowIndex, state)
6153 var bt = this.mainBody.dom;
6155 var rows = this.el.select('tbody > tr', true).elements;
6157 if(typeof(rows[rowIndex]) == 'undefined'){
6160 rows[rowIndex].dom.style.display = state ? '' : 'none';
6164 getSelectionModel : function(){
6166 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6168 return this.selModel;
6171 * Render the Roo.bootstrap object from renderder
6173 renderCellObject : function(r)
6177 var t = r.cfg.render(r.container);
6180 Roo.each(r.cfg.cn, function(c){
6182 container: t.getChildContainer(),
6185 _this.renderCellObject(child);
6190 getRowIndex : function(row)
6194 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6217 * @class Roo.bootstrap.TableCell
6218 * @extends Roo.bootstrap.Component
6219 * Bootstrap TableCell class
6220 * @cfg {String} html cell contain text
6221 * @cfg {String} cls cell class
6222 * @cfg {String} tag cell tag (td|th) default td
6223 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6224 * @cfg {String} align Aligns the content in a cell
6225 * @cfg {String} axis Categorizes cells
6226 * @cfg {String} bgcolor Specifies the background color of a cell
6227 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6228 * @cfg {Number} colspan Specifies the number of columns a cell should span
6229 * @cfg {String} headers Specifies one or more header cells a cell is related to
6230 * @cfg {Number} height Sets the height of a cell
6231 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6232 * @cfg {Number} rowspan Sets the number of rows a cell should span
6233 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6234 * @cfg {String} valign Vertical aligns the content in a cell
6235 * @cfg {Number} width Specifies the width of a cell
6238 * Create a new TableCell
6239 * @param {Object} config The config object
6242 Roo.bootstrap.TableCell = function(config){
6243 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6266 getAutoCreate : function(){
6267 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6287 cfg.align=this.align
6293 cfg.bgcolor=this.bgcolor
6296 cfg.charoff=this.charoff
6299 cfg.colspan=this.colspan
6302 cfg.headers=this.headers
6305 cfg.height=this.height
6308 cfg.nowrap=this.nowrap
6311 cfg.rowspan=this.rowspan
6314 cfg.scope=this.scope
6317 cfg.valign=this.valign
6320 cfg.width=this.width
6339 * @class Roo.bootstrap.TableRow
6340 * @extends Roo.bootstrap.Component
6341 * Bootstrap TableRow class
6342 * @cfg {String} cls row class
6343 * @cfg {String} align Aligns the content in a table row
6344 * @cfg {String} bgcolor Specifies a background color for a table row
6345 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6346 * @cfg {String} valign Vertical aligns the content in a table row
6349 * Create a new TableRow
6350 * @param {Object} config The config object
6353 Roo.bootstrap.TableRow = function(config){
6354 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6365 getAutoCreate : function(){
6366 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6376 cfg.align = this.align;
6379 cfg.bgcolor = this.bgcolor;
6382 cfg.charoff = this.charoff;
6385 cfg.valign = this.valign;
6403 * @class Roo.bootstrap.TableBody
6404 * @extends Roo.bootstrap.Component
6405 * Bootstrap TableBody class
6406 * @cfg {String} cls element class
6407 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6408 * @cfg {String} align Aligns the content inside the element
6409 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6410 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6413 * Create a new TableBody
6414 * @param {Object} config The config object
6417 Roo.bootstrap.TableBody = function(config){
6418 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6429 getAutoCreate : function(){
6430 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6444 cfg.align = this.align;
6447 cfg.charoff = this.charoff;
6450 cfg.valign = this.valign;
6457 // initEvents : function()
6464 // this.store = Roo.factory(this.store, Roo.data);
6465 // this.store.on('load', this.onLoad, this);
6467 // this.store.load();
6471 // onLoad: function ()
6473 // this.fireEvent('load', this);
6483 * Ext JS Library 1.1.1
6484 * Copyright(c) 2006-2007, Ext JS, LLC.
6486 * Originally Released Under LGPL - original licence link has changed is not relivant.
6489 * <script type="text/javascript">
6492 // as we use this in bootstrap.
6493 Roo.namespace('Roo.form');
6495 * @class Roo.form.Action
6496 * Internal Class used to handle form actions
6498 * @param {Roo.form.BasicForm} el The form element or its id
6499 * @param {Object} config Configuration options
6504 // define the action interface
6505 Roo.form.Action = function(form, options){
6507 this.options = options || {};
6510 * Client Validation Failed
6513 Roo.form.Action.CLIENT_INVALID = 'client';
6515 * Server Validation Failed
6518 Roo.form.Action.SERVER_INVALID = 'server';
6520 * Connect to Server Failed
6523 Roo.form.Action.CONNECT_FAILURE = 'connect';
6525 * Reading Data from Server Failed
6528 Roo.form.Action.LOAD_FAILURE = 'load';
6530 Roo.form.Action.prototype = {
6532 failureType : undefined,
6533 response : undefined,
6537 run : function(options){
6542 success : function(response){
6547 handleResponse : function(response){
6551 // default connection failure
6552 failure : function(response){
6554 this.response = response;
6555 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6556 this.form.afterAction(this, false);
6559 processResponse : function(response){
6560 this.response = response;
6561 if(!response.responseText){
6564 this.result = this.handleResponse(response);
6568 // utility functions used internally
6569 getUrl : function(appendParams){
6570 var url = this.options.url || this.form.url || this.form.el.dom.action;
6572 var p = this.getParams();
6574 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6580 getMethod : function(){
6581 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6584 getParams : function(){
6585 var bp = this.form.baseParams;
6586 var p = this.options.params;
6588 if(typeof p == "object"){
6589 p = Roo.urlEncode(Roo.applyIf(p, bp));
6590 }else if(typeof p == 'string' && bp){
6591 p += '&' + Roo.urlEncode(bp);
6594 p = Roo.urlEncode(bp);
6599 createCallback : function(){
6601 success: this.success,
6602 failure: this.failure,
6604 timeout: (this.form.timeout*1000),
6605 upload: this.form.fileUpload ? this.success : undefined
6610 Roo.form.Action.Submit = function(form, options){
6611 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6617 haveProgress : false,
6618 uploadComplete : false,
6620 // uploadProgress indicator.
6621 uploadProgress : function()
6623 if (!this.form.progressUrl) {
6627 if (!this.haveProgress) {
6628 Roo.MessageBox.progress("Uploading", "Uploading");
6630 if (this.uploadComplete) {
6631 Roo.MessageBox.hide();
6635 this.haveProgress = true;
6637 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6639 var c = new Roo.data.Connection();
6641 url : this.form.progressUrl,
6646 success : function(req){
6647 //console.log(data);
6651 rdata = Roo.decode(req.responseText)
6653 Roo.log("Invalid data from server..");
6657 if (!rdata || !rdata.success) {
6659 Roo.MessageBox.alert(Roo.encode(rdata));
6662 var data = rdata.data;
6664 if (this.uploadComplete) {
6665 Roo.MessageBox.hide();
6670 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6671 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6674 this.uploadProgress.defer(2000,this);
6677 failure: function(data) {
6678 Roo.log('progress url failed ');
6689 // run get Values on the form, so it syncs any secondary forms.
6690 this.form.getValues();
6692 var o = this.options;
6693 var method = this.getMethod();
6694 var isPost = method == 'POST';
6695 if(o.clientValidation === false || this.form.isValid()){
6697 if (this.form.progressUrl) {
6698 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6699 (new Date() * 1) + '' + Math.random());
6704 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6705 form:this.form.el.dom,
6706 url:this.getUrl(!isPost),
6708 params:isPost ? this.getParams() : null,
6709 isUpload: this.form.fileUpload
6712 this.uploadProgress();
6714 }else if (o.clientValidation !== false){ // client validation failed
6715 this.failureType = Roo.form.Action.CLIENT_INVALID;
6716 this.form.afterAction(this, false);
6720 success : function(response)
6722 this.uploadComplete= true;
6723 if (this.haveProgress) {
6724 Roo.MessageBox.hide();
6728 var result = this.processResponse(response);
6729 if(result === true || result.success){
6730 this.form.afterAction(this, true);
6734 this.form.markInvalid(result.errors);
6735 this.failureType = Roo.form.Action.SERVER_INVALID;
6737 this.form.afterAction(this, false);
6739 failure : function(response)
6741 this.uploadComplete= true;
6742 if (this.haveProgress) {
6743 Roo.MessageBox.hide();
6746 this.response = response;
6747 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6748 this.form.afterAction(this, false);
6751 handleResponse : function(response){
6752 if(this.form.errorReader){
6753 var rs = this.form.errorReader.read(response);
6756 for(var i = 0, len = rs.records.length; i < len; i++) {
6757 var r = rs.records[i];
6761 if(errors.length < 1){
6765 success : rs.success,
6771 ret = Roo.decode(response.responseText);
6775 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6785 Roo.form.Action.Load = function(form, options){
6786 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6787 this.reader = this.form.reader;
6790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6795 Roo.Ajax.request(Roo.apply(
6796 this.createCallback(), {
6797 method:this.getMethod(),
6798 url:this.getUrl(false),
6799 params:this.getParams()
6803 success : function(response){
6805 var result = this.processResponse(response);
6806 if(result === true || !result.success || !result.data){
6807 this.failureType = Roo.form.Action.LOAD_FAILURE;
6808 this.form.afterAction(this, false);
6811 this.form.clearInvalid();
6812 this.form.setValues(result.data);
6813 this.form.afterAction(this, true);
6816 handleResponse : function(response){
6817 if(this.form.reader){
6818 var rs = this.form.reader.read(response);
6819 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6821 success : rs.success,
6825 return Roo.decode(response.responseText);
6829 Roo.form.Action.ACTION_TYPES = {
6830 'load' : Roo.form.Action.Load,
6831 'submit' : Roo.form.Action.Submit
6840 * @class Roo.bootstrap.Form
6841 * @extends Roo.bootstrap.Component
6842 * Bootstrap Form class
6843 * @cfg {String} method GET | POST (default POST)
6844 * @cfg {String} labelAlign top | left (default top)
6845 * @cfg {String} align left | right - for navbars
6846 * @cfg {Boolean} loadMask load mask when submit (default true)
6851 * @param {Object} config The config object
6855 Roo.bootstrap.Form = function(config){
6856 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6859 * @event clientvalidation
6860 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6861 * @param {Form} this
6862 * @param {Boolean} valid true if the form has passed client-side validation
6864 clientvalidation: true,
6866 * @event beforeaction
6867 * Fires before any action is performed. Return false to cancel the action.
6868 * @param {Form} this
6869 * @param {Action} action The action to be performed
6873 * @event actionfailed
6874 * Fires when an action fails.
6875 * @param {Form} this
6876 * @param {Action} action The action that failed
6878 actionfailed : true,
6880 * @event actioncomplete
6881 * Fires when an action is completed.
6882 * @param {Form} this
6883 * @param {Action} action The action that completed
6885 actioncomplete : true
6890 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6893 * @cfg {String} method
6894 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6899 * The URL to use for form actions if one isn't supplied in the action options.
6902 * @cfg {Boolean} fileUpload
6903 * Set to true if this form is a file upload.
6907 * @cfg {Object} baseParams
6908 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6912 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6916 * @cfg {Sting} align (left|right) for navbar forms
6921 activeAction : null,
6924 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6925 * element by passing it or its id or mask the form itself by passing in true.
6928 waitMsgTarget : false,
6932 getAutoCreate : function(){
6936 method : this.method || 'POST',
6937 id : this.id || Roo.id(),
6940 if (this.parent().xtype.match(/^Nav/)) {
6941 cfg.cls = 'navbar-form navbar-' + this.align;
6945 if (this.labelAlign == 'left' ) {
6946 cfg.cls += ' form-horizontal';
6952 initEvents : function()
6954 this.el.on('submit', this.onSubmit, this);
6955 // this was added as random key presses on the form where triggering form submit.
6956 this.el.on('keypress', function(e) {
6957 if (e.getCharCode() != 13) {
6960 // we might need to allow it for textareas.. and some other items.
6961 // check e.getTarget().
6963 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6967 Roo.log("keypress blocked");
6975 onSubmit : function(e){
6980 * Returns true if client-side validation on the form is successful.
6983 isValid : function(){
6984 var items = this.getItems();
6986 items.each(function(f){
6995 * Returns true if any fields in this form have changed since their original load.
6998 isDirty : function(){
7000 var items = this.getItems();
7001 items.each(function(f){
7011 * Performs a predefined action (submit or load) or custom actions you define on this form.
7012 * @param {String} actionName The name of the action type
7013 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7014 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7015 * accept other config options):
7017 Property Type Description
7018 ---------------- --------------- ----------------------------------------------------------------------------------
7019 url String The url for the action (defaults to the form's url)
7020 method String The form method to use (defaults to the form's method, or POST if not defined)
7021 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7022 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7023 validate the form on the client (defaults to false)
7025 * @return {BasicForm} this
7027 doAction : function(action, options){
7028 if(typeof action == 'string'){
7029 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7031 if(this.fireEvent('beforeaction', this, action) !== false){
7032 this.beforeAction(action);
7033 action.run.defer(100, action);
7039 beforeAction : function(action){
7040 var o = action.options;
7043 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7045 // not really supported yet.. ??
7047 //if(this.waitMsgTarget === true){
7048 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7049 //}else if(this.waitMsgTarget){
7050 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7051 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7053 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7059 afterAction : function(action, success){
7060 this.activeAction = null;
7061 var o = action.options;
7063 //if(this.waitMsgTarget === true){
7065 //}else if(this.waitMsgTarget){
7066 // this.waitMsgTarget.unmask();
7068 // Roo.MessageBox.updateProgress(1);
7069 // Roo.MessageBox.hide();
7076 Roo.callback(o.success, o.scope, [this, action]);
7077 this.fireEvent('actioncomplete', this, action);
7081 // failure condition..
7082 // we have a scenario where updates need confirming.
7083 // eg. if a locking scenario exists..
7084 // we look for { errors : { needs_confirm : true }} in the response.
7086 (typeof(action.result) != 'undefined') &&
7087 (typeof(action.result.errors) != 'undefined') &&
7088 (typeof(action.result.errors.needs_confirm) != 'undefined')
7091 Roo.log("not supported yet");
7094 Roo.MessageBox.confirm(
7095 "Change requires confirmation",
7096 action.result.errorMsg,
7101 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7111 Roo.callback(o.failure, o.scope, [this, action]);
7112 // show an error message if no failed handler is set..
7113 if (!this.hasListener('actionfailed')) {
7114 Roo.log("need to add dialog support");
7116 Roo.MessageBox.alert("Error",
7117 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7118 action.result.errorMsg :
7119 "Saving Failed, please check your entries or try again"
7124 this.fireEvent('actionfailed', this, action);
7129 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7130 * @param {String} id The value to search for
7133 findField : function(id){
7134 var items = this.getItems();
7135 var field = items.get(id);
7137 items.each(function(f){
7138 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7145 return field || null;
7148 * Mark fields in this form invalid in bulk.
7149 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7150 * @return {BasicForm} this
7152 markInvalid : function(errors){
7153 if(errors instanceof Array){
7154 for(var i = 0, len = errors.length; i < len; i++){
7155 var fieldError = errors[i];
7156 var f = this.findField(fieldError.id);
7158 f.markInvalid(fieldError.msg);
7164 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7165 field.markInvalid(errors[id]);
7169 //Roo.each(this.childForms || [], function (f) {
7170 // f.markInvalid(errors);
7177 * Set values for fields in this form in bulk.
7178 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7179 * @return {BasicForm} this
7181 setValues : function(values){
7182 if(values instanceof Array){ // array of objects
7183 for(var i = 0, len = values.length; i < len; i++){
7185 var f = this.findField(v.id);
7187 f.setValue(v.value);
7188 if(this.trackResetOnLoad){
7189 f.originalValue = f.getValue();
7193 }else{ // object hash
7196 if(typeof values[id] != 'function' && (field = this.findField(id))){
7198 if (field.setFromData &&
7200 field.displayField &&
7201 // combos' with local stores can
7202 // be queried via setValue()
7203 // to set their value..
7204 (field.store && !field.store.isLocal)
7208 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7209 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7210 field.setFromData(sd);
7213 field.setValue(values[id]);
7217 if(this.trackResetOnLoad){
7218 field.originalValue = field.getValue();
7224 //Roo.each(this.childForms || [], function (f) {
7225 // f.setValues(values);
7232 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7233 * they are returned as an array.
7234 * @param {Boolean} asString
7237 getValues : function(asString){
7238 //if (this.childForms) {
7239 // copy values from the child forms
7240 // Roo.each(this.childForms, function (f) {
7241 // this.setValues(f.getValues());
7247 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7248 if(asString === true){
7251 return Roo.urlDecode(fs);
7255 * Returns the fields in this form as an object with key/value pairs.
7256 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7259 getFieldValues : function(with_hidden)
7261 var items = this.getItems();
7263 items.each(function(f){
7267 var v = f.getValue();
7268 if (f.inputType =='radio') {
7269 if (typeof(ret[f.getName()]) == 'undefined') {
7270 ret[f.getName()] = ''; // empty..
7273 if (!f.el.dom.checked) {
7281 // not sure if this supported any more..
7282 if ((typeof(v) == 'object') && f.getRawValue) {
7283 v = f.getRawValue() ; // dates..
7285 // combo boxes where name != hiddenName...
7286 if (f.name != f.getName()) {
7287 ret[f.name] = f.getRawValue();
7289 ret[f.getName()] = v;
7296 * Clears all invalid messages in this form.
7297 * @return {BasicForm} this
7299 clearInvalid : function(){
7300 var items = this.getItems();
7302 items.each(function(f){
7313 * @return {BasicForm} this
7316 var items = this.getItems();
7317 items.each(function(f){
7321 Roo.each(this.childForms || [], function (f) {
7328 getItems : function()
7330 var r=new Roo.util.MixedCollection(false, function(o){
7331 return o.id || (o.id = Roo.id());
7333 var iter = function(el) {
7340 Roo.each(el.items,function(e) {
7360 * Ext JS Library 1.1.1
7361 * Copyright(c) 2006-2007, Ext JS, LLC.
7363 * Originally Released Under LGPL - original licence link has changed is not relivant.
7366 * <script type="text/javascript">
7369 * @class Roo.form.VTypes
7370 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7373 Roo.form.VTypes = function(){
7374 // closure these in so they are only created once.
7375 var alpha = /^[a-zA-Z_]+$/;
7376 var alphanum = /^[a-zA-Z0-9_]+$/;
7377 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7378 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7380 // All these messages and functions are configurable
7383 * The function used to validate email addresses
7384 * @param {String} value The email address
7386 'email' : function(v){
7387 return email.test(v);
7390 * The error text to display when the email validation function returns false
7393 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7395 * The keystroke filter mask to be applied on email input
7398 'emailMask' : /[a-z0-9_\.\-@]/i,
7401 * The function used to validate URLs
7402 * @param {String} value The URL
7404 'url' : function(v){
7408 * The error text to display when the url validation function returns false
7411 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7414 * The function used to validate alpha values
7415 * @param {String} value The value
7417 'alpha' : function(v){
7418 return alpha.test(v);
7421 * The error text to display when the alpha validation function returns false
7424 'alphaText' : 'This field should only contain letters and _',
7426 * The keystroke filter mask to be applied on alpha input
7429 'alphaMask' : /[a-z_]/i,
7432 * The function used to validate alphanumeric values
7433 * @param {String} value The value
7435 'alphanum' : function(v){
7436 return alphanum.test(v);
7439 * The error text to display when the alphanumeric validation function returns false
7442 'alphanumText' : 'This field should only contain letters, numbers and _',
7444 * The keystroke filter mask to be applied on alphanumeric input
7447 'alphanumMask' : /[a-z0-9_]/i
7457 * @class Roo.bootstrap.Input
7458 * @extends Roo.bootstrap.Component
7459 * Bootstrap Input class
7460 * @cfg {Boolean} disabled is it disabled
7461 * @cfg {String} fieldLabel - the label associated
7462 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7463 * @cfg {String} name name of the input
7464 * @cfg {string} fieldLabel - the label associated
7465 * @cfg {string} inputType - input / file submit ...
7466 * @cfg {string} placeholder - placeholder to put in text.
7467 * @cfg {string} before - input group add on before
7468 * @cfg {string} after - input group add on after
7469 * @cfg {string} size - (lg|sm) or leave empty..
7470 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7471 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7472 * @cfg {Number} md colspan out of 12 for computer-sized screens
7473 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7474 * @cfg {string} value default value of the input
7475 * @cfg {Number} labelWidth set the width of label (0-12)
7476 * @cfg {String} labelAlign (top|left)
7477 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7478 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7480 * @cfg {String} align (left|center|right) Default left
7481 * @cfg {Boolean} forceFeedback (true|false) Default false
7487 * Create a new Input
7488 * @param {Object} config The config object
7491 Roo.bootstrap.Input = function(config){
7492 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7497 * Fires when this field receives input focus.
7498 * @param {Roo.form.Field} this
7503 * Fires when this field loses input focus.
7504 * @param {Roo.form.Field} this
7509 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7510 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7511 * @param {Roo.form.Field} this
7512 * @param {Roo.EventObject} e The event object
7517 * Fires just before the field blurs if the field value has changed.
7518 * @param {Roo.form.Field} this
7519 * @param {Mixed} newValue The new value
7520 * @param {Mixed} oldValue The original value
7525 * Fires after the field has been marked as invalid.
7526 * @param {Roo.form.Field} this
7527 * @param {String} msg The validation message
7532 * Fires after the field has been validated with no errors.
7533 * @param {Roo.form.Field} this
7538 * Fires after the key up
7539 * @param {Roo.form.Field} this
7540 * @param {Roo.EventObject} e The event Object
7546 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7548 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7549 automatic validation (defaults to "keyup").
7551 validationEvent : "keyup",
7553 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7555 validateOnBlur : true,
7557 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7559 validationDelay : 250,
7561 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7563 focusClass : "x-form-focus", // not needed???
7567 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7569 invalidClass : "has-warning",
7572 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7574 validClass : "has-success",
7577 * @cfg {Boolean} hasFeedback (true|false) default true
7582 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7584 invalidFeedbackClass : "glyphicon-warning-sign",
7587 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7589 validFeedbackClass : "glyphicon-ok",
7592 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7594 selectOnFocus : false,
7597 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7601 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7606 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7608 disableKeyFilter : false,
7611 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7615 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7619 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7621 blankText : "This field is required",
7624 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7628 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7630 maxLength : Number.MAX_VALUE,
7632 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7634 minLengthText : "The minimum length for this field is {0}",
7636 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7638 maxLengthText : "The maximum length for this field is {0}",
7642 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7643 * If available, this function will be called only after the basic validators all return true, and will be passed the
7644 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7648 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7649 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7650 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7654 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7658 autocomplete: false,
7677 formatedValue : false,
7678 forceFeedback : false,
7680 parentLabelAlign : function()
7683 while (parent.parent()) {
7684 parent = parent.parent();
7685 if (typeof(parent.labelAlign) !='undefined') {
7686 return parent.labelAlign;
7693 getAutoCreate : function(){
7695 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7701 if(this.inputType != 'hidden'){
7702 cfg.cls = 'form-group' //input-group
7708 type : this.inputType,
7710 cls : 'form-control',
7711 placeholder : this.placeholder || '',
7712 autocomplete : this.autocomplete || 'new-password'
7717 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7720 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7721 input.maxLength = this.maxLength;
7724 if (this.disabled) {
7725 input.disabled=true;
7728 if (this.readOnly) {
7729 input.readonly=true;
7733 input.name = this.name;
7736 input.cls += ' input-' + this.size;
7739 ['xs','sm','md','lg'].map(function(size){
7740 if (settings[size]) {
7741 cfg.cls += ' col-' + size + '-' + settings[size];
7745 var inputblock = input;
7749 cls: 'glyphicon form-control-feedback'
7752 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7755 cls : 'has-feedback',
7763 if (this.before || this.after) {
7766 cls : 'input-group',
7770 if (this.before && typeof(this.before) == 'string') {
7772 inputblock.cn.push({
7774 cls : 'roo-input-before input-group-addon',
7778 if (this.before && typeof(this.before) == 'object') {
7779 this.before = Roo.factory(this.before);
7780 Roo.log(this.before);
7781 inputblock.cn.push({
7783 cls : 'roo-input-before input-group-' +
7784 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7788 inputblock.cn.push(input);
7790 if (this.after && typeof(this.after) == 'string') {
7791 inputblock.cn.push({
7793 cls : 'roo-input-after input-group-addon',
7797 if (this.after && typeof(this.after) == 'object') {
7798 this.after = Roo.factory(this.after);
7799 Roo.log(this.after);
7800 inputblock.cn.push({
7802 cls : 'roo-input-after input-group-' +
7803 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7807 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7808 inputblock.cls += ' has-feedback';
7809 inputblock.cn.push(feedback);
7813 if (align ==='left' && this.fieldLabel.length) {
7814 Roo.log("left and has label");
7820 cls : 'control-label col-sm-' + this.labelWidth,
7821 html : this.fieldLabel
7825 cls : "col-sm-" + (12 - this.labelWidth),
7832 } else if ( this.fieldLabel.length) {
7838 //cls : 'input-group-addon',
7839 html : this.fieldLabel
7849 Roo.log(" no label && no align");
7858 Roo.log('input-parentType: ' + this.parentType);
7860 if (this.parentType === 'Navbar' && this.parent().bar) {
7861 cfg.cls += ' navbar-form';
7869 * return the real input element.
7871 inputEl: function ()
7873 return this.el.select('input.form-control',true).first();
7876 tooltipEl : function()
7878 return this.inputEl();
7881 setDisabled : function(v)
7883 var i = this.inputEl().dom;
7885 i.removeAttribute('disabled');
7889 i.setAttribute('disabled','true');
7891 initEvents : function()
7894 this.inputEl().on("keydown" , this.fireKey, this);
7895 this.inputEl().on("focus", this.onFocus, this);
7896 this.inputEl().on("blur", this.onBlur, this);
7898 this.inputEl().relayEvent('keyup', this);
7900 // reference to original value for reset
7901 this.originalValue = this.getValue();
7902 //Roo.form.TextField.superclass.initEvents.call(this);
7903 if(this.validationEvent == 'keyup'){
7904 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7905 this.inputEl().on('keyup', this.filterValidation, this);
7907 else if(this.validationEvent !== false){
7908 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7911 if(this.selectOnFocus){
7912 this.on("focus", this.preFocus, this);
7915 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7916 this.inputEl().on("keypress", this.filterKeys, this);
7919 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7920 this.el.on("click", this.autoSize, this);
7923 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7924 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7927 if (typeof(this.before) == 'object') {
7928 this.before.render(this.el.select('.roo-input-before',true).first());
7930 if (typeof(this.after) == 'object') {
7931 this.after.render(this.el.select('.roo-input-after',true).first());
7936 filterValidation : function(e){
7937 if(!e.isNavKeyPress()){
7938 this.validationTask.delay(this.validationDelay);
7942 * Validates the field value
7943 * @return {Boolean} True if the value is valid, else false
7945 validate : function(){
7946 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7947 if(this.disabled || this.validateValue(this.getRawValue())){
7958 * Validates a value according to the field's validation rules and marks the field as invalid
7959 * if the validation fails
7960 * @param {Mixed} value The value to validate
7961 * @return {Boolean} True if the value is valid, else false
7963 validateValue : function(value){
7964 if(value.length < 1) { // if it's blank
7965 if(this.allowBlank){
7971 if(value.length < this.minLength){
7974 if(value.length > this.maxLength){
7978 var vt = Roo.form.VTypes;
7979 if(!vt[this.vtype](value, this)){
7983 if(typeof this.validator == "function"){
7984 var msg = this.validator(value);
7990 if(this.regex && !this.regex.test(value)){
8000 fireKey : function(e){
8001 //Roo.log('field ' + e.getKey());
8002 if(e.isNavKeyPress()){
8003 this.fireEvent("specialkey", this, e);
8006 focus : function (selectText){
8008 this.inputEl().focus();
8009 if(selectText === true){
8010 this.inputEl().dom.select();
8016 onFocus : function(){
8017 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8018 // this.el.addClass(this.focusClass);
8021 this.hasFocus = true;
8022 this.startValue = this.getValue();
8023 this.fireEvent("focus", this);
8027 beforeBlur : Roo.emptyFn,
8031 onBlur : function(){
8033 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8034 //this.el.removeClass(this.focusClass);
8036 this.hasFocus = false;
8037 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8040 var v = this.getValue();
8041 if(String(v) !== String(this.startValue)){
8042 this.fireEvent('change', this, v, this.startValue);
8044 this.fireEvent("blur", this);
8048 * Resets the current field value to the originally loaded value and clears any validation messages
8051 this.setValue(this.originalValue);
8055 * Returns the name of the field
8056 * @return {Mixed} name The name field
8058 getName: function(){
8062 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8063 * @return {Mixed} value The field value
8065 getValue : function(){
8067 var v = this.inputEl().getValue();
8072 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8073 * @return {Mixed} value The field value
8075 getRawValue : function(){
8076 var v = this.inputEl().getValue();
8082 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8083 * @param {Mixed} value The value to set
8085 setRawValue : function(v){
8086 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8089 selectText : function(start, end){
8090 var v = this.getRawValue();
8092 start = start === undefined ? 0 : start;
8093 end = end === undefined ? v.length : end;
8094 var d = this.inputEl().dom;
8095 if(d.setSelectionRange){
8096 d.setSelectionRange(start, end);
8097 }else if(d.createTextRange){
8098 var range = d.createTextRange();
8099 range.moveStart("character", start);
8100 range.moveEnd("character", v.length-end);
8107 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8108 * @param {Mixed} value The value to set
8110 setValue : function(v){
8113 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8119 processValue : function(value){
8120 if(this.stripCharsRe){
8121 var newValue = value.replace(this.stripCharsRe, '');
8122 if(newValue !== value){
8123 this.setRawValue(newValue);
8130 preFocus : function(){
8132 if(this.selectOnFocus){
8133 this.inputEl().dom.select();
8136 filterKeys : function(e){
8138 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8141 var c = e.getCharCode(), cc = String.fromCharCode(c);
8142 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8145 if(!this.maskRe.test(cc)){
8150 * Clear any invalid styles/messages for this field
8152 clearInvalid : function(){
8154 if(!this.el || this.preventMark){ // not rendered
8157 this.el.removeClass(this.invalidClass);
8159 this.fireEvent('valid', this);
8163 * Mark this field as valid
8165 markValid : function(){
8166 if(!this.el || this.preventMark){ // not rendered
8170 this.el.removeClass([this.invalidClass, this.validClass]);
8172 if(this.disabled || this.allowBlank){
8176 this.el.addClass(this.validClass);
8178 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8180 var feedback = this.el.select('.form-control-feedback', true).first();
8183 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8184 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8189 this.fireEvent('valid', this);
8193 * Mark this field as invalid
8194 * @param {String} msg The validation message
8196 markInvalid : function(msg){
8197 if(!this.el || this.preventMark){ // not rendered
8201 this.el.removeClass([this.invalidClass, this.validClass]);
8203 if(this.disabled || this.allowBlank){
8207 this.el.addClass(this.invalidClass);
8209 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8211 var feedback = this.el.select('.form-control-feedback', true).first();
8214 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8216 if(this.getValue().length){
8217 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8224 this.fireEvent('invalid', this, msg);
8227 SafariOnKeyDown : function(event)
8229 // this is a workaround for a password hang bug on chrome/ webkit.
8231 var isSelectAll = false;
8233 if(this.inputEl().dom.selectionEnd > 0){
8234 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8236 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8237 event.preventDefault();
8242 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8244 event.preventDefault();
8245 // this is very hacky as keydown always get's upper case.
8247 var cc = String.fromCharCode(event.getCharCode());
8248 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8252 adjustWidth : function(tag, w){
8253 tag = tag.toLowerCase();
8254 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8255 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8259 if(tag == 'textarea'){
8262 }else if(Roo.isOpera){
8266 if(tag == 'textarea'){
8285 * @class Roo.bootstrap.TextArea
8286 * @extends Roo.bootstrap.Input
8287 * Bootstrap TextArea class
8288 * @cfg {Number} cols Specifies the visible width of a text area
8289 * @cfg {Number} rows Specifies the visible number of lines in a text area
8290 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8291 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8292 * @cfg {string} html text
8295 * Create a new TextArea
8296 * @param {Object} config The config object
8299 Roo.bootstrap.TextArea = function(config){
8300 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8304 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8314 getAutoCreate : function(){
8316 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8327 value : this.value || '',
8328 html: this.html || '',
8329 cls : 'form-control',
8330 placeholder : this.placeholder || ''
8334 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8335 input.maxLength = this.maxLength;
8339 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8343 input.cols = this.cols;
8346 if (this.readOnly) {
8347 input.readonly = true;
8351 input.name = this.name;
8355 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8359 ['xs','sm','md','lg'].map(function(size){
8360 if (settings[size]) {
8361 cfg.cls += ' col-' + size + '-' + settings[size];
8365 var inputblock = input;
8367 if(this.hasFeedback && !this.allowBlank){
8371 cls: 'glyphicon form-control-feedback'
8375 cls : 'has-feedback',
8384 if (this.before || this.after) {
8387 cls : 'input-group',
8391 inputblock.cn.push({
8393 cls : 'input-group-addon',
8398 inputblock.cn.push(input);
8400 if(this.hasFeedback && !this.allowBlank){
8401 inputblock.cls += ' has-feedback';
8402 inputblock.cn.push(feedback);
8406 inputblock.cn.push({
8408 cls : 'input-group-addon',
8415 if (align ==='left' && this.fieldLabel.length) {
8416 Roo.log("left and has label");
8422 cls : 'control-label col-sm-' + this.labelWidth,
8423 html : this.fieldLabel
8427 cls : "col-sm-" + (12 - this.labelWidth),
8434 } else if ( this.fieldLabel.length) {
8440 //cls : 'input-group-addon',
8441 html : this.fieldLabel
8451 Roo.log(" no label && no align");
8461 if (this.disabled) {
8462 input.disabled=true;
8469 * return the real textarea element.
8471 inputEl: function ()
8473 return this.el.select('textarea.form-control',true).first();
8481 * trigger field - base class for combo..
8486 * @class Roo.bootstrap.TriggerField
8487 * @extends Roo.bootstrap.Input
8488 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8489 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8490 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8491 * for which you can provide a custom implementation. For example:
8493 var trigger = new Roo.bootstrap.TriggerField();
8494 trigger.onTriggerClick = myTriggerFn;
8495 trigger.applyTo('my-field');
8498 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8499 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8500 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8501 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8502 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8505 * Create a new TriggerField.
8506 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8507 * to the base TextField)
8509 Roo.bootstrap.TriggerField = function(config){
8510 this.mimicing = false;
8511 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8514 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8516 * @cfg {String} triggerClass A CSS class to apply to the trigger
8519 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8524 * @cfg {Boolean} removable (true|false) special filter default false
8528 /** @cfg {Boolean} grow @hide */
8529 /** @cfg {Number} growMin @hide */
8530 /** @cfg {Number} growMax @hide */
8536 autoSize: Roo.emptyFn,
8543 actionMode : 'wrap',
8548 getAutoCreate : function(){
8550 var align = this.labelAlign || this.parentLabelAlign();
8555 cls: 'form-group' //input-group
8562 type : this.inputType,
8563 cls : 'form-control',
8564 autocomplete: 'new-password',
8565 placeholder : this.placeholder || ''
8569 input.name = this.name;
8572 input.cls += ' input-' + this.size;
8575 if (this.disabled) {
8576 input.disabled=true;
8579 var inputblock = input;
8581 if(this.hasFeedback && !this.allowBlank){
8585 cls: 'glyphicon form-control-feedback'
8588 if(this.removable && !this.editable && !this.tickable){
8590 cls : 'has-feedback',
8596 cls : 'roo-combo-removable-btn close'
8603 cls : 'has-feedback',
8612 if(this.removable && !this.editable && !this.tickable){
8614 cls : 'roo-removable',
8620 cls : 'roo-combo-removable-btn close'
8627 if (this.before || this.after) {
8630 cls : 'input-group',
8634 inputblock.cn.push({
8636 cls : 'input-group-addon',
8641 inputblock.cn.push(input);
8643 if(this.hasFeedback && !this.allowBlank){
8644 inputblock.cls += ' has-feedback';
8645 inputblock.cn.push(feedback);
8649 inputblock.cn.push({
8651 cls : 'input-group-addon',
8664 cls: 'form-hidden-field'
8672 Roo.log('multiple');
8680 cls: 'form-hidden-field'
8684 cls: 'select2-choices',
8688 cls: 'select2-search-field',
8701 cls: 'select2-container input-group',
8706 // cls: 'typeahead typeahead-long dropdown-menu',
8707 // style: 'display:none'
8712 if(!this.multiple && this.showToggleBtn){
8718 if (this.caret != false) {
8721 cls: 'fa fa-' + this.caret
8728 cls : 'input-group-addon btn dropdown-toggle',
8733 cls: 'combobox-clear',
8747 combobox.cls += ' select2-container-multi';
8750 if (align ==='left' && this.fieldLabel.length) {
8752 Roo.log("left and has label");
8758 cls : 'control-label col-sm-' + this.labelWidth,
8759 html : this.fieldLabel
8763 cls : "col-sm-" + (12 - this.labelWidth),
8770 } else if ( this.fieldLabel.length) {
8776 //cls : 'input-group-addon',
8777 html : this.fieldLabel
8787 Roo.log(" no label && no align");
8794 ['xs','sm','md','lg'].map(function(size){
8795 if (settings[size]) {
8796 cfg.cls += ' col-' + size + '-' + settings[size];
8807 onResize : function(w, h){
8808 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8809 // if(typeof w == 'number'){
8810 // var x = w - this.trigger.getWidth();
8811 // this.inputEl().setWidth(this.adjustWidth('input', x));
8812 // this.trigger.setStyle('left', x+'px');
8817 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8820 getResizeEl : function(){
8821 return this.inputEl();
8825 getPositionEl : function(){
8826 return this.inputEl();
8830 alignErrorIcon : function(){
8831 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8835 initEvents : function(){
8839 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8840 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8841 if(!this.multiple && this.showToggleBtn){
8842 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8843 if(this.hideTrigger){
8844 this.trigger.setDisplayed(false);
8846 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8850 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8853 if(this.removable && !this.editable && !this.tickable){
8854 var close = this.closeTriggerEl();
8857 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8858 close.on('click', this.removeBtnClick, this, close);
8862 //this.trigger.addClassOnOver('x-form-trigger-over');
8863 //this.trigger.addClassOnClick('x-form-trigger-click');
8866 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8870 closeTriggerEl : function()
8872 var close = this.el.select('.roo-combo-removable-btn', true).first();
8873 return close ? close : false;
8876 removeBtnClick : function(e, h, el)
8880 if(this.fireEvent("remove", this) !== false){
8885 createList : function()
8887 this.list = Roo.get(document.body).createChild({
8889 cls: 'typeahead typeahead-long dropdown-menu',
8890 style: 'display:none'
8893 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8898 initTrigger : function(){
8903 onDestroy : function(){
8905 this.trigger.removeAllListeners();
8906 // this.trigger.remove();
8909 // this.wrap.remove();
8911 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8915 onFocus : function(){
8916 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8919 this.wrap.addClass('x-trigger-wrap-focus');
8920 this.mimicing = true;
8921 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8922 if(this.monitorTab){
8923 this.el.on("keydown", this.checkTab, this);
8930 checkTab : function(e){
8931 if(e.getKey() == e.TAB){
8937 onBlur : function(){
8942 mimicBlur : function(e, t){
8944 if(!this.wrap.contains(t) && this.validateBlur()){
8951 triggerBlur : function(){
8952 this.mimicing = false;
8953 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8954 if(this.monitorTab){
8955 this.el.un("keydown", this.checkTab, this);
8957 //this.wrap.removeClass('x-trigger-wrap-focus');
8958 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8962 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8963 validateBlur : function(e, t){
8968 onDisable : function(){
8969 this.inputEl().dom.disabled = true;
8970 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8972 // this.wrap.addClass('x-item-disabled');
8977 onEnable : function(){
8978 this.inputEl().dom.disabled = false;
8979 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8981 // this.el.removeClass('x-item-disabled');
8986 onShow : function(){
8987 var ae = this.getActionEl();
8990 ae.dom.style.display = '';
8991 ae.dom.style.visibility = 'visible';
8997 onHide : function(){
8998 var ae = this.getActionEl();
8999 ae.dom.style.display = 'none';
9003 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9004 * by an implementing function.
9006 * @param {EventObject} e
9008 onTriggerClick : Roo.emptyFn
9012 * Ext JS Library 1.1.1
9013 * Copyright(c) 2006-2007, Ext JS, LLC.
9015 * Originally Released Under LGPL - original licence link has changed is not relivant.
9018 * <script type="text/javascript">
9023 * @class Roo.data.SortTypes
9025 * Defines the default sorting (casting?) comparison functions used when sorting data.
9027 Roo.data.SortTypes = {
9029 * Default sort that does nothing
9030 * @param {Mixed} s The value being converted
9031 * @return {Mixed} The comparison value
9038 * The regular expression used to strip tags
9042 stripTagsRE : /<\/?[^>]+>/gi,
9045 * Strips all HTML tags to sort on text only
9046 * @param {Mixed} s The value being converted
9047 * @return {String} The comparison value
9049 asText : function(s){
9050 return String(s).replace(this.stripTagsRE, "");
9054 * Strips all HTML tags to sort on text only - Case insensitive
9055 * @param {Mixed} s The value being converted
9056 * @return {String} The comparison value
9058 asUCText : function(s){
9059 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9063 * Case insensitive string
9064 * @param {Mixed} s The value being converted
9065 * @return {String} The comparison value
9067 asUCString : function(s) {
9068 return String(s).toUpperCase();
9073 * @param {Mixed} s The value being converted
9074 * @return {Number} The comparison value
9076 asDate : function(s) {
9080 if(s instanceof Date){
9083 return Date.parse(String(s));
9088 * @param {Mixed} s The value being converted
9089 * @return {Float} The comparison value
9091 asFloat : function(s) {
9092 var val = parseFloat(String(s).replace(/,/g, ""));
9093 if(isNaN(val)) val = 0;
9099 * @param {Mixed} s The value being converted
9100 * @return {Number} The comparison value
9102 asInt : function(s) {
9103 var val = parseInt(String(s).replace(/,/g, ""));
9104 if(isNaN(val)) val = 0;
9109 * Ext JS Library 1.1.1
9110 * Copyright(c) 2006-2007, Ext JS, LLC.
9112 * Originally Released Under LGPL - original licence link has changed is not relivant.
9115 * <script type="text/javascript">
9119 * @class Roo.data.Record
9120 * Instances of this class encapsulate both record <em>definition</em> information, and record
9121 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9122 * to access Records cached in an {@link Roo.data.Store} object.<br>
9124 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9125 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9128 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9130 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9131 * {@link #create}. The parameters are the same.
9132 * @param {Array} data An associative Array of data values keyed by the field name.
9133 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9134 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9135 * not specified an integer id is generated.
9137 Roo.data.Record = function(data, id){
9138 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9143 * Generate a constructor for a specific record layout.
9144 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9145 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9146 * Each field definition object may contain the following properties: <ul>
9147 * <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,
9148 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9149 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9150 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9151 * is being used, then this is a string containing the javascript expression to reference the data relative to
9152 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9153 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9154 * this may be omitted.</p></li>
9155 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9156 * <ul><li>auto (Default, implies no conversion)</li>
9161 * <li>date</li></ul></p></li>
9162 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9163 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9164 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9165 * by the Reader into an object that will be stored in the Record. It is passed the
9166 * following parameters:<ul>
9167 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9169 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9171 * <br>usage:<br><pre><code>
9172 var TopicRecord = Roo.data.Record.create(
9173 {name: 'title', mapping: 'topic_title'},
9174 {name: 'author', mapping: 'username'},
9175 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9176 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9177 {name: 'lastPoster', mapping: 'user2'},
9178 {name: 'excerpt', mapping: 'post_text'}
9181 var myNewRecord = new TopicRecord({
9182 title: 'Do my job please',
9185 lastPost: new Date(),
9186 lastPoster: 'Animal',
9187 excerpt: 'No way dude!'
9189 myStore.add(myNewRecord);
9194 Roo.data.Record.create = function(o){
9196 f.superclass.constructor.apply(this, arguments);
9198 Roo.extend(f, Roo.data.Record);
9199 var p = f.prototype;
9200 p.fields = new Roo.util.MixedCollection(false, function(field){
9203 for(var i = 0, len = o.length; i < len; i++){
9204 p.fields.add(new Roo.data.Field(o[i]));
9206 f.getField = function(name){
9207 return p.fields.get(name);
9212 Roo.data.Record.AUTO_ID = 1000;
9213 Roo.data.Record.EDIT = 'edit';
9214 Roo.data.Record.REJECT = 'reject';
9215 Roo.data.Record.COMMIT = 'commit';
9217 Roo.data.Record.prototype = {
9219 * Readonly flag - true if this record has been modified.
9228 join : function(store){
9233 * Set the named field to the specified value.
9234 * @param {String} name The name of the field to set.
9235 * @param {Object} value The value to set the field to.
9237 set : function(name, value){
9238 if(this.data[name] == value){
9245 if(typeof this.modified[name] == 'undefined'){
9246 this.modified[name] = this.data[name];
9248 this.data[name] = value;
9249 if(!this.editing && this.store){
9250 this.store.afterEdit(this);
9255 * Get the value of the named field.
9256 * @param {String} name The name of the field to get the value of.
9257 * @return {Object} The value of the field.
9259 get : function(name){
9260 return this.data[name];
9264 beginEdit : function(){
9265 this.editing = true;
9270 cancelEdit : function(){
9271 this.editing = false;
9272 delete this.modified;
9276 endEdit : function(){
9277 this.editing = false;
9278 if(this.dirty && this.store){
9279 this.store.afterEdit(this);
9284 * Usually called by the {@link Roo.data.Store} which owns the Record.
9285 * Rejects all changes made to the Record since either creation, or the last commit operation.
9286 * Modified fields are reverted to their original values.
9288 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9289 * of reject operations.
9291 reject : function(){
9292 var m = this.modified;
9294 if(typeof m[n] != "function"){
9295 this.data[n] = m[n];
9299 delete this.modified;
9300 this.editing = false;
9302 this.store.afterReject(this);
9307 * Usually called by the {@link Roo.data.Store} which owns the Record.
9308 * Commits all changes made to the Record since either creation, or the last commit operation.
9310 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9311 * of commit operations.
9313 commit : function(){
9315 delete this.modified;
9316 this.editing = false;
9318 this.store.afterCommit(this);
9323 hasError : function(){
9324 return this.error != null;
9328 clearError : function(){
9333 * Creates a copy of this record.
9334 * @param {String} id (optional) A new record id if you don't want to use this record's id
9337 copy : function(newId) {
9338 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9342 * Ext JS Library 1.1.1
9343 * Copyright(c) 2006-2007, Ext JS, LLC.
9345 * Originally Released Under LGPL - original licence link has changed is not relivant.
9348 * <script type="text/javascript">
9354 * @class Roo.data.Store
9355 * @extends Roo.util.Observable
9356 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9357 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9359 * 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
9360 * has no knowledge of the format of the data returned by the Proxy.<br>
9362 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9363 * instances from the data object. These records are cached and made available through accessor functions.
9365 * Creates a new Store.
9366 * @param {Object} config A config object containing the objects needed for the Store to access data,
9367 * and read the data into Records.
9369 Roo.data.Store = function(config){
9370 this.data = new Roo.util.MixedCollection(false);
9371 this.data.getKey = function(o){
9374 this.baseParams = {};
9381 "multisort" : "_multisort"
9384 if(config && config.data){
9385 this.inlineData = config.data;
9389 Roo.apply(this, config);
9391 if(this.reader){ // reader passed
9392 this.reader = Roo.factory(this.reader, Roo.data);
9393 this.reader.xmodule = this.xmodule || false;
9394 if(!this.recordType){
9395 this.recordType = this.reader.recordType;
9397 if(this.reader.onMetaChange){
9398 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9402 if(this.recordType){
9403 this.fields = this.recordType.prototype.fields;
9409 * @event datachanged
9410 * Fires when the data cache has changed, and a widget which is using this Store
9411 * as a Record cache should refresh its view.
9412 * @param {Store} this
9417 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9418 * @param {Store} this
9419 * @param {Object} meta The JSON metadata
9424 * Fires when Records have been added to the Store
9425 * @param {Store} this
9426 * @param {Roo.data.Record[]} records The array of Records added
9427 * @param {Number} index The index at which the record(s) were added
9432 * Fires when a Record has been removed from the Store
9433 * @param {Store} this
9434 * @param {Roo.data.Record} record The Record that was removed
9435 * @param {Number} index The index at which the record was removed
9440 * Fires when a Record has been updated
9441 * @param {Store} this
9442 * @param {Roo.data.Record} record The Record that was updated
9443 * @param {String} operation The update operation being performed. Value may be one of:
9445 Roo.data.Record.EDIT
9446 Roo.data.Record.REJECT
9447 Roo.data.Record.COMMIT
9453 * Fires when the data cache has been cleared.
9454 * @param {Store} this
9459 * Fires before a request is made for a new data object. If the beforeload handler returns false
9460 * the load action will be canceled.
9461 * @param {Store} this
9462 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9466 * @event beforeloadadd
9467 * Fires after a new set of Records has been loaded.
9468 * @param {Store} this
9469 * @param {Roo.data.Record[]} records The Records that were loaded
9470 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9472 beforeloadadd : true,
9475 * Fires after a new set of Records has been loaded, before they are added to the store.
9476 * @param {Store} this
9477 * @param {Roo.data.Record[]} records The Records that were loaded
9478 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9479 * @params {Object} return from reader
9483 * @event loadexception
9484 * Fires if an exception occurs in the Proxy during loading.
9485 * Called with the signature of the Proxy's "loadexception" event.
9486 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9489 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9490 * @param {Object} load options
9491 * @param {Object} jsonData from your request (normally this contains the Exception)
9493 loadexception : true
9497 this.proxy = Roo.factory(this.proxy, Roo.data);
9498 this.proxy.xmodule = this.xmodule || false;
9499 this.relayEvents(this.proxy, ["loadexception"]);
9501 this.sortToggle = {};
9502 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9504 Roo.data.Store.superclass.constructor.call(this);
9506 if(this.inlineData){
9507 this.loadData(this.inlineData);
9508 delete this.inlineData;
9512 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9514 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9515 * without a remote query - used by combo/forms at present.
9519 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9522 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9525 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9526 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9529 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9530 * on any HTTP request
9533 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9536 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9540 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9541 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9546 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9547 * loaded or when a record is removed. (defaults to false).
9549 pruneModifiedRecords : false,
9555 * Add Records to the Store and fires the add event.
9556 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9558 add : function(records){
9559 records = [].concat(records);
9560 for(var i = 0, len = records.length; i < len; i++){
9561 records[i].join(this);
9563 var index = this.data.length;
9564 this.data.addAll(records);
9565 this.fireEvent("add", this, records, index);
9569 * Remove a Record from the Store and fires the remove event.
9570 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9572 remove : function(record){
9573 var index = this.data.indexOf(record);
9574 this.data.removeAt(index);
9575 if(this.pruneModifiedRecords){
9576 this.modified.remove(record);
9578 this.fireEvent("remove", this, record, index);
9582 * Remove all Records from the Store and fires the clear event.
9584 removeAll : function(){
9586 if(this.pruneModifiedRecords){
9589 this.fireEvent("clear", this);
9593 * Inserts Records to the Store at the given index and fires the add event.
9594 * @param {Number} index The start index at which to insert the passed Records.
9595 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9597 insert : function(index, records){
9598 records = [].concat(records);
9599 for(var i = 0, len = records.length; i < len; i++){
9600 this.data.insert(index, records[i]);
9601 records[i].join(this);
9603 this.fireEvent("add", this, records, index);
9607 * Get the index within the cache of the passed Record.
9608 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9609 * @return {Number} The index of the passed Record. Returns -1 if not found.
9611 indexOf : function(record){
9612 return this.data.indexOf(record);
9616 * Get the index within the cache of the Record with the passed id.
9617 * @param {String} id The id of the Record to find.
9618 * @return {Number} The index of the Record. Returns -1 if not found.
9620 indexOfId : function(id){
9621 return this.data.indexOfKey(id);
9625 * Get the Record with the specified id.
9626 * @param {String} id The id of the Record to find.
9627 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9629 getById : function(id){
9630 return this.data.key(id);
9634 * Get the Record at the specified index.
9635 * @param {Number} index The index of the Record to find.
9636 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9638 getAt : function(index){
9639 return this.data.itemAt(index);
9643 * Returns a range of Records between specified indices.
9644 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9645 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9646 * @return {Roo.data.Record[]} An array of Records
9648 getRange : function(start, end){
9649 return this.data.getRange(start, end);
9653 storeOptions : function(o){
9654 o = Roo.apply({}, o);
9657 this.lastOptions = o;
9661 * Loads the Record cache from the configured Proxy using the configured Reader.
9663 * If using remote paging, then the first load call must specify the <em>start</em>
9664 * and <em>limit</em> properties in the options.params property to establish the initial
9665 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9667 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9668 * and this call will return before the new data has been loaded. Perform any post-processing
9669 * in a callback function, or in a "load" event handler.</strong>
9671 * @param {Object} options An object containing properties which control loading options:<ul>
9672 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9673 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9674 * passed the following arguments:<ul>
9675 * <li>r : Roo.data.Record[]</li>
9676 * <li>options: Options object from the load call</li>
9677 * <li>success: Boolean success indicator</li></ul></li>
9678 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9679 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9682 load : function(options){
9683 options = options || {};
9684 if(this.fireEvent("beforeload", this, options) !== false){
9685 this.storeOptions(options);
9686 var p = Roo.apply(options.params || {}, this.baseParams);
9687 // if meta was not loaded from remote source.. try requesting it.
9688 if (!this.reader.metaFromRemote) {
9691 if(this.sortInfo && this.remoteSort){
9692 var pn = this.paramNames;
9693 p[pn["sort"]] = this.sortInfo.field;
9694 p[pn["dir"]] = this.sortInfo.direction;
9696 if (this.multiSort) {
9697 var pn = this.paramNames;
9698 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9701 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9706 * Reloads the Record cache from the configured Proxy using the configured Reader and
9707 * the options from the last load operation performed.
9708 * @param {Object} options (optional) An object containing properties which may override the options
9709 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9710 * the most recently used options are reused).
9712 reload : function(options){
9713 this.load(Roo.applyIf(options||{}, this.lastOptions));
9717 // Called as a callback by the Reader during a load operation.
9718 loadRecords : function(o, options, success){
9719 if(!o || success === false){
9720 if(success !== false){
9721 this.fireEvent("load", this, [], options, o);
9723 if(options.callback){
9724 options.callback.call(options.scope || this, [], options, false);
9728 // if data returned failure - throw an exception.
9729 if (o.success === false) {
9730 // show a message if no listener is registered.
9731 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9732 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9734 // loadmask wil be hooked into this..
9735 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9738 var r = o.records, t = o.totalRecords || r.length;
9740 this.fireEvent("beforeloadadd", this, r, options, o);
9742 if(!options || options.add !== true){
9743 if(this.pruneModifiedRecords){
9746 for(var i = 0, len = r.length; i < len; i++){
9750 this.data = this.snapshot;
9751 delete this.snapshot;
9754 this.data.addAll(r);
9755 this.totalLength = t;
9757 this.fireEvent("datachanged", this);
9759 this.totalLength = Math.max(t, this.data.length+r.length);
9762 this.fireEvent("load", this, r, options, o);
9763 if(options.callback){
9764 options.callback.call(options.scope || this, r, options, true);
9770 * Loads data from a passed data block. A Reader which understands the format of the data
9771 * must have been configured in the constructor.
9772 * @param {Object} data The data block from which to read the Records. The format of the data expected
9773 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9774 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9776 loadData : function(o, append){
9777 var r = this.reader.readRecords(o);
9778 this.loadRecords(r, {add: append}, true);
9782 * Gets the number of cached records.
9784 * <em>If using paging, this may not be the total size of the dataset. If the data object
9785 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9786 * the data set size</em>
9788 getCount : function(){
9789 return this.data.length || 0;
9793 * Gets the total number of records in the dataset as returned by the server.
9795 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9796 * the dataset size</em>
9798 getTotalCount : function(){
9799 return this.totalLength || 0;
9803 * Returns the sort state of the Store as an object with two properties:
9805 field {String} The name of the field by which the Records are sorted
9806 direction {String} The sort order, "ASC" or "DESC"
9809 getSortState : function(){
9810 return this.sortInfo;
9814 applySort : function(){
9815 if(this.sortInfo && !this.remoteSort){
9816 var s = this.sortInfo, f = s.field;
9817 var st = this.fields.get(f).sortType;
9818 var fn = function(r1, r2){
9819 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9820 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9822 this.data.sort(s.direction, fn);
9823 if(this.snapshot && this.snapshot != this.data){
9824 this.snapshot.sort(s.direction, fn);
9830 * Sets the default sort column and order to be used by the next load operation.
9831 * @param {String} fieldName The name of the field to sort by.
9832 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9834 setDefaultSort : function(field, dir){
9835 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9840 * If remote sorting is used, the sort is performed on the server, and the cache is
9841 * reloaded. If local sorting is used, the cache is sorted internally.
9842 * @param {String} fieldName The name of the field to sort by.
9843 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9845 sort : function(fieldName, dir){
9846 var f = this.fields.get(fieldName);
9848 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9850 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9851 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9856 this.sortToggle[f.name] = dir;
9857 this.sortInfo = {field: f.name, direction: dir};
9858 if(!this.remoteSort){
9860 this.fireEvent("datachanged", this);
9862 this.load(this.lastOptions);
9867 * Calls the specified function for each of the Records in the cache.
9868 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9869 * Returning <em>false</em> aborts and exits the iteration.
9870 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9872 each : function(fn, scope){
9873 this.data.each(fn, scope);
9877 * Gets all records modified since the last commit. Modified records are persisted across load operations
9878 * (e.g., during paging).
9879 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9881 getModifiedRecords : function(){
9882 return this.modified;
9886 createFilterFn : function(property, value, anyMatch){
9887 if(!value.exec){ // not a regex
9888 value = String(value);
9889 if(value.length == 0){
9892 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9895 return value.test(r.data[property]);
9900 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9901 * @param {String} property A field on your records
9902 * @param {Number} start The record index to start at (defaults to 0)
9903 * @param {Number} end The last record index to include (defaults to length - 1)
9904 * @return {Number} The sum
9906 sum : function(property, start, end){
9907 var rs = this.data.items, v = 0;
9909 end = (end || end === 0) ? end : rs.length-1;
9911 for(var i = start; i <= end; i++){
9912 v += (rs[i].data[property] || 0);
9918 * Filter the records by a specified property.
9919 * @param {String} field A field on your records
9920 * @param {String/RegExp} value Either a string that the field
9921 * should start with or a RegExp to test against the field
9922 * @param {Boolean} anyMatch True to match any part not just the beginning
9924 filter : function(property, value, anyMatch){
9925 var fn = this.createFilterFn(property, value, anyMatch);
9926 return fn ? this.filterBy(fn) : this.clearFilter();
9930 * Filter by a function. The specified function will be called with each
9931 * record in this data source. If the function returns true the record is included,
9932 * otherwise it is filtered.
9933 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9934 * @param {Object} scope (optional) The scope of the function (defaults to this)
9936 filterBy : function(fn, scope){
9937 this.snapshot = this.snapshot || this.data;
9938 this.data = this.queryBy(fn, scope||this);
9939 this.fireEvent("datachanged", this);
9943 * Query the records by a specified property.
9944 * @param {String} field A field on your records
9945 * @param {String/RegExp} value Either a string that the field
9946 * should start with or a RegExp to test against the field
9947 * @param {Boolean} anyMatch True to match any part not just the beginning
9948 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9950 query : function(property, value, anyMatch){
9951 var fn = this.createFilterFn(property, value, anyMatch);
9952 return fn ? this.queryBy(fn) : this.data.clone();
9956 * Query by a function. The specified function will be called with each
9957 * record in this data source. If the function returns true the record is included
9959 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9960 * @param {Object} scope (optional) The scope of the function (defaults to this)
9961 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9963 queryBy : function(fn, scope){
9964 var data = this.snapshot || this.data;
9965 return data.filterBy(fn, scope||this);
9969 * Collects unique values for a particular dataIndex from this store.
9970 * @param {String} dataIndex The property to collect
9971 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9972 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9973 * @return {Array} An array of the unique values
9975 collect : function(dataIndex, allowNull, bypassFilter){
9976 var d = (bypassFilter === true && this.snapshot) ?
9977 this.snapshot.items : this.data.items;
9978 var v, sv, r = [], l = {};
9979 for(var i = 0, len = d.length; i < len; i++){
9980 v = d[i].data[dataIndex];
9982 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9991 * Revert to a view of the Record cache with no filtering applied.
9992 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9994 clearFilter : function(suppressEvent){
9995 if(this.snapshot && this.snapshot != this.data){
9996 this.data = this.snapshot;
9997 delete this.snapshot;
9998 if(suppressEvent !== true){
9999 this.fireEvent("datachanged", this);
10005 afterEdit : function(record){
10006 if(this.modified.indexOf(record) == -1){
10007 this.modified.push(record);
10009 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10013 afterReject : function(record){
10014 this.modified.remove(record);
10015 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10019 afterCommit : function(record){
10020 this.modified.remove(record);
10021 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10025 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10026 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10028 commitChanges : function(){
10029 var m = this.modified.slice(0);
10030 this.modified = [];
10031 for(var i = 0, len = m.length; i < len; i++){
10037 * Cancel outstanding changes on all changed records.
10039 rejectChanges : function(){
10040 var m = this.modified.slice(0);
10041 this.modified = [];
10042 for(var i = 0, len = m.length; i < len; i++){
10047 onMetaChange : function(meta, rtype, o){
10048 this.recordType = rtype;
10049 this.fields = rtype.prototype.fields;
10050 delete this.snapshot;
10051 this.sortInfo = meta.sortInfo || this.sortInfo;
10052 this.modified = [];
10053 this.fireEvent('metachange', this, this.reader.meta);
10056 moveIndex : function(data, type)
10058 var index = this.indexOf(data);
10060 var newIndex = index + type;
10064 this.insert(newIndex, data);
10069 * Ext JS Library 1.1.1
10070 * Copyright(c) 2006-2007, Ext JS, LLC.
10072 * Originally Released Under LGPL - original licence link has changed is not relivant.
10075 * <script type="text/javascript">
10079 * @class Roo.data.SimpleStore
10080 * @extends Roo.data.Store
10081 * Small helper class to make creating Stores from Array data easier.
10082 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10083 * @cfg {Array} fields An array of field definition objects, or field name strings.
10084 * @cfg {Array} data The multi-dimensional array of data
10086 * @param {Object} config
10088 Roo.data.SimpleStore = function(config){
10089 Roo.data.SimpleStore.superclass.constructor.call(this, {
10091 reader: new Roo.data.ArrayReader({
10094 Roo.data.Record.create(config.fields)
10096 proxy : new Roo.data.MemoryProxy(config.data)
10100 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10102 * Ext JS Library 1.1.1
10103 * Copyright(c) 2006-2007, Ext JS, LLC.
10105 * Originally Released Under LGPL - original licence link has changed is not relivant.
10108 * <script type="text/javascript">
10113 * @extends Roo.data.Store
10114 * @class Roo.data.JsonStore
10115 * Small helper class to make creating Stores for JSON data easier. <br/>
10117 var store = new Roo.data.JsonStore({
10118 url: 'get-images.php',
10120 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10123 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10124 * JsonReader and HttpProxy (unless inline data is provided).</b>
10125 * @cfg {Array} fields An array of field definition objects, or field name strings.
10127 * @param {Object} config
10129 Roo.data.JsonStore = function(c){
10130 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10131 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10132 reader: new Roo.data.JsonReader(c, c.fields)
10135 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10137 * Ext JS Library 1.1.1
10138 * Copyright(c) 2006-2007, Ext JS, LLC.
10140 * Originally Released Under LGPL - original licence link has changed is not relivant.
10143 * <script type="text/javascript">
10147 Roo.data.Field = function(config){
10148 if(typeof config == "string"){
10149 config = {name: config};
10151 Roo.apply(this, config);
10154 this.type = "auto";
10157 var st = Roo.data.SortTypes;
10158 // named sortTypes are supported, here we look them up
10159 if(typeof this.sortType == "string"){
10160 this.sortType = st[this.sortType];
10163 // set default sortType for strings and dates
10164 if(!this.sortType){
10167 this.sortType = st.asUCString;
10170 this.sortType = st.asDate;
10173 this.sortType = st.none;
10178 var stripRe = /[\$,%]/g;
10180 // prebuilt conversion function for this field, instead of
10181 // switching every time we're reading a value
10183 var cv, dateFormat = this.dateFormat;
10188 cv = function(v){ return v; };
10191 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10195 return v !== undefined && v !== null && v !== '' ?
10196 parseInt(String(v).replace(stripRe, ""), 10) : '';
10201 return v !== undefined && v !== null && v !== '' ?
10202 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10207 cv = function(v){ return v === true || v === "true" || v == 1; };
10214 if(v instanceof Date){
10218 if(dateFormat == "timestamp"){
10219 return new Date(v*1000);
10221 return Date.parseDate(v, dateFormat);
10223 var parsed = Date.parse(v);
10224 return parsed ? new Date(parsed) : null;
10233 Roo.data.Field.prototype = {
10241 * Ext JS Library 1.1.1
10242 * Copyright(c) 2006-2007, Ext JS, LLC.
10244 * Originally Released Under LGPL - original licence link has changed is not relivant.
10247 * <script type="text/javascript">
10250 // Base class for reading structured data from a data source. This class is intended to be
10251 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10254 * @class Roo.data.DataReader
10255 * Base class for reading structured data from a data source. This class is intended to be
10256 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10259 Roo.data.DataReader = function(meta, recordType){
10263 this.recordType = recordType instanceof Array ?
10264 Roo.data.Record.create(recordType) : recordType;
10267 Roo.data.DataReader.prototype = {
10269 * Create an empty record
10270 * @param {Object} data (optional) - overlay some values
10271 * @return {Roo.data.Record} record created.
10273 newRow : function(d) {
10275 this.recordType.prototype.fields.each(function(c) {
10277 case 'int' : da[c.name] = 0; break;
10278 case 'date' : da[c.name] = new Date(); break;
10279 case 'float' : da[c.name] = 0.0; break;
10280 case 'boolean' : da[c.name] = false; break;
10281 default : da[c.name] = ""; break;
10285 return new this.recordType(Roo.apply(da, d));
10290 * Ext JS Library 1.1.1
10291 * Copyright(c) 2006-2007, Ext JS, LLC.
10293 * Originally Released Under LGPL - original licence link has changed is not relivant.
10296 * <script type="text/javascript">
10300 * @class Roo.data.DataProxy
10301 * @extends Roo.data.Observable
10302 * This class is an abstract base class for implementations which provide retrieval of
10303 * unformatted data objects.<br>
10305 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10306 * (of the appropriate type which knows how to parse the data object) to provide a block of
10307 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10309 * Custom implementations must implement the load method as described in
10310 * {@link Roo.data.HttpProxy#load}.
10312 Roo.data.DataProxy = function(){
10315 * @event beforeload
10316 * Fires before a network request is made to retrieve a data object.
10317 * @param {Object} This DataProxy object.
10318 * @param {Object} params The params parameter to the load function.
10323 * Fires before the load method's callback is called.
10324 * @param {Object} This DataProxy object.
10325 * @param {Object} o The data object.
10326 * @param {Object} arg The callback argument object passed to the load function.
10330 * @event loadexception
10331 * Fires if an Exception occurs during data retrieval.
10332 * @param {Object} This DataProxy object.
10333 * @param {Object} o The data object.
10334 * @param {Object} arg The callback argument object passed to the load function.
10335 * @param {Object} e The Exception.
10337 loadexception : true
10339 Roo.data.DataProxy.superclass.constructor.call(this);
10342 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10345 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10349 * Ext JS Library 1.1.1
10350 * Copyright(c) 2006-2007, Ext JS, LLC.
10352 * Originally Released Under LGPL - original licence link has changed is not relivant.
10355 * <script type="text/javascript">
10358 * @class Roo.data.MemoryProxy
10359 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10360 * to the Reader when its load method is called.
10362 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10364 Roo.data.MemoryProxy = function(data){
10368 Roo.data.MemoryProxy.superclass.constructor.call(this);
10372 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10374 * Load data from the requested source (in this case an in-memory
10375 * data object passed to the constructor), read the data object into
10376 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10377 * process that block using the passed callback.
10378 * @param {Object} params This parameter is not used by the MemoryProxy class.
10379 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10380 * object into a block of Roo.data.Records.
10381 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10382 * The function must be passed <ul>
10383 * <li>The Record block object</li>
10384 * <li>The "arg" argument from the load function</li>
10385 * <li>A boolean success indicator</li>
10387 * @param {Object} scope The scope in which to call the callback
10388 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10390 load : function(params, reader, callback, scope, arg){
10391 params = params || {};
10394 result = reader.readRecords(this.data);
10396 this.fireEvent("loadexception", this, arg, null, e);
10397 callback.call(scope, null, arg, false);
10400 callback.call(scope, result, arg, true);
10404 update : function(params, records){
10409 * Ext JS Library 1.1.1
10410 * Copyright(c) 2006-2007, Ext JS, LLC.
10412 * Originally Released Under LGPL - original licence link has changed is not relivant.
10415 * <script type="text/javascript">
10418 * @class Roo.data.HttpProxy
10419 * @extends Roo.data.DataProxy
10420 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10421 * configured to reference a certain URL.<br><br>
10423 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10424 * from which the running page was served.<br><br>
10426 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10428 * Be aware that to enable the browser to parse an XML document, the server must set
10429 * the Content-Type header in the HTTP response to "text/xml".
10431 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10432 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10433 * will be used to make the request.
10435 Roo.data.HttpProxy = function(conn){
10436 Roo.data.HttpProxy.superclass.constructor.call(this);
10437 // is conn a conn config or a real conn?
10439 this.useAjax = !conn || !conn.events;
10443 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10444 // thse are take from connection...
10447 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10450 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10451 * extra parameters to each request made by this object. (defaults to undefined)
10454 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10455 * to each request made by this object. (defaults to undefined)
10458 * @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)
10461 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10464 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10470 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10474 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10475 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10476 * a finer-grained basis than the DataProxy events.
10478 getConnection : function(){
10479 return this.useAjax ? Roo.Ajax : this.conn;
10483 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10484 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10485 * process that block using the passed callback.
10486 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10487 * for the request to the remote server.
10488 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10489 * object into a block of Roo.data.Records.
10490 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10491 * The function must be passed <ul>
10492 * <li>The Record block object</li>
10493 * <li>The "arg" argument from the load function</li>
10494 * <li>A boolean success indicator</li>
10496 * @param {Object} scope The scope in which to call the callback
10497 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10499 load : function(params, reader, callback, scope, arg){
10500 if(this.fireEvent("beforeload", this, params) !== false){
10502 params : params || {},
10504 callback : callback,
10509 callback : this.loadResponse,
10513 Roo.applyIf(o, this.conn);
10514 if(this.activeRequest){
10515 Roo.Ajax.abort(this.activeRequest);
10517 this.activeRequest = Roo.Ajax.request(o);
10519 this.conn.request(o);
10522 callback.call(scope||this, null, arg, false);
10527 loadResponse : function(o, success, response){
10528 delete this.activeRequest;
10530 this.fireEvent("loadexception", this, o, response);
10531 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10536 result = o.reader.read(response);
10538 this.fireEvent("loadexception", this, o, response, e);
10539 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10543 this.fireEvent("load", this, o, o.request.arg);
10544 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10548 update : function(dataSet){
10553 updateResponse : function(dataSet){
10558 * Ext JS Library 1.1.1
10559 * Copyright(c) 2006-2007, Ext JS, LLC.
10561 * Originally Released Under LGPL - original licence link has changed is not relivant.
10564 * <script type="text/javascript">
10568 * @class Roo.data.ScriptTagProxy
10569 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10570 * other than the originating domain of the running page.<br><br>
10572 * <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
10573 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10575 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10576 * source code that is used as the source inside a <script> tag.<br><br>
10578 * In order for the browser to process the returned data, the server must wrap the data object
10579 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10580 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10581 * depending on whether the callback name was passed:
10584 boolean scriptTag = false;
10585 String cb = request.getParameter("callback");
10588 response.setContentType("text/javascript");
10590 response.setContentType("application/x-json");
10592 Writer out = response.getWriter();
10594 out.write(cb + "(");
10596 out.print(dataBlock.toJsonString());
10603 * @param {Object} config A configuration object.
10605 Roo.data.ScriptTagProxy = function(config){
10606 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10607 Roo.apply(this, config);
10608 this.head = document.getElementsByTagName("head")[0];
10611 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10613 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10615 * @cfg {String} url The URL from which to request the data object.
10618 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10622 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10623 * the server the name of the callback function set up by the load call to process the returned data object.
10624 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10625 * javascript output which calls this named function passing the data object as its only parameter.
10627 callbackParam : "callback",
10629 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10630 * name to the request.
10635 * Load data from the configured URL, read the data object into
10636 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10637 * process that block using the passed callback.
10638 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10639 * for the request to the remote server.
10640 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10641 * object into a block of Roo.data.Records.
10642 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10643 * The function must be passed <ul>
10644 * <li>The Record block object</li>
10645 * <li>The "arg" argument from the load function</li>
10646 * <li>A boolean success indicator</li>
10648 * @param {Object} scope The scope in which to call the callback
10649 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10651 load : function(params, reader, callback, scope, arg){
10652 if(this.fireEvent("beforeload", this, params) !== false){
10654 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10656 var url = this.url;
10657 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10659 url += "&_dc=" + (new Date().getTime());
10661 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10664 cb : "stcCallback"+transId,
10665 scriptId : "stcScript"+transId,
10669 callback : callback,
10675 window[trans.cb] = function(o){
10676 conn.handleResponse(o, trans);
10679 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10681 if(this.autoAbort !== false){
10685 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10687 var script = document.createElement("script");
10688 script.setAttribute("src", url);
10689 script.setAttribute("type", "text/javascript");
10690 script.setAttribute("id", trans.scriptId);
10691 this.head.appendChild(script);
10693 this.trans = trans;
10695 callback.call(scope||this, null, arg, false);
10700 isLoading : function(){
10701 return this.trans ? true : false;
10705 * Abort the current server request.
10707 abort : function(){
10708 if(this.isLoading()){
10709 this.destroyTrans(this.trans);
10714 destroyTrans : function(trans, isLoaded){
10715 this.head.removeChild(document.getElementById(trans.scriptId));
10716 clearTimeout(trans.timeoutId);
10718 window[trans.cb] = undefined;
10720 delete window[trans.cb];
10723 // if hasn't been loaded, wait for load to remove it to prevent script error
10724 window[trans.cb] = function(){
10725 window[trans.cb] = undefined;
10727 delete window[trans.cb];
10734 handleResponse : function(o, trans){
10735 this.trans = false;
10736 this.destroyTrans(trans, true);
10739 result = trans.reader.readRecords(o);
10741 this.fireEvent("loadexception", this, o, trans.arg, e);
10742 trans.callback.call(trans.scope||window, null, trans.arg, false);
10745 this.fireEvent("load", this, o, trans.arg);
10746 trans.callback.call(trans.scope||window, result, trans.arg, true);
10750 handleFailure : function(trans){
10751 this.trans = false;
10752 this.destroyTrans(trans, false);
10753 this.fireEvent("loadexception", this, null, trans.arg);
10754 trans.callback.call(trans.scope||window, null, trans.arg, false);
10758 * Ext JS Library 1.1.1
10759 * Copyright(c) 2006-2007, Ext JS, LLC.
10761 * Originally Released Under LGPL - original licence link has changed is not relivant.
10764 * <script type="text/javascript">
10768 * @class Roo.data.JsonReader
10769 * @extends Roo.data.DataReader
10770 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10771 * based on mappings in a provided Roo.data.Record constructor.
10773 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10774 * in the reply previously.
10779 var RecordDef = Roo.data.Record.create([
10780 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10781 {name: 'occupation'} // This field will use "occupation" as the mapping.
10783 var myReader = new Roo.data.JsonReader({
10784 totalProperty: "results", // The property which contains the total dataset size (optional)
10785 root: "rows", // The property which contains an Array of row objects
10786 id: "id" // The property within each row object that provides an ID for the record (optional)
10790 * This would consume a JSON file like this:
10792 { 'results': 2, 'rows': [
10793 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10794 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10797 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10798 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10799 * paged from the remote server.
10800 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10801 * @cfg {String} root name of the property which contains the Array of row objects.
10802 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10803 * @cfg {Array} fields Array of field definition objects
10805 * Create a new JsonReader
10806 * @param {Object} meta Metadata configuration options
10807 * @param {Object} recordType Either an Array of field definition objects,
10808 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10810 Roo.data.JsonReader = function(meta, recordType){
10813 // set some defaults:
10814 Roo.applyIf(meta, {
10815 totalProperty: 'total',
10816 successProperty : 'success',
10821 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10823 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10826 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10827 * Used by Store query builder to append _requestMeta to params.
10830 metaFromRemote : false,
10832 * This method is only used by a DataProxy which has retrieved data from a remote server.
10833 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10834 * @return {Object} data A data block which is used by an Roo.data.Store object as
10835 * a cache of Roo.data.Records.
10837 read : function(response){
10838 var json = response.responseText;
10840 var o = /* eval:var:o */ eval("("+json+")");
10842 throw {message: "JsonReader.read: Json object not found"};
10848 this.metaFromRemote = true;
10849 this.meta = o.metaData;
10850 this.recordType = Roo.data.Record.create(o.metaData.fields);
10851 this.onMetaChange(this.meta, this.recordType, o);
10853 return this.readRecords(o);
10856 // private function a store will implement
10857 onMetaChange : function(meta, recordType, o){
10864 simpleAccess: function(obj, subsc) {
10871 getJsonAccessor: function(){
10873 return function(expr) {
10875 return(re.test(expr))
10876 ? new Function("obj", "return obj." + expr)
10881 return Roo.emptyFn;
10886 * Create a data block containing Roo.data.Records from an XML document.
10887 * @param {Object} o An object which contains an Array of row objects in the property specified
10888 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10889 * which contains the total size of the dataset.
10890 * @return {Object} data A data block which is used by an Roo.data.Store object as
10891 * a cache of Roo.data.Records.
10893 readRecords : function(o){
10895 * After any data loads, the raw JSON data is available for further custom processing.
10899 var s = this.meta, Record = this.recordType,
10900 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10902 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10904 if(s.totalProperty) {
10905 this.getTotal = this.getJsonAccessor(s.totalProperty);
10907 if(s.successProperty) {
10908 this.getSuccess = this.getJsonAccessor(s.successProperty);
10910 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10912 var g = this.getJsonAccessor(s.id);
10913 this.getId = function(rec) {
10915 return (r === undefined || r === "") ? null : r;
10918 this.getId = function(){return null;};
10921 for(var jj = 0; jj < fl; jj++){
10923 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10924 this.ef[jj] = this.getJsonAccessor(map);
10928 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10929 if(s.totalProperty){
10930 var vt = parseInt(this.getTotal(o), 10);
10935 if(s.successProperty){
10936 var vs = this.getSuccess(o);
10937 if(vs === false || vs === 'false'){
10942 for(var i = 0; i < c; i++){
10945 var id = this.getId(n);
10946 for(var j = 0; j < fl; j++){
10948 var v = this.ef[j](n);
10950 Roo.log('missing convert for ' + f.name);
10954 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10956 var record = new Record(values, id);
10958 records[i] = record;
10964 totalRecords : totalRecords
10969 * Ext JS Library 1.1.1
10970 * Copyright(c) 2006-2007, Ext JS, LLC.
10972 * Originally Released Under LGPL - original licence link has changed is not relivant.
10975 * <script type="text/javascript">
10979 * @class Roo.data.ArrayReader
10980 * @extends Roo.data.DataReader
10981 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10982 * Each element of that Array represents a row of data fields. The
10983 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10984 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10988 var RecordDef = Roo.data.Record.create([
10989 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10990 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10992 var myReader = new Roo.data.ArrayReader({
10993 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10997 * This would consume an Array like this:
10999 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11001 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11003 * Create a new JsonReader
11004 * @param {Object} meta Metadata configuration options.
11005 * @param {Object} recordType Either an Array of field definition objects
11006 * as specified to {@link Roo.data.Record#create},
11007 * or an {@link Roo.data.Record} object
11008 * created using {@link Roo.data.Record#create}.
11010 Roo.data.ArrayReader = function(meta, recordType){
11011 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11014 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11016 * Create a data block containing Roo.data.Records from an XML document.
11017 * @param {Object} o An Array of row objects which represents the dataset.
11018 * @return {Object} data A data block which is used by an Roo.data.Store object as
11019 * a cache of Roo.data.Records.
11021 readRecords : function(o){
11022 var sid = this.meta ? this.meta.id : null;
11023 var recordType = this.recordType, fields = recordType.prototype.fields;
11026 for(var i = 0; i < root.length; i++){
11029 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11030 for(var j = 0, jlen = fields.length; j < jlen; j++){
11031 var f = fields.items[j];
11032 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11033 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11035 values[f.name] = v;
11037 var record = new recordType(values, id);
11039 records[records.length] = record;
11043 totalRecords : records.length
11052 * @class Roo.bootstrap.ComboBox
11053 * @extends Roo.bootstrap.TriggerField
11054 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11055 * @cfg {Boolean} append (true|false) default false
11056 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11057 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11058 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11059 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11060 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11061 * @cfg {Boolean} animate default true
11062 * @cfg {Boolean} emptyResultText only for touch device
11064 * Create a new ComboBox.
11065 * @param {Object} config Configuration options
11067 Roo.bootstrap.ComboBox = function(config){
11068 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11072 * Fires when the dropdown list is expanded
11073 * @param {Roo.bootstrap.ComboBox} combo This combo box
11078 * Fires when the dropdown list is collapsed
11079 * @param {Roo.bootstrap.ComboBox} combo This combo box
11083 * @event beforeselect
11084 * Fires before a list item is selected. Return false to cancel the selection.
11085 * @param {Roo.bootstrap.ComboBox} combo This combo box
11086 * @param {Roo.data.Record} record The data record returned from the underlying store
11087 * @param {Number} index The index of the selected item in the dropdown list
11089 'beforeselect' : true,
11092 * Fires when a list item is selected
11093 * @param {Roo.bootstrap.ComboBox} combo This combo box
11094 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11095 * @param {Number} index The index of the selected item in the dropdown list
11099 * @event beforequery
11100 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11101 * The event object passed has these properties:
11102 * @param {Roo.bootstrap.ComboBox} combo This combo box
11103 * @param {String} query The query
11104 * @param {Boolean} forceAll true to force "all" query
11105 * @param {Boolean} cancel true to cancel the query
11106 * @param {Object} e The query event object
11108 'beforequery': true,
11111 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11112 * @param {Roo.bootstrap.ComboBox} combo This combo box
11117 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11118 * @param {Roo.bootstrap.ComboBox} combo This combo box
11119 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11124 * Fires when the remove value from the combobox array
11125 * @param {Roo.bootstrap.ComboBox} combo This combo box
11129 * @event specialfilter
11130 * Fires when specialfilter
11131 * @param {Roo.bootstrap.ComboBox} combo This combo box
11133 'specialfilter' : true
11138 this.tickItems = [];
11140 this.selectedIndex = -1;
11141 if(this.mode == 'local'){
11142 if(config.queryDelay === undefined){
11143 this.queryDelay = 10;
11145 if(config.minChars === undefined){
11151 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11154 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11155 * rendering into an Roo.Editor, defaults to false)
11158 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11159 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11162 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11165 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11166 * the dropdown list (defaults to undefined, with no header element)
11170 * @cfg {String/Roo.Template} tpl The template to use to render the output
11174 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11176 listWidth: undefined,
11178 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11179 * mode = 'remote' or 'text' if mode = 'local')
11181 displayField: undefined,
11184 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11185 * mode = 'remote' or 'value' if mode = 'local').
11186 * Note: use of a valueField requires the user make a selection
11187 * in order for a value to be mapped.
11189 valueField: undefined,
11193 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11194 * field's data value (defaults to the underlying DOM element's name)
11196 hiddenName: undefined,
11198 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11202 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11204 selectedClass: 'active',
11207 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11211 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11212 * anchor positions (defaults to 'tl-bl')
11214 listAlign: 'tl-bl?',
11216 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11220 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11221 * query specified by the allQuery config option (defaults to 'query')
11223 triggerAction: 'query',
11225 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11226 * (defaults to 4, does not apply if editable = false)
11230 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11231 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11235 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11236 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11240 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11241 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11245 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11246 * when editable = true (defaults to false)
11248 selectOnFocus:false,
11250 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11252 queryParam: 'query',
11254 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11255 * when mode = 'remote' (defaults to 'Loading...')
11257 loadingText: 'Loading...',
11259 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11263 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11267 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11268 * traditional select (defaults to true)
11272 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11276 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11280 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11281 * listWidth has a higher value)
11285 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11286 * allow the user to set arbitrary text into the field (defaults to false)
11288 forceSelection:false,
11290 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11291 * if typeAhead = true (defaults to 250)
11293 typeAheadDelay : 250,
11295 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11296 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11298 valueNotFoundText : undefined,
11300 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11302 blockFocus : false,
11305 * @cfg {Boolean} disableClear Disable showing of clear button.
11307 disableClear : false,
11309 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11311 alwaysQuery : false,
11314 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11319 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11321 invalidClass : "has-warning",
11324 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11326 validClass : "has-success",
11329 * @cfg {Boolean} specialFilter (true|false) special filter default false
11331 specialFilter : false,
11334 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11336 mobileTouchView : true,
11348 btnPosition : 'right',
11349 triggerList : true,
11350 showToggleBtn : true,
11352 emptyResultText: 'Empty',
11353 // element that contains real text value.. (when hidden is used..)
11355 getAutoCreate : function()
11363 if(Roo.isTouch && this.mobileTouchView){
11364 cfg = this.getAutoCreateTouchView();
11371 if(!this.tickable){
11372 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11377 * ComboBox with tickable selections
11380 var align = this.labelAlign || this.parentLabelAlign();
11383 cls : 'form-group roo-combobox-tickable' //input-group
11388 cls : 'tickable-buttons',
11393 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11400 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11407 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11414 buttons.cn.unshift({
11416 cls: 'select2-search-field-input'
11422 Roo.each(buttons.cn, function(c){
11424 c.cls += ' btn-' + _this.size;
11427 if (_this.disabled) {
11438 cls: 'form-hidden-field'
11442 cls: 'select2-choices',
11446 cls: 'select2-search-field',
11458 cls: 'select2-container input-group select2-container-multi',
11463 // cls: 'typeahead typeahead-long dropdown-menu',
11464 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11469 if(this.hasFeedback && !this.allowBlank){
11473 cls: 'glyphicon form-control-feedback'
11476 combobox.cn.push(feedback);
11479 if (align ==='left' && this.fieldLabel.length) {
11481 Roo.log("left and has label");
11487 cls : 'control-label col-sm-' + this.labelWidth,
11488 html : this.fieldLabel
11492 cls : "col-sm-" + (12 - this.labelWidth),
11499 } else if ( this.fieldLabel.length) {
11505 //cls : 'input-group-addon',
11506 html : this.fieldLabel
11516 Roo.log(" no label && no align");
11523 ['xs','sm','md','lg'].map(function(size){
11524 if (settings[size]) {
11525 cfg.cls += ' col-' + size + '-' + settings[size];
11534 initEvents: function()
11538 throw "can not find store for combo";
11541 this.store = Roo.factory(this.store, Roo.data);
11547 if(Roo.isTouch && this.mobileTouchView){
11548 this.initTouchView();
11553 this.initTickableEvents();
11557 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11559 if(this.hiddenName){
11561 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11563 this.hiddenField.dom.value =
11564 this.hiddenValue !== undefined ? this.hiddenValue :
11565 this.value !== undefined ? this.value : '';
11567 // prevent input submission
11568 this.el.dom.removeAttribute('name');
11569 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11574 // this.el.dom.setAttribute('autocomplete', 'off');
11577 var cls = 'x-combo-list';
11579 //this.list = new Roo.Layer({
11580 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11586 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11587 _this.list.setWidth(lw);
11590 this.list.on('mouseover', this.onViewOver, this);
11591 this.list.on('mousemove', this.onViewMove, this);
11593 this.list.on('scroll', this.onViewScroll, this);
11596 this.list.swallowEvent('mousewheel');
11597 this.assetHeight = 0;
11600 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11601 this.assetHeight += this.header.getHeight();
11604 this.innerList = this.list.createChild({cls:cls+'-inner'});
11605 this.innerList.on('mouseover', this.onViewOver, this);
11606 this.innerList.on('mousemove', this.onViewMove, this);
11607 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11609 if(this.allowBlank && !this.pageSize && !this.disableClear){
11610 this.footer = this.list.createChild({cls:cls+'-ft'});
11611 this.pageTb = new Roo.Toolbar(this.footer);
11615 this.footer = this.list.createChild({cls:cls+'-ft'});
11616 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11617 {pageSize: this.pageSize});
11621 if (this.pageTb && this.allowBlank && !this.disableClear) {
11623 this.pageTb.add(new Roo.Toolbar.Fill(), {
11624 cls: 'x-btn-icon x-btn-clear',
11626 handler: function()
11629 _this.clearValue();
11630 _this.onSelect(false, -1);
11635 this.assetHeight += this.footer.getHeight();
11640 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11643 this.view = new Roo.View(this.list, this.tpl, {
11644 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11646 //this.view.wrapEl.setDisplayed(false);
11647 this.view.on('click', this.onViewClick, this);
11651 this.store.on('beforeload', this.onBeforeLoad, this);
11652 this.store.on('load', this.onLoad, this);
11653 this.store.on('loadexception', this.onLoadException, this);
11655 if(this.resizable){
11656 this.resizer = new Roo.Resizable(this.list, {
11657 pinned:true, handles:'se'
11659 this.resizer.on('resize', function(r, w, h){
11660 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11661 this.listWidth = w;
11662 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11663 this.restrictHeight();
11665 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11668 if(!this.editable){
11669 this.editable = true;
11670 this.setEditable(false);
11675 if (typeof(this.events.add.listeners) != 'undefined') {
11677 this.addicon = this.wrap.createChild(
11678 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11680 this.addicon.on('click', function(e) {
11681 this.fireEvent('add', this);
11684 if (typeof(this.events.edit.listeners) != 'undefined') {
11686 this.editicon = this.wrap.createChild(
11687 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11688 if (this.addicon) {
11689 this.editicon.setStyle('margin-left', '40px');
11691 this.editicon.on('click', function(e) {
11693 // we fire even if inothing is selected..
11694 this.fireEvent('edit', this, this.lastData );
11700 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11701 "up" : function(e){
11702 this.inKeyMode = true;
11706 "down" : function(e){
11707 if(!this.isExpanded()){
11708 this.onTriggerClick();
11710 this.inKeyMode = true;
11715 "enter" : function(e){
11716 // this.onViewClick();
11720 if(this.fireEvent("specialkey", this, e)){
11721 this.onViewClick(false);
11727 "esc" : function(e){
11731 "tab" : function(e){
11734 if(this.fireEvent("specialkey", this, e)){
11735 this.onViewClick(false);
11743 doRelay : function(foo, bar, hname){
11744 if(hname == 'down' || this.scope.isExpanded()){
11745 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11754 this.queryDelay = Math.max(this.queryDelay || 10,
11755 this.mode == 'local' ? 10 : 250);
11758 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11760 if(this.typeAhead){
11761 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11763 if(this.editable !== false){
11764 this.inputEl().on("keyup", this.onKeyUp, this);
11766 if(this.forceSelection){
11767 this.inputEl().on('blur', this.doForce, this);
11771 this.choices = this.el.select('ul.select2-choices', true).first();
11772 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11776 initTickableEvents: function()
11780 if(this.hiddenName){
11782 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11784 this.hiddenField.dom.value =
11785 this.hiddenValue !== undefined ? this.hiddenValue :
11786 this.value !== undefined ? this.value : '';
11788 // prevent input submission
11789 this.el.dom.removeAttribute('name');
11790 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11795 // this.list = this.el.select('ul.dropdown-menu',true).first();
11797 this.choices = this.el.select('ul.select2-choices', true).first();
11798 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11799 if(this.triggerList){
11800 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11803 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11804 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11806 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11807 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11809 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11810 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11812 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11813 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11814 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11817 this.cancelBtn.hide();
11822 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11823 _this.list.setWidth(lw);
11826 this.list.on('mouseover', this.onViewOver, this);
11827 this.list.on('mousemove', this.onViewMove, this);
11829 this.list.on('scroll', this.onViewScroll, this);
11832 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>';
11835 this.view = new Roo.View(this.list, this.tpl, {
11836 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11839 //this.view.wrapEl.setDisplayed(false);
11840 this.view.on('click', this.onViewClick, this);
11844 this.store.on('beforeload', this.onBeforeLoad, this);
11845 this.store.on('load', this.onLoad, this);
11846 this.store.on('loadexception', this.onLoadException, this);
11849 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11850 "up" : function(e){
11851 this.inKeyMode = true;
11855 "down" : function(e){
11856 this.inKeyMode = true;
11860 "enter" : function(e){
11861 if(this.fireEvent("specialkey", this, e)){
11862 this.onViewClick(false);
11868 "esc" : function(e){
11869 this.onTickableFooterButtonClick(e, false, false);
11872 "tab" : function(e){
11873 this.fireEvent("specialkey", this, e);
11875 this.onTickableFooterButtonClick(e, false, false);
11882 doRelay : function(e, fn, key){
11883 if(this.scope.isExpanded()){
11884 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11893 this.queryDelay = Math.max(this.queryDelay || 10,
11894 this.mode == 'local' ? 10 : 250);
11897 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11899 if(this.typeAhead){
11900 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11903 if(this.editable !== false){
11904 this.tickableInputEl().on("keyup", this.onKeyUp, this);
11909 onDestroy : function(){
11911 this.view.setStore(null);
11912 this.view.el.removeAllListeners();
11913 this.view.el.remove();
11914 this.view.purgeListeners();
11917 this.list.dom.innerHTML = '';
11921 this.store.un('beforeload', this.onBeforeLoad, this);
11922 this.store.un('load', this.onLoad, this);
11923 this.store.un('loadexception', this.onLoadException, this);
11925 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11929 fireKey : function(e){
11930 if(e.isNavKeyPress() && !this.list.isVisible()){
11931 this.fireEvent("specialkey", this, e);
11936 onResize: function(w, h){
11937 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11939 // if(typeof w != 'number'){
11940 // // we do not handle it!?!?
11943 // var tw = this.trigger.getWidth();
11944 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11945 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11947 // this.inputEl().setWidth( this.adjustWidth('input', x));
11949 // //this.trigger.setStyle('left', x+'px');
11951 // if(this.list && this.listWidth === undefined){
11952 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11953 // this.list.setWidth(lw);
11954 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11962 * Allow or prevent the user from directly editing the field text. If false is passed,
11963 * the user will only be able to select from the items defined in the dropdown list. This method
11964 * is the runtime equivalent of setting the 'editable' config option at config time.
11965 * @param {Boolean} value True to allow the user to directly edit the field text
11967 setEditable : function(value){
11968 if(value == this.editable){
11971 this.editable = value;
11973 this.inputEl().dom.setAttribute('readOnly', true);
11974 this.inputEl().on('mousedown', this.onTriggerClick, this);
11975 this.inputEl().addClass('x-combo-noedit');
11977 this.inputEl().dom.setAttribute('readOnly', false);
11978 this.inputEl().un('mousedown', this.onTriggerClick, this);
11979 this.inputEl().removeClass('x-combo-noedit');
11985 onBeforeLoad : function(combo,opts){
11986 if(!this.hasFocus){
11990 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11992 this.restrictHeight();
11993 this.selectedIndex = -1;
11997 onLoad : function(){
11999 this.hasQuery = false;
12001 if(!this.hasFocus){
12005 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12006 this.loading.hide();
12009 if(this.store.getCount() > 0){
12011 this.restrictHeight();
12012 if(this.lastQuery == this.allQuery){
12013 if(this.editable && !this.tickable){
12014 this.inputEl().dom.select();
12018 !this.selectByValue(this.value, true) &&
12021 !this.store.lastOptions ||
12022 typeof(this.store.lastOptions.add) == 'undefined' ||
12023 this.store.lastOptions.add != true
12026 this.select(0, true);
12029 if(this.autoFocus){
12032 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12033 this.taTask.delay(this.typeAheadDelay);
12037 this.onEmptyResults();
12043 onLoadException : function()
12045 this.hasQuery = false;
12047 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12048 this.loading.hide();
12051 if(this.tickable && this.editable){
12057 Roo.log(this.store.reader.jsonData);
12058 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12060 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12066 onTypeAhead : function(){
12067 if(this.store.getCount() > 0){
12068 var r = this.store.getAt(0);
12069 var newValue = r.data[this.displayField];
12070 var len = newValue.length;
12071 var selStart = this.getRawValue().length;
12073 if(selStart != len){
12074 this.setRawValue(newValue);
12075 this.selectText(selStart, newValue.length);
12081 onSelect : function(record, index){
12083 if(this.fireEvent('beforeselect', this, record, index) !== false){
12085 this.setFromData(index > -1 ? record.data : false);
12088 this.fireEvent('select', this, record, index);
12093 * Returns the currently selected field value or empty string if no value is set.
12094 * @return {String} value The selected value
12096 getValue : function(){
12099 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12102 if(this.valueField){
12103 return typeof this.value != 'undefined' ? this.value : '';
12105 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12110 * Clears any text/value currently set in the field
12112 clearValue : function(){
12113 if(this.hiddenField){
12114 this.hiddenField.dom.value = '';
12117 this.setRawValue('');
12118 this.lastSelectionText = '';
12119 this.lastData = false;
12121 var close = this.closeTriggerEl();
12130 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12131 * will be displayed in the field. If the value does not match the data value of an existing item,
12132 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12133 * Otherwise the field will be blank (although the value will still be set).
12134 * @param {String} value The value to match
12136 setValue : function(v){
12143 if(this.valueField){
12144 var r = this.findRecord(this.valueField, v);
12146 text = r.data[this.displayField];
12147 }else if(this.valueNotFoundText !== undefined){
12148 text = this.valueNotFoundText;
12151 this.lastSelectionText = text;
12152 if(this.hiddenField){
12153 this.hiddenField.dom.value = v;
12155 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12158 var close = this.closeTriggerEl();
12161 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12165 * @property {Object} the last set data for the element
12170 * Sets the value of the field based on a object which is related to the record format for the store.
12171 * @param {Object} value the value to set as. or false on reset?
12173 setFromData : function(o){
12180 var dv = ''; // display value
12181 var vv = ''; // value value..
12183 if (this.displayField) {
12184 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12186 // this is an error condition!!!
12187 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12190 if(this.valueField){
12191 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12194 var close = this.closeTriggerEl();
12197 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12200 if(this.hiddenField){
12201 this.hiddenField.dom.value = vv;
12203 this.lastSelectionText = dv;
12204 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12208 // no hidden field.. - we store the value in 'value', but still display
12209 // display field!!!!
12210 this.lastSelectionText = dv;
12211 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12218 reset : function(){
12219 // overridden so that last data is reset..
12226 this.setValue(this.originalValue);
12227 this.clearInvalid();
12228 this.lastData = false;
12230 this.view.clearSelections();
12234 findRecord : function(prop, value){
12236 if(this.store.getCount() > 0){
12237 this.store.each(function(r){
12238 if(r.data[prop] == value){
12248 getName: function()
12250 // returns hidden if it's set..
12251 if (!this.rendered) {return ''};
12252 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12256 onViewMove : function(e, t){
12257 this.inKeyMode = false;
12261 onViewOver : function(e, t){
12262 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12265 var item = this.view.findItemFromChild(t);
12268 var index = this.view.indexOf(item);
12269 this.select(index, false);
12274 onViewClick : function(view, doFocus, el, e)
12276 var index = this.view.getSelectedIndexes()[0];
12278 var r = this.store.getAt(index);
12282 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12289 Roo.each(this.tickItems, function(v,k){
12291 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12292 _this.tickItems.splice(k, 1);
12294 if(typeof(e) == 'undefined' && view == false){
12295 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12307 this.tickItems.push(r.data);
12309 if(typeof(e) == 'undefined' && view == false){
12310 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12317 this.onSelect(r, index);
12319 if(doFocus !== false && !this.blockFocus){
12320 this.inputEl().focus();
12325 restrictHeight : function(){
12326 //this.innerList.dom.style.height = '';
12327 //var inner = this.innerList.dom;
12328 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12329 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12330 //this.list.beginUpdate();
12331 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12332 this.list.alignTo(this.inputEl(), this.listAlign);
12333 this.list.alignTo(this.inputEl(), this.listAlign);
12334 //this.list.endUpdate();
12338 onEmptyResults : function(){
12340 if(this.tickable && this.editable){
12341 this.restrictHeight();
12349 * Returns true if the dropdown list is expanded, else false.
12351 isExpanded : function(){
12352 return this.list.isVisible();
12356 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12357 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12358 * @param {String} value The data value of the item to select
12359 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12360 * selected item if it is not currently in view (defaults to true)
12361 * @return {Boolean} True if the value matched an item in the list, else false
12363 selectByValue : function(v, scrollIntoView){
12364 if(v !== undefined && v !== null){
12365 var r = this.findRecord(this.valueField || this.displayField, v);
12367 this.select(this.store.indexOf(r), scrollIntoView);
12375 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12376 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12377 * @param {Number} index The zero-based index of the list item to select
12378 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12379 * selected item if it is not currently in view (defaults to true)
12381 select : function(index, scrollIntoView){
12382 this.selectedIndex = index;
12383 this.view.select(index);
12384 if(scrollIntoView !== false){
12385 var el = this.view.getNode(index);
12387 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12390 this.list.scrollChildIntoView(el, false);
12396 selectNext : function(){
12397 var ct = this.store.getCount();
12399 if(this.selectedIndex == -1){
12401 }else if(this.selectedIndex < ct-1){
12402 this.select(this.selectedIndex+1);
12408 selectPrev : function(){
12409 var ct = this.store.getCount();
12411 if(this.selectedIndex == -1){
12413 }else if(this.selectedIndex != 0){
12414 this.select(this.selectedIndex-1);
12420 onKeyUp : function(e){
12421 if(this.editable !== false && !e.isSpecialKey()){
12422 this.lastKey = e.getKey();
12423 this.dqTask.delay(this.queryDelay);
12428 validateBlur : function(){
12429 return !this.list || !this.list.isVisible();
12433 initQuery : function(){
12435 var v = this.getRawValue();
12437 if(this.tickable && this.editable){
12438 v = this.tickableInputEl().getValue();
12445 doForce : function(){
12446 if(this.inputEl().dom.value.length > 0){
12447 this.inputEl().dom.value =
12448 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12454 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12455 * query allowing the query action to be canceled if needed.
12456 * @param {String} query The SQL query to execute
12457 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12458 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12459 * saved in the current store (defaults to false)
12461 doQuery : function(q, forceAll){
12463 if(q === undefined || q === null){
12468 forceAll: forceAll,
12472 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12477 forceAll = qe.forceAll;
12478 if(forceAll === true || (q.length >= this.minChars)){
12480 this.hasQuery = true;
12482 if(this.lastQuery != q || this.alwaysQuery){
12483 this.lastQuery = q;
12484 if(this.mode == 'local'){
12485 this.selectedIndex = -1;
12487 this.store.clearFilter();
12490 if(this.specialFilter){
12491 this.fireEvent('specialfilter', this);
12496 this.store.filter(this.displayField, q);
12499 this.store.fireEvent("datachanged", this.store);
12506 this.store.baseParams[this.queryParam] = q;
12508 var options = {params : this.getParams(q)};
12511 options.add = true;
12512 options.params.start = this.page * this.pageSize;
12515 this.store.load(options);
12518 * this code will make the page width larger, at the beginning, the list not align correctly,
12519 * we should expand the list on onLoad
12520 * so command out it
12525 this.selectedIndex = -1;
12530 this.loadNext = false;
12534 getParams : function(q){
12536 //p[this.queryParam] = q;
12540 p.limit = this.pageSize;
12546 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12548 collapse : function(){
12549 if(!this.isExpanded()){
12556 this.hasFocus = false;
12558 this.cancelBtn.hide();
12559 this.trigger.show();
12562 this.tickableInputEl().dom.value = '';
12563 this.tickableInputEl().blur();
12568 Roo.get(document).un('mousedown', this.collapseIf, this);
12569 Roo.get(document).un('mousewheel', this.collapseIf, this);
12570 if (!this.editable) {
12571 Roo.get(document).un('keydown', this.listKeyPress, this);
12573 this.fireEvent('collapse', this);
12577 collapseIf : function(e){
12578 var in_combo = e.within(this.el);
12579 var in_list = e.within(this.list);
12580 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12582 if (in_combo || in_list || is_list) {
12583 //e.stopPropagation();
12588 this.onTickableFooterButtonClick(e, false, false);
12596 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12598 expand : function(){
12600 if(this.isExpanded() || !this.hasFocus){
12604 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12605 this.list.setWidth(lw);
12612 this.restrictHeight();
12616 this.tickItems = Roo.apply([], this.item);
12619 this.cancelBtn.show();
12620 this.trigger.hide();
12623 this.tickableInputEl().focus();
12628 Roo.get(document).on('mousedown', this.collapseIf, this);
12629 Roo.get(document).on('mousewheel', this.collapseIf, this);
12630 if (!this.editable) {
12631 Roo.get(document).on('keydown', this.listKeyPress, this);
12634 this.fireEvent('expand', this);
12638 // Implements the default empty TriggerField.onTriggerClick function
12639 onTriggerClick : function(e)
12641 Roo.log('trigger click');
12643 if(this.disabled || !this.triggerList){
12648 this.loadNext = false;
12650 if(this.isExpanded()){
12652 if (!this.blockFocus) {
12653 this.inputEl().focus();
12657 this.hasFocus = true;
12658 if(this.triggerAction == 'all') {
12659 this.doQuery(this.allQuery, true);
12661 this.doQuery(this.getRawValue());
12663 if (!this.blockFocus) {
12664 this.inputEl().focus();
12669 onTickableTriggerClick : function(e)
12676 this.loadNext = false;
12677 this.hasFocus = true;
12679 if(this.triggerAction == 'all') {
12680 this.doQuery(this.allQuery, true);
12682 this.doQuery(this.getRawValue());
12686 onSearchFieldClick : function(e)
12688 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12689 this.onTickableFooterButtonClick(e, false, false);
12693 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12698 this.loadNext = false;
12699 this.hasFocus = true;
12701 if(this.triggerAction == 'all') {
12702 this.doQuery(this.allQuery, true);
12704 this.doQuery(this.getRawValue());
12708 listKeyPress : function(e)
12710 //Roo.log('listkeypress');
12711 // scroll to first matching element based on key pres..
12712 if (e.isSpecialKey()) {
12715 var k = String.fromCharCode(e.getKey()).toUpperCase();
12718 var csel = this.view.getSelectedNodes();
12719 var cselitem = false;
12721 var ix = this.view.indexOf(csel[0]);
12722 cselitem = this.store.getAt(ix);
12723 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12729 this.store.each(function(v) {
12731 // start at existing selection.
12732 if (cselitem.id == v.id) {
12738 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12739 match = this.store.indexOf(v);
12745 if (match === false) {
12746 return true; // no more action?
12749 this.view.select(match);
12750 var sn = Roo.get(this.view.getSelectedNodes()[0])
12751 sn.scrollIntoView(sn.dom.parentNode, false);
12754 onViewScroll : function(e, t){
12756 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){
12760 this.hasQuery = true;
12762 this.loading = this.list.select('.loading', true).first();
12764 if(this.loading === null){
12765 this.list.createChild({
12767 cls: 'loading select2-more-results select2-active',
12768 html: 'Loading more results...'
12771 this.loading = this.list.select('.loading', true).first();
12773 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12775 this.loading.hide();
12778 this.loading.show();
12783 this.loadNext = true;
12785 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12790 addItem : function(o)
12792 var dv = ''; // display value
12794 if (this.displayField) {
12795 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12797 // this is an error condition!!!
12798 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12805 var choice = this.choices.createChild({
12807 cls: 'select2-search-choice',
12816 cls: 'select2-search-choice-close',
12821 }, this.searchField);
12823 var close = choice.select('a.select2-search-choice-close', true).first()
12825 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12833 this.inputEl().dom.value = '';
12838 onRemoveItem : function(e, _self, o)
12840 e.preventDefault();
12842 this.lastItem = Roo.apply([], this.item);
12844 var index = this.item.indexOf(o.data) * 1;
12847 Roo.log('not this item?!');
12851 this.item.splice(index, 1);
12856 this.fireEvent('remove', this, e);
12862 syncValue : function()
12864 if(!this.item.length){
12871 Roo.each(this.item, function(i){
12872 if(_this.valueField){
12873 value.push(i[_this.valueField]);
12880 this.value = value.join(',');
12882 if(this.hiddenField){
12883 this.hiddenField.dom.value = this.value;
12886 this.store.fireEvent("datachanged", this.store);
12889 clearItem : function()
12891 if(!this.multiple){
12897 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12906 inputEl: function ()
12908 if(Roo.isTouch && this.mobileTouchView){
12909 return this.el.select('input.form-control',true).first();
12913 return this.searchField;
12916 return this.el.select('input.form-control',true).first();
12920 onTickableFooterButtonClick : function(e, btn, el)
12922 e.preventDefault();
12924 this.lastItem = Roo.apply([], this.item);
12926 if(btn && btn.name == 'cancel'){
12927 this.tickItems = Roo.apply([], this.item);
12936 Roo.each(this.tickItems, function(o){
12944 validate : function()
12946 var v = this.getRawValue();
12949 v = this.getValue();
12952 if(this.disabled || this.allowBlank || v.length){
12957 this.markInvalid();
12961 tickableInputEl : function()
12963 if(!this.tickable || !this.editable){
12964 return this.inputEl();
12967 return this.inputEl().select('.select2-search-field-input', true).first();
12971 getAutoCreateTouchView : function()
12976 cls: 'form-group' //input-group
12982 type : this.inputType,
12983 cls : 'form-control x-combo-noedit',
12984 autocomplete: 'new-password',
12985 placeholder : this.placeholder || '',
12990 input.name = this.name;
12994 input.cls += ' input-' + this.size;
12997 if (this.disabled) {
12998 input.disabled = true;
13009 inputblock.cls += ' input-group';
13011 inputblock.cn.unshift({
13013 cls : 'input-group-addon',
13018 if(this.removable && !this.multiple){
13019 inputblock.cls += ' roo-removable';
13021 inputblock.cn.push({
13024 cls : 'roo-combo-removable-btn close'
13028 if(this.hasFeedback && !this.allowBlank){
13030 inputblock.cls += ' has-feedback';
13032 inputblock.cn.push({
13034 cls: 'glyphicon form-control-feedback'
13041 inputblock.cls += (this.before) ? '' : ' input-group';
13043 inputblock.cn.push({
13045 cls : 'input-group-addon',
13056 cls: 'form-hidden-field'
13070 cls: 'form-hidden-field'
13074 cls: 'select2-choices',
13078 cls: 'select2-search-field',
13091 cls: 'select2-container input-group',
13098 combobox.cls += ' select2-container-multi';
13101 var align = this.labelAlign || this.parentLabelAlign();
13105 if(this.fieldLabel.length){
13107 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13108 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13113 cls : 'control-label ' + lw,
13114 html : this.fieldLabel
13126 var settings = this;
13128 ['xs','sm','md','lg'].map(function(size){
13129 if (settings[size]) {
13130 cfg.cls += ' col-' + size + '-' + settings[size];
13137 initTouchView : function()
13139 this.renderTouchView();
13141 this.touchViewEl.on('scroll', function(){
13142 this.el.dom.scrollTop = 0;
13145 this.inputEl().on("click", this.showTouchView, this);
13146 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13147 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13149 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13151 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13152 this.store.on('load', this.onTouchViewLoad, this);
13153 this.store.on('loadexception', this.onTouchViewLoadException, this);
13155 if(this.hiddenName){
13157 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13159 this.hiddenField.dom.value =
13160 this.hiddenValue !== undefined ? this.hiddenValue :
13161 this.value !== undefined ? this.value : '';
13163 this.el.dom.removeAttribute('name');
13164 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13168 this.choices = this.el.select('ul.select2-choices', true).first();
13169 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13172 if(this.removable && !this.multiple){
13173 var close = this.closeTriggerEl();
13175 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13176 close.on('click', this.removeBtnClick, this, close);
13185 renderTouchView : function()
13187 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13188 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13190 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13191 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13193 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13194 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13195 this.touchViewBodyEl.setStyle('overflow', 'auto');
13197 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13198 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13200 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13201 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13205 showTouchView : function()
13207 this.touchViewHeaderEl.hide();
13209 if(this.fieldLabel.length){
13210 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13211 this.touchViewHeaderEl.show();
13214 this.touchViewEl.show();
13216 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13217 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13219 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13221 if(this.fieldLabel.length){
13222 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13225 this.touchViewBodyEl.setHeight(bodyHeight);
13229 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13231 this.touchViewEl.addClass('in');
13234 this.doTouchViewQuery();
13238 hideTouchView : function()
13240 this.touchViewEl.removeClass('in');
13244 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13246 this.touchViewEl.setStyle('display', 'none');
13251 setTouchViewValue : function()
13258 Roo.each(this.tickItems, function(o){
13263 this.hideTouchView();
13266 doTouchViewQuery : function()
13275 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13279 if(!this.alwaysQuery || this.mode == 'local'){
13280 this.onTouchViewLoad();
13287 onTouchViewBeforeLoad : function(combo,opts)
13293 onTouchViewLoad : function()
13295 if(this.store.getCount() < 1){
13296 this.onTouchViewEmptyResults();
13300 this.clearTouchView();
13302 var rawValue = this.getRawValue();
13304 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13306 this.tickItems = [];
13308 this.store.data.each(function(d, rowIndex){
13309 var row = this.touchViewListGroup.createChild(template);
13311 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13312 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13315 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13316 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13319 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13320 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13321 this.tickItems.push(d.data);
13324 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13328 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13330 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13332 if(this.fieldLabel.length){
13333 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13336 var listHeight = this.touchViewListGroup.getHeight();
13338 if(firstChecked && listHeight > bodyHeight){
13339 (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13344 onTouchViewLoadException : function()
13346 this.hideTouchView();
13349 onTouchViewEmptyResults : function()
13351 this.clearTouchView();
13353 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13355 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13359 clearTouchView : function()
13361 this.touchViewListGroup.dom.innerHTML = '';
13364 onTouchViewClick : function(e, el, o)
13366 e.preventDefault();
13369 var rowIndex = o.rowIndex;
13371 var r = this.store.getAt(rowIndex);
13373 if(!this.multiple){
13374 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13375 c.dom.removeAttribute('checked');
13378 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13380 this.setFromData(r.data);
13382 var close = this.closeTriggerEl();
13388 this.hideTouchView();
13390 this.fireEvent('select', this, r, rowIndex);
13395 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13396 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13397 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13401 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13402 this.addItem(r.data);
13403 this.tickItems.push(r.data);
13409 * @cfg {Boolean} grow
13413 * @cfg {Number} growMin
13417 * @cfg {Number} growMax
13426 Roo.apply(Roo.bootstrap.ComboBox, {
13430 cls: 'modal-header',
13452 cls: 'list-group-item',
13456 cls: 'roo-combobox-list-group-item-value'
13460 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13474 listItemCheckbox : {
13476 cls: 'list-group-item',
13480 cls: 'roo-combobox-list-group-item-value'
13484 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13500 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13505 cls: 'modal-footer',
13513 cls: 'col-xs-6 text-left',
13516 cls: 'btn btn-danger roo-touch-view-cancel',
13522 cls: 'col-xs-6 text-right',
13525 cls: 'btn btn-success roo-touch-view-ok',
13536 Roo.apply(Roo.bootstrap.ComboBox, {
13538 touchViewTemplate : {
13540 cls: 'modal fade roo-combobox-touch-view',
13544 cls: 'modal-dialog',
13548 cls: 'modal-content',
13550 Roo.bootstrap.ComboBox.header,
13551 Roo.bootstrap.ComboBox.body,
13552 Roo.bootstrap.ComboBox.footer
13561 * Ext JS Library 1.1.1
13562 * Copyright(c) 2006-2007, Ext JS, LLC.
13564 * Originally Released Under LGPL - original licence link has changed is not relivant.
13567 * <script type="text/javascript">
13572 * @extends Roo.util.Observable
13573 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13574 * This class also supports single and multi selection modes. <br>
13575 * Create a data model bound view:
13577 var store = new Roo.data.Store(...);
13579 var view = new Roo.View({
13581 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13583 singleSelect: true,
13584 selectedClass: "ydataview-selected",
13588 // listen for node click?
13589 view.on("click", function(vw, index, node, e){
13590 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13594 dataModel.load("foobar.xml");
13596 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13598 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13599 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13601 * Note: old style constructor is still suported (container, template, config)
13604 * Create a new View
13605 * @param {Object} config The config object
13608 Roo.View = function(config, depreciated_tpl, depreciated_config){
13610 this.parent = false;
13612 if (typeof(depreciated_tpl) == 'undefined') {
13613 // new way.. - universal constructor.
13614 Roo.apply(this, config);
13615 this.el = Roo.get(this.el);
13618 this.el = Roo.get(config);
13619 this.tpl = depreciated_tpl;
13620 Roo.apply(this, depreciated_config);
13622 this.wrapEl = this.el.wrap().wrap();
13623 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13626 if(typeof(this.tpl) == "string"){
13627 this.tpl = new Roo.Template(this.tpl);
13629 // support xtype ctors..
13630 this.tpl = new Roo.factory(this.tpl, Roo);
13634 this.tpl.compile();
13639 * @event beforeclick
13640 * Fires before a click is processed. Returns false to cancel the default action.
13641 * @param {Roo.View} this
13642 * @param {Number} index The index of the target node
13643 * @param {HTMLElement} node The target node
13644 * @param {Roo.EventObject} e The raw event object
13646 "beforeclick" : true,
13649 * Fires when a template node is clicked.
13650 * @param {Roo.View} this
13651 * @param {Number} index The index of the target node
13652 * @param {HTMLElement} node The target node
13653 * @param {Roo.EventObject} e The raw event object
13658 * Fires when a template node is double clicked.
13659 * @param {Roo.View} this
13660 * @param {Number} index The index of the target node
13661 * @param {HTMLElement} node The target node
13662 * @param {Roo.EventObject} e The raw event object
13666 * @event contextmenu
13667 * Fires when a template node is right clicked.
13668 * @param {Roo.View} this
13669 * @param {Number} index The index of the target node
13670 * @param {HTMLElement} node The target node
13671 * @param {Roo.EventObject} e The raw event object
13673 "contextmenu" : true,
13675 * @event selectionchange
13676 * Fires when the selected nodes change.
13677 * @param {Roo.View} this
13678 * @param {Array} selections Array of the selected nodes
13680 "selectionchange" : true,
13683 * @event beforeselect
13684 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13685 * @param {Roo.View} this
13686 * @param {HTMLElement} node The node to be selected
13687 * @param {Array} selections Array of currently selected nodes
13689 "beforeselect" : true,
13691 * @event preparedata
13692 * Fires on every row to render, to allow you to change the data.
13693 * @param {Roo.View} this
13694 * @param {Object} data to be rendered (change this)
13696 "preparedata" : true
13704 "click": this.onClick,
13705 "dblclick": this.onDblClick,
13706 "contextmenu": this.onContextMenu,
13710 this.selections = [];
13712 this.cmp = new Roo.CompositeElementLite([]);
13714 this.store = Roo.factory(this.store, Roo.data);
13715 this.setStore(this.store, true);
13718 if ( this.footer && this.footer.xtype) {
13720 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13722 this.footer.dataSource = this.store
13723 this.footer.container = fctr;
13724 this.footer = Roo.factory(this.footer, Roo);
13725 fctr.insertFirst(this.el);
13727 // this is a bit insane - as the paging toolbar seems to detach the el..
13728 // dom.parentNode.parentNode.parentNode
13729 // they get detached?
13733 Roo.View.superclass.constructor.call(this);
13738 Roo.extend(Roo.View, Roo.util.Observable, {
13741 * @cfg {Roo.data.Store} store Data store to load data from.
13746 * @cfg {String|Roo.Element} el The container element.
13751 * @cfg {String|Roo.Template} tpl The template used by this View
13755 * @cfg {String} dataName the named area of the template to use as the data area
13756 * Works with domtemplates roo-name="name"
13760 * @cfg {String} selectedClass The css class to add to selected nodes
13762 selectedClass : "x-view-selected",
13764 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13769 * @cfg {String} text to display on mask (default Loading)
13773 * @cfg {Boolean} multiSelect Allow multiple selection
13775 multiSelect : false,
13777 * @cfg {Boolean} singleSelect Allow single selection
13779 singleSelect: false,
13782 * @cfg {Boolean} toggleSelect - selecting
13784 toggleSelect : false,
13787 * @cfg {Boolean} tickable - selecting
13792 * Returns the element this view is bound to.
13793 * @return {Roo.Element}
13795 getEl : function(){
13796 return this.wrapEl;
13802 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13804 refresh : function(){
13805 //Roo.log('refresh');
13808 // if we are using something like 'domtemplate', then
13809 // the what gets used is:
13810 // t.applySubtemplate(NAME, data, wrapping data..)
13811 // the outer template then get' applied with
13812 // the store 'extra data'
13813 // and the body get's added to the
13814 // roo-name="data" node?
13815 // <span class='roo-tpl-{name}'></span> ?????
13819 this.clearSelections();
13820 this.el.update("");
13822 var records = this.store.getRange();
13823 if(records.length < 1) {
13825 // is this valid?? = should it render a template??
13827 this.el.update(this.emptyText);
13831 if (this.dataName) {
13832 this.el.update(t.apply(this.store.meta)); //????
13833 el = this.el.child('.roo-tpl-' + this.dataName);
13836 for(var i = 0, len = records.length; i < len; i++){
13837 var data = this.prepareData(records[i].data, i, records[i]);
13838 this.fireEvent("preparedata", this, data, i, records[i]);
13840 var d = Roo.apply({}, data);
13843 Roo.apply(d, {'roo-id' : Roo.id()});
13847 Roo.each(this.parent.item, function(item){
13848 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13851 Roo.apply(d, {'roo-data-checked' : 'checked'});
13855 html[html.length] = Roo.util.Format.trim(
13857 t.applySubtemplate(this.dataName, d, this.store.meta) :
13864 el.update(html.join(""));
13865 this.nodes = el.dom.childNodes;
13866 this.updateIndexes(0);
13871 * Function to override to reformat the data that is sent to
13872 * the template for each node.
13873 * DEPRICATED - use the preparedata event handler.
13874 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13875 * a JSON object for an UpdateManager bound view).
13877 prepareData : function(data, index, record)
13879 this.fireEvent("preparedata", this, data, index, record);
13883 onUpdate : function(ds, record){
13884 // Roo.log('on update');
13885 this.clearSelections();
13886 var index = this.store.indexOf(record);
13887 var n = this.nodes[index];
13888 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13889 n.parentNode.removeChild(n);
13890 this.updateIndexes(index, index);
13896 onAdd : function(ds, records, index)
13898 //Roo.log(['on Add', ds, records, index] );
13899 this.clearSelections();
13900 if(this.nodes.length == 0){
13904 var n = this.nodes[index];
13905 for(var i = 0, len = records.length; i < len; i++){
13906 var d = this.prepareData(records[i].data, i, records[i]);
13908 this.tpl.insertBefore(n, d);
13911 this.tpl.append(this.el, d);
13914 this.updateIndexes(index);
13917 onRemove : function(ds, record, index){
13918 // Roo.log('onRemove');
13919 this.clearSelections();
13920 var el = this.dataName ?
13921 this.el.child('.roo-tpl-' + this.dataName) :
13924 el.dom.removeChild(this.nodes[index]);
13925 this.updateIndexes(index);
13929 * Refresh an individual node.
13930 * @param {Number} index
13932 refreshNode : function(index){
13933 this.onUpdate(this.store, this.store.getAt(index));
13936 updateIndexes : function(startIndex, endIndex){
13937 var ns = this.nodes;
13938 startIndex = startIndex || 0;
13939 endIndex = endIndex || ns.length - 1;
13940 for(var i = startIndex; i <= endIndex; i++){
13941 ns[i].nodeIndex = i;
13946 * Changes the data store this view uses and refresh the view.
13947 * @param {Store} store
13949 setStore : function(store, initial){
13950 if(!initial && this.store){
13951 this.store.un("datachanged", this.refresh);
13952 this.store.un("add", this.onAdd);
13953 this.store.un("remove", this.onRemove);
13954 this.store.un("update", this.onUpdate);
13955 this.store.un("clear", this.refresh);
13956 this.store.un("beforeload", this.onBeforeLoad);
13957 this.store.un("load", this.onLoad);
13958 this.store.un("loadexception", this.onLoad);
13962 store.on("datachanged", this.refresh, this);
13963 store.on("add", this.onAdd, this);
13964 store.on("remove", this.onRemove, this);
13965 store.on("update", this.onUpdate, this);
13966 store.on("clear", this.refresh, this);
13967 store.on("beforeload", this.onBeforeLoad, this);
13968 store.on("load", this.onLoad, this);
13969 store.on("loadexception", this.onLoad, this);
13977 * onbeforeLoad - masks the loading area.
13980 onBeforeLoad : function(store,opts)
13982 //Roo.log('onBeforeLoad');
13984 this.el.update("");
13986 this.el.mask(this.mask ? this.mask : "Loading" );
13988 onLoad : function ()
13995 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13996 * @param {HTMLElement} node
13997 * @return {HTMLElement} The template node
13999 findItemFromChild : function(node){
14000 var el = this.dataName ?
14001 this.el.child('.roo-tpl-' + this.dataName,true) :
14004 if(!node || node.parentNode == el){
14007 var p = node.parentNode;
14008 while(p && p != el){
14009 if(p.parentNode == el){
14018 onClick : function(e){
14019 var item = this.findItemFromChild(e.getTarget());
14021 var index = this.indexOf(item);
14022 if(this.onItemClick(item, index, e) !== false){
14023 this.fireEvent("click", this, index, item, e);
14026 this.clearSelections();
14031 onContextMenu : function(e){
14032 var item = this.findItemFromChild(e.getTarget());
14034 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14039 onDblClick : function(e){
14040 var item = this.findItemFromChild(e.getTarget());
14042 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14046 onItemClick : function(item, index, e)
14048 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14051 if (this.toggleSelect) {
14052 var m = this.isSelected(item) ? 'unselect' : 'select';
14055 _t[m](item, true, false);
14058 if(this.multiSelect || this.singleSelect){
14059 if(this.multiSelect && e.shiftKey && this.lastSelection){
14060 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14062 this.select(item, this.multiSelect && e.ctrlKey);
14063 this.lastSelection = item;
14066 if(!this.tickable){
14067 e.preventDefault();
14075 * Get the number of selected nodes.
14078 getSelectionCount : function(){
14079 return this.selections.length;
14083 * Get the currently selected nodes.
14084 * @return {Array} An array of HTMLElements
14086 getSelectedNodes : function(){
14087 return this.selections;
14091 * Get the indexes of the selected nodes.
14094 getSelectedIndexes : function(){
14095 var indexes = [], s = this.selections;
14096 for(var i = 0, len = s.length; i < len; i++){
14097 indexes.push(s[i].nodeIndex);
14103 * Clear all selections
14104 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14106 clearSelections : function(suppressEvent){
14107 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14108 this.cmp.elements = this.selections;
14109 this.cmp.removeClass(this.selectedClass);
14110 this.selections = [];
14111 if(!suppressEvent){
14112 this.fireEvent("selectionchange", this, this.selections);
14118 * Returns true if the passed node is selected
14119 * @param {HTMLElement/Number} node The node or node index
14120 * @return {Boolean}
14122 isSelected : function(node){
14123 var s = this.selections;
14127 node = this.getNode(node);
14128 return s.indexOf(node) !== -1;
14133 * @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
14134 * @param {Boolean} keepExisting (optional) true to keep existing selections
14135 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14137 select : function(nodeInfo, keepExisting, suppressEvent){
14138 if(nodeInfo instanceof Array){
14140 this.clearSelections(true);
14142 for(var i = 0, len = nodeInfo.length; i < len; i++){
14143 this.select(nodeInfo[i], true, true);
14147 var node = this.getNode(nodeInfo);
14148 if(!node || this.isSelected(node)){
14149 return; // already selected.
14152 this.clearSelections(true);
14155 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14156 Roo.fly(node).addClass(this.selectedClass);
14157 this.selections.push(node);
14158 if(!suppressEvent){
14159 this.fireEvent("selectionchange", this, this.selections);
14167 * @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
14168 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14169 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14171 unselect : function(nodeInfo, keepExisting, suppressEvent)
14173 if(nodeInfo instanceof Array){
14174 Roo.each(this.selections, function(s) {
14175 this.unselect(s, nodeInfo);
14179 var node = this.getNode(nodeInfo);
14180 if(!node || !this.isSelected(node)){
14181 //Roo.log("not selected");
14182 return; // not selected.
14186 Roo.each(this.selections, function(s) {
14188 Roo.fly(node).removeClass(this.selectedClass);
14195 this.selections= ns;
14196 this.fireEvent("selectionchange", this, this.selections);
14200 * Gets a template node.
14201 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14202 * @return {HTMLElement} The node or null if it wasn't found
14204 getNode : function(nodeInfo){
14205 if(typeof nodeInfo == "string"){
14206 return document.getElementById(nodeInfo);
14207 }else if(typeof nodeInfo == "number"){
14208 return this.nodes[nodeInfo];
14214 * Gets a range template nodes.
14215 * @param {Number} startIndex
14216 * @param {Number} endIndex
14217 * @return {Array} An array of nodes
14219 getNodes : function(start, end){
14220 var ns = this.nodes;
14221 start = start || 0;
14222 end = typeof end == "undefined" ? ns.length - 1 : end;
14225 for(var i = start; i <= end; i++){
14229 for(var i = start; i >= end; i--){
14237 * Finds the index of the passed node
14238 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14239 * @return {Number} The index of the node or -1
14241 indexOf : function(node){
14242 node = this.getNode(node);
14243 if(typeof node.nodeIndex == "number"){
14244 return node.nodeIndex;
14246 var ns = this.nodes;
14247 for(var i = 0, len = ns.length; i < len; i++){
14258 * based on jquery fullcalendar
14262 Roo.bootstrap = Roo.bootstrap || {};
14264 * @class Roo.bootstrap.Calendar
14265 * @extends Roo.bootstrap.Component
14266 * Bootstrap Calendar class
14267 * @cfg {Boolean} loadMask (true|false) default false
14268 * @cfg {Object} header generate the user specific header of the calendar, default false
14271 * Create a new Container
14272 * @param {Object} config The config object
14277 Roo.bootstrap.Calendar = function(config){
14278 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14282 * Fires when a date is selected
14283 * @param {DatePicker} this
14284 * @param {Date} date The selected date
14288 * @event monthchange
14289 * Fires when the displayed month changes
14290 * @param {DatePicker} this
14291 * @param {Date} date The selected month
14293 'monthchange': true,
14295 * @event evententer
14296 * Fires when mouse over an event
14297 * @param {Calendar} this
14298 * @param {event} Event
14300 'evententer': true,
14302 * @event eventleave
14303 * Fires when the mouse leaves an
14304 * @param {Calendar} this
14307 'eventleave': true,
14309 * @event eventclick
14310 * Fires when the mouse click an
14311 * @param {Calendar} this
14320 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14323 * @cfg {Number} startDay
14324 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14332 getAutoCreate : function(){
14335 var fc_button = function(name, corner, style, content ) {
14336 return Roo.apply({},{
14338 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14340 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14343 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14354 style : 'width:100%',
14361 cls : 'fc-header-left',
14363 fc_button('prev', 'left', 'arrow', '‹' ),
14364 fc_button('next', 'right', 'arrow', '›' ),
14365 { tag: 'span', cls: 'fc-header-space' },
14366 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14374 cls : 'fc-header-center',
14378 cls: 'fc-header-title',
14381 html : 'month / year'
14389 cls : 'fc-header-right',
14391 /* fc_button('month', 'left', '', 'month' ),
14392 fc_button('week', '', '', 'week' ),
14393 fc_button('day', 'right', '', 'day' )
14405 header = this.header;
14408 var cal_heads = function() {
14410 // fixme - handle this.
14412 for (var i =0; i < Date.dayNames.length; i++) {
14413 var d = Date.dayNames[i];
14416 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14417 html : d.substring(0,3)
14421 ret[0].cls += ' fc-first';
14422 ret[6].cls += ' fc-last';
14425 var cal_cell = function(n) {
14428 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14433 cls: 'fc-day-number',
14437 cls: 'fc-day-content',
14441 style: 'position: relative;' // height: 17px;
14453 var cal_rows = function() {
14456 for (var r = 0; r < 6; r++) {
14463 for (var i =0; i < Date.dayNames.length; i++) {
14464 var d = Date.dayNames[i];
14465 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14468 row.cn[0].cls+=' fc-first';
14469 row.cn[0].cn[0].style = 'min-height:90px';
14470 row.cn[6].cls+=' fc-last';
14474 ret[0].cls += ' fc-first';
14475 ret[4].cls += ' fc-prev-last';
14476 ret[5].cls += ' fc-last';
14483 cls: 'fc-border-separate',
14484 style : 'width:100%',
14492 cls : 'fc-first fc-last',
14510 cls : 'fc-content',
14511 style : "position: relative;",
14514 cls : 'fc-view fc-view-month fc-grid',
14515 style : 'position: relative',
14516 unselectable : 'on',
14519 cls : 'fc-event-container',
14520 style : 'position:absolute;z-index:8;top:0;left:0;'
14538 initEvents : function()
14541 throw "can not find store for calendar";
14547 style: "text-align:center",
14551 style: "background-color:white;width:50%;margin:250 auto",
14555 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14566 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14568 var size = this.el.select('.fc-content', true).first().getSize();
14569 this.maskEl.setSize(size.width, size.height);
14570 this.maskEl.enableDisplayMode("block");
14571 if(!this.loadMask){
14572 this.maskEl.hide();
14575 this.store = Roo.factory(this.store, Roo.data);
14576 this.store.on('load', this.onLoad, this);
14577 this.store.on('beforeload', this.onBeforeLoad, this);
14581 this.cells = this.el.select('.fc-day',true);
14582 //Roo.log(this.cells);
14583 this.textNodes = this.el.query('.fc-day-number');
14584 this.cells.addClassOnOver('fc-state-hover');
14586 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14587 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14588 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14589 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14591 this.on('monthchange', this.onMonthChange, this);
14593 this.update(new Date().clearTime());
14596 resize : function() {
14597 var sz = this.el.getSize();
14599 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14600 this.el.select('.fc-day-content div',true).setHeight(34);
14605 showPrevMonth : function(e){
14606 this.update(this.activeDate.add("mo", -1));
14608 showToday : function(e){
14609 this.update(new Date().clearTime());
14612 showNextMonth : function(e){
14613 this.update(this.activeDate.add("mo", 1));
14617 showPrevYear : function(){
14618 this.update(this.activeDate.add("y", -1));
14622 showNextYear : function(){
14623 this.update(this.activeDate.add("y", 1));
14628 update : function(date)
14630 var vd = this.activeDate;
14631 this.activeDate = date;
14632 // if(vd && this.el){
14633 // var t = date.getTime();
14634 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14635 // Roo.log('using add remove');
14637 // this.fireEvent('monthchange', this, date);
14639 // this.cells.removeClass("fc-state-highlight");
14640 // this.cells.each(function(c){
14641 // if(c.dateValue == t){
14642 // c.addClass("fc-state-highlight");
14643 // setTimeout(function(){
14644 // try{c.dom.firstChild.focus();}catch(e){}
14654 var days = date.getDaysInMonth();
14656 var firstOfMonth = date.getFirstDateOfMonth();
14657 var startingPos = firstOfMonth.getDay()-this.startDay;
14659 if(startingPos < this.startDay){
14663 var pm = date.add(Date.MONTH, -1);
14664 var prevStart = pm.getDaysInMonth()-startingPos;
14666 this.cells = this.el.select('.fc-day',true);
14667 this.textNodes = this.el.query('.fc-day-number');
14668 this.cells.addClassOnOver('fc-state-hover');
14670 var cells = this.cells.elements;
14671 var textEls = this.textNodes;
14673 Roo.each(cells, function(cell){
14674 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14677 days += startingPos;
14679 // convert everything to numbers so it's fast
14680 var day = 86400000;
14681 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14684 //Roo.log(prevStart);
14686 var today = new Date().clearTime().getTime();
14687 var sel = date.clearTime().getTime();
14688 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14689 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14690 var ddMatch = this.disabledDatesRE;
14691 var ddText = this.disabledDatesText;
14692 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14693 var ddaysText = this.disabledDaysText;
14694 var format = this.format;
14696 var setCellClass = function(cal, cell){
14700 //Roo.log('set Cell Class');
14702 var t = d.getTime();
14706 cell.dateValue = t;
14708 cell.className += " fc-today";
14709 cell.className += " fc-state-highlight";
14710 cell.title = cal.todayText;
14713 // disable highlight in other month..
14714 //cell.className += " fc-state-highlight";
14719 cell.className = " fc-state-disabled";
14720 cell.title = cal.minText;
14724 cell.className = " fc-state-disabled";
14725 cell.title = cal.maxText;
14729 if(ddays.indexOf(d.getDay()) != -1){
14730 cell.title = ddaysText;
14731 cell.className = " fc-state-disabled";
14734 if(ddMatch && format){
14735 var fvalue = d.dateFormat(format);
14736 if(ddMatch.test(fvalue)){
14737 cell.title = ddText.replace("%0", fvalue);
14738 cell.className = " fc-state-disabled";
14742 if (!cell.initialClassName) {
14743 cell.initialClassName = cell.dom.className;
14746 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14751 for(; i < startingPos; i++) {
14752 textEls[i].innerHTML = (++prevStart);
14753 d.setDate(d.getDate()+1);
14755 cells[i].className = "fc-past fc-other-month";
14756 setCellClass(this, cells[i]);
14761 for(; i < days; i++){
14762 intDay = i - startingPos + 1;
14763 textEls[i].innerHTML = (intDay);
14764 d.setDate(d.getDate()+1);
14766 cells[i].className = ''; // "x-date-active";
14767 setCellClass(this, cells[i]);
14771 for(; i < 42; i++) {
14772 textEls[i].innerHTML = (++extraDays);
14773 d.setDate(d.getDate()+1);
14775 cells[i].className = "fc-future fc-other-month";
14776 setCellClass(this, cells[i]);
14779 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14781 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14783 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14784 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14786 if(totalRows != 6){
14787 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14788 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14791 this.fireEvent('monthchange', this, date);
14795 if(!this.internalRender){
14796 var main = this.el.dom.firstChild;
14797 var w = main.offsetWidth;
14798 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14799 Roo.fly(main).setWidth(w);
14800 this.internalRender = true;
14801 // opera does not respect the auto grow header center column
14802 // then, after it gets a width opera refuses to recalculate
14803 // without a second pass
14804 if(Roo.isOpera && !this.secondPass){
14805 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14806 this.secondPass = true;
14807 this.update.defer(10, this, [date]);
14814 findCell : function(dt) {
14815 dt = dt.clearTime().getTime();
14817 this.cells.each(function(c){
14818 //Roo.log("check " +c.dateValue + '?=' + dt);
14819 if(c.dateValue == dt){
14829 findCells : function(ev) {
14830 var s = ev.start.clone().clearTime().getTime();
14832 var e= ev.end.clone().clearTime().getTime();
14835 this.cells.each(function(c){
14836 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14838 if(c.dateValue > e){
14841 if(c.dateValue < s){
14850 // findBestRow: function(cells)
14854 // for (var i =0 ; i < cells.length;i++) {
14855 // ret = Math.max(cells[i].rows || 0,ret);
14862 addItem : function(ev)
14864 // look for vertical location slot in
14865 var cells = this.findCells(ev);
14867 // ev.row = this.findBestRow(cells);
14869 // work out the location.
14873 for(var i =0; i < cells.length; i++) {
14875 cells[i].row = cells[0].row;
14878 cells[i].row = cells[i].row + 1;
14888 if (crow.start.getY() == cells[i].getY()) {
14890 crow.end = cells[i];
14907 cells[0].events.push(ev);
14909 this.calevents.push(ev);
14912 clearEvents: function() {
14914 if(!this.calevents){
14918 Roo.each(this.cells.elements, function(c){
14924 Roo.each(this.calevents, function(e) {
14925 Roo.each(e.els, function(el) {
14926 el.un('mouseenter' ,this.onEventEnter, this);
14927 el.un('mouseleave' ,this.onEventLeave, this);
14932 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14938 renderEvents: function()
14942 this.cells.each(function(c) {
14951 if(c.row != c.events.length){
14952 r = 4 - (4 - (c.row - c.events.length));
14955 c.events = ev.slice(0, r);
14956 c.more = ev.slice(r);
14958 if(c.more.length && c.more.length == 1){
14959 c.events.push(c.more.pop());
14962 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14966 this.cells.each(function(c) {
14968 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14971 for (var e = 0; e < c.events.length; e++){
14972 var ev = c.events[e];
14973 var rows = ev.rows;
14975 for(var i = 0; i < rows.length; i++) {
14977 // how many rows should it span..
14980 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14981 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14983 unselectable : "on",
14986 cls: 'fc-event-inner',
14990 // cls: 'fc-event-time',
14991 // html : cells.length > 1 ? '' : ev.time
14995 cls: 'fc-event-title',
14996 html : String.format('{0}', ev.title)
15003 cls: 'ui-resizable-handle ui-resizable-e',
15004 html : '  '
15011 cfg.cls += ' fc-event-start';
15013 if ((i+1) == rows.length) {
15014 cfg.cls += ' fc-event-end';
15017 var ctr = _this.el.select('.fc-event-container',true).first();
15018 var cg = ctr.createChild(cfg);
15020 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15021 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15023 var r = (c.more.length) ? 1 : 0;
15024 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15025 cg.setWidth(ebox.right - sbox.x -2);
15027 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15028 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15029 cg.on('click', _this.onEventClick, _this, ev);
15040 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15041 style : 'position: absolute',
15042 unselectable : "on",
15045 cls: 'fc-event-inner',
15049 cls: 'fc-event-title',
15057 cls: 'ui-resizable-handle ui-resizable-e',
15058 html : '  '
15064 var ctr = _this.el.select('.fc-event-container',true).first();
15065 var cg = ctr.createChild(cfg);
15067 var sbox = c.select('.fc-day-content',true).first().getBox();
15068 var ebox = c.select('.fc-day-content',true).first().getBox();
15070 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15071 cg.setWidth(ebox.right - sbox.x -2);
15073 cg.on('click', _this.onMoreEventClick, _this, c.more);
15083 onEventEnter: function (e, el,event,d) {
15084 this.fireEvent('evententer', this, el, event);
15087 onEventLeave: function (e, el,event,d) {
15088 this.fireEvent('eventleave', this, el, event);
15091 onEventClick: function (e, el,event,d) {
15092 this.fireEvent('eventclick', this, el, event);
15095 onMonthChange: function () {
15099 onMoreEventClick: function(e, el, more)
15103 this.calpopover.placement = 'right';
15104 this.calpopover.setTitle('More');
15106 this.calpopover.setContent('');
15108 var ctr = this.calpopover.el.select('.popover-content', true).first();
15110 Roo.each(more, function(m){
15112 cls : 'fc-event-hori fc-event-draggable',
15115 var cg = ctr.createChild(cfg);
15117 cg.on('click', _this.onEventClick, _this, m);
15120 this.calpopover.show(el);
15125 onLoad: function ()
15127 this.calevents = [];
15130 if(this.store.getCount() > 0){
15131 this.store.data.each(function(d){
15134 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15135 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15136 time : d.data.start_time,
15137 title : d.data.title,
15138 description : d.data.description,
15139 venue : d.data.venue
15144 this.renderEvents();
15146 if(this.calevents.length && this.loadMask){
15147 this.maskEl.hide();
15151 onBeforeLoad: function()
15153 this.clearEvents();
15155 this.maskEl.show();
15169 * @class Roo.bootstrap.Popover
15170 * @extends Roo.bootstrap.Component
15171 * Bootstrap Popover class
15172 * @cfg {String} html contents of the popover (or false to use children..)
15173 * @cfg {String} title of popover (or false to hide)
15174 * @cfg {String} placement how it is placed
15175 * @cfg {String} trigger click || hover (or false to trigger manually)
15176 * @cfg {String} over what (parent or false to trigger manually.)
15177 * @cfg {Number} delay - delay before showing
15180 * Create a new Popover
15181 * @param {Object} config The config object
15184 Roo.bootstrap.Popover = function(config){
15185 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15188 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15190 title: 'Fill in a title',
15193 placement : 'right',
15194 trigger : 'hover', // hover
15200 can_build_overlaid : false,
15202 getChildContainer : function()
15204 return this.el.select('.popover-content',true).first();
15207 getAutoCreate : function(){
15208 Roo.log('make popover?');
15210 cls : 'popover roo-dynamic',
15211 style: 'display:block',
15217 cls : 'popover-inner',
15221 cls: 'popover-title',
15225 cls : 'popover-content',
15236 setTitle: function(str)
15239 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15241 setContent: function(str)
15244 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15246 // as it get's added to the bottom of the page.
15247 onRender : function(ct, position)
15249 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15251 var cfg = Roo.apply({}, this.getAutoCreate());
15255 cfg.cls += ' ' + this.cls;
15258 cfg.style = this.style;
15260 Roo.log("adding to ")
15261 this.el = Roo.get(document.body).createChild(cfg, position);
15267 initEvents : function()
15269 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15270 this.el.enableDisplayMode('block');
15272 if (this.over === false) {
15275 if (this.triggers === false) {
15278 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15279 var triggers = this.trigger ? this.trigger.split(' ') : [];
15280 Roo.each(triggers, function(trigger) {
15282 if (trigger == 'click') {
15283 on_el.on('click', this.toggle, this);
15284 } else if (trigger != 'manual') {
15285 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15286 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15288 on_el.on(eventIn ,this.enter, this);
15289 on_el.on(eventOut, this.leave, this);
15300 toggle : function () {
15301 this.hoverState == 'in' ? this.leave() : this.enter();
15304 enter : function () {
15307 clearTimeout(this.timeout);
15309 this.hoverState = 'in';
15311 if (!this.delay || !this.delay.show) {
15316 this.timeout = setTimeout(function () {
15317 if (_t.hoverState == 'in') {
15320 }, this.delay.show)
15322 leave : function() {
15323 clearTimeout(this.timeout);
15325 this.hoverState = 'out';
15327 if (!this.delay || !this.delay.hide) {
15332 this.timeout = setTimeout(function () {
15333 if (_t.hoverState == 'out') {
15336 }, this.delay.hide)
15339 show : function (on_el)
15342 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15345 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15346 if (this.html !== false) {
15347 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15349 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15350 if (!this.title.length) {
15351 this.el.select('.popover-title',true).hide();
15354 var placement = typeof this.placement == 'function' ?
15355 this.placement.call(this, this.el, on_el) :
15358 var autoToken = /\s?auto?\s?/i;
15359 var autoPlace = autoToken.test(placement);
15361 placement = placement.replace(autoToken, '') || 'top';
15365 //this.el.setXY([0,0]);
15367 this.el.dom.style.display='block';
15368 this.el.addClass(placement);
15370 //this.el.appendTo(on_el);
15372 var p = this.getPosition();
15373 var box = this.el.getBox();
15378 var align = Roo.bootstrap.Popover.alignment[placement];
15379 this.el.alignTo(on_el, align[0],align[1]);
15380 //var arrow = this.el.select('.arrow',true).first();
15381 //arrow.set(align[2],
15383 this.el.addClass('in');
15386 if (this.el.hasClass('fade')) {
15393 this.el.setXY([0,0]);
15394 this.el.removeClass('in');
15396 this.hoverState = null;
15402 Roo.bootstrap.Popover.alignment = {
15403 'left' : ['r-l', [-10,0], 'right'],
15404 'right' : ['l-r', [10,0], 'left'],
15405 'bottom' : ['t-b', [0,10], 'top'],
15406 'top' : [ 'b-t', [0,-10], 'bottom']
15417 * @class Roo.bootstrap.Progress
15418 * @extends Roo.bootstrap.Component
15419 * Bootstrap Progress class
15420 * @cfg {Boolean} striped striped of the progress bar
15421 * @cfg {Boolean} active animated of the progress bar
15425 * Create a new Progress
15426 * @param {Object} config The config object
15429 Roo.bootstrap.Progress = function(config){
15430 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15433 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15438 getAutoCreate : function(){
15446 cfg.cls += ' progress-striped';
15450 cfg.cls += ' active';
15469 * @class Roo.bootstrap.ProgressBar
15470 * @extends Roo.bootstrap.Component
15471 * Bootstrap ProgressBar class
15472 * @cfg {Number} aria_valuenow aria-value now
15473 * @cfg {Number} aria_valuemin aria-value min
15474 * @cfg {Number} aria_valuemax aria-value max
15475 * @cfg {String} label label for the progress bar
15476 * @cfg {String} panel (success | info | warning | danger )
15477 * @cfg {String} role role of the progress bar
15478 * @cfg {String} sr_only text
15482 * Create a new ProgressBar
15483 * @param {Object} config The config object
15486 Roo.bootstrap.ProgressBar = function(config){
15487 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15490 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15494 aria_valuemax : 100,
15500 getAutoCreate : function()
15505 cls: 'progress-bar',
15506 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15518 cfg.role = this.role;
15521 if(this.aria_valuenow){
15522 cfg['aria-valuenow'] = this.aria_valuenow;
15525 if(this.aria_valuemin){
15526 cfg['aria-valuemin'] = this.aria_valuemin;
15529 if(this.aria_valuemax){
15530 cfg['aria-valuemax'] = this.aria_valuemax;
15533 if(this.label && !this.sr_only){
15534 cfg.html = this.label;
15538 cfg.cls += ' progress-bar-' + this.panel;
15544 update : function(aria_valuenow)
15546 this.aria_valuenow = aria_valuenow;
15548 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15563 * @class Roo.bootstrap.TabGroup
15564 * @extends Roo.bootstrap.Column
15565 * Bootstrap Column class
15566 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15567 * @cfg {Boolean} carousel true to make the group behave like a carousel
15568 * @cfg {Number} bullets show the panel pointer.. default 0
15569 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15570 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15571 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15574 * Create a new TabGroup
15575 * @param {Object} config The config object
15578 Roo.bootstrap.TabGroup = function(config){
15579 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15581 this.navId = Roo.id();
15584 Roo.bootstrap.TabGroup.register(this);
15588 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15591 transition : false,
15596 slideOnTouch : false,
15598 getAutoCreate : function()
15600 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15602 cfg.cls += ' tab-content';
15604 Roo.log('get auto create...............');
15606 if (this.carousel) {
15607 cfg.cls += ' carousel slide';
15610 cls : 'carousel-inner'
15613 if(this.bullets > 0 && !Roo.isTouch){
15616 cls : 'carousel-bullets',
15620 if(this.bullets_cls){
15621 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15624 for (var i = 0; i < this.bullets; i++){
15626 cls : 'bullet bullet-' + i
15634 cfg.cn[0].cn = bullets;
15641 initEvents: function()
15643 Roo.log('-------- init events on tab group ---------');
15645 if(this.bullets > 0 && !Roo.isTouch){
15651 if(Roo.isTouch && this.slideOnTouch){
15652 this.el.on("touchstart", this.onTouchStart, this);
15655 if(this.autoslide){
15658 this.slideFn = window.setInterval(function() {
15659 _this.showPanelNext();
15665 onTouchStart : function(e, el, o)
15667 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15671 this.showPanelNext();
15674 getChildContainer : function()
15676 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15680 * register a Navigation item
15681 * @param {Roo.bootstrap.NavItem} the navitem to add
15683 register : function(item)
15685 this.tabs.push( item);
15686 item.navId = this.navId; // not really needed..
15690 getActivePanel : function()
15693 Roo.each(this.tabs, function(t) {
15703 getPanelByName : function(n)
15706 Roo.each(this.tabs, function(t) {
15707 if (t.tabId == n) {
15715 indexOfPanel : function(p)
15718 Roo.each(this.tabs, function(t,i) {
15719 if (t.tabId == p.tabId) {
15728 * show a specific panel
15729 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15730 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15732 showPanel : function (pan)
15734 if(this.transition){
15735 Roo.log("waiting for the transitionend");
15739 if (typeof(pan) == 'number') {
15740 pan = this.tabs[pan];
15742 if (typeof(pan) == 'string') {
15743 pan = this.getPanelByName(pan);
15745 if (pan.tabId == this.getActivePanel().tabId) {
15748 var cur = this.getActivePanel();
15750 if (false === cur.fireEvent('beforedeactivate')) {
15754 if(this.bullets > 0 && !Roo.isTouch){
15755 this.setActiveBullet(this.indexOfPanel(pan));
15758 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15760 this.transition = true;
15761 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15762 var lr = dir == 'next' ? 'left' : 'right';
15763 pan.el.addClass(dir); // or prev
15764 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15765 cur.el.addClass(lr); // or right
15766 pan.el.addClass(lr);
15769 cur.el.on('transitionend', function() {
15770 Roo.log("trans end?");
15772 pan.el.removeClass([lr,dir]);
15773 pan.setActive(true);
15775 cur.el.removeClass([lr]);
15776 cur.setActive(false);
15778 _this.transition = false;
15780 }, this, { single: true } );
15785 cur.setActive(false);
15786 pan.setActive(true);
15791 showPanelNext : function()
15793 var i = this.indexOfPanel(this.getActivePanel());
15795 if (i >= this.tabs.length - 1 && !this.autoslide) {
15799 if (i >= this.tabs.length - 1 && this.autoslide) {
15803 this.showPanel(this.tabs[i+1]);
15806 showPanelPrev : function()
15808 var i = this.indexOfPanel(this.getActivePanel());
15810 if (i < 1 && !this.autoslide) {
15814 if (i < 1 && this.autoslide) {
15815 i = this.tabs.length;
15818 this.showPanel(this.tabs[i-1]);
15821 initBullet : function()
15829 for (var i = 0; i < this.bullets; i++){
15830 var bullet = this.el.select('.bullet-' + i, true).first();
15836 bullet.on('click', (function(e, el, o, ii, t){
15838 e.preventDefault();
15840 _this.showPanel(ii);
15842 if(_this.autoslide && _this.slideFn){
15843 clearInterval(_this.slideFn);
15844 _this.slideFn = window.setInterval(function() {
15845 _this.showPanelNext();
15849 }).createDelegate(this, [i, bullet], true));
15853 setActiveBullet : function(i)
15859 Roo.each(this.el.select('.bullet', true).elements, function(el){
15860 el.removeClass('selected');
15863 var bullet = this.el.select('.bullet-' + i, true).first();
15869 bullet.addClass('selected');
15880 Roo.apply(Roo.bootstrap.TabGroup, {
15884 * register a Navigation Group
15885 * @param {Roo.bootstrap.NavGroup} the navgroup to add
15887 register : function(navgrp)
15889 this.groups[navgrp.navId] = navgrp;
15893 * fetch a Navigation Group based on the navigation ID
15894 * if one does not exist , it will get created.
15895 * @param {string} the navgroup to add
15896 * @returns {Roo.bootstrap.NavGroup} the navgroup
15898 get: function(navId) {
15899 if (typeof(this.groups[navId]) == 'undefined') {
15900 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15902 return this.groups[navId] ;
15917 * @class Roo.bootstrap.TabPanel
15918 * @extends Roo.bootstrap.Component
15919 * Bootstrap TabPanel class
15920 * @cfg {Boolean} active panel active
15921 * @cfg {String} html panel content
15922 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15923 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15927 * Create a new TabPanel
15928 * @param {Object} config The config object
15931 Roo.bootstrap.TabPanel = function(config){
15932 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15936 * Fires when the active status changes
15937 * @param {Roo.bootstrap.TabPanel} this
15938 * @param {Boolean} state the new state
15943 * @event beforedeactivate
15944 * Fires before a tab is de-activated - can be used to do validation on a form.
15945 * @param {Roo.bootstrap.TabPanel} this
15946 * @return {Boolean} false if there is an error
15949 'beforedeactivate': true
15952 this.tabId = this.tabId || Roo.id();
15956 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
15963 getAutoCreate : function(){
15966 // item is needed for carousel - not sure if it has any effect otherwise
15967 cls: 'tab-pane item',
15968 html: this.html || ''
15972 cfg.cls += ' active';
15976 cfg.tabId = this.tabId;
15983 initEvents: function()
15985 Roo.log('-------- init events on tab panel ---------');
15987 var p = this.parent();
15988 this.navId = this.navId || p.navId;
15990 if (typeof(this.navId) != 'undefined') {
15991 // not really needed.. but just in case.. parent should be a NavGroup.
15992 var tg = Roo.bootstrap.TabGroup.get(this.navId);
15993 Roo.log(['register', tg, this]);
15996 var i = tg.tabs.length - 1;
15998 if(this.active && tg.bullets > 0 && i < tg.bullets){
15999 tg.setActiveBullet(i);
16006 onRender : function(ct, position)
16008 // Roo.log("Call onRender: " + this.xtype);
16010 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16018 setActive: function(state)
16020 Roo.log("panel - set active " + this.tabId + "=" + state);
16022 this.active = state;
16024 this.el.removeClass('active');
16026 } else if (!this.el.hasClass('active')) {
16027 this.el.addClass('active');
16030 this.fireEvent('changed', this, state);
16047 * @class Roo.bootstrap.DateField
16048 * @extends Roo.bootstrap.Input
16049 * Bootstrap DateField class
16050 * @cfg {Number} weekStart default 0
16051 * @cfg {String} viewMode default empty, (months|years)
16052 * @cfg {String} minViewMode default empty, (months|years)
16053 * @cfg {Number} startDate default -Infinity
16054 * @cfg {Number} endDate default Infinity
16055 * @cfg {Boolean} todayHighlight default false
16056 * @cfg {Boolean} todayBtn default false
16057 * @cfg {Boolean} calendarWeeks default false
16058 * @cfg {Object} daysOfWeekDisabled default empty
16059 * @cfg {Boolean} singleMode default false (true | false)
16061 * @cfg {Boolean} keyboardNavigation default true
16062 * @cfg {String} language default en
16065 * Create a new DateField
16066 * @param {Object} config The config object
16069 Roo.bootstrap.DateField = function(config){
16070 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16074 * Fires when this field show.
16075 * @param {Roo.bootstrap.DateField} this
16076 * @param {Mixed} date The date value
16081 * Fires when this field hide.
16082 * @param {Roo.bootstrap.DateField} this
16083 * @param {Mixed} date The date value
16088 * Fires when select a date.
16089 * @param {Roo.bootstrap.DateField} this
16090 * @param {Mixed} date The date value
16096 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16099 * @cfg {String} format
16100 * The default date format string which can be overriden for localization support. The format must be
16101 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16105 * @cfg {String} altFormats
16106 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16107 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16109 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16117 todayHighlight : false,
16123 keyboardNavigation: true,
16125 calendarWeeks: false,
16127 startDate: -Infinity,
16131 daysOfWeekDisabled: [],
16135 singleMode : false,
16137 UTCDate: function()
16139 return new Date(Date.UTC.apply(Date, arguments));
16142 UTCToday: function()
16144 var today = new Date();
16145 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16148 getDate: function() {
16149 var d = this.getUTCDate();
16150 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16153 getUTCDate: function() {
16157 setDate: function(d) {
16158 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16161 setUTCDate: function(d) {
16163 this.setValue(this.formatDate(this.date));
16166 onRender: function(ct, position)
16169 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16171 this.language = this.language || 'en';
16172 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16173 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16175 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16176 this.format = this.format || 'm/d/y';
16177 this.isInline = false;
16178 this.isInput = true;
16179 this.component = this.el.select('.add-on', true).first() || false;
16180 this.component = (this.component && this.component.length === 0) ? false : this.component;
16181 this.hasInput = this.component && this.inputEL().length;
16183 if (typeof(this.minViewMode === 'string')) {
16184 switch (this.minViewMode) {
16186 this.minViewMode = 1;
16189 this.minViewMode = 2;
16192 this.minViewMode = 0;
16197 if (typeof(this.viewMode === 'string')) {
16198 switch (this.viewMode) {
16211 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16213 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16215 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16217 this.picker().on('mousedown', this.onMousedown, this);
16218 this.picker().on('click', this.onClick, this);
16220 this.picker().addClass('datepicker-dropdown');
16222 this.startViewMode = this.viewMode;
16224 if(this.singleMode){
16225 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16226 v.setVisibilityMode(Roo.Element.DISPLAY)
16230 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16231 v.setStyle('width', '189px');
16235 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16236 if(!this.calendarWeeks){
16241 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16242 v.attr('colspan', function(i, val){
16243 return parseInt(val) + 1;
16248 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16250 this.setStartDate(this.startDate);
16251 this.setEndDate(this.endDate);
16253 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16260 if(this.isInline) {
16265 picker : function()
16267 return this.pickerEl;
16268 // return this.el.select('.datepicker', true).first();
16271 fillDow: function()
16273 var dowCnt = this.weekStart;
16282 if(this.calendarWeeks){
16290 while (dowCnt < this.weekStart + 7) {
16294 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16298 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16301 fillMonths: function()
16304 var months = this.picker().select('>.datepicker-months td', true).first();
16306 months.dom.innerHTML = '';
16312 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16315 months.createChild(month);
16322 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;
16324 if (this.date < this.startDate) {
16325 this.viewDate = new Date(this.startDate);
16326 } else if (this.date > this.endDate) {
16327 this.viewDate = new Date(this.endDate);
16329 this.viewDate = new Date(this.date);
16337 var d = new Date(this.viewDate),
16338 year = d.getUTCFullYear(),
16339 month = d.getUTCMonth(),
16340 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16341 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16342 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16343 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16344 currentDate = this.date && this.date.valueOf(),
16345 today = this.UTCToday();
16347 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16349 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16351 // this.picker.select('>tfoot th.today').
16352 // .text(dates[this.language].today)
16353 // .toggle(this.todayBtn !== false);
16355 this.updateNavArrows();
16358 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16360 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16362 prevMonth.setUTCDate(day);
16364 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16366 var nextMonth = new Date(prevMonth);
16368 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16370 nextMonth = nextMonth.valueOf();
16372 var fillMonths = false;
16374 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16376 while(prevMonth.valueOf() < nextMonth) {
16379 if (prevMonth.getUTCDay() === this.weekStart) {
16381 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16389 if(this.calendarWeeks){
16390 // ISO 8601: First week contains first thursday.
16391 // ISO also states week starts on Monday, but we can be more abstract here.
16393 // Start of current week: based on weekstart/current date
16394 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16395 // Thursday of this week
16396 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16397 // First Thursday of year, year from thursday
16398 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16399 // Calendar week: ms between thursdays, div ms per day, div 7 days
16400 calWeek = (th - yth) / 864e5 / 7 + 1;
16402 fillMonths.cn.push({
16410 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16412 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16415 if (this.todayHighlight &&
16416 prevMonth.getUTCFullYear() == today.getFullYear() &&
16417 prevMonth.getUTCMonth() == today.getMonth() &&
16418 prevMonth.getUTCDate() == today.getDate()) {
16419 clsName += ' today';
16422 if (currentDate && prevMonth.valueOf() === currentDate) {
16423 clsName += ' active';
16426 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16427 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16428 clsName += ' disabled';
16431 fillMonths.cn.push({
16433 cls: 'day ' + clsName,
16434 html: prevMonth.getDate()
16437 prevMonth.setDate(prevMonth.getDate()+1);
16440 var currentYear = this.date && this.date.getUTCFullYear();
16441 var currentMonth = this.date && this.date.getUTCMonth();
16443 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16445 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16446 v.removeClass('active');
16448 if(currentYear === year && k === currentMonth){
16449 v.addClass('active');
16452 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16453 v.addClass('disabled');
16459 year = parseInt(year/10, 10) * 10;
16461 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16463 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16466 for (var i = -1; i < 11; i++) {
16467 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16469 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16477 showMode: function(dir)
16480 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16483 Roo.each(this.picker().select('>div',true).elements, function(v){
16484 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16487 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16492 if(this.isInline) return;
16494 this.picker().removeClass(['bottom', 'top']);
16496 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16498 * place to the top of element!
16502 this.picker().addClass('top');
16503 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16508 this.picker().addClass('bottom');
16510 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16513 parseDate : function(value)
16515 if(!value || value instanceof Date){
16518 var v = Date.parseDate(value, this.format);
16519 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16520 v = Date.parseDate(value, 'Y-m-d');
16522 if(!v && this.altFormats){
16523 if(!this.altFormatsArray){
16524 this.altFormatsArray = this.altFormats.split("|");
16526 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16527 v = Date.parseDate(value, this.altFormatsArray[i]);
16533 formatDate : function(date, fmt)
16535 return (!date || !(date instanceof Date)) ?
16536 date : date.dateFormat(fmt || this.format);
16539 onFocus : function()
16541 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16545 onBlur : function()
16547 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16549 var d = this.inputEl().getValue();
16558 this.picker().show();
16562 this.fireEvent('show', this, this.date);
16567 if(this.isInline) return;
16568 this.picker().hide();
16569 this.viewMode = this.startViewMode;
16572 this.fireEvent('hide', this, this.date);
16576 onMousedown: function(e)
16578 e.stopPropagation();
16579 e.preventDefault();
16584 Roo.bootstrap.DateField.superclass.keyup.call(this);
16588 setValue: function(v)
16591 // v can be a string or a date..
16594 var d = new Date(this.parseDate(v) ).clearTime();
16596 if(isNaN(d.getTime())){
16597 this.date = this.viewDate = '';
16598 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16602 v = this.formatDate(d);
16604 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16606 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16610 this.fireEvent('select', this, this.date);
16614 getValue: function()
16616 return this.formatDate(this.date);
16619 fireKey: function(e)
16621 if (!this.picker().isVisible()){
16622 if (e.keyCode == 27) // allow escape to hide and re-show picker
16627 var dateChanged = false,
16629 newDate, newViewDate;
16634 e.preventDefault();
16638 if (!this.keyboardNavigation) break;
16639 dir = e.keyCode == 37 ? -1 : 1;
16642 newDate = this.moveYear(this.date, dir);
16643 newViewDate = this.moveYear(this.viewDate, dir);
16644 } else if (e.shiftKey){
16645 newDate = this.moveMonth(this.date, dir);
16646 newViewDate = this.moveMonth(this.viewDate, dir);
16648 newDate = new Date(this.date);
16649 newDate.setUTCDate(this.date.getUTCDate() + dir);
16650 newViewDate = new Date(this.viewDate);
16651 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16653 if (this.dateWithinRange(newDate)){
16654 this.date = newDate;
16655 this.viewDate = newViewDate;
16656 this.setValue(this.formatDate(this.date));
16658 e.preventDefault();
16659 dateChanged = true;
16664 if (!this.keyboardNavigation) break;
16665 dir = e.keyCode == 38 ? -1 : 1;
16667 newDate = this.moveYear(this.date, dir);
16668 newViewDate = this.moveYear(this.viewDate, dir);
16669 } else if (e.shiftKey){
16670 newDate = this.moveMonth(this.date, dir);
16671 newViewDate = this.moveMonth(this.viewDate, dir);
16673 newDate = new Date(this.date);
16674 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16675 newViewDate = new Date(this.viewDate);
16676 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16678 if (this.dateWithinRange(newDate)){
16679 this.date = newDate;
16680 this.viewDate = newViewDate;
16681 this.setValue(this.formatDate(this.date));
16683 e.preventDefault();
16684 dateChanged = true;
16688 this.setValue(this.formatDate(this.date));
16690 e.preventDefault();
16693 this.setValue(this.formatDate(this.date));
16707 onClick: function(e)
16709 e.stopPropagation();
16710 e.preventDefault();
16712 var target = e.getTarget();
16714 if(target.nodeName.toLowerCase() === 'i'){
16715 target = Roo.get(target).dom.parentNode;
16718 var nodeName = target.nodeName;
16719 var className = target.className;
16720 var html = target.innerHTML;
16721 //Roo.log(nodeName);
16723 switch(nodeName.toLowerCase()) {
16725 switch(className) {
16731 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16732 switch(this.viewMode){
16734 this.viewDate = this.moveMonth(this.viewDate, dir);
16738 this.viewDate = this.moveYear(this.viewDate, dir);
16744 var date = new Date();
16745 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16747 this.setValue(this.formatDate(this.date));
16754 if (className.indexOf('disabled') < 0) {
16755 this.viewDate.setUTCDate(1);
16756 if (className.indexOf('month') > -1) {
16757 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16759 var year = parseInt(html, 10) || 0;
16760 this.viewDate.setUTCFullYear(year);
16764 if(this.singleMode){
16765 this.setValue(this.formatDate(this.viewDate));
16776 //Roo.log(className);
16777 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16778 var day = parseInt(html, 10) || 1;
16779 var year = this.viewDate.getUTCFullYear(),
16780 month = this.viewDate.getUTCMonth();
16782 if (className.indexOf('old') > -1) {
16789 } else if (className.indexOf('new') > -1) {
16797 //Roo.log([year,month,day]);
16798 this.date = this.UTCDate(year, month, day,0,0,0,0);
16799 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16801 //Roo.log(this.formatDate(this.date));
16802 this.setValue(this.formatDate(this.date));
16809 setStartDate: function(startDate)
16811 this.startDate = startDate || -Infinity;
16812 if (this.startDate !== -Infinity) {
16813 this.startDate = this.parseDate(this.startDate);
16816 this.updateNavArrows();
16819 setEndDate: function(endDate)
16821 this.endDate = endDate || Infinity;
16822 if (this.endDate !== Infinity) {
16823 this.endDate = this.parseDate(this.endDate);
16826 this.updateNavArrows();
16829 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16831 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16832 if (typeof(this.daysOfWeekDisabled) !== 'object') {
16833 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16835 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16836 return parseInt(d, 10);
16839 this.updateNavArrows();
16842 updateNavArrows: function()
16844 if(this.singleMode){
16848 var d = new Date(this.viewDate),
16849 year = d.getUTCFullYear(),
16850 month = d.getUTCMonth();
16852 Roo.each(this.picker().select('.prev', true).elements, function(v){
16854 switch (this.viewMode) {
16857 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16863 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16870 Roo.each(this.picker().select('.next', true).elements, function(v){
16872 switch (this.viewMode) {
16875 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16881 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16889 moveMonth: function(date, dir)
16891 if (!dir) return date;
16892 var new_date = new Date(date.valueOf()),
16893 day = new_date.getUTCDate(),
16894 month = new_date.getUTCMonth(),
16895 mag = Math.abs(dir),
16897 dir = dir > 0 ? 1 : -1;
16900 // If going back one month, make sure month is not current month
16901 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16903 return new_date.getUTCMonth() == month;
16905 // If going forward one month, make sure month is as expected
16906 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16908 return new_date.getUTCMonth() != new_month;
16910 new_month = month + dir;
16911 new_date.setUTCMonth(new_month);
16912 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16913 if (new_month < 0 || new_month > 11)
16914 new_month = (new_month + 12) % 12;
16916 // For magnitudes >1, move one month at a time...
16917 for (var i=0; i<mag; i++)
16918 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16919 new_date = this.moveMonth(new_date, dir);
16920 // ...then reset the day, keeping it in the new month
16921 new_month = new_date.getUTCMonth();
16922 new_date.setUTCDate(day);
16924 return new_month != new_date.getUTCMonth();
16927 // Common date-resetting loop -- if date is beyond end of month, make it
16930 new_date.setUTCDate(--day);
16931 new_date.setUTCMonth(new_month);
16936 moveYear: function(date, dir)
16938 return this.moveMonth(date, dir*12);
16941 dateWithinRange: function(date)
16943 return date >= this.startDate && date <= this.endDate;
16949 this.picker().remove();
16954 Roo.apply(Roo.bootstrap.DateField, {
16965 html: '<i class="fa fa-arrow-left"/>'
16975 html: '<i class="fa fa-arrow-right"/>'
17017 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17018 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17019 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17020 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17021 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17034 navFnc: 'FullYear',
17039 navFnc: 'FullYear',
17044 Roo.apply(Roo.bootstrap.DateField, {
17048 cls: 'datepicker dropdown-menu roo-dynamic',
17052 cls: 'datepicker-days',
17056 cls: 'table-condensed',
17058 Roo.bootstrap.DateField.head,
17062 Roo.bootstrap.DateField.footer
17069 cls: 'datepicker-months',
17073 cls: 'table-condensed',
17075 Roo.bootstrap.DateField.head,
17076 Roo.bootstrap.DateField.content,
17077 Roo.bootstrap.DateField.footer
17084 cls: 'datepicker-years',
17088 cls: 'table-condensed',
17090 Roo.bootstrap.DateField.head,
17091 Roo.bootstrap.DateField.content,
17092 Roo.bootstrap.DateField.footer
17111 * @class Roo.bootstrap.TimeField
17112 * @extends Roo.bootstrap.Input
17113 * Bootstrap DateField class
17117 * Create a new TimeField
17118 * @param {Object} config The config object
17121 Roo.bootstrap.TimeField = function(config){
17122 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17126 * Fires when this field show.
17127 * @param {Roo.bootstrap.DateField} thisthis
17128 * @param {Mixed} date The date value
17133 * Fires when this field hide.
17134 * @param {Roo.bootstrap.DateField} this
17135 * @param {Mixed} date The date value
17140 * Fires when select a date.
17141 * @param {Roo.bootstrap.DateField} this
17142 * @param {Mixed} date The date value
17148 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17151 * @cfg {String} format
17152 * The default time format string which can be overriden for localization support. The format must be
17153 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17157 onRender: function(ct, position)
17160 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17162 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17164 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17166 this.pop = this.picker().select('>.datepicker-time',true).first();
17167 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17169 this.picker().on('mousedown', this.onMousedown, this);
17170 this.picker().on('click', this.onClick, this);
17172 this.picker().addClass('datepicker-dropdown');
17177 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17178 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17179 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17180 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17181 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17182 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17186 fireKey: function(e){
17187 if (!this.picker().isVisible()){
17188 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17194 e.preventDefault();
17202 this.onTogglePeriod();
17205 this.onIncrementMinutes();
17208 this.onDecrementMinutes();
17217 onClick: function(e) {
17218 e.stopPropagation();
17219 e.preventDefault();
17222 picker : function()
17224 return this.el.select('.datepicker', true).first();
17227 fillTime: function()
17229 var time = this.pop.select('tbody', true).first();
17231 time.dom.innerHTML = '';
17246 cls: 'hours-up glyphicon glyphicon-chevron-up'
17266 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17287 cls: 'timepicker-hour',
17302 cls: 'timepicker-minute',
17317 cls: 'btn btn-primary period',
17339 cls: 'hours-down glyphicon glyphicon-chevron-down'
17359 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17377 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17384 var hours = this.time.getHours();
17385 var minutes = this.time.getMinutes();
17398 hours = hours - 12;
17402 hours = '0' + hours;
17406 minutes = '0' + minutes;
17409 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17410 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17411 this.pop.select('button', true).first().dom.innerHTML = period;
17417 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17419 var cls = ['bottom'];
17421 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17428 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17433 this.picker().addClass(cls.join('-'));
17437 Roo.each(cls, function(c){
17439 _this.picker().setTop(_this.inputEl().getHeight());
17443 _this.picker().setTop(0 - _this.picker().getHeight());
17448 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17452 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17459 onFocus : function()
17461 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17465 onBlur : function()
17467 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17473 this.picker().show();
17478 this.fireEvent('show', this, this.date);
17483 this.picker().hide();
17486 this.fireEvent('hide', this, this.date);
17489 setTime : function()
17492 this.setValue(this.time.format(this.format));
17494 this.fireEvent('select', this, this.date);
17499 onMousedown: function(e){
17500 e.stopPropagation();
17501 e.preventDefault();
17504 onIncrementHours: function()
17506 Roo.log('onIncrementHours');
17507 this.time = this.time.add(Date.HOUR, 1);
17512 onDecrementHours: function()
17514 Roo.log('onDecrementHours');
17515 this.time = this.time.add(Date.HOUR, -1);
17519 onIncrementMinutes: function()
17521 Roo.log('onIncrementMinutes');
17522 this.time = this.time.add(Date.MINUTE, 1);
17526 onDecrementMinutes: function()
17528 Roo.log('onDecrementMinutes');
17529 this.time = this.time.add(Date.MINUTE, -1);
17533 onTogglePeriod: function()
17535 Roo.log('onTogglePeriod');
17536 this.time = this.time.add(Date.HOUR, 12);
17543 Roo.apply(Roo.bootstrap.TimeField, {
17573 cls: 'btn btn-info ok',
17585 Roo.apply(Roo.bootstrap.TimeField, {
17589 cls: 'datepicker dropdown-menu',
17593 cls: 'datepicker-time',
17597 cls: 'table-condensed',
17599 Roo.bootstrap.TimeField.content,
17600 Roo.bootstrap.TimeField.footer
17619 * @class Roo.bootstrap.MonthField
17620 * @extends Roo.bootstrap.Input
17621 * Bootstrap MonthField class
17623 * @cfg {String} language default en
17626 * Create a new MonthField
17627 * @param {Object} config The config object
17630 Roo.bootstrap.MonthField = function(config){
17631 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17636 * Fires when this field show.
17637 * @param {Roo.bootstrap.MonthField} this
17638 * @param {Mixed} date The date value
17643 * Fires when this field hide.
17644 * @param {Roo.bootstrap.MonthField} this
17645 * @param {Mixed} date The date value
17650 * Fires when select a date.
17651 * @param {Roo.bootstrap.MonthField} this
17652 * @param {String} oldvalue The old value
17653 * @param {String} newvalue The new value
17659 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17661 onRender: function(ct, position)
17664 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17666 this.language = this.language || 'en';
17667 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17668 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17670 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17671 this.isInline = false;
17672 this.isInput = true;
17673 this.component = this.el.select('.add-on', true).first() || false;
17674 this.component = (this.component && this.component.length === 0) ? false : this.component;
17675 this.hasInput = this.component && this.inputEL().length;
17677 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17679 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17681 this.picker().on('mousedown', this.onMousedown, this);
17682 this.picker().on('click', this.onClick, this);
17684 this.picker().addClass('datepicker-dropdown');
17686 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17687 v.setStyle('width', '189px');
17694 if(this.isInline) {
17700 setValue: function(v, suppressEvent)
17702 var o = this.getValue();
17704 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17708 if(suppressEvent !== true){
17709 this.fireEvent('select', this, o, v);
17714 getValue: function()
17719 onClick: function(e)
17721 e.stopPropagation();
17722 e.preventDefault();
17724 var target = e.getTarget();
17726 if(target.nodeName.toLowerCase() === 'i'){
17727 target = Roo.get(target).dom.parentNode;
17730 var nodeName = target.nodeName;
17731 var className = target.className;
17732 var html = target.innerHTML;
17734 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17738 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17740 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17746 picker : function()
17748 return this.pickerEl;
17751 fillMonths: function()
17754 var months = this.picker().select('>.datepicker-months td', true).first();
17756 months.dom.innerHTML = '';
17762 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17765 months.createChild(month);
17774 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17775 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17778 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17779 e.removeClass('active');
17781 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17782 e.addClass('active');
17789 if(this.isInline) return;
17791 this.picker().removeClass(['bottom', 'top']);
17793 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17795 * place to the top of element!
17799 this.picker().addClass('top');
17800 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17805 this.picker().addClass('bottom');
17807 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17810 onFocus : function()
17812 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17816 onBlur : function()
17818 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17820 var d = this.inputEl().getValue();
17829 this.picker().show();
17830 this.picker().select('>.datepicker-months', true).first().show();
17834 this.fireEvent('show', this, this.date);
17839 if(this.isInline) return;
17840 this.picker().hide();
17841 this.fireEvent('hide', this, this.date);
17845 onMousedown: function(e)
17847 e.stopPropagation();
17848 e.preventDefault();
17853 Roo.bootstrap.MonthField.superclass.keyup.call(this);
17857 fireKey: function(e)
17859 if (!this.picker().isVisible()){
17860 if (e.keyCode == 27) // allow escape to hide and re-show picker
17870 e.preventDefault();
17874 dir = e.keyCode == 37 ? -1 : 1;
17876 this.vIndex = this.vIndex + dir;
17878 if(this.vIndex < 0){
17882 if(this.vIndex > 11){
17886 if(isNaN(this.vIndex)){
17890 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17896 dir = e.keyCode == 38 ? -1 : 1;
17898 this.vIndex = this.vIndex + dir * 4;
17900 if(this.vIndex < 0){
17904 if(this.vIndex > 11){
17908 if(isNaN(this.vIndex)){
17912 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17917 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17918 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17922 e.preventDefault();
17925 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17926 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17942 this.picker().remove();
17947 Roo.apply(Roo.bootstrap.MonthField, {
17966 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17967 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17972 Roo.apply(Roo.bootstrap.MonthField, {
17976 cls: 'datepicker dropdown-menu roo-dynamic',
17980 cls: 'datepicker-months',
17984 cls: 'table-condensed',
17986 Roo.bootstrap.DateField.content
18006 * @class Roo.bootstrap.CheckBox
18007 * @extends Roo.bootstrap.Input
18008 * Bootstrap CheckBox class
18010 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18011 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18012 * @cfg {String} boxLabel The text that appears beside the checkbox
18013 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18014 * @cfg {Boolean} checked initnal the element
18015 * @cfg {Boolean} inline inline the element (default false)
18016 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18019 * Create a new CheckBox
18020 * @param {Object} config The config object
18023 Roo.bootstrap.CheckBox = function(config){
18024 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18029 * Fires when the element is checked or unchecked.
18030 * @param {Roo.bootstrap.CheckBox} this This input
18031 * @param {Boolean} checked The new checked value
18038 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18040 inputType: 'checkbox',
18048 getAutoCreate : function()
18050 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18056 cfg.cls = 'form-group ' + this.inputType; //input-group
18059 cfg.cls += ' ' + this.inputType + '-inline';
18065 type : this.inputType,
18066 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18067 cls : 'roo-' + this.inputType, //'form-box',
18068 placeholder : this.placeholder || ''
18072 if (this.weight) { // Validity check?
18073 cfg.cls += " " + this.inputType + "-" + this.weight;
18076 if (this.disabled) {
18077 input.disabled=true;
18081 input.checked = this.checked;
18085 input.name = this.name;
18089 input.cls += ' input-' + this.size;
18094 ['xs','sm','md','lg'].map(function(size){
18095 if (settings[size]) {
18096 cfg.cls += ' col-' + size + '-' + settings[size];
18100 var inputblock = input;
18102 if (this.before || this.after) {
18105 cls : 'input-group',
18110 inputblock.cn.push({
18112 cls : 'input-group-addon',
18117 inputblock.cn.push(input);
18120 inputblock.cn.push({
18122 cls : 'input-group-addon',
18129 if (align ==='left' && this.fieldLabel.length) {
18130 Roo.log("left and has label");
18136 cls : 'control-label col-md-' + this.labelWidth,
18137 html : this.fieldLabel
18141 cls : "col-md-" + (12 - this.labelWidth),
18148 } else if ( this.fieldLabel.length) {
18153 tag: this.boxLabel ? 'span' : 'label',
18155 cls: 'control-label box-input-label',
18156 //cls : 'input-group-addon',
18157 html : this.fieldLabel
18167 Roo.log(" no label && no align");
18168 cfg.cn = [ inputblock ] ;
18173 var boxLabelCfg = {
18175 //'for': id, // box label is handled by onclick - so no for...
18177 html: this.boxLabel
18181 boxLabelCfg.tooltip = this.tooltip;
18184 cfg.cn.push(boxLabelCfg);
18194 * return the real input element.
18196 inputEl: function ()
18198 return this.el.select('input.roo-' + this.inputType,true).first();
18201 labelEl: function()
18203 return this.el.select('label.control-label',true).first();
18205 /* depricated... */
18209 return this.labelEl();
18212 initEvents : function()
18214 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18216 this.inputEl().on('click', this.onClick, this);
18218 if (this.boxLabel) {
18219 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18222 this.startValue = this.getValue();
18225 Roo.bootstrap.CheckBox.register(this);
18229 onClick : function()
18231 this.setChecked(!this.checked);
18234 setChecked : function(state,suppressEvent)
18236 this.startValue = this.getValue();
18238 if(this.inputType == 'radio'){
18240 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18241 e.dom.checked = false;
18244 this.inputEl().dom.checked = true;
18246 this.inputEl().dom.value = this.inputValue;
18248 if(suppressEvent !== true){
18249 this.fireEvent('check', this, true);
18257 this.checked = state;
18259 this.inputEl().dom.checked = state;
18261 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18263 if(suppressEvent !== true){
18264 this.fireEvent('check', this, state);
18270 getValue : function()
18272 if(this.inputType == 'radio'){
18273 return this.getGroupValue();
18276 return this.inputEl().getValue();
18280 getGroupValue : function()
18282 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18286 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18289 setValue : function(v,suppressEvent)
18291 if(this.inputType == 'radio'){
18292 this.setGroupValue(v, suppressEvent);
18296 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18301 setGroupValue : function(v, suppressEvent)
18303 this.startValue = this.getValue();
18305 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18306 e.dom.checked = false;
18308 if(e.dom.value == v){
18309 e.dom.checked = true;
18313 if(suppressEvent !== true){
18314 this.fireEvent('check', this, true);
18322 validate : function()
18326 (this.inputType == 'radio' && this.validateRadio()) ||
18327 (this.inputType == 'checkbox' && this.validateCheckbox())
18333 this.markInvalid();
18337 validateRadio : function()
18341 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18342 if(!e.dom.checked){
18354 validateCheckbox : function()
18357 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18360 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18368 for(var i in group){
18373 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18380 * Mark this field as valid
18382 markValid : function()
18384 if(this.allowBlank){
18390 this.fireEvent('valid', this);
18392 if(this.inputType == 'radio'){
18393 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18394 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18395 e.findParent('.form-group', false, true).addClass(_this.validClass);
18402 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18403 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18407 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18413 for(var i in group){
18414 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18415 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18420 * Mark this field as invalid
18421 * @param {String} msg The validation message
18423 markInvalid : function(msg)
18425 if(this.allowBlank){
18431 this.fireEvent('invalid', this, msg);
18433 if(this.inputType == 'radio'){
18434 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18435 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18436 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18443 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18444 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18448 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18454 for(var i in group){
18455 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18456 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18463 Roo.apply(Roo.bootstrap.CheckBox, {
18468 * register a CheckBox Group
18469 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18471 register : function(checkbox)
18473 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18474 this.groups[checkbox.groupId] = {};
18477 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18481 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18485 * fetch a CheckBox Group based on the group ID
18486 * @param {string} the group ID
18487 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18489 get: function(groupId) {
18490 if (typeof(this.groups[groupId]) == 'undefined') {
18494 return this.groups[groupId] ;
18506 *<div class="radio">
18508 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18509 Option one is this and that—be sure to include why it's great
18516 *<label class="radio-inline">fieldLabel</label>
18517 *<label class="radio-inline">
18518 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18526 * @class Roo.bootstrap.Radio
18527 * @extends Roo.bootstrap.CheckBox
18528 * Bootstrap Radio class
18531 * Create a new Radio
18532 * @param {Object} config The config object
18535 Roo.bootstrap.Radio = function(config){
18536 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18540 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18542 inputType: 'radio',
18546 getAutoCreate : function()
18548 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18549 align = align || 'left'; // default...
18556 tag : this.inline ? 'span' : 'div',
18561 var inline = this.inline ? ' radio-inline' : '';
18565 // does not need for, as we wrap the input with it..
18567 cls : 'control-label box-label' + inline,
18570 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18574 //cls : 'control-label' + inline,
18575 html : this.fieldLabel,
18576 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18585 type : this.inputType,
18586 //value : (!this.checked) ? this.valueOff : this.inputValue,
18587 value : this.inputValue,
18589 placeholder : this.placeholder || '' // ?? needed????
18592 if (this.weight) { // Validity check?
18593 input.cls += " radio-" + this.weight;
18595 if (this.disabled) {
18596 input.disabled=true;
18600 input.checked = this.checked;
18604 input.name = this.name;
18608 input.cls += ' input-' + this.size;
18611 //?? can span's inline have a width??
18614 ['xs','sm','md','lg'].map(function(size){
18615 if (settings[size]) {
18616 cfg.cls += ' col-' + size + '-' + settings[size];
18620 var inputblock = input;
18622 if (this.before || this.after) {
18625 cls : 'input-group',
18630 inputblock.cn.push({
18632 cls : 'input-group-addon',
18636 inputblock.cn.push(input);
18638 inputblock.cn.push({
18640 cls : 'input-group-addon',
18648 if (this.fieldLabel && this.fieldLabel.length) {
18649 cfg.cn.push(fieldLabel);
18652 // normal bootstrap puts the input inside the label.
18653 // however with our styled version - it has to go after the input.
18655 //lbl.cn.push(inputblock);
18659 cls: 'radio' + inline,
18666 cfg.cn.push( lblwrap);
18671 html: this.boxLabel
18680 initEvents : function()
18682 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18684 this.inputEl().on('click', this.onClick, this);
18685 if (this.boxLabel) {
18686 Roo.log('find label')
18687 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18692 inputEl: function ()
18694 return this.el.select('input.roo-radio',true).first();
18696 onClick : function()
18699 this.setChecked(true);
18702 setChecked : function(state,suppressEvent)
18705 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18706 v.dom.checked = false;
18709 Roo.log(this.inputEl().dom);
18710 this.checked = state;
18711 this.inputEl().dom.checked = state;
18713 if(suppressEvent !== true){
18714 this.fireEvent('check', this, state);
18717 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18721 getGroupValue : function()
18724 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18725 if(v.dom.checked == true){
18726 value = v.dom.value;
18734 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18735 * @return {Mixed} value The field value
18737 getValue : function(){
18738 return this.getGroupValue();
18744 //<script type="text/javascript">
18747 * Based Ext JS Library 1.1.1
18748 * Copyright(c) 2006-2007, Ext JS, LLC.
18754 * @class Roo.HtmlEditorCore
18755 * @extends Roo.Component
18756 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18758 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18761 Roo.HtmlEditorCore = function(config){
18764 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18769 * @event initialize
18770 * Fires when the editor is fully initialized (including the iframe)
18771 * @param {Roo.HtmlEditorCore} this
18776 * Fires when the editor is first receives the focus. Any insertion must wait
18777 * until after this event.
18778 * @param {Roo.HtmlEditorCore} this
18782 * @event beforesync
18783 * Fires before the textarea is updated with content from the editor iframe. Return false
18784 * to cancel the sync.
18785 * @param {Roo.HtmlEditorCore} this
18786 * @param {String} html
18790 * @event beforepush
18791 * Fires before the iframe editor is updated with content from the textarea. Return false
18792 * to cancel the push.
18793 * @param {Roo.HtmlEditorCore} this
18794 * @param {String} html
18799 * Fires when the textarea is updated with content from the editor iframe.
18800 * @param {Roo.HtmlEditorCore} this
18801 * @param {String} html
18806 * Fires when the iframe editor is updated with content from the textarea.
18807 * @param {Roo.HtmlEditorCore} this
18808 * @param {String} html
18813 * @event editorevent
18814 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18815 * @param {Roo.HtmlEditorCore} this
18821 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18823 // defaults : white / black...
18824 this.applyBlacklists();
18831 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
18835 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
18841 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18846 * @cfg {Number} height (in pixels)
18850 * @cfg {Number} width (in pixels)
18855 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18858 stylesheets: false,
18863 // private properties
18864 validationEvent : false,
18866 initialized : false,
18868 sourceEditMode : false,
18869 onFocus : Roo.emptyFn,
18871 hideMode:'offsets',
18875 // blacklist + whitelisted elements..
18882 * Protected method that will not generally be called directly. It
18883 * is called when the editor initializes the iframe with HTML contents. Override this method if you
18884 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18886 getDocMarkup : function(){
18890 // inherit styels from page...??
18891 if (this.stylesheets === false) {
18893 Roo.get(document.head).select('style').each(function(node) {
18894 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18897 Roo.get(document.head).select('link').each(function(node) {
18898 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18901 } else if (!this.stylesheets.length) {
18903 st = '<style type="text/css">' +
18904 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18910 st += '<style type="text/css">' +
18911 'IMG { cursor: pointer } ' +
18915 return '<html><head>' + st +
18916 //<style type="text/css">' +
18917 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18919 ' </head><body class="roo-htmleditor-body"></body></html>';
18923 onRender : function(ct, position)
18926 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18927 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18930 this.el.dom.style.border = '0 none';
18931 this.el.dom.setAttribute('tabIndex', -1);
18932 this.el.addClass('x-hidden hide');
18936 if(Roo.isIE){ // fix IE 1px bogus margin
18937 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18941 this.frameId = Roo.id();
18945 var iframe = this.owner.wrap.createChild({
18947 cls: 'form-control', // bootstrap..
18949 name: this.frameId,
18950 frameBorder : 'no',
18951 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
18956 this.iframe = iframe.dom;
18958 this.assignDocWin();
18960 this.doc.designMode = 'on';
18963 this.doc.write(this.getDocMarkup());
18967 var task = { // must defer to wait for browser to be ready
18969 //console.log("run task?" + this.doc.readyState);
18970 this.assignDocWin();
18971 if(this.doc.body || this.doc.readyState == 'complete'){
18973 this.doc.designMode="on";
18977 Roo.TaskMgr.stop(task);
18978 this.initEditor.defer(10, this);
18985 Roo.TaskMgr.start(task);
18990 onResize : function(w, h)
18992 Roo.log('resize: ' +w + ',' + h );
18993 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18997 if(typeof w == 'number'){
18999 this.iframe.style.width = w + 'px';
19001 if(typeof h == 'number'){
19003 this.iframe.style.height = h + 'px';
19005 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19012 * Toggles the editor between standard and source edit mode.
19013 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19015 toggleSourceEdit : function(sourceEditMode){
19017 this.sourceEditMode = sourceEditMode === true;
19019 if(this.sourceEditMode){
19021 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19024 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19025 //this.iframe.className = '';
19028 //this.setSize(this.owner.wrap.getSize());
19029 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19036 * Protected method that will not generally be called directly. If you need/want
19037 * custom HTML cleanup, this is the method you should override.
19038 * @param {String} html The HTML to be cleaned
19039 * return {String} The cleaned HTML
19041 cleanHtml : function(html){
19042 html = String(html);
19043 if(html.length > 5){
19044 if(Roo.isSafari){ // strip safari nonsense
19045 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19048 if(html == ' '){
19055 * HTML Editor -> Textarea
19056 * Protected method that will not generally be called directly. Syncs the contents
19057 * of the editor iframe with the textarea.
19059 syncValue : function(){
19060 if(this.initialized){
19061 var bd = (this.doc.body || this.doc.documentElement);
19062 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19063 var html = bd.innerHTML;
19065 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19066 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19068 html = '<div style="'+m[0]+'">' + html + '</div>';
19071 html = this.cleanHtml(html);
19072 // fix up the special chars.. normaly like back quotes in word...
19073 // however we do not want to do this with chinese..
19074 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19075 var cc = b.charCodeAt();
19077 (cc >= 0x4E00 && cc < 0xA000 ) ||
19078 (cc >= 0x3400 && cc < 0x4E00 ) ||
19079 (cc >= 0xf900 && cc < 0xfb00 )
19085 if(this.owner.fireEvent('beforesync', this, html) !== false){
19086 this.el.dom.value = html;
19087 this.owner.fireEvent('sync', this, html);
19093 * Protected method that will not generally be called directly. Pushes the value of the textarea
19094 * into the iframe editor.
19096 pushValue : function(){
19097 if(this.initialized){
19098 var v = this.el.dom.value.trim();
19100 // if(v.length < 1){
19104 if(this.owner.fireEvent('beforepush', this, v) !== false){
19105 var d = (this.doc.body || this.doc.documentElement);
19107 this.cleanUpPaste();
19108 this.el.dom.value = d.innerHTML;
19109 this.owner.fireEvent('push', this, v);
19115 deferFocus : function(){
19116 this.focus.defer(10, this);
19120 focus : function(){
19121 if(this.win && !this.sourceEditMode){
19128 assignDocWin: function()
19130 var iframe = this.iframe;
19133 this.doc = iframe.contentWindow.document;
19134 this.win = iframe.contentWindow;
19136 // if (!Roo.get(this.frameId)) {
19139 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19140 // this.win = Roo.get(this.frameId).dom.contentWindow;
19142 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19146 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19147 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19152 initEditor : function(){
19153 //console.log("INIT EDITOR");
19154 this.assignDocWin();
19158 this.doc.designMode="on";
19160 this.doc.write(this.getDocMarkup());
19163 var dbody = (this.doc.body || this.doc.documentElement);
19164 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19165 // this copies styles from the containing element into thsi one..
19166 // not sure why we need all of this..
19167 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19169 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19170 //ss['background-attachment'] = 'fixed'; // w3c
19171 dbody.bgProperties = 'fixed'; // ie
19172 //Roo.DomHelper.applyStyles(dbody, ss);
19173 Roo.EventManager.on(this.doc, {
19174 //'mousedown': this.onEditorEvent,
19175 'mouseup': this.onEditorEvent,
19176 'dblclick': this.onEditorEvent,
19177 'click': this.onEditorEvent,
19178 'keyup': this.onEditorEvent,
19183 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19185 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19186 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19188 this.initialized = true;
19190 this.owner.fireEvent('initialize', this);
19195 onDestroy : function(){
19201 //for (var i =0; i < this.toolbars.length;i++) {
19202 // // fixme - ask toolbars for heights?
19203 // this.toolbars[i].onDestroy();
19206 //this.wrap.dom.innerHTML = '';
19207 //this.wrap.remove();
19212 onFirstFocus : function(){
19214 this.assignDocWin();
19217 this.activated = true;
19220 if(Roo.isGecko){ // prevent silly gecko errors
19222 var s = this.win.getSelection();
19223 if(!s.focusNode || s.focusNode.nodeType != 3){
19224 var r = s.getRangeAt(0);
19225 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19230 this.execCmd('useCSS', true);
19231 this.execCmd('styleWithCSS', false);
19234 this.owner.fireEvent('activate', this);
19238 adjustFont: function(btn){
19239 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19240 //if(Roo.isSafari){ // safari
19243 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19244 if(Roo.isSafari){ // safari
19245 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19246 v = (v < 10) ? 10 : v;
19247 v = (v > 48) ? 48 : v;
19248 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19253 v = Math.max(1, v+adjust);
19255 this.execCmd('FontSize', v );
19258 onEditorEvent : function(e)
19260 this.owner.fireEvent('editorevent', this, e);
19261 // this.updateToolbar();
19262 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19265 insertTag : function(tg)
19267 // could be a bit smarter... -> wrap the current selected tRoo..
19268 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19270 range = this.createRange(this.getSelection());
19271 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19272 wrappingNode.appendChild(range.extractContents());
19273 range.insertNode(wrappingNode);
19280 this.execCmd("formatblock", tg);
19284 insertText : function(txt)
19288 var range = this.createRange();
19289 range.deleteContents();
19290 //alert(Sender.getAttribute('label'));
19292 range.insertNode(this.doc.createTextNode(txt));
19298 * Executes a Midas editor command on the editor document and performs necessary focus and
19299 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19300 * @param {String} cmd The Midas command
19301 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19303 relayCmd : function(cmd, value){
19305 this.execCmd(cmd, value);
19306 this.owner.fireEvent('editorevent', this);
19307 //this.updateToolbar();
19308 this.owner.deferFocus();
19312 * Executes a Midas editor command directly on the editor document.
19313 * For visual commands, you should use {@link #relayCmd} instead.
19314 * <b>This should only be called after the editor is initialized.</b>
19315 * @param {String} cmd The Midas command
19316 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19318 execCmd : function(cmd, value){
19319 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19326 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19328 * @param {String} text | dom node..
19330 insertAtCursor : function(text)
19335 if(!this.activated){
19341 var r = this.doc.selection.createRange();
19352 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19356 // from jquery ui (MIT licenced)
19358 var win = this.win;
19360 if (win.getSelection && win.getSelection().getRangeAt) {
19361 range = win.getSelection().getRangeAt(0);
19362 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19363 range.insertNode(node);
19364 } else if (win.document.selection && win.document.selection.createRange) {
19365 // no firefox support
19366 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19367 win.document.selection.createRange().pasteHTML(txt);
19369 // no firefox support
19370 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19371 this.execCmd('InsertHTML', txt);
19380 mozKeyPress : function(e){
19382 var c = e.getCharCode(), cmd;
19385 c = String.fromCharCode(c).toLowerCase();
19399 this.cleanUpPaste.defer(100, this);
19407 e.preventDefault();
19415 fixKeys : function(){ // load time branching for fastest keydown performance
19417 return function(e){
19418 var k = e.getKey(), r;
19421 r = this.doc.selection.createRange();
19424 r.pasteHTML('    ');
19431 r = this.doc.selection.createRange();
19433 var target = r.parentElement();
19434 if(!target || target.tagName.toLowerCase() != 'li'){
19436 r.pasteHTML('<br />');
19442 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19443 this.cleanUpPaste.defer(100, this);
19449 }else if(Roo.isOpera){
19450 return function(e){
19451 var k = e.getKey();
19455 this.execCmd('InsertHTML','    ');
19458 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19459 this.cleanUpPaste.defer(100, this);
19464 }else if(Roo.isSafari){
19465 return function(e){
19466 var k = e.getKey();
19470 this.execCmd('InsertText','\t');
19474 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19475 this.cleanUpPaste.defer(100, this);
19483 getAllAncestors: function()
19485 var p = this.getSelectedNode();
19488 a.push(p); // push blank onto stack..
19489 p = this.getParentElement();
19493 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19497 a.push(this.doc.body);
19501 lastSelNode : false,
19504 getSelection : function()
19506 this.assignDocWin();
19507 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19510 getSelectedNode: function()
19512 // this may only work on Gecko!!!
19514 // should we cache this!!!!
19519 var range = this.createRange(this.getSelection()).cloneRange();
19522 var parent = range.parentElement();
19524 var testRange = range.duplicate();
19525 testRange.moveToElementText(parent);
19526 if (testRange.inRange(range)) {
19529 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19532 parent = parent.parentElement;
19537 // is ancestor a text element.
19538 var ac = range.commonAncestorContainer;
19539 if (ac.nodeType == 3) {
19540 ac = ac.parentNode;
19543 var ar = ac.childNodes;
19546 var other_nodes = [];
19547 var has_other_nodes = false;
19548 for (var i=0;i<ar.length;i++) {
19549 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19552 // fullly contained node.
19554 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19559 // probably selected..
19560 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19561 other_nodes.push(ar[i]);
19565 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19570 has_other_nodes = true;
19572 if (!nodes.length && other_nodes.length) {
19573 nodes= other_nodes;
19575 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19581 createRange: function(sel)
19583 // this has strange effects when using with
19584 // top toolbar - not sure if it's a great idea.
19585 //this.editor.contentWindow.focus();
19586 if (typeof sel != "undefined") {
19588 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19590 return this.doc.createRange();
19593 return this.doc.createRange();
19596 getParentElement: function()
19599 this.assignDocWin();
19600 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19602 var range = this.createRange(sel);
19605 var p = range.commonAncestorContainer;
19606 while (p.nodeType == 3) { // text node
19617 * Range intersection.. the hard stuff...
19621 * [ -- selected range --- ]
19625 * if end is before start or hits it. fail.
19626 * if start is after end or hits it fail.
19628 * if either hits (but other is outside. - then it's not
19634 // @see http://www.thismuchiknow.co.uk/?p=64.
19635 rangeIntersectsNode : function(range, node)
19637 var nodeRange = node.ownerDocument.createRange();
19639 nodeRange.selectNode(node);
19641 nodeRange.selectNodeContents(node);
19644 var rangeStartRange = range.cloneRange();
19645 rangeStartRange.collapse(true);
19647 var rangeEndRange = range.cloneRange();
19648 rangeEndRange.collapse(false);
19650 var nodeStartRange = nodeRange.cloneRange();
19651 nodeStartRange.collapse(true);
19653 var nodeEndRange = nodeRange.cloneRange();
19654 nodeEndRange.collapse(false);
19656 return rangeStartRange.compareBoundaryPoints(
19657 Range.START_TO_START, nodeEndRange) == -1 &&
19658 rangeEndRange.compareBoundaryPoints(
19659 Range.START_TO_START, nodeStartRange) == 1;
19663 rangeCompareNode : function(range, node)
19665 var nodeRange = node.ownerDocument.createRange();
19667 nodeRange.selectNode(node);
19669 nodeRange.selectNodeContents(node);
19673 range.collapse(true);
19675 nodeRange.collapse(true);
19677 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19678 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19680 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19682 var nodeIsBefore = ss == 1;
19683 var nodeIsAfter = ee == -1;
19685 if (nodeIsBefore && nodeIsAfter)
19687 if (!nodeIsBefore && nodeIsAfter)
19688 return 1; //right trailed.
19690 if (nodeIsBefore && !nodeIsAfter)
19691 return 2; // left trailed.
19696 // private? - in a new class?
19697 cleanUpPaste : function()
19699 // cleans up the whole document..
19700 Roo.log('cleanuppaste');
19702 this.cleanUpChildren(this.doc.body);
19703 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19704 if (clean != this.doc.body.innerHTML) {
19705 this.doc.body.innerHTML = clean;
19710 cleanWordChars : function(input) {// change the chars to hex code
19711 var he = Roo.HtmlEditorCore;
19713 var output = input;
19714 Roo.each(he.swapCodes, function(sw) {
19715 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19717 output = output.replace(swapper, sw[1]);
19724 cleanUpChildren : function (n)
19726 if (!n.childNodes.length) {
19729 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19730 this.cleanUpChild(n.childNodes[i]);
19737 cleanUpChild : function (node)
19740 //console.log(node);
19741 if (node.nodeName == "#text") {
19742 // clean up silly Windows -- stuff?
19745 if (node.nodeName == "#comment") {
19746 node.parentNode.removeChild(node);
19747 // clean up silly Windows -- stuff?
19750 var lcname = node.tagName.toLowerCase();
19751 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19752 // whitelist of tags..
19754 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19756 node.parentNode.removeChild(node);
19761 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19763 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19764 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19766 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19767 // remove_keep_children = true;
19770 if (remove_keep_children) {
19771 this.cleanUpChildren(node);
19772 // inserts everything just before this node...
19773 while (node.childNodes.length) {
19774 var cn = node.childNodes[0];
19775 node.removeChild(cn);
19776 node.parentNode.insertBefore(cn, node);
19778 node.parentNode.removeChild(node);
19782 if (!node.attributes || !node.attributes.length) {
19783 this.cleanUpChildren(node);
19787 function cleanAttr(n,v)
19790 if (v.match(/^\./) || v.match(/^\//)) {
19793 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19796 if (v.match(/^#/)) {
19799 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19800 node.removeAttribute(n);
19804 var cwhite = this.cwhite;
19805 var cblack = this.cblack;
19807 function cleanStyle(n,v)
19809 if (v.match(/expression/)) { //XSS?? should we even bother..
19810 node.removeAttribute(n);
19814 var parts = v.split(/;/);
19817 Roo.each(parts, function(p) {
19818 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19822 var l = p.split(':').shift().replace(/\s+/g,'');
19823 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19825 if ( cwhite.length && cblack.indexOf(l) > -1) {
19826 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19827 //node.removeAttribute(n);
19831 // only allow 'c whitelisted system attributes'
19832 if ( cwhite.length && cwhite.indexOf(l) < 0) {
19833 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19834 //node.removeAttribute(n);
19844 if (clean.length) {
19845 node.setAttribute(n, clean.join(';'));
19847 node.removeAttribute(n);
19853 for (var i = node.attributes.length-1; i > -1 ; i--) {
19854 var a = node.attributes[i];
19857 if (a.name.toLowerCase().substr(0,2)=='on') {
19858 node.removeAttribute(a.name);
19861 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19862 node.removeAttribute(a.name);
19865 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19866 cleanAttr(a.name,a.value); // fixme..
19869 if (a.name == 'style') {
19870 cleanStyle(a.name,a.value);
19873 /// clean up MS crap..
19874 // tecnically this should be a list of valid class'es..
19877 if (a.name == 'class') {
19878 if (a.value.match(/^Mso/)) {
19879 node.className = '';
19882 if (a.value.match(/body/)) {
19883 node.className = '';
19894 this.cleanUpChildren(node);
19900 * Clean up MS wordisms...
19902 cleanWord : function(node)
19907 this.cleanWord(this.doc.body);
19910 if (node.nodeName == "#text") {
19911 // clean up silly Windows -- stuff?
19914 if (node.nodeName == "#comment") {
19915 node.parentNode.removeChild(node);
19916 // clean up silly Windows -- stuff?
19920 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19921 node.parentNode.removeChild(node);
19925 // remove - but keep children..
19926 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19927 while (node.childNodes.length) {
19928 var cn = node.childNodes[0];
19929 node.removeChild(cn);
19930 node.parentNode.insertBefore(cn, node);
19932 node.parentNode.removeChild(node);
19933 this.iterateChildren(node, this.cleanWord);
19937 if (node.className.length) {
19939 var cn = node.className.split(/\W+/);
19941 Roo.each(cn, function(cls) {
19942 if (cls.match(/Mso[a-zA-Z]+/)) {
19947 node.className = cna.length ? cna.join(' ') : '';
19949 node.removeAttribute("class");
19953 if (node.hasAttribute("lang")) {
19954 node.removeAttribute("lang");
19957 if (node.hasAttribute("style")) {
19959 var styles = node.getAttribute("style").split(";");
19961 Roo.each(styles, function(s) {
19962 if (!s.match(/:/)) {
19965 var kv = s.split(":");
19966 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19969 // what ever is left... we allow.
19972 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19973 if (!nstyle.length) {
19974 node.removeAttribute('style');
19977 this.iterateChildren(node, this.cleanWord);
19983 * iterateChildren of a Node, calling fn each time, using this as the scole..
19984 * @param {DomNode} node node to iterate children of.
19985 * @param {Function} fn method of this class to call on each item.
19987 iterateChildren : function(node, fn)
19989 if (!node.childNodes.length) {
19992 for (var i = node.childNodes.length-1; i > -1 ; i--) {
19993 fn.call(this, node.childNodes[i])
19999 * cleanTableWidths.
20001 * Quite often pasting from word etc.. results in tables with column and widths.
20002 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20005 cleanTableWidths : function(node)
20010 this.cleanTableWidths(this.doc.body);
20015 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20018 Roo.log(node.tagName);
20019 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20020 this.iterateChildren(node, this.cleanTableWidths);
20023 if (node.hasAttribute('width')) {
20024 node.removeAttribute('width');
20028 if (node.hasAttribute("style")) {
20031 var styles = node.getAttribute("style").split(";");
20033 Roo.each(styles, function(s) {
20034 if (!s.match(/:/)) {
20037 var kv = s.split(":");
20038 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20041 // what ever is left... we allow.
20044 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20045 if (!nstyle.length) {
20046 node.removeAttribute('style');
20050 this.iterateChildren(node, this.cleanTableWidths);
20058 domToHTML : function(currentElement, depth, nopadtext) {
20060 depth = depth || 0;
20061 nopadtext = nopadtext || false;
20063 if (!currentElement) {
20064 return this.domToHTML(this.doc.body);
20067 //Roo.log(currentElement);
20069 var allText = false;
20070 var nodeName = currentElement.nodeName;
20071 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20073 if (nodeName == '#text') {
20075 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20080 if (nodeName != 'BODY') {
20083 // Prints the node tagName, such as <A>, <IMG>, etc
20086 for(i = 0; i < currentElement.attributes.length;i++) {
20088 var aname = currentElement.attributes.item(i).name;
20089 if (!currentElement.attributes.item(i).value.length) {
20092 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20095 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20104 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20107 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20112 // Traverse the tree
20114 var currentElementChild = currentElement.childNodes.item(i);
20115 var allText = true;
20116 var innerHTML = '';
20118 while (currentElementChild) {
20119 // Formatting code (indent the tree so it looks nice on the screen)
20120 var nopad = nopadtext;
20121 if (lastnode == 'SPAN') {
20125 if (currentElementChild.nodeName == '#text') {
20126 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20127 toadd = nopadtext ? toadd : toadd.trim();
20128 if (!nopad && toadd.length > 80) {
20129 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20131 innerHTML += toadd;
20134 currentElementChild = currentElement.childNodes.item(i);
20140 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20142 // Recursively traverse the tree structure of the child node
20143 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20144 lastnode = currentElementChild.nodeName;
20146 currentElementChild=currentElement.childNodes.item(i);
20152 // The remaining code is mostly for formatting the tree
20153 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20158 ret+= "</"+tagName+">";
20164 applyBlacklists : function()
20166 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20167 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20171 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20172 if (b.indexOf(tag) > -1) {
20175 this.white.push(tag);
20179 Roo.each(w, function(tag) {
20180 if (b.indexOf(tag) > -1) {
20183 if (this.white.indexOf(tag) > -1) {
20186 this.white.push(tag);
20191 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20192 if (w.indexOf(tag) > -1) {
20195 this.black.push(tag);
20199 Roo.each(b, function(tag) {
20200 if (w.indexOf(tag) > -1) {
20203 if (this.black.indexOf(tag) > -1) {
20206 this.black.push(tag);
20211 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20212 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20216 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20217 if (b.indexOf(tag) > -1) {
20220 this.cwhite.push(tag);
20224 Roo.each(w, function(tag) {
20225 if (b.indexOf(tag) > -1) {
20228 if (this.cwhite.indexOf(tag) > -1) {
20231 this.cwhite.push(tag);
20236 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20237 if (w.indexOf(tag) > -1) {
20240 this.cblack.push(tag);
20244 Roo.each(b, function(tag) {
20245 if (w.indexOf(tag) > -1) {
20248 if (this.cblack.indexOf(tag) > -1) {
20251 this.cblack.push(tag);
20256 setStylesheets : function(stylesheets)
20258 if(typeof(stylesheets) == 'string'){
20259 Roo.get(this.iframe.contentDocument.head).createChild({
20261 rel : 'stylesheet',
20270 Roo.each(stylesheets, function(s) {
20275 Roo.get(_this.iframe.contentDocument.head).createChild({
20277 rel : 'stylesheet',
20286 removeStylesheets : function()
20290 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20295 // hide stuff that is not compatible
20309 * @event specialkey
20313 * @cfg {String} fieldClass @hide
20316 * @cfg {String} focusClass @hide
20319 * @cfg {String} autoCreate @hide
20322 * @cfg {String} inputType @hide
20325 * @cfg {String} invalidClass @hide
20328 * @cfg {String} invalidText @hide
20331 * @cfg {String} msgFx @hide
20334 * @cfg {String} validateOnBlur @hide
20338 Roo.HtmlEditorCore.white = [
20339 'area', 'br', 'img', 'input', 'hr', 'wbr',
20341 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20342 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20343 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20344 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20345 'table', 'ul', 'xmp',
20347 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20350 'dir', 'menu', 'ol', 'ul', 'dl',
20356 Roo.HtmlEditorCore.black = [
20357 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20359 'base', 'basefont', 'bgsound', 'blink', 'body',
20360 'frame', 'frameset', 'head', 'html', 'ilayer',
20361 'iframe', 'layer', 'link', 'meta', 'object',
20362 'script', 'style' ,'title', 'xml' // clean later..
20364 Roo.HtmlEditorCore.clean = [
20365 'script', 'style', 'title', 'xml'
20367 Roo.HtmlEditorCore.remove = [
20372 Roo.HtmlEditorCore.ablack = [
20376 Roo.HtmlEditorCore.aclean = [
20377 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20381 Roo.HtmlEditorCore.pwhite= [
20382 'http', 'https', 'mailto'
20385 // white listed style attributes.
20386 Roo.HtmlEditorCore.cwhite= [
20387 // 'text-align', /// default is to allow most things..
20393 // black listed style attributes.
20394 Roo.HtmlEditorCore.cblack= [
20395 // 'font-size' -- this can be set by the project
20399 Roo.HtmlEditorCore.swapCodes =[
20418 * @class Roo.bootstrap.HtmlEditor
20419 * @extends Roo.bootstrap.TextArea
20420 * Bootstrap HtmlEditor class
20423 * Create a new HtmlEditor
20424 * @param {Object} config The config object
20427 Roo.bootstrap.HtmlEditor = function(config){
20428 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20429 if (!this.toolbars) {
20430 this.toolbars = [];
20432 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20435 * @event initialize
20436 * Fires when the editor is fully initialized (including the iframe)
20437 * @param {HtmlEditor} this
20442 * Fires when the editor is first receives the focus. Any insertion must wait
20443 * until after this event.
20444 * @param {HtmlEditor} this
20448 * @event beforesync
20449 * Fires before the textarea is updated with content from the editor iframe. Return false
20450 * to cancel the sync.
20451 * @param {HtmlEditor} this
20452 * @param {String} html
20456 * @event beforepush
20457 * Fires before the iframe editor is updated with content from the textarea. Return false
20458 * to cancel the push.
20459 * @param {HtmlEditor} this
20460 * @param {String} html
20465 * Fires when the textarea is updated with content from the editor iframe.
20466 * @param {HtmlEditor} this
20467 * @param {String} html
20472 * Fires when the iframe editor is updated with content from the textarea.
20473 * @param {HtmlEditor} this
20474 * @param {String} html
20478 * @event editmodechange
20479 * Fires when the editor switches edit modes
20480 * @param {HtmlEditor} this
20481 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20483 editmodechange: true,
20485 * @event editorevent
20486 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20487 * @param {HtmlEditor} this
20491 * @event firstfocus
20492 * Fires when on first focus - needed by toolbars..
20493 * @param {HtmlEditor} this
20498 * Auto save the htmlEditor value as a file into Events
20499 * @param {HtmlEditor} this
20503 * @event savedpreview
20504 * preview the saved version of htmlEditor
20505 * @param {HtmlEditor} this
20512 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20516 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20521 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20526 * @cfg {Number} height (in pixels)
20530 * @cfg {Number} width (in pixels)
20535 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20538 stylesheets: false,
20543 // private properties
20544 validationEvent : false,
20546 initialized : false,
20549 onFocus : Roo.emptyFn,
20551 hideMode:'offsets',
20554 tbContainer : false,
20556 toolbarContainer :function() {
20557 return this.wrap.select('.x-html-editor-tb',true).first();
20561 * Protected method that will not generally be called directly. It
20562 * is called when the editor creates its toolbar. Override this method if you need to
20563 * add custom toolbar buttons.
20564 * @param {HtmlEditor} editor
20566 createToolbar : function(){
20568 Roo.log("create toolbars");
20570 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20571 this.toolbars[0].render(this.toolbarContainer());
20575 // if (!editor.toolbars || !editor.toolbars.length) {
20576 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20579 // for (var i =0 ; i < editor.toolbars.length;i++) {
20580 // editor.toolbars[i] = Roo.factory(
20581 // typeof(editor.toolbars[i]) == 'string' ?
20582 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20583 // Roo.bootstrap.HtmlEditor);
20584 // editor.toolbars[i].init(editor);
20590 onRender : function(ct, position)
20592 // Roo.log("Call onRender: " + this.xtype);
20594 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20596 this.wrap = this.inputEl().wrap({
20597 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20600 this.editorcore.onRender(ct, position);
20602 if (this.resizable) {
20603 this.resizeEl = new Roo.Resizable(this.wrap, {
20607 minHeight : this.height,
20608 height: this.height,
20609 handles : this.resizable,
20612 resize : function(r, w, h) {
20613 _t.onResize(w,h); // -something
20619 this.createToolbar(this);
20622 if(!this.width && this.resizable){
20623 this.setSize(this.wrap.getSize());
20625 if (this.resizeEl) {
20626 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20627 // should trigger onReize..
20633 onResize : function(w, h)
20635 Roo.log('resize: ' +w + ',' + h );
20636 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20640 if(this.inputEl() ){
20641 if(typeof w == 'number'){
20642 var aw = w - this.wrap.getFrameWidth('lr');
20643 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20646 if(typeof h == 'number'){
20647 var tbh = -11; // fixme it needs to tool bar size!
20648 for (var i =0; i < this.toolbars.length;i++) {
20649 // fixme - ask toolbars for heights?
20650 tbh += this.toolbars[i].el.getHeight();
20651 //if (this.toolbars[i].footer) {
20652 // tbh += this.toolbars[i].footer.el.getHeight();
20660 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20661 ah -= 5; // knock a few pixes off for look..
20662 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20666 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20667 this.editorcore.onResize(ew,eh);
20672 * Toggles the editor between standard and source edit mode.
20673 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20675 toggleSourceEdit : function(sourceEditMode)
20677 this.editorcore.toggleSourceEdit(sourceEditMode);
20679 if(this.editorcore.sourceEditMode){
20680 Roo.log('editor - showing textarea');
20683 // Roo.log(this.syncValue());
20685 this.inputEl().removeClass(['hide', 'x-hidden']);
20686 this.inputEl().dom.removeAttribute('tabIndex');
20687 this.inputEl().focus();
20689 Roo.log('editor - hiding textarea');
20691 // Roo.log(this.pushValue());
20694 this.inputEl().addClass(['hide', 'x-hidden']);
20695 this.inputEl().dom.setAttribute('tabIndex', -1);
20696 //this.deferFocus();
20699 if(this.resizable){
20700 this.setSize(this.wrap.getSize());
20703 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20706 // private (for BoxComponent)
20707 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20709 // private (for BoxComponent)
20710 getResizeEl : function(){
20714 // private (for BoxComponent)
20715 getPositionEl : function(){
20720 initEvents : function(){
20721 this.originalValue = this.getValue();
20725 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20728 // markInvalid : Roo.emptyFn,
20730 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20733 // clearInvalid : Roo.emptyFn,
20735 setValue : function(v){
20736 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20737 this.editorcore.pushValue();
20742 deferFocus : function(){
20743 this.focus.defer(10, this);
20747 focus : function(){
20748 this.editorcore.focus();
20754 onDestroy : function(){
20760 for (var i =0; i < this.toolbars.length;i++) {
20761 // fixme - ask toolbars for heights?
20762 this.toolbars[i].onDestroy();
20765 this.wrap.dom.innerHTML = '';
20766 this.wrap.remove();
20771 onFirstFocus : function(){
20772 //Roo.log("onFirstFocus");
20773 this.editorcore.onFirstFocus();
20774 for (var i =0; i < this.toolbars.length;i++) {
20775 this.toolbars[i].onFirstFocus();
20781 syncValue : function()
20783 this.editorcore.syncValue();
20786 pushValue : function()
20788 this.editorcore.pushValue();
20792 // hide stuff that is not compatible
20806 * @event specialkey
20810 * @cfg {String} fieldClass @hide
20813 * @cfg {String} focusClass @hide
20816 * @cfg {String} autoCreate @hide
20819 * @cfg {String} inputType @hide
20822 * @cfg {String} invalidClass @hide
20825 * @cfg {String} invalidText @hide
20828 * @cfg {String} msgFx @hide
20831 * @cfg {String} validateOnBlur @hide
20840 Roo.namespace('Roo.bootstrap.htmleditor');
20842 * @class Roo.bootstrap.HtmlEditorToolbar1
20847 new Roo.bootstrap.HtmlEditor({
20850 new Roo.bootstrap.HtmlEditorToolbar1({
20851 disable : { fonts: 1 , format: 1, ..., ... , ...],
20857 * @cfg {Object} disable List of elements to disable..
20858 * @cfg {Array} btns List of additional buttons.
20862 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20865 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20868 Roo.apply(this, config);
20870 // default disabled, based on 'good practice'..
20871 this.disable = this.disable || {};
20872 Roo.applyIf(this.disable, {
20875 specialElements : true
20877 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20879 this.editor = config.editor;
20880 this.editorcore = config.editor.editorcore;
20882 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20884 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20885 // dont call parent... till later.
20887 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
20892 editorcore : false,
20897 "h1","h2","h3","h4","h5","h6",
20899 "abbr", "acronym", "address", "cite", "samp", "var",
20903 onRender : function(ct, position)
20905 // Roo.log("Call onRender: " + this.xtype);
20907 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20909 this.el.dom.style.marginBottom = '0';
20911 var editorcore = this.editorcore;
20912 var editor= this.editor;
20915 var btn = function(id,cmd , toggle, handler){
20917 var event = toggle ? 'toggle' : 'click';
20922 xns: Roo.bootstrap,
20925 enableToggle:toggle !== false,
20927 pressed : toggle ? false : null,
20930 a.listeners[toggle ? 'toggle' : 'click'] = function() {
20931 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
20940 xns: Roo.bootstrap,
20941 glyphicon : 'font',
20945 xns: Roo.bootstrap,
20949 Roo.each(this.formats, function(f) {
20950 style.menu.items.push({
20952 xns: Roo.bootstrap,
20953 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20958 editorcore.insertTag(this.tagname);
20965 children.push(style);
20968 btn('bold',false,true);
20969 btn('italic',false,true);
20970 btn('align-left', 'justifyleft',true);
20971 btn('align-center', 'justifycenter',true);
20972 btn('align-right' , 'justifyright',true);
20973 btn('link', false, false, function(btn) {
20974 //Roo.log("create link?");
20975 var url = prompt(this.createLinkText, this.defaultLinkValue);
20976 if(url && url != 'http:/'+'/'){
20977 this.editorcore.relayCmd('createlink', url);
20980 btn('list','insertunorderedlist',true);
20981 btn('pencil', false,true, function(btn){
20984 this.toggleSourceEdit(btn.pressed);
20990 xns: Roo.bootstrap,
20995 xns: Roo.bootstrap,
21000 cog.menu.items.push({
21002 xns: Roo.bootstrap,
21003 html : Clean styles,
21008 editorcore.insertTag(this.tagname);
21017 this.xtype = 'NavSimplebar';
21019 for(var i=0;i< children.length;i++) {
21021 this.buttons.add(this.addxtypeChild(children[i]));
21025 editor.on('editorevent', this.updateToolbar, this);
21027 onBtnClick : function(id)
21029 this.editorcore.relayCmd(id);
21030 this.editorcore.focus();
21034 * Protected method that will not generally be called directly. It triggers
21035 * a toolbar update by reading the markup state of the current selection in the editor.
21037 updateToolbar: function(){
21039 if(!this.editorcore.activated){
21040 this.editor.onFirstFocus(); // is this neeed?
21044 var btns = this.buttons;
21045 var doc = this.editorcore.doc;
21046 btns.get('bold').setActive(doc.queryCommandState('bold'));
21047 btns.get('italic').setActive(doc.queryCommandState('italic'));
21048 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21050 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21051 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21052 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21054 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21055 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21058 var ans = this.editorcore.getAllAncestors();
21059 if (this.formatCombo) {
21062 var store = this.formatCombo.store;
21063 this.formatCombo.setValue("");
21064 for (var i =0; i < ans.length;i++) {
21065 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21067 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21075 // hides menus... - so this cant be on a menu...
21076 Roo.bootstrap.MenuMgr.hideAll();
21078 Roo.bootstrap.MenuMgr.hideAll();
21079 //this.editorsyncValue();
21081 onFirstFocus: function() {
21082 this.buttons.each(function(item){
21086 toggleSourceEdit : function(sourceEditMode){
21089 if(sourceEditMode){
21090 Roo.log("disabling buttons");
21091 this.buttons.each( function(item){
21092 if(item.cmd != 'pencil'){
21098 Roo.log("enabling buttons");
21099 if(this.editorcore.initialized){
21100 this.buttons.each( function(item){
21106 Roo.log("calling toggole on editor");
21107 // tell the editor that it's been pressed..
21108 this.editor.toggleSourceEdit(sourceEditMode);
21118 * @class Roo.bootstrap.Table.AbstractSelectionModel
21119 * @extends Roo.util.Observable
21120 * Abstract base class for grid SelectionModels. It provides the interface that should be
21121 * implemented by descendant classes. This class should not be directly instantiated.
21124 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21125 this.locked = false;
21126 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21130 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21131 /** @ignore Called by the grid automatically. Do not call directly. */
21132 init : function(grid){
21138 * Locks the selections.
21141 this.locked = true;
21145 * Unlocks the selections.
21147 unlock : function(){
21148 this.locked = false;
21152 * Returns true if the selections are locked.
21153 * @return {Boolean}
21155 isLocked : function(){
21156 return this.locked;
21160 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21161 * @class Roo.bootstrap.Table.RowSelectionModel
21162 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21163 * It supports multiple selections and keyboard selection/navigation.
21165 * @param {Object} config
21168 Roo.bootstrap.Table.RowSelectionModel = function(config){
21169 Roo.apply(this, config);
21170 this.selections = new Roo.util.MixedCollection(false, function(o){
21175 this.lastActive = false;
21179 * @event selectionchange
21180 * Fires when the selection changes
21181 * @param {SelectionModel} this
21183 "selectionchange" : true,
21185 * @event afterselectionchange
21186 * Fires after the selection changes (eg. by key press or clicking)
21187 * @param {SelectionModel} this
21189 "afterselectionchange" : true,
21191 * @event beforerowselect
21192 * Fires when a row is selected being selected, return false to cancel.
21193 * @param {SelectionModel} this
21194 * @param {Number} rowIndex The selected index
21195 * @param {Boolean} keepExisting False if other selections will be cleared
21197 "beforerowselect" : true,
21200 * Fires when a row is selected.
21201 * @param {SelectionModel} this
21202 * @param {Number} rowIndex The selected index
21203 * @param {Roo.data.Record} r The record
21205 "rowselect" : true,
21207 * @event rowdeselect
21208 * Fires when a row is deselected.
21209 * @param {SelectionModel} this
21210 * @param {Number} rowIndex The selected index
21212 "rowdeselect" : true
21214 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21215 this.locked = false;
21218 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21220 * @cfg {Boolean} singleSelect
21221 * True to allow selection of only one row at a time (defaults to false)
21223 singleSelect : false,
21226 initEvents : function(){
21228 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21229 this.grid.on("mousedown", this.handleMouseDown, this);
21230 }else{ // allow click to work like normal
21231 this.grid.on("rowclick", this.handleDragableRowClick, this);
21234 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21235 "up" : function(e){
21237 this.selectPrevious(e.shiftKey);
21238 }else if(this.last !== false && this.lastActive !== false){
21239 var last = this.last;
21240 this.selectRange(this.last, this.lastActive-1);
21241 this.grid.getView().focusRow(this.lastActive);
21242 if(last !== false){
21246 this.selectFirstRow();
21248 this.fireEvent("afterselectionchange", this);
21250 "down" : function(e){
21252 this.selectNext(e.shiftKey);
21253 }else if(this.last !== false && this.lastActive !== false){
21254 var last = this.last;
21255 this.selectRange(this.last, this.lastActive+1);
21256 this.grid.getView().focusRow(this.lastActive);
21257 if(last !== false){
21261 this.selectFirstRow();
21263 this.fireEvent("afterselectionchange", this);
21268 var view = this.grid.view;
21269 view.on("refresh", this.onRefresh, this);
21270 view.on("rowupdated", this.onRowUpdated, this);
21271 view.on("rowremoved", this.onRemove, this);
21275 onRefresh : function(){
21276 var ds = this.grid.dataSource, i, v = this.grid.view;
21277 var s = this.selections;
21278 s.each(function(r){
21279 if((i = ds.indexOfId(r.id)) != -1){
21288 onRemove : function(v, index, r){
21289 this.selections.remove(r);
21293 onRowUpdated : function(v, index, r){
21294 if(this.isSelected(r)){
21295 v.onRowSelect(index);
21301 * @param {Array} records The records to select
21302 * @param {Boolean} keepExisting (optional) True to keep existing selections
21304 selectRecords : function(records, keepExisting){
21306 this.clearSelections();
21308 var ds = this.grid.dataSource;
21309 for(var i = 0, len = records.length; i < len; i++){
21310 this.selectRow(ds.indexOf(records[i]), true);
21315 * Gets the number of selected rows.
21318 getCount : function(){
21319 return this.selections.length;
21323 * Selects the first row in the grid.
21325 selectFirstRow : function(){
21330 * Select the last row.
21331 * @param {Boolean} keepExisting (optional) True to keep existing selections
21333 selectLastRow : function(keepExisting){
21334 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21338 * Selects the row immediately following the last selected row.
21339 * @param {Boolean} keepExisting (optional) True to keep existing selections
21341 selectNext : function(keepExisting){
21342 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21343 this.selectRow(this.last+1, keepExisting);
21344 this.grid.getView().focusRow(this.last);
21349 * Selects the row that precedes the last selected row.
21350 * @param {Boolean} keepExisting (optional) True to keep existing selections
21352 selectPrevious : function(keepExisting){
21354 this.selectRow(this.last-1, keepExisting);
21355 this.grid.getView().focusRow(this.last);
21360 * Returns the selected records
21361 * @return {Array} Array of selected records
21363 getSelections : function(){
21364 return [].concat(this.selections.items);
21368 * Returns the first selected record.
21371 getSelected : function(){
21372 return this.selections.itemAt(0);
21377 * Clears all selections.
21379 clearSelections : function(fast){
21380 if(this.locked) return;
21382 var ds = this.grid.dataSource;
21383 var s = this.selections;
21384 s.each(function(r){
21385 this.deselectRow(ds.indexOfId(r.id));
21389 this.selections.clear();
21396 * Selects all rows.
21398 selectAll : function(){
21399 if(this.locked) return;
21400 this.selections.clear();
21401 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21402 this.selectRow(i, true);
21407 * Returns True if there is a selection.
21408 * @return {Boolean}
21410 hasSelection : function(){
21411 return this.selections.length > 0;
21415 * Returns True if the specified row is selected.
21416 * @param {Number/Record} record The record or index of the record to check
21417 * @return {Boolean}
21419 isSelected : function(index){
21420 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21421 return (r && this.selections.key(r.id) ? true : false);
21425 * Returns True if the specified record id is selected.
21426 * @param {String} id The id of record to check
21427 * @return {Boolean}
21429 isIdSelected : function(id){
21430 return (this.selections.key(id) ? true : false);
21434 handleMouseDown : function(e, t){
21435 var view = this.grid.getView(), rowIndex;
21436 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21439 if(e.shiftKey && this.last !== false){
21440 var last = this.last;
21441 this.selectRange(last, rowIndex, e.ctrlKey);
21442 this.last = last; // reset the last
21443 view.focusRow(rowIndex);
21445 var isSelected = this.isSelected(rowIndex);
21446 if(e.button !== 0 && isSelected){
21447 view.focusRow(rowIndex);
21448 }else if(e.ctrlKey && isSelected){
21449 this.deselectRow(rowIndex);
21450 }else if(!isSelected){
21451 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21452 view.focusRow(rowIndex);
21455 this.fireEvent("afterselectionchange", this);
21458 handleDragableRowClick : function(grid, rowIndex, e)
21460 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21461 this.selectRow(rowIndex, false);
21462 grid.view.focusRow(rowIndex);
21463 this.fireEvent("afterselectionchange", this);
21468 * Selects multiple rows.
21469 * @param {Array} rows Array of the indexes of the row to select
21470 * @param {Boolean} keepExisting (optional) True to keep existing selections
21472 selectRows : function(rows, keepExisting){
21474 this.clearSelections();
21476 for(var i = 0, len = rows.length; i < len; i++){
21477 this.selectRow(rows[i], true);
21482 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21483 * @param {Number} startRow The index of the first row in the range
21484 * @param {Number} endRow The index of the last row in the range
21485 * @param {Boolean} keepExisting (optional) True to retain existing selections
21487 selectRange : function(startRow, endRow, keepExisting){
21488 if(this.locked) return;
21490 this.clearSelections();
21492 if(startRow <= endRow){
21493 for(var i = startRow; i <= endRow; i++){
21494 this.selectRow(i, true);
21497 for(var i = startRow; i >= endRow; i--){
21498 this.selectRow(i, true);
21504 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21505 * @param {Number} startRow The index of the first row in the range
21506 * @param {Number} endRow The index of the last row in the range
21508 deselectRange : function(startRow, endRow, preventViewNotify){
21509 if(this.locked) return;
21510 for(var i = startRow; i <= endRow; i++){
21511 this.deselectRow(i, preventViewNotify);
21517 * @param {Number} row The index of the row to select
21518 * @param {Boolean} keepExisting (optional) True to keep existing selections
21520 selectRow : function(index, keepExisting, preventViewNotify){
21521 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21522 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21523 if(!keepExisting || this.singleSelect){
21524 this.clearSelections();
21526 var r = this.grid.dataSource.getAt(index);
21527 this.selections.add(r);
21528 this.last = this.lastActive = index;
21529 if(!preventViewNotify){
21530 this.grid.getView().onRowSelect(index);
21532 this.fireEvent("rowselect", this, index, r);
21533 this.fireEvent("selectionchange", this);
21539 * @param {Number} row The index of the row to deselect
21541 deselectRow : function(index, preventViewNotify){
21542 if(this.locked) return;
21543 if(this.last == index){
21546 if(this.lastActive == index){
21547 this.lastActive = false;
21549 var r = this.grid.dataSource.getAt(index);
21550 this.selections.remove(r);
21551 if(!preventViewNotify){
21552 this.grid.getView().onRowDeselect(index);
21554 this.fireEvent("rowdeselect", this, index);
21555 this.fireEvent("selectionchange", this);
21559 restoreLast : function(){
21561 this.last = this._last;
21566 acceptsNav : function(row, col, cm){
21567 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21571 onEditorKey : function(field, e){
21572 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21577 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21579 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21581 }else if(k == e.ENTER && !e.ctrlKey){
21585 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21587 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21589 }else if(k == e.ESC){
21593 g.startEditing(newCell[0], newCell[1]);
21598 * Ext JS Library 1.1.1
21599 * Copyright(c) 2006-2007, Ext JS, LLC.
21601 * Originally Released Under LGPL - original licence link has changed is not relivant.
21604 * <script type="text/javascript">
21608 * @class Roo.bootstrap.PagingToolbar
21610 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21612 * Create a new PagingToolbar
21613 * @param {Object} config The config object
21615 Roo.bootstrap.PagingToolbar = function(config)
21617 // old args format still supported... - xtype is prefered..
21618 // created from xtype...
21619 var ds = config.dataSource;
21620 this.toolbarItems = [];
21621 if (config.items) {
21622 this.toolbarItems = config.items;
21623 // config.items = [];
21626 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21633 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21637 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21639 * @cfg {Roo.data.Store} dataSource
21640 * The underlying data store providing the paged data
21643 * @cfg {String/HTMLElement/Element} container
21644 * container The id or element that will contain the toolbar
21647 * @cfg {Boolean} displayInfo
21648 * True to display the displayMsg (defaults to false)
21651 * @cfg {Number} pageSize
21652 * The number of records to display per page (defaults to 20)
21656 * @cfg {String} displayMsg
21657 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21659 displayMsg : 'Displaying {0} - {1} of {2}',
21661 * @cfg {String} emptyMsg
21662 * The message to display when no records are found (defaults to "No data to display")
21664 emptyMsg : 'No data to display',
21666 * Customizable piece of the default paging text (defaults to "Page")
21669 beforePageText : "Page",
21671 * Customizable piece of the default paging text (defaults to "of %0")
21674 afterPageText : "of {0}",
21676 * Customizable piece of the default paging text (defaults to "First Page")
21679 firstText : "First Page",
21681 * Customizable piece of the default paging text (defaults to "Previous Page")
21684 prevText : "Previous Page",
21686 * Customizable piece of the default paging text (defaults to "Next Page")
21689 nextText : "Next Page",
21691 * Customizable piece of the default paging text (defaults to "Last Page")
21694 lastText : "Last Page",
21696 * Customizable piece of the default paging text (defaults to "Refresh")
21699 refreshText : "Refresh",
21703 onRender : function(ct, position)
21705 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21706 this.navgroup.parentId = this.id;
21707 this.navgroup.onRender(this.el, null);
21708 // add the buttons to the navgroup
21710 if(this.displayInfo){
21711 Roo.log(this.el.select('ul.navbar-nav',true).first());
21712 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21713 this.displayEl = this.el.select('.x-paging-info', true).first();
21714 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21715 // this.displayEl = navel.el.select('span',true).first();
21721 Roo.each(_this.buttons, function(e){
21722 Roo.factory(e).onRender(_this.el, null);
21726 Roo.each(_this.toolbarItems, function(e) {
21727 _this.navgroup.addItem(e);
21731 this.first = this.navgroup.addItem({
21732 tooltip: this.firstText,
21734 icon : 'fa fa-backward',
21736 preventDefault: true,
21737 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21740 this.prev = this.navgroup.addItem({
21741 tooltip: this.prevText,
21743 icon : 'fa fa-step-backward',
21745 preventDefault: true,
21746 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21748 //this.addSeparator();
21751 var field = this.navgroup.addItem( {
21753 cls : 'x-paging-position',
21755 html : this.beforePageText +
21756 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21757 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21760 this.field = field.el.select('input', true).first();
21761 this.field.on("keydown", this.onPagingKeydown, this);
21762 this.field.on("focus", function(){this.dom.select();});
21765 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21766 //this.field.setHeight(18);
21767 //this.addSeparator();
21768 this.next = this.navgroup.addItem({
21769 tooltip: this.nextText,
21771 html : ' <i class="fa fa-step-forward">',
21773 preventDefault: true,
21774 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21776 this.last = this.navgroup.addItem({
21777 tooltip: this.lastText,
21778 icon : 'fa fa-forward',
21781 preventDefault: true,
21782 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21784 //this.addSeparator();
21785 this.loading = this.navgroup.addItem({
21786 tooltip: this.refreshText,
21787 icon: 'fa fa-refresh',
21788 preventDefault: true,
21789 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21795 updateInfo : function(){
21796 if(this.displayEl){
21797 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21798 var msg = count == 0 ?
21802 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21804 this.displayEl.update(msg);
21809 onLoad : function(ds, r, o){
21810 this.cursor = o.params ? o.params.start : 0;
21811 var d = this.getPageData(),
21815 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21816 this.field.dom.value = ap;
21817 this.first.setDisabled(ap == 1);
21818 this.prev.setDisabled(ap == 1);
21819 this.next.setDisabled(ap == ps);
21820 this.last.setDisabled(ap == ps);
21821 this.loading.enable();
21826 getPageData : function(){
21827 var total = this.ds.getTotalCount();
21830 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21831 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21836 onLoadError : function(){
21837 this.loading.enable();
21841 onPagingKeydown : function(e){
21842 var k = e.getKey();
21843 var d = this.getPageData();
21845 var v = this.field.dom.value, pageNum;
21846 if(!v || isNaN(pageNum = parseInt(v, 10))){
21847 this.field.dom.value = d.activePage;
21850 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21851 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21854 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))
21856 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21857 this.field.dom.value = pageNum;
21858 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21861 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21863 var v = this.field.dom.value, pageNum;
21864 var increment = (e.shiftKey) ? 10 : 1;
21865 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21867 if(!v || isNaN(pageNum = parseInt(v, 10))) {
21868 this.field.dom.value = d.activePage;
21871 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21873 this.field.dom.value = parseInt(v, 10) + increment;
21874 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21875 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21882 beforeLoad : function(){
21884 this.loading.disable();
21889 onClick : function(which){
21898 ds.load({params:{start: 0, limit: this.pageSize}});
21901 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21904 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21907 var total = ds.getTotalCount();
21908 var extra = total % this.pageSize;
21909 var lastStart = extra ? (total - extra) : total-this.pageSize;
21910 ds.load({params:{start: lastStart, limit: this.pageSize}});
21913 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21919 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21920 * @param {Roo.data.Store} store The data store to unbind
21922 unbind : function(ds){
21923 ds.un("beforeload", this.beforeLoad, this);
21924 ds.un("load", this.onLoad, this);
21925 ds.un("loadexception", this.onLoadError, this);
21926 ds.un("remove", this.updateInfo, this);
21927 ds.un("add", this.updateInfo, this);
21928 this.ds = undefined;
21932 * Binds the paging toolbar to the specified {@link Roo.data.Store}
21933 * @param {Roo.data.Store} store The data store to bind
21935 bind : function(ds){
21936 ds.on("beforeload", this.beforeLoad, this);
21937 ds.on("load", this.onLoad, this);
21938 ds.on("loadexception", this.onLoadError, this);
21939 ds.on("remove", this.updateInfo, this);
21940 ds.on("add", this.updateInfo, this);
21951 * @class Roo.bootstrap.MessageBar
21952 * @extends Roo.bootstrap.Component
21953 * Bootstrap MessageBar class
21954 * @cfg {String} html contents of the MessageBar
21955 * @cfg {String} weight (info | success | warning | danger) default info
21956 * @cfg {String} beforeClass insert the bar before the given class
21957 * @cfg {Boolean} closable (true | false) default false
21958 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21961 * Create a new Element
21962 * @param {Object} config The config object
21965 Roo.bootstrap.MessageBar = function(config){
21966 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21969 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
21975 beforeClass: 'bootstrap-sticky-wrap',
21977 getAutoCreate : function(){
21981 cls: 'alert alert-dismissable alert-' + this.weight,
21986 html: this.html || ''
21992 cfg.cls += ' alert-messages-fixed';
22006 onRender : function(ct, position)
22008 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22011 var cfg = Roo.apply({}, this.getAutoCreate());
22015 cfg.cls += ' ' + this.cls;
22018 cfg.style = this.style;
22020 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22022 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22025 this.el.select('>button.close').on('click', this.hide, this);
22031 if (!this.rendered) {
22037 this.fireEvent('show', this);
22043 if (!this.rendered) {
22049 this.fireEvent('hide', this);
22052 update : function()
22054 // var e = this.el.dom.firstChild;
22056 // if(this.closable){
22057 // e = e.nextSibling;
22060 // e.data = this.html || '';
22062 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22078 * @class Roo.bootstrap.Graph
22079 * @extends Roo.bootstrap.Component
22080 * Bootstrap Graph class
22084 @cfg {String} graphtype bar | vbar | pie
22085 @cfg {number} g_x coodinator | centre x (pie)
22086 @cfg {number} g_y coodinator | centre y (pie)
22087 @cfg {number} g_r radius (pie)
22088 @cfg {number} g_height height of the chart (respected by all elements in the set)
22089 @cfg {number} g_width width of the chart (respected by all elements in the set)
22090 @cfg {Object} title The title of the chart
22093 -opts (object) options for the chart
22095 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22096 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22098 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.
22099 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22101 o stretch (boolean)
22103 -opts (object) options for the pie
22106 o startAngle (number)
22107 o endAngle (number)
22111 * Create a new Input
22112 * @param {Object} config The config object
22115 Roo.bootstrap.Graph = function(config){
22116 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22122 * The img click event for the img.
22123 * @param {Roo.EventObject} e
22129 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22140 //g_colors: this.colors,
22147 getAutoCreate : function(){
22158 onRender : function(ct,position){
22159 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22160 this.raphael = Raphael(this.el.dom);
22162 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22163 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22164 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22165 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22167 r.text(160, 10, "Single Series Chart").attr(txtattr);
22168 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22169 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22170 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22172 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22173 r.barchart(330, 10, 300, 220, data1);
22174 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22175 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22178 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22179 // r.barchart(30, 30, 560, 250, xdata, {
22180 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22181 // axis : "0 0 1 1",
22182 // axisxlabels : xdata
22183 // //yvalues : cols,
22186 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22188 // this.load(null,xdata,{
22189 // axis : "0 0 1 1",
22190 // axisxlabels : xdata
22195 load : function(graphtype,xdata,opts){
22196 this.raphael.clear();
22198 graphtype = this.graphtype;
22203 var r = this.raphael,
22204 fin = function () {
22205 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22207 fout = function () {
22208 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22210 pfin = function() {
22211 this.sector.stop();
22212 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22215 this.label[0].stop();
22216 this.label[0].attr({ r: 7.5 });
22217 this.label[1].attr({ "font-weight": 800 });
22220 pfout = function() {
22221 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22224 this.label[0].animate({ r: 5 }, 500, "bounce");
22225 this.label[1].attr({ "font-weight": 400 });
22231 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22234 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22237 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22238 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22240 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22247 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22252 setTitle: function(o)
22257 initEvents: function() {
22260 this.el.on('click', this.onClick, this);
22264 onClick : function(e)
22266 Roo.log('img onclick');
22267 this.fireEvent('click', this, e);
22279 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22282 * @class Roo.bootstrap.dash.NumberBox
22283 * @extends Roo.bootstrap.Component
22284 * Bootstrap NumberBox class
22285 * @cfg {String} headline Box headline
22286 * @cfg {String} content Box content
22287 * @cfg {String} icon Box icon
22288 * @cfg {String} footer Footer text
22289 * @cfg {String} fhref Footer href
22292 * Create a new NumberBox
22293 * @param {Object} config The config object
22297 Roo.bootstrap.dash.NumberBox = function(config){
22298 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22302 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22311 getAutoCreate : function(){
22315 cls : 'small-box ',
22323 cls : 'roo-headline',
22324 html : this.headline
22328 cls : 'roo-content',
22329 html : this.content
22343 cls : 'ion ' + this.icon
22352 cls : 'small-box-footer',
22353 href : this.fhref || '#',
22357 cfg.cn.push(footer);
22364 onRender : function(ct,position){
22365 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22372 setHeadline: function (value)
22374 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22377 setFooter: function (value, href)
22379 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22382 this.el.select('a.small-box-footer',true).first().attr('href', href);
22387 setContent: function (value)
22389 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22392 initEvents: function()
22406 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22409 * @class Roo.bootstrap.dash.TabBox
22410 * @extends Roo.bootstrap.Component
22411 * Bootstrap TabBox class
22412 * @cfg {String} title Title of the TabBox
22413 * @cfg {String} icon Icon of the TabBox
22414 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22415 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22418 * Create a new TabBox
22419 * @param {Object} config The config object
22423 Roo.bootstrap.dash.TabBox = function(config){
22424 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22429 * When a pane is added
22430 * @param {Roo.bootstrap.dash.TabPane} pane
22434 * @event activatepane
22435 * When a pane is activated
22436 * @param {Roo.bootstrap.dash.TabPane} pane
22438 "activatepane" : true
22446 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22451 tabScrollable : false,
22453 getChildContainer : function()
22455 return this.el.select('.tab-content', true).first();
22458 getAutoCreate : function(){
22462 cls: 'pull-left header',
22470 cls: 'fa ' + this.icon
22476 cls: 'nav nav-tabs pull-right',
22482 if(this.tabScrollable){
22489 cls: 'nav nav-tabs pull-right',
22500 cls: 'nav-tabs-custom',
22505 cls: 'tab-content no-padding',
22513 initEvents : function()
22515 //Roo.log('add add pane handler');
22516 this.on('addpane', this.onAddPane, this);
22519 * Updates the box title
22520 * @param {String} html to set the title to.
22522 setTitle : function(value)
22524 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22526 onAddPane : function(pane)
22528 this.panes.push(pane);
22529 //Roo.log('addpane');
22531 // tabs are rendere left to right..
22532 if(!this.showtabs){
22536 var ctr = this.el.select('.nav-tabs', true).first();
22539 var existing = ctr.select('.nav-tab',true);
22540 var qty = existing.getCount();;
22543 var tab = ctr.createChild({
22545 cls : 'nav-tab' + (qty ? '' : ' active'),
22553 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22556 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22558 pane.el.addClass('active');
22563 onTabClick : function(ev,un,ob,pane)
22565 //Roo.log('tab - prev default');
22566 ev.preventDefault();
22569 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22570 pane.tab.addClass('active');
22571 //Roo.log(pane.title);
22572 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22573 // technically we should have a deactivate event.. but maybe add later.
22574 // and it should not de-activate the selected tab...
22575 this.fireEvent('activatepane', pane);
22576 pane.el.addClass('active');
22577 pane.fireEvent('activate');
22582 getActivePane : function()
22585 Roo.each(this.panes, function(p) {
22586 if(p.el.hasClass('active')){
22607 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22609 * @class Roo.bootstrap.TabPane
22610 * @extends Roo.bootstrap.Component
22611 * Bootstrap TabPane class
22612 * @cfg {Boolean} active (false | true) Default false
22613 * @cfg {String} title title of panel
22617 * Create a new TabPane
22618 * @param {Object} config The config object
22621 Roo.bootstrap.dash.TabPane = function(config){
22622 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22628 * When a pane is activated
22629 * @param {Roo.bootstrap.dash.TabPane} pane
22636 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22641 // the tabBox that this is attached to.
22644 getAutoCreate : function()
22652 cfg.cls += ' active';
22657 initEvents : function()
22659 //Roo.log('trigger add pane handler');
22660 this.parent().fireEvent('addpane', this)
22664 * Updates the tab title
22665 * @param {String} html to set the title to.
22667 setTitle: function(str)
22673 this.tab.select('a', true).first().dom.innerHTML = str;
22690 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22693 * @class Roo.bootstrap.menu.Menu
22694 * @extends Roo.bootstrap.Component
22695 * Bootstrap Menu class - container for Menu
22696 * @cfg {String} html Text of the menu
22697 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22698 * @cfg {String} icon Font awesome icon
22699 * @cfg {String} pos Menu align to (top | bottom) default bottom
22703 * Create a new Menu
22704 * @param {Object} config The config object
22708 Roo.bootstrap.menu.Menu = function(config){
22709 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22713 * @event beforeshow
22714 * Fires before this menu is displayed
22715 * @param {Roo.bootstrap.menu.Menu} this
22719 * @event beforehide
22720 * Fires before this menu is hidden
22721 * @param {Roo.bootstrap.menu.Menu} this
22726 * Fires after this menu is displayed
22727 * @param {Roo.bootstrap.menu.Menu} this
22732 * Fires after this menu is hidden
22733 * @param {Roo.bootstrap.menu.Menu} this
22738 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22739 * @param {Roo.bootstrap.menu.Menu} this
22740 * @param {Roo.EventObject} e
22747 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22751 weight : 'default',
22756 getChildContainer : function() {
22757 if(this.isSubMenu){
22761 return this.el.select('ul.dropdown-menu', true).first();
22764 getAutoCreate : function()
22769 cls : 'roo-menu-text',
22777 cls : 'fa ' + this.icon
22788 cls : 'dropdown-button btn btn-' + this.weight,
22793 cls : 'dropdown-toggle btn btn-' + this.weight,
22803 cls : 'dropdown-menu'
22809 if(this.pos == 'top'){
22810 cfg.cls += ' dropup';
22813 if(this.isSubMenu){
22816 cls : 'dropdown-menu'
22823 onRender : function(ct, position)
22825 this.isSubMenu = ct.hasClass('dropdown-submenu');
22827 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22830 initEvents : function()
22832 if(this.isSubMenu){
22836 this.hidden = true;
22838 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22839 this.triggerEl.on('click', this.onTriggerPress, this);
22841 this.buttonEl = this.el.select('button.dropdown-button', true).first();
22842 this.buttonEl.on('click', this.onClick, this);
22848 if(this.isSubMenu){
22852 return this.el.select('ul.dropdown-menu', true).first();
22855 onClick : function(e)
22857 this.fireEvent("click", this, e);
22860 onTriggerPress : function(e)
22862 if (this.isVisible()) {
22869 isVisible : function(){
22870 return !this.hidden;
22875 this.fireEvent("beforeshow", this);
22877 this.hidden = false;
22878 this.el.addClass('open');
22880 Roo.get(document).on("mouseup", this.onMouseUp, this);
22882 this.fireEvent("show", this);
22889 this.fireEvent("beforehide", this);
22891 this.hidden = true;
22892 this.el.removeClass('open');
22894 Roo.get(document).un("mouseup", this.onMouseUp);
22896 this.fireEvent("hide", this);
22899 onMouseUp : function()
22913 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22916 * @class Roo.bootstrap.menu.Item
22917 * @extends Roo.bootstrap.Component
22918 * Bootstrap MenuItem class
22919 * @cfg {Boolean} submenu (true | false) default false
22920 * @cfg {String} html text of the item
22921 * @cfg {String} href the link
22922 * @cfg {Boolean} disable (true | false) default false
22923 * @cfg {Boolean} preventDefault (true | false) default true
22924 * @cfg {String} icon Font awesome icon
22925 * @cfg {String} pos Submenu align to (left | right) default right
22929 * Create a new Item
22930 * @param {Object} config The config object
22934 Roo.bootstrap.menu.Item = function(config){
22935 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22939 * Fires when the mouse is hovering over this menu
22940 * @param {Roo.bootstrap.menu.Item} this
22941 * @param {Roo.EventObject} e
22946 * Fires when the mouse exits this menu
22947 * @param {Roo.bootstrap.menu.Item} this
22948 * @param {Roo.EventObject} e
22954 * The raw click event for the entire grid.
22955 * @param {Roo.EventObject} e
22961 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
22966 preventDefault: true,
22971 getAutoCreate : function()
22976 cls : 'roo-menu-item-text',
22984 cls : 'fa ' + this.icon
22993 href : this.href || '#',
23000 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23004 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23006 if(this.pos == 'left'){
23007 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23014 initEvents : function()
23016 this.el.on('mouseover', this.onMouseOver, this);
23017 this.el.on('mouseout', this.onMouseOut, this);
23019 this.el.select('a', true).first().on('click', this.onClick, this);
23023 onClick : function(e)
23025 if(this.preventDefault){
23026 e.preventDefault();
23029 this.fireEvent("click", this, e);
23032 onMouseOver : function(e)
23034 if(this.submenu && this.pos == 'left'){
23035 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23038 this.fireEvent("mouseover", this, e);
23041 onMouseOut : function(e)
23043 this.fireEvent("mouseout", this, e);
23055 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23058 * @class Roo.bootstrap.menu.Separator
23059 * @extends Roo.bootstrap.Component
23060 * Bootstrap Separator class
23063 * Create a new Separator
23064 * @param {Object} config The config object
23068 Roo.bootstrap.menu.Separator = function(config){
23069 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23072 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23074 getAutoCreate : function(){
23095 * @class Roo.bootstrap.Tooltip
23096 * Bootstrap Tooltip class
23097 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23098 * to determine which dom element triggers the tooltip.
23100 * It needs to add support for additional attributes like tooltip-position
23103 * Create a new Toolti
23104 * @param {Object} config The config object
23107 Roo.bootstrap.Tooltip = function(config){
23108 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23111 Roo.apply(Roo.bootstrap.Tooltip, {
23113 * @function init initialize tooltip monitoring.
23117 currentTip : false,
23118 currentRegion : false,
23124 Roo.get(document).on('mouseover', this.enter ,this);
23125 Roo.get(document).on('mouseout', this.leave, this);
23128 this.currentTip = new Roo.bootstrap.Tooltip();
23131 enter : function(ev)
23133 var dom = ev.getTarget();
23135 //Roo.log(['enter',dom]);
23136 var el = Roo.fly(dom);
23137 if (this.currentEl) {
23139 //Roo.log(this.currentEl);
23140 //Roo.log(this.currentEl.contains(dom));
23141 if (this.currentEl == el) {
23144 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23152 if (this.currentTip.el) {
23153 this.currentTip.el.hide(); // force hiding...
23158 // you can not look for children, as if el is the body.. then everythign is the child..
23159 if (!el.attr('tooltip')) { //
23160 if (!el.select("[tooltip]").elements.length) {
23163 // is the mouse over this child...?
23164 bindEl = el.select("[tooltip]").first();
23165 var xy = ev.getXY();
23166 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23167 //Roo.log("not in region.");
23170 //Roo.log("child element over..");
23173 this.currentEl = bindEl;
23174 this.currentTip.bind(bindEl);
23175 this.currentRegion = Roo.lib.Region.getRegion(dom);
23176 this.currentTip.enter();
23179 leave : function(ev)
23181 var dom = ev.getTarget();
23182 //Roo.log(['leave',dom]);
23183 if (!this.currentEl) {
23188 if (dom != this.currentEl.dom) {
23191 var xy = ev.getXY();
23192 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23195 // only activate leave if mouse cursor is outside... bounding box..
23200 if (this.currentTip) {
23201 this.currentTip.leave();
23203 //Roo.log('clear currentEl');
23204 this.currentEl = false;
23209 'left' : ['r-l', [-2,0], 'right'],
23210 'right' : ['l-r', [2,0], 'left'],
23211 'bottom' : ['t-b', [0,2], 'top'],
23212 'top' : [ 'b-t', [0,-2], 'bottom']
23218 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23223 delay : null, // can be { show : 300 , hide: 500}
23227 hoverState : null, //???
23229 placement : 'bottom',
23231 getAutoCreate : function(){
23238 cls : 'tooltip-arrow'
23241 cls : 'tooltip-inner'
23248 bind : function(el)
23254 enter : function () {
23256 if (this.timeout != null) {
23257 clearTimeout(this.timeout);
23260 this.hoverState = 'in';
23261 //Roo.log("enter - show");
23262 if (!this.delay || !this.delay.show) {
23267 this.timeout = setTimeout(function () {
23268 if (_t.hoverState == 'in') {
23271 }, this.delay.show);
23275 clearTimeout(this.timeout);
23277 this.hoverState = 'out';
23278 if (!this.delay || !this.delay.hide) {
23284 this.timeout = setTimeout(function () {
23285 //Roo.log("leave - timeout");
23287 if (_t.hoverState == 'out') {
23289 Roo.bootstrap.Tooltip.currentEl = false;
23297 this.render(document.body);
23300 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23302 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23304 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23306 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23308 var placement = typeof this.placement == 'function' ?
23309 this.placement.call(this, this.el, on_el) :
23312 var autoToken = /\s?auto?\s?/i;
23313 var autoPlace = autoToken.test(placement);
23315 placement = placement.replace(autoToken, '') || 'top';
23319 //this.el.setXY([0,0]);
23321 //this.el.dom.style.display='block';
23322 this.el.addClass(placement);
23324 //this.el.appendTo(on_el);
23326 var p = this.getPosition();
23327 var box = this.el.getBox();
23332 var align = Roo.bootstrap.Tooltip.alignment[placement];
23333 this.el.alignTo(this.bindEl, align[0],align[1]);
23334 //var arrow = this.el.select('.arrow',true).first();
23335 //arrow.set(align[2],
23337 this.el.addClass('in fade');
23338 this.hoverState = null;
23340 if (this.el.hasClass('fade')) {
23351 //this.el.setXY([0,0]);
23352 this.el.removeClass('in');
23368 * @class Roo.bootstrap.LocationPicker
23369 * @extends Roo.bootstrap.Component
23370 * Bootstrap LocationPicker class
23371 * @cfg {Number} latitude Position when init default 0
23372 * @cfg {Number} longitude Position when init default 0
23373 * @cfg {Number} zoom default 15
23374 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23375 * @cfg {Boolean} mapTypeControl default false
23376 * @cfg {Boolean} disableDoubleClickZoom default false
23377 * @cfg {Boolean} scrollwheel default true
23378 * @cfg {Boolean} streetViewControl default false
23379 * @cfg {Number} radius default 0
23380 * @cfg {String} locationName
23381 * @cfg {Boolean} draggable default true
23382 * @cfg {Boolean} enableAutocomplete default false
23383 * @cfg {Boolean} enableReverseGeocode default true
23384 * @cfg {String} markerTitle
23387 * Create a new LocationPicker
23388 * @param {Object} config The config object
23392 Roo.bootstrap.LocationPicker = function(config){
23394 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23399 * Fires when the picker initialized.
23400 * @param {Roo.bootstrap.LocationPicker} this
23401 * @param {Google Location} location
23405 * @event positionchanged
23406 * Fires when the picker position changed.
23407 * @param {Roo.bootstrap.LocationPicker} this
23408 * @param {Google Location} location
23410 positionchanged : true,
23413 * Fires when the map resize.
23414 * @param {Roo.bootstrap.LocationPicker} this
23419 * Fires when the map show.
23420 * @param {Roo.bootstrap.LocationPicker} this
23425 * Fires when the map hide.
23426 * @param {Roo.bootstrap.LocationPicker} this
23431 * Fires when click the map.
23432 * @param {Roo.bootstrap.LocationPicker} this
23433 * @param {Map event} e
23437 * @event mapRightClick
23438 * Fires when right click the map.
23439 * @param {Roo.bootstrap.LocationPicker} this
23440 * @param {Map event} e
23442 mapRightClick : true,
23444 * @event markerClick
23445 * Fires when click the marker.
23446 * @param {Roo.bootstrap.LocationPicker} this
23447 * @param {Map event} e
23449 markerClick : true,
23451 * @event markerRightClick
23452 * Fires when right click the marker.
23453 * @param {Roo.bootstrap.LocationPicker} this
23454 * @param {Map event} e
23456 markerRightClick : true,
23458 * @event OverlayViewDraw
23459 * Fires when OverlayView Draw
23460 * @param {Roo.bootstrap.LocationPicker} this
23462 OverlayViewDraw : true,
23464 * @event OverlayViewOnAdd
23465 * Fires when OverlayView Draw
23466 * @param {Roo.bootstrap.LocationPicker} this
23468 OverlayViewOnAdd : true,
23470 * @event OverlayViewOnRemove
23471 * Fires when OverlayView Draw
23472 * @param {Roo.bootstrap.LocationPicker} this
23474 OverlayViewOnRemove : true,
23476 * @event OverlayViewShow
23477 * Fires when OverlayView Draw
23478 * @param {Roo.bootstrap.LocationPicker} this
23479 * @param {Pixel} cpx
23481 OverlayViewShow : true,
23483 * @event OverlayViewHide
23484 * Fires when OverlayView Draw
23485 * @param {Roo.bootstrap.LocationPicker} this
23487 OverlayViewHide : true
23492 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23494 gMapContext: false,
23500 mapTypeControl: false,
23501 disableDoubleClickZoom: false,
23503 streetViewControl: false,
23507 enableAutocomplete: false,
23508 enableReverseGeocode: true,
23511 getAutoCreate: function()
23516 cls: 'roo-location-picker'
23522 initEvents: function(ct, position)
23524 if(!this.el.getWidth() || this.isApplied()){
23528 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23533 initial: function()
23535 if(!this.mapTypeId){
23536 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23539 this.gMapContext = this.GMapContext();
23541 this.initOverlayView();
23543 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23547 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23548 _this.setPosition(_this.gMapContext.marker.position);
23551 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23552 _this.fireEvent('mapClick', this, event);
23556 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23557 _this.fireEvent('mapRightClick', this, event);
23561 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23562 _this.fireEvent('markerClick', this, event);
23566 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23567 _this.fireEvent('markerRightClick', this, event);
23571 this.setPosition(this.gMapContext.location);
23573 this.fireEvent('initial', this, this.gMapContext.location);
23576 initOverlayView: function()
23580 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23584 _this.fireEvent('OverlayViewDraw', _this);
23589 _this.fireEvent('OverlayViewOnAdd', _this);
23592 onRemove: function()
23594 _this.fireEvent('OverlayViewOnRemove', _this);
23597 show: function(cpx)
23599 _this.fireEvent('OverlayViewShow', _this, cpx);
23604 _this.fireEvent('OverlayViewHide', _this);
23610 fromLatLngToContainerPixel: function(event)
23612 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23615 isApplied: function()
23617 return this.getGmapContext() == false ? false : true;
23620 getGmapContext: function()
23622 return this.gMapContext
23625 GMapContext: function()
23627 var position = new google.maps.LatLng(this.latitude, this.longitude);
23629 var _map = new google.maps.Map(this.el.dom, {
23632 mapTypeId: this.mapTypeId,
23633 mapTypeControl: this.mapTypeControl,
23634 disableDoubleClickZoom: this.disableDoubleClickZoom,
23635 scrollwheel: this.scrollwheel,
23636 streetViewControl: this.streetViewControl,
23637 locationName: this.locationName,
23638 draggable: this.draggable,
23639 enableAutocomplete: this.enableAutocomplete,
23640 enableReverseGeocode: this.enableReverseGeocode
23643 var _marker = new google.maps.Marker({
23644 position: position,
23646 title: this.markerTitle,
23647 draggable: this.draggable
23654 location: position,
23655 radius: this.radius,
23656 locationName: this.locationName,
23657 addressComponents: {
23658 formatted_address: null,
23659 addressLine1: null,
23660 addressLine2: null,
23662 streetNumber: null,
23666 stateOrProvince: null
23669 domContainer: this.el.dom,
23670 geodecoder: new google.maps.Geocoder()
23674 drawCircle: function(center, radius, options)
23676 if (this.gMapContext.circle != null) {
23677 this.gMapContext.circle.setMap(null);
23681 options = Roo.apply({}, options, {
23682 strokeColor: "#0000FF",
23683 strokeOpacity: .35,
23685 fillColor: "#0000FF",
23689 options.map = this.gMapContext.map;
23690 options.radius = radius;
23691 options.center = center;
23692 this.gMapContext.circle = new google.maps.Circle(options);
23693 return this.gMapContext.circle;
23699 setPosition: function(location)
23701 this.gMapContext.location = location;
23702 this.gMapContext.marker.setPosition(location);
23703 this.gMapContext.map.panTo(location);
23704 this.drawCircle(location, this.gMapContext.radius, {});
23708 if (this.gMapContext.settings.enableReverseGeocode) {
23709 this.gMapContext.geodecoder.geocode({
23710 latLng: this.gMapContext.location
23711 }, function(results, status) {
23713 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23714 _this.gMapContext.locationName = results[0].formatted_address;
23715 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23717 _this.fireEvent('positionchanged', this, location);
23724 this.fireEvent('positionchanged', this, location);
23729 google.maps.event.trigger(this.gMapContext.map, "resize");
23731 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23733 this.fireEvent('resize', this);
23736 setPositionByLatLng: function(latitude, longitude)
23738 this.setPosition(new google.maps.LatLng(latitude, longitude));
23741 getCurrentPosition: function()
23744 latitude: this.gMapContext.location.lat(),
23745 longitude: this.gMapContext.location.lng()
23749 getAddressName: function()
23751 return this.gMapContext.locationName;
23754 getAddressComponents: function()
23756 return this.gMapContext.addressComponents;
23759 address_component_from_google_geocode: function(address_components)
23763 for (var i = 0; i < address_components.length; i++) {
23764 var component = address_components[i];
23765 if (component.types.indexOf("postal_code") >= 0) {
23766 result.postalCode = component.short_name;
23767 } else if (component.types.indexOf("street_number") >= 0) {
23768 result.streetNumber = component.short_name;
23769 } else if (component.types.indexOf("route") >= 0) {
23770 result.streetName = component.short_name;
23771 } else if (component.types.indexOf("neighborhood") >= 0) {
23772 result.city = component.short_name;
23773 } else if (component.types.indexOf("locality") >= 0) {
23774 result.city = component.short_name;
23775 } else if (component.types.indexOf("sublocality") >= 0) {
23776 result.district = component.short_name;
23777 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23778 result.stateOrProvince = component.short_name;
23779 } else if (component.types.indexOf("country") >= 0) {
23780 result.country = component.short_name;
23784 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23785 result.addressLine2 = "";
23789 setZoomLevel: function(zoom)
23791 this.gMapContext.map.setZoom(zoom);
23804 this.fireEvent('show', this);
23815 this.fireEvent('hide', this);
23820 Roo.apply(Roo.bootstrap.LocationPicker, {
23822 OverlayView : function(map, options)
23824 options = options || {};
23838 * @class Roo.bootstrap.Alert
23839 * @extends Roo.bootstrap.Component
23840 * Bootstrap Alert class
23841 * @cfg {String} title The title of alert
23842 * @cfg {String} html The content of alert
23843 * @cfg {String} weight ( success | info | warning | danger )
23844 * @cfg {String} faicon font-awesomeicon
23847 * Create a new alert
23848 * @param {Object} config The config object
23852 Roo.bootstrap.Alert = function(config){
23853 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23857 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
23864 getAutoCreate : function()
23873 cls : 'roo-alert-icon'
23878 cls : 'roo-alert-title',
23883 cls : 'roo-alert-text',
23890 cfg.cn[0].cls += ' fa ' + this.faicon;
23894 cfg.cls += ' alert-' + this.weight;
23900 initEvents: function()
23902 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23905 setTitle : function(str)
23907 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23910 setText : function(str)
23912 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23915 setWeight : function(weight)
23918 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23921 this.weight = weight;
23923 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23926 setIcon : function(icon)
23929 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23934 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23955 * @class Roo.bootstrap.UploadCropbox
23956 * @extends Roo.bootstrap.Component
23957 * Bootstrap UploadCropbox class
23958 * @cfg {String} emptyText show when image has been loaded
23959 * @cfg {String} rotateNotify show when image too small to rotate
23960 * @cfg {Number} errorTimeout default 3000
23961 * @cfg {Number} minWidth default 300
23962 * @cfg {Number} minHeight default 300
23963 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23966 * Create a new UploadCropbox
23967 * @param {Object} config The config object
23970 Roo.bootstrap.UploadCropbox = function(config){
23971 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23975 * @event beforeselectfile
23976 * Fire before select file
23977 * @param {Roo.bootstrap.UploadCropbox} this
23979 "beforeselectfile" : true,
23982 * Fire after initEvent
23983 * @param {Roo.bootstrap.UploadCropbox} this
23988 * Fire after initEvent
23989 * @param {Roo.bootstrap.UploadCropbox} this
23990 * @param {String} data
23995 * Fire when preparing the file data
23996 * @param {Roo.bootstrap.UploadCropbox} this
23997 * @param {Object} file
24002 * Fire when get exception
24003 * @param {Roo.bootstrap.UploadCropbox} this
24004 * @param {Object} options
24006 "exception" : true,
24008 * @event beforeloadcanvas
24009 * Fire before load the canvas
24010 * @param {Roo.bootstrap.UploadCropbox} this
24011 * @param {String} src
24013 "beforeloadcanvas" : true,
24016 * Fire when trash image
24017 * @param {Roo.bootstrap.UploadCropbox} this
24022 * Fire when download the image
24023 * @param {Roo.bootstrap.UploadCropbox} this
24027 * @event footerbuttonclick
24028 * Fire when footerbuttonclick
24029 * @param {Roo.bootstrap.UploadCropbox} this
24030 * @param {String} type
24032 "footerbuttonclick" : true,
24036 * @param {Roo.bootstrap.UploadCropbox} this
24042 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24045 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24047 emptyText : 'Click to upload image',
24048 rotateNotify : 'Image is too small to rotate',
24049 errorTimeout : 3000,
24063 cropType : 'image/jpeg',
24065 canvasLoaded : false,
24067 getAutoCreate : function()
24071 cls : 'roo-upload-cropbox',
24075 cls : 'roo-upload-cropbox-body',
24076 style : 'cursor:pointer',
24080 cls : 'roo-upload-cropbox-preview'
24084 cls : 'roo-upload-cropbox-thumb'
24088 cls : 'roo-upload-cropbox-empty-notify',
24089 html : this.emptyText
24093 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24094 html : this.rotateNotify
24100 cls : 'roo-upload-cropbox-footer',
24103 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24113 onRender : function(ct, position)
24115 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24117 if (this.buttons.length) {
24119 Roo.each(this.buttons, function(bb) {
24121 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24123 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24129 initEvents : function()
24131 this.urlAPI = (window.createObjectURL && window) ||
24132 (window.URL && URL.revokeObjectURL && URL) ||
24133 (window.webkitURL && webkitURL);
24135 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24136 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24138 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24139 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24141 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24142 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24143 this.thumbEl.hide();
24145 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24146 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24148 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24149 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24150 this.errorEl.hide();
24152 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24153 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24154 this.footerEl.hide();
24156 this.setThumbBoxSize();
24162 this.fireEvent('initial', this);
24169 window.addEventListener("resize", function() { _this.resize(); } );
24171 this.bodyEl.on('click', this.beforeSelectFile, this);
24174 this.bodyEl.on('touchstart', this.onTouchStart, this);
24175 this.bodyEl.on('touchmove', this.onTouchMove, this);
24176 this.bodyEl.on('touchend', this.onTouchEnd, this);
24180 this.bodyEl.on('mousedown', this.onMouseDown, this);
24181 this.bodyEl.on('mousemove', this.onMouseMove, this);
24182 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24183 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24184 Roo.get(document).on('mouseup', this.onMouseUp, this);
24191 this.baseScale = 1;
24193 this.baseRotate = 1;
24194 this.dragable = false;
24195 this.pinching = false;
24198 this.cropData = false;
24199 this.notifyEl.dom.innerHTML = this.emptyText;
24203 resize : function()
24205 if(this.fireEvent('resize', this) != false){
24206 this.setThumbBoxPosition();
24207 this.setCanvasPosition();
24211 onFooterButtonClick : function(e, el, o, type)
24214 case 'rotate-left' :
24215 this.onRotateLeft(e);
24217 case 'rotate-right' :
24218 this.onRotateRight(e);
24221 this.beforeSelectFile(e);
24236 this.fireEvent('footerbuttonclick', this, type);
24239 beforeSelectFile : function(e)
24241 this.fireEvent('beforeselectfile', this);
24244 trash : function(e)
24246 this.fireEvent('trash', this);
24249 download : function(e)
24251 this.fireEvent('download', this);
24254 loadCanvas : function(src)
24256 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24260 this.imageEl = document.createElement('img');
24264 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24266 this.imageEl.src = src;
24270 onLoadCanvas : function()
24272 this.bodyEl.un('click', this.beforeSelectFile, this);
24274 this.notifyEl.hide();
24275 this.thumbEl.show();
24276 this.footerEl.show();
24278 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24279 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24281 this.setThumbBoxPosition();
24282 this.baseRotateLevel();
24283 this.baseScaleLevel();
24289 this.canvasLoaded = true;
24293 setCanvasPosition : function()
24295 if(!this.canvasEl){
24299 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24300 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24302 this.previewEl.setLeft(pw);
24303 this.previewEl.setTop(ph);
24307 onMouseDown : function(e)
24311 this.dragable = true;
24312 this.pinching = false;
24314 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24315 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24319 onMouseMove : function(e)
24323 if(!this.canvasLoaded){
24327 if (!this.dragable){
24331 var minX = Math.ceil(this.thumbEl.getLeft(true));
24332 var minY = Math.ceil(this.thumbEl.getTop(true));
24334 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24335 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24337 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24338 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24340 x = x - this.mouseX;
24341 y = y - this.mouseY;
24343 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24344 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24346 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24347 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24349 this.previewEl.setLeft(bgX);
24350 this.previewEl.setTop(bgY);
24352 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24353 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24356 onMouseUp : function(e)
24360 this.dragable = false;
24363 onMouseWheel : function(e)
24367 this.startScale = this.scale;
24369 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24371 if(!this.zoomable()){
24372 this.scale = this.startScale;
24381 zoomable : function()
24383 var minScale = this.thumbEl.getWidth() / this.minWidth;
24385 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24386 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24389 (this.rotate == 0 || this.rotate == 180) &&
24391 width / minScale < this.minWidth ||
24392 width / minScale > this.imageEl.OriginWidth ||
24393 height / minScale < this.minHeight ||
24394 height / minScale > this.imageEl.OriginHeight
24401 (this.rotate == 90 || this.rotate == 270) &&
24403 width / minScale < this.minHeight ||
24404 width / minScale > this.imageEl.OriginWidth ||
24405 height / minScale < this.minWidth ||
24406 height / minScale > this.imageEl.OriginHeight
24416 onRotateLeft : function(e)
24418 var minScale = this.thumbEl.getWidth() / this.minWidth;
24420 if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24422 var bw = this.canvasEl.width / this.getScaleLevel();
24423 var bh = this.canvasEl.height / this.getScaleLevel();
24425 this.startScale = this.scale;
24427 while (this.getScaleLevel() < minScale){
24429 this.scale = this.scale + 1;
24431 if(!this.zoomable()){
24436 bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24437 bh * this.getScaleLevel() < this.thumbEl.getWidth()
24442 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24449 this.scale = this.startScale;
24451 this.onRotateFail();
24456 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24462 onRotateRight : function(e)
24464 var minScale = this.thumbEl.getWidth() / this.minWidth;
24466 if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24468 var bw = this.canvasEl.width / this.getScaleLevel();
24469 var bh = this.canvasEl.height / this.getScaleLevel();
24471 this.startScale = this.scale;
24473 while (this.getScaleLevel() < minScale){
24475 this.scale = this.scale + 1;
24477 if(!this.zoomable()){
24482 bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24483 bh * this.getScaleLevel() < this.thumbEl.getWidth()
24488 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24495 this.scale = this.startScale;
24497 this.onRotateFail();
24502 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24507 onRotateFail : function()
24509 this.errorEl.show(true);
24513 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24518 this.previewEl.dom.innerHTML = '';
24520 var canvasEl = document.createElement("canvas");
24522 var contextEl = canvasEl.getContext("2d");
24524 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24525 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24526 var center = this.imageEl.OriginWidth / 2;
24528 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24529 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24530 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24531 center = this.imageEl.OriginHeight / 2;
24534 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24536 contextEl.translate(center, center);
24537 contextEl.rotate(this.rotate * Math.PI / 180);
24539 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24541 this.canvasEl = document.createElement("canvas");
24543 this.contextEl = this.canvasEl.getContext("2d");
24545 switch (this.rotate) {
24548 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24549 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24551 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24556 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24557 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24559 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24560 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);
24564 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24569 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24570 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24572 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24573 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);
24577 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24582 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24583 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24585 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24586 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24590 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);
24597 this.previewEl.appendChild(this.canvasEl);
24599 this.setCanvasPosition();
24604 if(!this.canvasLoaded){
24607 var canvas = document.createElement("canvas");
24609 var context = canvas.getContext("2d");
24611 canvas.width = this.minWidth;
24612 canvas.height = this.minHeight;
24614 var cropWidth = this.thumbEl.getWidth();
24615 var cropHeight = this.thumbEl.getHeight();
24617 var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24618 var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24620 if(this.canvasEl.width - cropWidth < x){
24621 x = this.canvasEl.width - cropWidth;
24624 if(this.canvasEl.height - cropHeight < y){
24625 y = this.canvasEl.height - cropHeight;
24631 context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24633 this.cropData = canvas.toDataURL(this.cropType);
24635 this.fireEvent('crop', this, this.cropData);
24639 setThumbBoxSize : function()
24642 var width = Math.ceil(this.minWidth * height / this.minHeight);
24644 if(this.minWidth > this.minHeight){
24646 height = Math.ceil(this.minHeight * width / this.minWidth);
24649 this.thumbEl.setStyle({
24650 width : width + 'px',
24651 height : height + 'px'
24658 setThumbBoxPosition : function()
24660 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24661 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24663 this.thumbEl.setLeft(x);
24664 this.thumbEl.setTop(y);
24668 baseRotateLevel : function()
24670 this.baseRotate = 1;
24673 typeof(this.exif) != 'undefined' &&
24674 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24675 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24677 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24680 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24684 baseScaleLevel : function()
24688 if(this.baseRotate == 6 || this.baseRotate == 8){
24690 width = this.thumbEl.getHeight();
24691 this.baseScale = height / this.imageEl.OriginHeight;
24693 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24694 height = this.thumbEl.getWidth();
24695 this.baseScale = height / this.imageEl.OriginHeight;
24698 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24699 height = this.thumbEl.getWidth();
24700 this.baseScale = height / this.imageEl.OriginHeight;
24702 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24703 width = this.thumbEl.getHeight();
24704 this.baseScale = width / this.imageEl.OriginWidth;
24711 width = this.thumbEl.getWidth();
24712 this.baseScale = width / this.imageEl.OriginWidth;
24714 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24715 height = this.thumbEl.getHeight();
24716 this.baseScale = height / this.imageEl.OriginHeight;
24720 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24722 height = this.thumbEl.getHeight();
24723 this.baseScale = height / this.imageEl.OriginHeight;
24725 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24726 width = this.thumbEl.getWidth();
24727 this.baseScale = width / this.imageEl.OriginWidth;
24735 getScaleLevel : function()
24737 return this.baseScale * Math.pow(1.1, this.scale);
24740 onTouchStart : function(e)
24742 if(!this.canvasLoaded){
24743 this.beforeSelectFile(e);
24747 var touches = e.browserEvent.touches;
24753 if(touches.length == 1){
24754 this.onMouseDown(e);
24758 if(touches.length != 2){
24764 for(var i = 0, finger; finger = touches[i]; i++){
24765 coords.push(finger.pageX, finger.pageY);
24768 var x = Math.pow(coords[0] - coords[2], 2);
24769 var y = Math.pow(coords[1] - coords[3], 2);
24771 this.startDistance = Math.sqrt(x + y);
24773 this.startScale = this.scale;
24775 this.pinching = true;
24776 this.dragable = false;
24780 onTouchMove : function(e)
24782 if(!this.pinching && !this.dragable){
24786 var touches = e.browserEvent.touches;
24793 this.onMouseMove(e);
24799 for(var i = 0, finger; finger = touches[i]; i++){
24800 coords.push(finger.pageX, finger.pageY);
24803 var x = Math.pow(coords[0] - coords[2], 2);
24804 var y = Math.pow(coords[1] - coords[3], 2);
24806 this.endDistance = Math.sqrt(x + y);
24808 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24810 if(!this.zoomable()){
24811 this.scale = this.startScale;
24819 onTouchEnd : function(e)
24821 this.pinching = false;
24822 this.dragable = false;
24826 prepare : function(input)
24831 if(typeof(input) === 'string'){
24832 this.loadCanvas(input);
24836 if(!input.files || !input.files[0] || !this.urlAPI){
24840 this.file = input.files[0];
24841 this.cropType = this.file.type;
24845 if(this.fireEvent('prepare', this, this.file) != false){
24847 var reader = new FileReader();
24849 reader.onload = function (e) {
24850 if (e.target.error) {
24851 Roo.log(e.target.error);
24855 var buffer = e.target.result,
24856 dataView = new DataView(buffer),
24858 maxOffset = dataView.byteLength - 4,
24862 if (dataView.getUint16(0) === 0xffd8) {
24863 while (offset < maxOffset) {
24864 markerBytes = dataView.getUint16(offset);
24866 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24867 markerLength = dataView.getUint16(offset + 2) + 2;
24868 if (offset + markerLength > dataView.byteLength) {
24869 Roo.log('Invalid meta data: Invalid segment size.');
24873 if(markerBytes == 0xffe1){
24874 _this.parseExifData(
24881 offset += markerLength;
24891 var url = _this.urlAPI.createObjectURL(_this.file);
24893 _this.loadCanvas(url);
24898 reader.readAsArrayBuffer(this.file);
24904 parseExifData : function(dataView, offset, length)
24906 var tiffOffset = offset + 10,
24910 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24911 // No Exif data, might be XMP data instead
24915 // Check for the ASCII code for "Exif" (0x45786966):
24916 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24917 // No Exif data, might be XMP data instead
24920 if (tiffOffset + 8 > dataView.byteLength) {
24921 Roo.log('Invalid Exif data: Invalid segment size.');
24924 // Check for the two null bytes:
24925 if (dataView.getUint16(offset + 8) !== 0x0000) {
24926 Roo.log('Invalid Exif data: Missing byte alignment offset.');
24929 // Check the byte alignment:
24930 switch (dataView.getUint16(tiffOffset)) {
24932 littleEndian = true;
24935 littleEndian = false;
24938 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24941 // Check for the TIFF tag marker (0x002A):
24942 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24943 Roo.log('Invalid Exif data: Missing TIFF marker.');
24946 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24947 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24949 this.parseExifTags(
24952 tiffOffset + dirOffset,
24957 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24962 if (dirOffset + 6 > dataView.byteLength) {
24963 Roo.log('Invalid Exif data: Invalid directory offset.');
24966 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24967 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24968 if (dirEndOffset + 4 > dataView.byteLength) {
24969 Roo.log('Invalid Exif data: Invalid directory size.');
24972 for (i = 0; i < tagsNumber; i += 1) {
24976 dirOffset + 2 + 12 * i, // tag offset
24980 // Return the offset to the next directory:
24981 return dataView.getUint32(dirEndOffset, littleEndian);
24984 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
24986 var tag = dataView.getUint16(offset, littleEndian);
24988 this.exif[tag] = this.getExifValue(
24992 dataView.getUint16(offset + 2, littleEndian), // tag type
24993 dataView.getUint32(offset + 4, littleEndian), // tag length
24998 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25000 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25009 Roo.log('Invalid Exif data: Invalid tag type.');
25013 tagSize = tagType.size * length;
25014 // Determine if the value is contained in the dataOffset bytes,
25015 // or if the value at the dataOffset is a pointer to the actual data:
25016 dataOffset = tagSize > 4 ?
25017 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25018 if (dataOffset + tagSize > dataView.byteLength) {
25019 Roo.log('Invalid Exif data: Invalid data offset.');
25022 if (length === 1) {
25023 return tagType.getValue(dataView, dataOffset, littleEndian);
25026 for (i = 0; i < length; i += 1) {
25027 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25030 if (tagType.ascii) {
25032 // Concatenate the chars:
25033 for (i = 0; i < values.length; i += 1) {
25035 // Ignore the terminating NULL byte(s):
25036 if (c === '\u0000') {
25048 Roo.apply(Roo.bootstrap.UploadCropbox, {
25050 'Orientation': 0x0112
25054 1: 0, //'top-left',
25056 3: 180, //'bottom-right',
25057 // 4: 'bottom-left',
25059 6: 90, //'right-top',
25060 // 7: 'right-bottom',
25061 8: 270 //'left-bottom'
25065 // byte, 8-bit unsigned int:
25067 getValue: function (dataView, dataOffset) {
25068 return dataView.getUint8(dataOffset);
25072 // ascii, 8-bit byte:
25074 getValue: function (dataView, dataOffset) {
25075 return String.fromCharCode(dataView.getUint8(dataOffset));
25080 // short, 16 bit int:
25082 getValue: function (dataView, dataOffset, littleEndian) {
25083 return dataView.getUint16(dataOffset, littleEndian);
25087 // long, 32 bit int:
25089 getValue: function (dataView, dataOffset, littleEndian) {
25090 return dataView.getUint32(dataOffset, littleEndian);
25094 // rational = two long values, first is numerator, second is denominator:
25096 getValue: function (dataView, dataOffset, littleEndian) {
25097 return dataView.getUint32(dataOffset, littleEndian) /
25098 dataView.getUint32(dataOffset + 4, littleEndian);
25102 // slong, 32 bit signed int:
25104 getValue: function (dataView, dataOffset, littleEndian) {
25105 return dataView.getInt32(dataOffset, littleEndian);
25109 // srational, two slongs, first is numerator, second is denominator:
25111 getValue: function (dataView, dataOffset, littleEndian) {
25112 return dataView.getInt32(dataOffset, littleEndian) /
25113 dataView.getInt32(dataOffset + 4, littleEndian);
25123 cls : 'btn-group roo-upload-cropbox-rotate-left',
25124 action : 'rotate-left',
25128 cls : 'btn btn-default',
25129 html : '<i class="fa fa-undo"></i>'
25135 cls : 'btn-group roo-upload-cropbox-picture',
25136 action : 'picture',
25140 cls : 'btn btn-default',
25141 html : '<i class="fa fa-picture-o"></i>'
25147 cls : 'btn-group roo-upload-cropbox-rotate-right',
25148 action : 'rotate-right',
25152 cls : 'btn btn-default',
25153 html : '<i class="fa fa-repeat"></i>'
25161 cls : 'btn-group roo-upload-cropbox-rotate-left',
25162 action : 'rotate-left',
25166 cls : 'btn btn-default',
25167 html : '<i class="fa fa-undo"></i>'
25173 cls : 'btn-group roo-upload-cropbox-download',
25174 action : 'download',
25178 cls : 'btn btn-default',
25179 html : '<i class="fa fa-download"></i>'
25185 cls : 'btn-group roo-upload-cropbox-crop',
25190 cls : 'btn btn-default',
25191 html : '<i class="fa fa-crop"></i>'
25197 cls : 'btn-group roo-upload-cropbox-trash',
25202 cls : 'btn btn-default',
25203 html : '<i class="fa fa-trash"></i>'
25209 cls : 'btn-group roo-upload-cropbox-rotate-right',
25210 action : 'rotate-right',
25214 cls : 'btn btn-default',
25215 html : '<i class="fa fa-repeat"></i>'
25228 * @class Roo.bootstrap.DocumentManager
25229 * @extends Roo.bootstrap.Component
25230 * Bootstrap DocumentManager class
25231 * @cfg {String} paramName default 'imageUpload'
25232 * @cfg {String} method default POST
25233 * @cfg {String} url action url
25234 * @cfg {Number} boxes number of boxes default 12
25235 * @cfg {Boolean} multiple multiple upload default true
25236 * @cfg {Number} minWidth default 300
25237 * @cfg {Number} minHeight default 300
25238 * @cfg {Number} thumbSize default 300
25239 * @cfg {String} fieldLabel
25240 * @cfg {Number} labelWidth default 4
25241 * @cfg {String} labelAlign (left|top) default left
25244 * Create a new DocumentManager
25245 * @param {Object} config The config object
25248 Roo.bootstrap.DocumentManager = function(config){
25249 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25254 * Fire when initial the DocumentManager
25255 * @param {Roo.bootstrap.DocumentManager} this
25260 * inspect selected file
25261 * @param {Roo.bootstrap.DocumentManager} this
25262 * @param {File} file
25267 * Fire when xhr load exception
25268 * @param {Roo.bootstrap.DocumentManager} this
25269 * @param {XMLHttpRequest} xhr
25271 "exception" : true,
25274 * prepare the form data
25275 * @param {Roo.bootstrap.DocumentManager} this
25276 * @param {Object} formData
25281 * Fire when remove the file
25282 * @param {Roo.bootstrap.DocumentManager} this
25283 * @param {Object} file
25288 * Fire after refresh the file
25289 * @param {Roo.bootstrap.DocumentManager} this
25294 * Fire after click the image
25295 * @param {Roo.bootstrap.DocumentManager} this
25296 * @param {Object} file
25303 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25314 paramName : 'imageUpload',
25317 labelAlign : 'left',
25319 getAutoCreate : function()
25321 var managerWidget = {
25323 cls : 'roo-document-manager',
25327 cls : 'roo-document-manager-selector',
25332 cls : 'roo-document-manager-uploader',
25336 cls : 'roo-document-manager-upload-btn',
25337 html : '<i class="fa fa-plus"></i>'
25348 cls : 'column col-md-12',
25353 if(this.fieldLabel.length){
25358 cls : 'column col-md-12',
25359 html : this.fieldLabel
25363 cls : 'column col-md-12',
25368 if(this.labelAlign == 'left'){
25372 cls : 'column col-md-' + this.labelWidth,
25373 html : this.fieldLabel
25377 cls : 'column col-md-' + (12 - this.labelWidth),
25387 cls : 'row clearfix',
25395 initEvents : function()
25397 this.managerEl = this.el.select('.roo-document-manager', true).first();
25398 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25400 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25401 this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25402 this.selectorEl.hide();
25405 this.selectorEl.attr('multiple', 'multiple');
25408 this.selectorEl.on('change', this.onSelect, this);
25410 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25411 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25413 this.uploader.on('click', this.onUpload, this);
25417 window.addEventListener("resize", function() { _this.refresh(); } );
25419 this.fireEvent('initial', this);
25422 onUpload : function(e)
25424 e.preventDefault();
25426 this.selectorEl.dom.click();
25430 onSelect : function(e)
25432 e.preventDefault();
25434 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25438 Roo.each(this.selectorEl.dom.files, function(file){
25439 if(this.fireEvent('inspect', this, file) != false){
25440 this.files.push(file);
25448 process : function()
25450 this.selectorEl.dom.value = '';
25452 if(!this.files.length){
25456 if(this.files.length > this.boxes){
25457 this.files = this.files.slice(0, this.boxes);
25460 var xhr = new XMLHttpRequest();
25462 Roo.each(this.files, function(file, index){
25463 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25469 this.managerEl.createChild({
25471 cls : 'roo-document-manager-loading',
25475 tooltip : file.name,
25476 cls : 'roo-document-manager-thumb',
25477 html : '<i class="fa fa-spinner fa-pulse"></i>'
25485 if(this.files.length > this.boxes - 1 ){
25486 this.uploader.hide();
25490 "Accept": "application/json",
25491 "Cache-Control": "no-cache",
25492 "X-Requested-With": "XMLHttpRequest"
25495 xhr.open(this.method, this.url, true);
25497 for (var headerName in headers) {
25498 var headerValue = headers[headerName];
25500 xhr.setRequestHeader(headerName, headerValue);
25506 xhr.onload = function()
25508 _this.xhrOnLoad(xhr);
25511 xhr.onerror = function()
25513 _this.xhrOnError(xhr);
25516 var formData = new FormData();
25518 formData.append('returnHTML', 'NO');
25520 Roo.each(this.files, function(file, index){
25522 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25526 formData.append(this.getParamName(index), file, file.name);
25530 if(this.fireEvent('prepare', this, formData) != false){
25531 xhr.send(formData);
25536 getParamName : function(i)
25538 if(!this.multiple){
25539 return this.paramName;
25542 return this.paramName + "_" + i;
25545 refresh : function()
25547 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25554 Roo.each(this.files, function(file){
25556 if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25565 var previewEl = this.managerEl.createChild({
25567 cls : 'roo-document-manager-preview',
25571 tooltip : file.filename,
25572 cls : 'roo-document-manager-thumb',
25573 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25583 var close = previewEl.select('button.close', true).first();
25585 close.on('click', this.onRemove, this, file);
25587 file.target = previewEl;
25589 var image = previewEl.select('img', true).first();
25591 image.on('click', this.onClick, this, file);
25599 this.files = files;
25601 this.uploader.show();
25603 if(this.files.length > this.boxes - 1){
25604 this.uploader.hide();
25607 Roo.isTouch ? this.closable(false) : this.closable(true);
25609 this.fireEvent('refresh', this);
25612 onRemove : function(e, el, o)
25614 e.preventDefault();
25616 this.fireEvent('remove', this, o);
25620 remove : function(o)
25624 Roo.each(this.files, function(file){
25625 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25634 this.files = files;
25639 onClick : function(e, el, o)
25641 e.preventDefault();
25643 this.fireEvent('click', this, o);
25647 closable : function(closable)
25649 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25651 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25663 xhrOnLoad : function(xhr)
25665 if (xhr.readyState !== 4) {
25667 this.fireEvent('exception', this, xhr);
25671 var response = Roo.decode(xhr.responseText);
25673 if(!response.success){
25675 this.fireEvent('exception', this, xhr);
25681 Roo.each(this.files, function(file, index){
25683 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25687 this.files[index] = response.data[i];
25698 xhrOnError : function()
25700 Roo.log('xhr on error');
25702 var response = Roo.decode(xhr.responseText);
25715 * @class Roo.bootstrap.DocumentViewer
25716 * @extends Roo.bootstrap.Component
25717 * Bootstrap DocumentViewer class
25720 * Create a new DocumentViewer
25721 * @param {Object} config The config object
25724 Roo.bootstrap.DocumentViewer = function(config){
25725 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25730 * Fire after initEvent
25731 * @param {Roo.bootstrap.DocumentViewer} this
25737 * @param {Roo.bootstrap.DocumentViewer} this
25742 * Fire after trash button
25743 * @param {Roo.bootstrap.DocumentViewer} this
25750 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
25752 getAutoCreate : function()
25756 cls : 'roo-document-viewer',
25760 cls : 'roo-document-viewer-body',
25764 cls : 'roo-document-viewer-thumb',
25768 cls : 'roo-document-viewer-image'
25776 cls : 'roo-document-viewer-footer',
25779 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25787 cls : 'btn btn-default roo-document-viewer-trash',
25788 html : '<i class="fa fa-trash"></i>'
25801 initEvents : function()
25804 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25805 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25807 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25808 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25810 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25811 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25813 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25814 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25816 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25817 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25819 this.bodyEl.on('click', this.onClick, this);
25821 this.trashBtn.on('click', this.onTrash, this);
25825 initial : function()
25827 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25830 this.fireEvent('initial', this);
25834 onClick : function(e)
25836 e.preventDefault();
25838 this.fireEvent('click', this);
25841 onTrash : function(e)
25843 e.preventDefault();
25845 this.fireEvent('trash', this);