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 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');
1973 this.el.on('touchstart' , this.onTouch, this);
1975 this.el.on('click' , this.onClick, this);
1977 this.el.on("mouseover", this.onMouseOver, this);
1978 this.el.on("mouseout", this.onMouseOut, this);
1982 findTargetItem : function(e){
1983 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1987 //Roo.log(t); Roo.log(t.id);
1989 //Roo.log(this.menuitems);
1990 return this.menuitems.get(t.id);
1992 //return this.items.get(t.menuItemId);
1998 onTouch : function(e) {
2003 onClick : function(e){
2004 Roo.log("menu.onClick");
2005 var t = this.findTargetItem(e);
2006 if(!t || t.isContainer){
2011 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2012 if(t == this.activeItem && t.shouldDeactivate(e)){
2013 this.activeItem.deactivate();
2014 delete this.activeItem;
2018 this.setActiveItem(t, true);
2026 Roo.log('pass click event');
2030 this.fireEvent("click", this, t, e);
2034 onMouseOver : function(e){
2035 var t = this.findTargetItem(e);
2038 // if(t.canActivate && !t.disabled){
2039 // this.setActiveItem(t, true);
2043 this.fireEvent("mouseover", this, e, t);
2045 isVisible : function(){
2046 return !this.hidden;
2048 onMouseOut : function(e){
2049 var t = this.findTargetItem(e);
2052 // if(t == this.activeItem && t.shouldDeactivate(e)){
2053 // this.activeItem.deactivate();
2054 // delete this.activeItem;
2057 this.fireEvent("mouseout", this, e, t);
2062 * Displays this menu relative to another element
2063 * @param {String/HTMLElement/Roo.Element} element The element to align to
2064 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065 * the element (defaults to this.defaultAlign)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 show : function(el, pos, parentMenu){
2069 this.parentMenu = parentMenu;
2073 this.fireEvent("beforeshow", this);
2074 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2077 * Displays this menu at a specific xy position
2078 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2081 showAt : function(xy, parentMenu, /* private: */_e){
2082 this.parentMenu = parentMenu;
2087 this.fireEvent("beforeshow", this);
2088 //xy = this.el.adjustForConstraints(xy);
2092 this.hideMenuItems();
2093 this.hidden = false;
2094 this.triggerEl.addClass('open');
2096 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2102 this.fireEvent("show", this);
2108 this.doFocus.defer(50, this);
2112 doFocus : function(){
2114 this.focusEl.focus();
2119 * Hides this menu and optionally all parent menus
2120 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2122 hide : function(deep){
2124 this.hideMenuItems();
2125 if(this.el && this.isVisible()){
2126 this.fireEvent("beforehide", this);
2127 if(this.activeItem){
2128 this.activeItem.deactivate();
2129 this.activeItem = null;
2131 this.triggerEl.removeClass('open');;
2133 this.fireEvent("hide", this);
2135 if(deep === true && this.parentMenu){
2136 this.parentMenu.hide(true);
2140 onTriggerPress : function(e)
2143 Roo.log('trigger press');
2144 //Roo.log(e.getTarget());
2145 // Roo.log(this.triggerEl.dom);
2146 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2150 if (this.isVisible()) {
2155 this.show(this.triggerEl, false, false);
2164 hideMenuItems : function()
2166 //$(backdrop).remove()
2167 Roo.select('.open',true).each(function(aa) {
2169 aa.removeClass('open');
2170 //var parent = getParent($(this))
2171 //var relatedTarget = { relatedTarget: this }
2173 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174 //if (e.isDefaultPrevented()) return
2175 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2178 addxtypeChild : function (tree, cntr) {
2179 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2181 this.menuitems.add(comp);
2202 * @class Roo.bootstrap.MenuItem
2203 * @extends Roo.bootstrap.Component
2204 * Bootstrap MenuItem class
2205 * @cfg {String} html the menu label
2206 * @cfg {String} href the link
2207 * @cfg {Boolean} preventDefault (true | false) default true
2208 * @cfg {Boolean} isContainer (true | false) default false
2212 * Create a new MenuItem
2213 * @param {Object} config The config object
2217 Roo.bootstrap.MenuItem = function(config){
2218 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2223 * The raw click event for the entire grid.
2224 * @param {Roo.bootstrap.MenuItem} this
2225 * @param {Roo.EventObject} e
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2235 preventDefault: true,
2236 isContainer : false,
2238 getAutoCreate : function(){
2240 if(this.isContainer){
2243 cls: 'dropdown-menu-item'
2249 cls: 'dropdown-menu-item',
2258 if (this.parent().type == 'treeview') {
2259 cfg.cls = 'treeview-menu';
2262 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2267 initEvents: function() {
2269 //this.el.select('a').on('click', this.onClick, this);
2272 onClick : function(e)
2274 Roo.log('item on click ');
2275 //if(this.preventDefault){
2276 // e.preventDefault();
2278 //this.parent().hideMenuItems();
2280 this.fireEvent('click', this, e);
2299 * @class Roo.bootstrap.MenuSeparator
2300 * @extends Roo.bootstrap.Component
2301 * Bootstrap MenuSeparator class
2304 * Create a new MenuItem
2305 * @param {Object} config The config object
2309 Roo.bootstrap.MenuSeparator = function(config){
2310 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2315 getAutoCreate : function(){
2334 * @class Roo.bootstrap.Modal
2335 * @extends Roo.bootstrap.Component
2336 * Bootstrap Modal class
2337 * @cfg {String} title Title of dialog
2338 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2340 * @cfg {Boolean} specificTitle default false
2341 * @cfg {Array} buttons Array of buttons or standard button set..
2342 * @cfg {String} buttonPosition (left|right|center) default right
2343 * @cfg {Boolean} animate default true
2344 * @cfg {Boolean} allow_close default true
2347 * Create a new Modal Dialog
2348 * @param {Object} config The config object
2351 Roo.bootstrap.Modal = function(config){
2352 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2357 * The raw btnclick event for the button
2358 * @param {Roo.EventObject} e
2362 this.buttons = this.buttons || [];
2365 this.tmpl = Roo.factory(this.tmpl);
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2372 title : 'test dialog',
2382 specificTitle: false,
2384 buttonPosition: 'right',
2398 onRender : function(ct, position)
2400 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2403 var cfg = Roo.apply({}, this.getAutoCreate());
2406 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2408 //if (!cfg.name.length) {
2412 cfg.cls += ' ' + this.cls;
2415 cfg.style = this.style;
2417 this.el = Roo.get(document.body).createChild(cfg, position);
2419 //var type = this.el.dom.type;
2424 if(this.tabIndex !== undefined){
2425 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2429 this.bodyEl = this.el.select('.modal-body',true).first();
2430 this.closeEl = this.el.select('.modal-header .close', true).first();
2431 this.footerEl = this.el.select('.modal-footer',true).first();
2432 this.titleEl = this.el.select('.modal-title',true).first();
2436 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437 this.maskEl.enableDisplayMode("block");
2439 //this.el.addClass("x-dlg-modal");
2441 if (this.buttons.length) {
2442 Roo.each(this.buttons, function(bb) {
2443 var b = Roo.apply({}, bb);
2444 b.xns = b.xns || Roo.bootstrap;
2445 b.xtype = b.xtype || 'Button';
2446 if (typeof(b.listeners) == 'undefined') {
2447 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2450 var btn = Roo.factory(b);
2452 btn.onRender(this.el.select('.modal-footer div').first());
2456 // render the children.
2459 if(typeof(this.items) != 'undefined'){
2460 var items = this.items;
2463 for(var i =0;i < items.length;i++) {
2464 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2468 this.items = nitems;
2470 // where are these used - they used to be body/close/footer
2474 //this.el.addClass([this.fieldClass, this.cls]);
2478 getAutoCreate : function(){
2483 html : this.html || ''
2488 cls : 'modal-title',
2492 if(this.specificTitle){
2498 if (this.allow_close) {
2509 style : 'display: none',
2512 cls: "modal-dialog",
2515 cls : "modal-content",
2518 cls : 'modal-header',
2523 cls : 'modal-footer',
2527 cls: 'btn-' + this.buttonPosition
2544 modal.cls += ' fade';
2550 getChildContainer : function() {
2555 getButtonContainer : function() {
2556 return this.el.select('.modal-footer div',true).first();
2559 initEvents : function()
2561 if (this.allow_close) {
2562 this.closeEl.on('click', this.hide, this);
2567 window.addEventListener("resize", function() { _this.resize(); } );
2573 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2578 if (!this.rendered) {
2582 this.el.setStyle('display', 'block');
2586 (function(){ _this.el.addClass('in'); }).defer(50);
2588 this.el.addClass('in');
2591 // not sure how we can show data in here..
2593 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2596 Roo.get(document.body).addClass("x-body-masked");
2597 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2599 this.el.setStyle('zIndex', '10001');
2601 this.fireEvent('show', this);
2608 Roo.get(document.body).removeClass("x-body-masked");
2609 this.el.removeClass('in');
2613 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2615 this.el.setStyle('display', 'none');
2618 this.fireEvent('hide', this);
2621 addButton : function(str, cb)
2625 var b = Roo.apply({}, { html : str } );
2626 b.xns = b.xns || Roo.bootstrap;
2627 b.xtype = b.xtype || 'Button';
2628 if (typeof(b.listeners) == 'undefined') {
2629 b.listeners = { click : cb.createDelegate(this) };
2632 var btn = Roo.factory(b);
2634 btn.onRender(this.el.select('.modal-footer div').first());
2640 setDefaultButton : function(btn)
2642 //this.el.select('.modal-footer').()
2644 resizeTo: function(w,h)
2648 setContentSize : function(w, h)
2652 onButtonClick: function(btn,e)
2655 this.fireEvent('btnclick', btn.name, e);
2658 * Set the title of the Dialog
2659 * @param {String} str new Title
2661 setTitle: function(str) {
2662 this.titleEl.dom.innerHTML = str;
2665 * Set the body of the Dialog
2666 * @param {String} str new Title
2668 setBody: function(str) {
2669 this.bodyEl.dom.innerHTML = str;
2672 * Set the body of the Dialog using the template
2673 * @param {Obj} data - apply this data to the template and replace the body contents.
2675 applyBody: function(obj)
2678 Roo.log("Error - using apply Body without a template");
2681 this.tmpl.overwrite(this.bodyEl, obj);
2687 Roo.apply(Roo.bootstrap.Modal, {
2689 * Button config that displays a single OK button
2698 * Button config that displays Yes and No buttons
2714 * Button config that displays OK and Cancel buttons
2729 * Button config that displays Yes, No and Cancel buttons
2752 * messagebox - can be used as a replace
2756 * @class Roo.MessageBox
2757 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2766 // process text value...
2770 // Show a dialog using config options:
2772 title:'Save Changes?',
2773 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774 buttons: Roo.Msg.YESNOCANCEL,
2781 Roo.bootstrap.MessageBox = function(){
2782 var dlg, opt, mask, waitTimer;
2783 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784 var buttons, activeTextEl, bwidth;
2788 var handleButton = function(button){
2790 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2794 var handleHide = function(){
2796 dlg.el.removeClass(opt.cls);
2799 // Roo.TaskMgr.stop(waitTimer);
2800 // waitTimer = null;
2805 var updateButtons = function(b){
2808 buttons["ok"].hide();
2809 buttons["cancel"].hide();
2810 buttons["yes"].hide();
2811 buttons["no"].hide();
2812 //dlg.footer.dom.style.display = 'none';
2815 dlg.footerEl.dom.style.display = '';
2816 for(var k in buttons){
2817 if(typeof buttons[k] != "function"){
2820 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821 width += buttons[k].el.getWidth()+15;
2831 var handleEsc = function(d, k, e){
2832 if(opt && opt.closable !== false){
2842 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843 * @return {Roo.BasicDialog} The BasicDialog element
2845 getDialog : function(){
2847 dlg = new Roo.bootstrap.Modal( {
2850 //constraintoviewport:false,
2852 //collapsible : false,
2857 //buttonAlign:"center",
2858 closeClick : function(){
2859 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2862 handleButton("cancel");
2867 dlg.on("hide", handleHide);
2869 //dlg.addKeyListener(27, handleEsc);
2871 this.buttons = buttons;
2872 var bt = this.buttonText;
2873 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2878 bodyEl = dlg.bodyEl.createChild({
2880 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881 '<textarea class="roo-mb-textarea"></textarea>' +
2882 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2884 msgEl = bodyEl.dom.firstChild;
2885 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886 textboxEl.enableDisplayMode();
2887 textboxEl.addKeyListener([10,13], function(){
2888 if(dlg.isVisible() && opt && opt.buttons){
2891 }else if(opt.buttons.yes){
2892 handleButton("yes");
2896 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897 textareaEl.enableDisplayMode();
2898 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899 progressEl.enableDisplayMode();
2900 var pf = progressEl.dom.firstChild;
2902 pp = Roo.get(pf.firstChild);
2903 pp.setHeight(pf.offsetHeight);
2911 * Updates the message box body text
2912 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913 * the XHTML-compliant non-breaking space character '&#160;')
2914 * @return {Roo.MessageBox} This message box
2916 updateText : function(text){
2917 if(!dlg.isVisible() && !opt.width){
2918 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2920 msgEl.innerHTML = text || ' ';
2922 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2925 Math.min(opt.width || cw , this.maxWidth),
2926 Math.max(opt.minWidth || this.minWidth, bwidth)
2929 activeTextEl.setWidth(w);
2931 if(dlg.isVisible()){
2932 dlg.fixedcenter = false;
2934 // to big, make it scroll. = But as usual stupid IE does not support
2937 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2941 bodyEl.dom.style.height = '';
2942 bodyEl.dom.style.overflowY = '';
2945 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2947 bodyEl.dom.style.overflowX = '';
2950 dlg.setContentSize(w, bodyEl.getHeight());
2951 if(dlg.isVisible()){
2952 dlg.fixedcenter = true;
2958 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2959 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962 * @return {Roo.MessageBox} This message box
2964 updateProgress : function(value, text){
2966 this.updateText(text);
2968 if (pp) { // weird bug on my firefox - for some reason this is not defined
2969 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2975 * Returns true if the message box is currently displayed
2976 * @return {Boolean} True if the message box is visible, else false
2978 isVisible : function(){
2979 return dlg && dlg.isVisible();
2983 * Hides the message box if it is displayed
2986 if(this.isVisible()){
2992 * Displays a new message box, or reinitializes an existing message box, based on the config options
2993 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994 * The following config object properties are supported:
2996 Property Type Description
2997 ---------- --------------- ------------------------------------------------------------------------------------
2998 animEl String/Element An id or Element from which the message box should animate as it opens and
2999 closes (defaults to undefined)
3000 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable Boolean False to hide the top-right close button (defaults to true). Note that
3003 progress and wait dialogs will ignore this property and always hide the
3004 close button as they can only be closed programmatically.
3005 cls String A custom CSS class to apply to the message box element
3006 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3007 displayed (defaults to 75)
3008 fn Function A callback function to execute after closing the dialog. The arguments to the
3009 function will be btn (the name of the button that was clicked, if applicable,
3010 e.g. "ok"), and text (the value of the active text field, if applicable).
3011 Progress and wait dialogs will ignore this option since they do not respond to
3012 user actions and can only be closed programmatically, so any required function
3013 should be called by the same code after it closes the dialog.
3014 icon String A CSS class that provides a background image to be used as an icon for
3015 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3017 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3018 modal Boolean False to allow user interaction with the page while the message box is
3019 displayed (defaults to true)
3020 msg String A string that will replace the existing message box body text (defaults
3021 to the XHTML-compliant non-breaking space character ' ')
3022 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3023 progress Boolean True to display a progress bar (defaults to false)
3024 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3027 title String The title text
3028 value String The string value to set into the active textbox element if displayed
3029 wait Boolean True to display a progress bar (defaults to false)
3030 width Number The width of the dialog in pixels
3037 msg: 'Please enter your address:',
3039 buttons: Roo.MessageBox.OKCANCEL,
3042 animEl: 'addAddressBtn'
3045 * @param {Object} config Configuration options
3046 * @return {Roo.MessageBox} This message box
3048 show : function(options)
3051 // this causes nightmares if you show one dialog after another
3052 // especially on callbacks..
3054 if(this.isVisible()){
3057 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3059 Roo.log("New Dialog Message:" + options.msg )
3060 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3064 var d = this.getDialog();
3066 d.setTitle(opt.title || " ");
3067 d.closeEl.setDisplayed(opt.closable !== false);
3068 activeTextEl = textboxEl;
3069 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3074 textareaEl.setHeight(typeof opt.multiline == "number" ?
3075 opt.multiline : this.defaultTextHeight);
3076 activeTextEl = textareaEl;
3085 progressEl.setDisplayed(opt.progress === true);
3086 this.updateProgress(0);
3087 activeTextEl.dom.value = opt.value || "";
3089 dlg.setDefaultButton(activeTextEl);
3091 var bs = opt.buttons;
3095 }else if(bs && bs.yes){
3096 db = buttons["yes"];
3098 dlg.setDefaultButton(db);
3100 bwidth = updateButtons(opt.buttons);
3101 this.updateText(opt.msg);
3103 d.el.addClass(opt.cls);
3105 d.proxyDrag = opt.proxyDrag === true;
3106 d.modal = opt.modal !== false;
3107 d.mask = opt.modal !== false ? mask : false;
3109 // force it to the end of the z-index stack so it gets a cursor in FF
3110 document.body.appendChild(dlg.el.dom);
3111 d.animateTarget = null;
3112 d.show(options.animEl);
3118 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3119 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120 * and closing the message box when the process is complete.
3121 * @param {String} title The title bar text
3122 * @param {String} msg The message box body text
3123 * @return {Roo.MessageBox} This message box
3125 progress : function(title, msg){
3132 minWidth: this.minProgressWidth,
3139 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140 * If a callback function is passed it will be called after the user clicks the button, and the
3141 * id of the button that was clicked will be passed as the only parameter to the callback
3142 * (could also be the top-right close button).
3143 * @param {String} title The title bar text
3144 * @param {String} msg The message box body text
3145 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146 * @param {Object} scope (optional) The scope of the callback function
3147 * @return {Roo.MessageBox} This message box
3149 alert : function(title, msg, fn, scope){
3162 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3163 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164 * You are responsible for closing the message box when the process is complete.
3165 * @param {String} msg The message box body text
3166 * @param {String} title (optional) The title bar text
3167 * @return {Roo.MessageBox} This message box
3169 wait : function(msg, title){
3180 waitTimer = Roo.TaskMgr.start({
3182 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3190 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193 * @param {String} title The title bar text
3194 * @param {String} msg The message box body text
3195 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196 * @param {Object} scope (optional) The scope of the callback function
3197 * @return {Roo.MessageBox} This message box
3199 confirm : function(title, msg, fn, scope){
3203 buttons: this.YESNO,
3212 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3214 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215 * (could also be the top-right close button) and the text that was entered will be passed as the two
3216 * parameters to the callback.
3217 * @param {String} title The title bar text
3218 * @param {String} msg The message box body text
3219 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220 * @param {Object} scope (optional) The scope of the callback function
3221 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223 * @return {Roo.MessageBox} This message box
3225 prompt : function(title, msg, fn, scope, multiline){
3229 buttons: this.OKCANCEL,
3234 multiline: multiline,
3241 * Button config that displays a single OK button
3246 * Button config that displays Yes and No buttons
3249 YESNO : {yes:true, no:true},
3251 * Button config that displays OK and Cancel buttons
3254 OKCANCEL : {ok:true, cancel:true},
3256 * Button config that displays Yes, No and Cancel buttons
3259 YESNOCANCEL : {yes:true, no:true, cancel:true},
3262 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3265 defaultTextHeight : 75,
3267 * The maximum width in pixels of the message box (defaults to 600)
3272 * The minimum width in pixels of the message box (defaults to 100)
3277 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3278 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3281 minProgressWidth : 250,
3283 * An object containing the default button text strings that can be overriden for localized language support.
3284 * Supported properties are: ok, cancel, yes and no.
3285 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3298 * Shorthand for {@link Roo.MessageBox}
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3310 * @class Roo.bootstrap.Navbar
3311 * @extends Roo.bootstrap.Component
3312 * Bootstrap Navbar class
3315 * Create a new Navbar
3316 * @param {Object} config The config object
3320 Roo.bootstrap.Navbar = function(config){
3321 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3334 getAutoCreate : function(){
3337 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3341 initEvents :function ()
3343 //Roo.log(this.el.select('.navbar-toggle',true));
3344 this.el.select('.navbar-toggle',true).on('click', function() {
3345 // Roo.log('click');
3346 this.el.select('.navbar-collapse',true).toggleClass('in');
3354 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3356 var size = this.el.getSize();
3357 this.maskEl.setSize(size.width, size.height);
3358 this.maskEl.enableDisplayMode("block");
3367 getChildContainer : function()
3369 if (this.el.select('.collapse').getCount()) {
3370 return this.el.select('.collapse',true).first();
3403 * @class Roo.bootstrap.NavSimplebar
3404 * @extends Roo.bootstrap.Navbar
3405 * Bootstrap Sidebar class
3407 * @cfg {Boolean} inverse is inverted color
3409 * @cfg {String} type (nav | pills | tabs)
3410 * @cfg {Boolean} arrangement stacked | justified
3411 * @cfg {String} align (left | right) alignment
3413 * @cfg {Boolean} main (true|false) main nav bar? default false
3414 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3416 * @cfg {String} tag (header|footer|nav|div) default is nav
3422 * Create a new Sidebar
3423 * @param {Object} config The config object
3427 Roo.bootstrap.NavSimplebar = function(config){
3428 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3447 getAutoCreate : function(){
3451 tag : this.tag || 'div',
3464 this.type = this.type || 'nav';
3465 if (['tabs','pills'].indexOf(this.type)!==-1) {
3466 cfg.cn[0].cls += ' nav-' + this.type
3470 if (this.type!=='nav') {
3471 Roo.log('nav type must be nav/tabs/pills')
3473 cfg.cn[0].cls += ' navbar-nav'
3479 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480 cfg.cn[0].cls += ' nav-' + this.arrangement;
3484 if (this.align === 'right') {
3485 cfg.cn[0].cls += ' navbar-right';
3489 cfg.cls += ' navbar-inverse';
3516 * @class Roo.bootstrap.NavHeaderbar
3517 * @extends Roo.bootstrap.NavSimplebar
3518 * Bootstrap Sidebar class
3520 * @cfg {String} brand what is brand
3521 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522 * @cfg {String} brand_href href of the brand
3523 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3524 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3529 * Create a new Sidebar
3530 * @param {Object} config The config object
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3546 desktopCenter : false,
3549 getAutoCreate : function(){
3552 tag: this.nav || 'nav',
3559 if (this.desktopCenter) {
3560 cn.push({cls : 'container', cn : []});
3567 cls: 'navbar-header',
3572 cls: 'navbar-toggle',
3573 'data-toggle': 'collapse',
3578 html: 'Toggle navigation'
3600 cls: 'collapse navbar-collapse',
3604 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3606 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607 cfg.cls += ' navbar-' + this.position;
3609 // tag can override this..
3611 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3614 if (this.brand !== '') {
3617 href: this.brand_href ? this.brand_href : '#',
3618 cls: 'navbar-brand',
3626 cfg.cls += ' main-nav';
3634 getHeaderChildContainer : function()
3636 if (this.el.select('.navbar-header').getCount()) {
3637 return this.el.select('.navbar-header',true).first();
3640 return this.getChildContainer();
3644 initEvents : function()
3646 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3648 if (this.autohide) {
3653 Roo.get(document).on('scroll',function(e) {
3654 var ns = Roo.get(document).getScroll().top;
3655 var os = prevScroll;
3659 ft.removeClass('slideDown');
3660 ft.addClass('slideUp');
3663 ft.removeClass('slideUp');
3664 ft.addClass('slideDown');
3685 * @class Roo.bootstrap.NavSidebar
3686 * @extends Roo.bootstrap.Navbar
3687 * Bootstrap Sidebar class
3690 * Create a new Sidebar
3691 * @param {Object} config The config object
3695 Roo.bootstrap.NavSidebar = function(config){
3696 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3701 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3703 getAutoCreate : function(){
3708 cls: 'sidebar sidebar-nav'
3730 * @class Roo.bootstrap.NavGroup
3731 * @extends Roo.bootstrap.Component
3732 * Bootstrap NavGroup class
3733 * @cfg {String} align (left|right)
3734 * @cfg {Boolean} inverse
3735 * @cfg {String} type (nav|pills|tab) default nav
3736 * @cfg {String} navId - reference Id for navbar.
3740 * Create a new nav group
3741 * @param {Object} config The config object
3744 Roo.bootstrap.NavGroup = function(config){
3745 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3748 Roo.bootstrap.NavGroup.register(this);
3752 * Fires when the active item changes
3753 * @param {Roo.bootstrap.NavGroup} this
3754 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3773 getAutoCreate : function()
3775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3782 if (['tabs','pills'].indexOf(this.type)!==-1) {
3783 cfg.cls += ' nav-' + this.type
3785 if (this.type!=='nav') {
3786 Roo.log('nav type must be nav/tabs/pills')
3788 cfg.cls += ' navbar-nav'
3791 if (this.parent().sidebar) {
3794 cls: 'dashboard-menu sidebar-menu'
3800 if (this.form === true) {
3806 if (this.align === 'right') {
3807 cfg.cls += ' navbar-right';
3809 cfg.cls += ' navbar-left';
3813 if (this.align === 'right') {
3814 cfg.cls += ' navbar-right';
3818 cfg.cls += ' navbar-inverse';
3826 * sets the active Navigation item
3827 * @param {Roo.bootstrap.NavItem} the new current navitem
3829 setActiveItem : function(item)
3832 Roo.each(this.navItems, function(v){
3837 v.setActive(false, true);
3844 item.setActive(true, true);
3845 this.fireEvent('changed', this, item, prev);
3850 * gets the active Navigation item
3851 * @return {Roo.bootstrap.NavItem} the current navitem
3853 getActive : function()
3857 Roo.each(this.navItems, function(v){
3868 indexOfNav : function()
3872 Roo.each(this.navItems, function(v,i){
3883 * adds a Navigation item
3884 * @param {Roo.bootstrap.NavItem} the navitem to add
3886 addItem : function(cfg)
3888 var cn = new Roo.bootstrap.NavItem(cfg);
3890 cn.parentId = this.id;
3891 cn.onRender(this.el, null);
3895 * register a Navigation item
3896 * @param {Roo.bootstrap.NavItem} the navitem to add
3898 register : function(item)
3900 this.navItems.push( item);
3901 item.navId = this.navId;
3906 * clear all the Navigation item
3909 clearAll : function()
3912 this.el.dom.innerHTML = '';
3915 getNavItem: function(tabId)
3918 Roo.each(this.navItems, function(e) {
3919 if (e.tabId == tabId) {
3929 setActiveNext : function()
3931 var i = this.indexOfNav(this.getActive());
3932 if (i > this.navItems.length) {
3935 this.setActiveItem(this.navItems[i+1]);
3937 setActivePrev : function()
3939 var i = this.indexOfNav(this.getActive());
3943 this.setActiveItem(this.navItems[i-1]);
3945 clearWasActive : function(except) {
3946 Roo.each(this.navItems, function(e) {
3947 if (e.tabId != except.tabId && e.was_active) {
3948 e.was_active = false;
3955 getWasActive : function ()
3958 Roo.each(this.navItems, function(e) {
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3977 * register a Navigation Group
3978 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3980 register : function(navgrp)
3982 this.groups[navgrp.navId] = navgrp;
3986 * fetch a Navigation Group based on the navigation ID
3987 * @param {string} the navgroup to add
3988 * @returns {Roo.bootstrap.NavGroup} the navgroup
3990 get: function(navId) {
3991 if (typeof(this.groups[navId]) == 'undefined') {
3993 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3995 return this.groups[navId] ;
4010 * @class Roo.bootstrap.NavItem
4011 * @extends Roo.bootstrap.Component
4012 * Bootstrap Navbar.NavItem class
4013 * @cfg {String} href link to
4014 * @cfg {String} html content of button
4015 * @cfg {String} badge text inside badge
4016 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017 * @cfg {String} glyphicon name of glyphicon
4018 * @cfg {String} icon name of font awesome icon
4019 * @cfg {Boolean} active Is item active
4020 * @cfg {Boolean} disabled Is item disabled
4022 * @cfg {Boolean} preventDefault (true | false) default false
4023 * @cfg {String} tabId the tab that this item activates.
4024 * @cfg {String} tagtype (a|span) render as a href or span?
4025 * @cfg {Boolean} animateRef (true|false) link to element default false
4028 * Create a new Navbar Item
4029 * @param {Object} config The config object
4031 Roo.bootstrap.NavItem = function(config){
4032 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4037 * The raw click event for the entire grid.
4038 * @param {Roo.EventObject} e
4043 * Fires when the active item active state changes
4044 * @param {Roo.bootstrap.NavItem} this
4045 * @param {boolean} state the new state
4051 * Fires when scroll to element
4052 * @param {Roo.bootstrap.NavItem} this
4053 * @param {Object} options
4054 * @param {Roo.EventObject} e
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4070 preventDefault : false,
4077 getAutoCreate : function(){
4086 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4088 if (this.disabled) {
4089 cfg.cls += ' disabled';
4092 if (this.href || this.html || this.glyphicon || this.icon) {
4096 href : this.href || "#",
4097 html: this.html || ''
4102 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4105 if(this.glyphicon) {
4106 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4111 cfg.cn[0].html += " <span class='caret'></span>";
4115 if (this.badge !== '') {
4117 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4125 initEvents: function()
4127 if (typeof (this.menu) != 'undefined') {
4128 this.menu.parentType = this.xtype;
4129 this.menu.triggerEl = this.el;
4130 this.menu = this.addxtype(Roo.apply({}, this.menu));
4133 this.el.select('a',true).on('click', this.onClick, this);
4135 if(this.tagtype == 'span'){
4136 this.el.select('span',true).on('click', this.onClick, this);
4139 // at this point parent should be available..
4140 this.parent().register(this);
4143 onClick : function(e)
4146 this.preventDefault ||
4153 if (this.disabled) {
4157 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158 if (tg && tg.transition) {
4159 Roo.log("waiting for the transitionend");
4165 //Roo.log("fire event clicked");
4166 if(this.fireEvent('click', this, e) === false){
4170 if(this.tagtype == 'span'){
4174 //Roo.log(this.href);
4175 var ael = this.el.select('a',true).first();
4178 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181 return; // ignore... - it's a 'hash' to another page.
4185 this.scrollToElement(e);
4189 var p = this.parent();
4191 if (['tabs','pills'].indexOf(p.type)!==-1) {
4192 if (typeof(p.setActiveItem) !== 'undefined') {
4193 p.setActiveItem(this);
4197 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199 // remove the collapsed menu expand...
4200 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4204 isActive: function () {
4207 setActive : function(state, fire, is_was_active)
4209 if (this.active && !state && this.navId) {
4210 this.was_active = true;
4211 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4213 nv.clearWasActive(this);
4217 this.active = state;
4220 this.el.removeClass('active');
4221 } else if (!this.el.hasClass('active')) {
4222 this.el.addClass('active');
4225 this.fireEvent('changed', this, state);
4228 // show a panel if it's registered and related..
4230 if (!this.navId || !this.tabId || !state || is_was_active) {
4234 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4238 var pan = tg.getPanelByName(this.tabId);
4242 // if we can not flip to new panel - go back to old nav highlight..
4243 if (false == tg.showPanel(pan)) {
4244 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4246 var onav = nv.getWasActive();
4248 onav.setActive(true, false, true);
4257 // this should not be here...
4258 setDisabled : function(state)
4260 this.disabled = state;
4262 this.el.removeClass('disabled');
4263 } else if (!this.el.hasClass('disabled')) {
4264 this.el.addClass('disabled');
4270 * Fetch the element to display the tooltip on.
4271 * @return {Roo.Element} defaults to this.el
4273 tooltipEl : function()
4275 return this.el.select('' + this.tagtype + '', true).first();
4278 scrollToElement : function(e)
4280 var c = document.body;
4283 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4285 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286 c = document.documentElement;
4289 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4295 var o = target.calcOffsetsTo(c);
4302 this.fireEvent('scrollto', this, options, e);
4304 Roo.get(c).scrollTo('top', options.value, true);
4317 * <span> icon </span>
4318 * <span> text </span>
4319 * <span>badge </span>
4323 * @class Roo.bootstrap.NavSidebarItem
4324 * @extends Roo.bootstrap.NavItem
4325 * Bootstrap Navbar.NavSidebarItem class
4326 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4328 * Create a new Navbar Button
4329 * @param {Object} config The config object
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4337 * The raw click event for the entire grid.
4338 * @param {Roo.EventObject} e
4343 * Fires when the active item active state changes
4344 * @param {Roo.bootstrap.NavSidebarItem} this
4345 * @param {boolean} state the new state
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4355 badgeWeight : 'default',
4357 getAutoCreate : function(){
4362 href : this.href || '#',
4374 html : this.html || ''
4379 cfg.cls += ' active';
4382 if (this.disabled) {
4383 cfg.cls += ' disabled';
4387 if (this.glyphicon || this.icon) {
4388 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4389 a.cn.push({ tag : 'i', cls : c }) ;
4394 if (this.badge !== '') {
4396 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4400 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401 a.cls += 'dropdown-toggle treeview' ;
4412 initEvents : function()
4414 this.el.on('click', this.onClick, this);
4417 if(this.badge !== ''){
4419 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4424 onClick : function(e)
4431 if(this.preventDefault){
4435 this.fireEvent('click', this);
4438 disable : function()
4440 this.setDisabled(true);
4445 this.setDisabled(false);
4448 setDisabled : function(state)
4450 if(this.disabled == state){
4454 this.disabled = state;
4457 this.el.addClass('disabled');
4461 this.el.removeClass('disabled');
4466 setActive : function(state)
4468 if(this.active == state){
4472 this.active = state;
4475 this.el.addClass('active');
4479 this.el.removeClass('active');
4484 isActive: function ()
4489 setBadge : function(str)
4495 this.badgeEl.dom.innerHTML = str;
4512 * @class Roo.bootstrap.Row
4513 * @extends Roo.bootstrap.Component
4514 * Bootstrap Row class (contains columns...)
4518 * @param {Object} config The config object
4521 Roo.bootstrap.Row = function(config){
4522 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4527 getAutoCreate : function(){
4546 * @class Roo.bootstrap.Element
4547 * @extends Roo.bootstrap.Component
4548 * Bootstrap Element class
4549 * @cfg {String} html contents of the element
4550 * @cfg {String} tag tag of the element
4551 * @cfg {String} cls class of the element
4552 * @cfg {Boolean} preventDefault (true|false) default false
4553 * @cfg {Boolean} clickable (true|false) default false
4556 * Create a new Element
4557 * @param {Object} config The config object
4560 Roo.bootstrap.Element = function(config){
4561 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4567 * When a element is chick
4568 * @param {Roo.bootstrap.Element} this
4569 * @param {Roo.EventObject} e
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4580 preventDefault: false,
4583 getAutoCreate : function(){
4594 initEvents: function()
4596 Roo.bootstrap.Element.superclass.initEvents.call(this);
4599 this.el.on('click', this.onClick, this);
4604 onClick : function(e)
4606 if(this.preventDefault){
4610 this.fireEvent('click', this, e);
4613 getValue : function()
4615 return this.el.dom.innerHTML;
4618 setValue : function(value)
4620 this.el.dom.innerHTML = value;
4635 * @class Roo.bootstrap.Pagination
4636 * @extends Roo.bootstrap.Component
4637 * Bootstrap Pagination class
4638 * @cfg {String} size xs | sm | md | lg
4639 * @cfg {Boolean} inverse false | true
4642 * Create a new Pagination
4643 * @param {Object} config The config object
4646 Roo.bootstrap.Pagination = function(config){
4647 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4656 getAutoCreate : function(){
4662 cfg.cls += ' inverse';
4668 cfg.cls += " " + this.cls;
4686 * @class Roo.bootstrap.PaginationItem
4687 * @extends Roo.bootstrap.Component
4688 * Bootstrap PaginationItem class
4689 * @cfg {String} html text
4690 * @cfg {String} href the link
4691 * @cfg {Boolean} preventDefault (true | false) default true
4692 * @cfg {Boolean} active (true | false) default false
4693 * @cfg {Boolean} disabled default false
4697 * Create a new PaginationItem
4698 * @param {Object} config The config object
4702 Roo.bootstrap.PaginationItem = function(config){
4703 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4708 * The raw click event for the entire grid.
4709 * @param {Roo.EventObject} e
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4719 preventDefault: true,
4724 getAutoCreate : function(){
4730 href : this.href ? this.href : '#',
4731 html : this.html ? this.html : ''
4741 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4745 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4751 initEvents: function() {
4753 this.el.on('click', this.onClick, this);
4756 onClick : function(e)
4758 Roo.log('PaginationItem on click ');
4759 if(this.preventDefault){
4767 this.fireEvent('click', this, e);
4783 * @class Roo.bootstrap.Slider
4784 * @extends Roo.bootstrap.Component
4785 * Bootstrap Slider class
4788 * Create a new Slider
4789 * @param {Object} config The config object
4792 Roo.bootstrap.Slider = function(config){
4793 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4798 getAutoCreate : function(){
4802 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4806 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4818 * Ext JS Library 1.1.1
4819 * Copyright(c) 2006-2007, Ext JS, LLC.
4821 * Originally Released Under LGPL - original licence link has changed is not relivant.
4824 * <script type="text/javascript">
4829 * @class Roo.grid.ColumnModel
4830 * @extends Roo.util.Observable
4831 * This is the default implementation of a ColumnModel used by the Grid. It defines
4832 * the columns in the grid.
4835 var colModel = new Roo.grid.ColumnModel([
4836 {header: "Ticker", width: 60, sortable: true, locked: true},
4837 {header: "Company Name", width: 150, sortable: true},
4838 {header: "Market Cap.", width: 100, sortable: true},
4839 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840 {header: "Employees", width: 100, sortable: true, resizable: false}
4845 * The config options listed for this class are options which may appear in each
4846 * individual column definition.
4847 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4849 * @param {Object} config An Array of column config objects. See this class's
4850 * config objects for details.
4852 Roo.grid.ColumnModel = function(config){
4854 * The config passed into the constructor
4856 this.config = config;
4859 // if no id, create one
4860 // if the column does not have a dataIndex mapping,
4861 // map it to the order it is in the config
4862 for(var i = 0, len = config.length; i < len; i++){
4864 if(typeof c.dataIndex == "undefined"){
4867 if(typeof c.renderer == "string"){
4868 c.renderer = Roo.util.Format[c.renderer];
4870 if(typeof c.id == "undefined"){
4873 if(c.editor && c.editor.xtype){
4874 c.editor = Roo.factory(c.editor, Roo.grid);
4876 if(c.editor && c.editor.isFormField){
4877 c.editor = new Roo.grid.GridEditor(c.editor);
4879 this.lookup[c.id] = c;
4883 * The width of columns which have no width specified (defaults to 100)
4886 this.defaultWidth = 100;
4889 * Default sortable of columns which have no sortable specified (defaults to false)
4892 this.defaultSortable = false;
4896 * @event widthchange
4897 * Fires when the width of a column changes.
4898 * @param {ColumnModel} this
4899 * @param {Number} columnIndex The column index
4900 * @param {Number} newWidth The new width
4902 "widthchange": true,
4904 * @event headerchange
4905 * Fires when the text of a header changes.
4906 * @param {ColumnModel} this
4907 * @param {Number} columnIndex The column index
4908 * @param {Number} newText The new header text
4910 "headerchange": true,
4912 * @event hiddenchange
4913 * Fires when a column is hidden or "unhidden".
4914 * @param {ColumnModel} this
4915 * @param {Number} columnIndex The column index
4916 * @param {Boolean} hidden true if hidden, false otherwise
4918 "hiddenchange": true,
4920 * @event columnmoved
4921 * Fires when a column is moved.
4922 * @param {ColumnModel} this
4923 * @param {Number} oldIndex
4924 * @param {Number} newIndex
4926 "columnmoved" : true,
4928 * @event columlockchange
4929 * Fires when a column's locked state is changed
4930 * @param {ColumnModel} this
4931 * @param {Number} colIndex
4932 * @param {Boolean} locked true if locked
4934 "columnlockchange" : true
4936 Roo.grid.ColumnModel.superclass.constructor.call(this);
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4940 * @cfg {String} header The header text to display in the Grid view.
4943 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945 * specified, the column's index is used as an index into the Record's data Array.
4948 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4952 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953 * Defaults to the value of the {@link #defaultSortable} property.
4954 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4957 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4960 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4963 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4966 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4969 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4975 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4978 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4981 * @cfg {String} cursor (Optional)
4984 * @cfg {String} tooltip (Optional)
4987 * @cfg {Number} xs (Optional)
4990 * @cfg {Number} sm (Optional)
4993 * @cfg {Number} md (Optional)
4996 * @cfg {Number} lg (Optional)
4999 * Returns the id of the column at the specified index.
5000 * @param {Number} index The column index
5001 * @return {String} the id
5003 getColumnId : function(index){
5004 return this.config[index].id;
5008 * Returns the column for a specified id.
5009 * @param {String} id The column id
5010 * @return {Object} the column
5012 getColumnById : function(id){
5013 return this.lookup[id];
5018 * Returns the column for a specified dataIndex.
5019 * @param {String} dataIndex The column dataIndex
5020 * @return {Object|Boolean} the column or false if not found
5022 getColumnByDataIndex: function(dataIndex){
5023 var index = this.findColumnIndex(dataIndex);
5024 return index > -1 ? this.config[index] : false;
5028 * Returns the index for a specified column id.
5029 * @param {String} id The column id
5030 * @return {Number} the index, or -1 if not found
5032 getIndexById : function(id){
5033 for(var i = 0, len = this.config.length; i < len; i++){
5034 if(this.config[i].id == id){
5042 * Returns the index for a specified column dataIndex.
5043 * @param {String} dataIndex The column dataIndex
5044 * @return {Number} the index, or -1 if not found
5047 findColumnIndex : function(dataIndex){
5048 for(var i = 0, len = this.config.length; i < len; i++){
5049 if(this.config[i].dataIndex == dataIndex){
5057 moveColumn : function(oldIndex, newIndex){
5058 var c = this.config[oldIndex];
5059 this.config.splice(oldIndex, 1);
5060 this.config.splice(newIndex, 0, c);
5061 this.dataMap = null;
5062 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5065 isLocked : function(colIndex){
5066 return this.config[colIndex].locked === true;
5069 setLocked : function(colIndex, value, suppressEvent){
5070 if(this.isLocked(colIndex) == value){
5073 this.config[colIndex].locked = value;
5075 this.fireEvent("columnlockchange", this, colIndex, value);
5079 getTotalLockedWidth : function(){
5081 for(var i = 0; i < this.config.length; i++){
5082 if(this.isLocked(i) && !this.isHidden(i)){
5083 this.totalWidth += this.getColumnWidth(i);
5089 getLockedCount : function(){
5090 for(var i = 0, len = this.config.length; i < len; i++){
5091 if(!this.isLocked(i)){
5098 * Returns the number of columns.
5101 getColumnCount : function(visibleOnly){
5102 if(visibleOnly === true){
5104 for(var i = 0, len = this.config.length; i < len; i++){
5105 if(!this.isHidden(i)){
5111 return this.config.length;
5115 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116 * @param {Function} fn
5117 * @param {Object} scope (optional)
5118 * @return {Array} result
5120 getColumnsBy : function(fn, scope){
5122 for(var i = 0, len = this.config.length; i < len; i++){
5123 var c = this.config[i];
5124 if(fn.call(scope||this, c, i) === true){
5132 * Returns true if the specified column is sortable.
5133 * @param {Number} col The column index
5136 isSortable : function(col){
5137 if(typeof this.config[col].sortable == "undefined"){
5138 return this.defaultSortable;
5140 return this.config[col].sortable;
5144 * Returns the rendering (formatting) function defined for the column.
5145 * @param {Number} col The column index.
5146 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5148 getRenderer : function(col){
5149 if(!this.config[col].renderer){
5150 return Roo.grid.ColumnModel.defaultRenderer;
5152 return this.config[col].renderer;
5156 * Sets the rendering (formatting) function for a column.
5157 * @param {Number} col The column index
5158 * @param {Function} fn The function to use to process the cell's raw data
5159 * to return HTML markup for the grid view. The render function is called with
5160 * the following parameters:<ul>
5161 * <li>Data value.</li>
5162 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163 * <li>css A CSS style string to apply to the table cell.</li>
5164 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166 * <li>Row index</li>
5167 * <li>Column index</li>
5168 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5170 setRenderer : function(col, fn){
5171 this.config[col].renderer = fn;
5175 * Returns the width for the specified column.
5176 * @param {Number} col The column index
5179 getColumnWidth : function(col){
5180 return this.config[col].width * 1 || this.defaultWidth;
5184 * Sets the width for a column.
5185 * @param {Number} col The column index
5186 * @param {Number} width The new width
5188 setColumnWidth : function(col, width, suppressEvent){
5189 this.config[col].width = width;
5190 this.totalWidth = null;
5192 this.fireEvent("widthchange", this, col, width);
5197 * Returns the total width of all columns.
5198 * @param {Boolean} includeHidden True to include hidden column widths
5201 getTotalWidth : function(includeHidden){
5202 if(!this.totalWidth){
5203 this.totalWidth = 0;
5204 for(var i = 0, len = this.config.length; i < len; i++){
5205 if(includeHidden || !this.isHidden(i)){
5206 this.totalWidth += this.getColumnWidth(i);
5210 return this.totalWidth;
5214 * Returns the header for the specified column.
5215 * @param {Number} col The column index
5218 getColumnHeader : function(col){
5219 return this.config[col].header;
5223 * Sets the header for a column.
5224 * @param {Number} col The column index
5225 * @param {String} header The new header
5227 setColumnHeader : function(col, header){
5228 this.config[col].header = header;
5229 this.fireEvent("headerchange", this, col, header);
5233 * Returns the tooltip for the specified column.
5234 * @param {Number} col The column index
5237 getColumnTooltip : function(col){
5238 return this.config[col].tooltip;
5241 * Sets the tooltip for a column.
5242 * @param {Number} col The column index
5243 * @param {String} tooltip The new tooltip
5245 setColumnTooltip : function(col, tooltip){
5246 this.config[col].tooltip = tooltip;
5250 * Returns the dataIndex for the specified column.
5251 * @param {Number} col The column index
5254 getDataIndex : function(col){
5255 return this.config[col].dataIndex;
5259 * Sets the dataIndex for a column.
5260 * @param {Number} col The column index
5261 * @param {Number} dataIndex The new dataIndex
5263 setDataIndex : function(col, dataIndex){
5264 this.config[col].dataIndex = dataIndex;
5270 * Returns true if the cell is editable.
5271 * @param {Number} colIndex The column index
5272 * @param {Number} rowIndex The row index
5275 isCellEditable : function(colIndex, rowIndex){
5276 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5280 * Returns the editor defined for the cell/column.
5281 * return false or null to disable editing.
5282 * @param {Number} colIndex The column index
5283 * @param {Number} rowIndex The row index
5286 getCellEditor : function(colIndex, rowIndex){
5287 return this.config[colIndex].editor;
5291 * Sets if a column is editable.
5292 * @param {Number} col The column index
5293 * @param {Boolean} editable True if the column is editable
5295 setEditable : function(col, editable){
5296 this.config[col].editable = editable;
5301 * Returns true if the column is hidden.
5302 * @param {Number} colIndex The column index
5305 isHidden : function(colIndex){
5306 return this.config[colIndex].hidden;
5311 * Returns true if the column width cannot be changed
5313 isFixed : function(colIndex){
5314 return this.config[colIndex].fixed;
5318 * Returns true if the column can be resized
5321 isResizable : function(colIndex){
5322 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5325 * Sets if a column is hidden.
5326 * @param {Number} colIndex The column index
5327 * @param {Boolean} hidden True if the column is hidden
5329 setHidden : function(colIndex, hidden){
5330 this.config[colIndex].hidden = hidden;
5331 this.totalWidth = null;
5332 this.fireEvent("hiddenchange", this, colIndex, hidden);
5336 * Sets the editor for a column.
5337 * @param {Number} col The column index
5338 * @param {Object} editor The editor object
5340 setEditor : function(col, editor){
5341 this.config[col].editor = editor;
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346 if(typeof value == "string" && value.length < 1){
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5356 * Ext JS Library 1.1.1
5357 * Copyright(c) 2006-2007, Ext JS, LLC.
5359 * Originally Released Under LGPL - original licence link has changed is not relivant.
5362 * <script type="text/javascript">
5366 * @class Roo.LoadMask
5367 * A simple utility class for generically masking elements while loading data. If the element being masked has
5368 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5370 * element's UpdateManager load indicator and will be destroyed after the initial load.
5372 * Create a new LoadMask
5373 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374 * @param {Object} config The config object
5376 Roo.LoadMask = function(el, config){
5377 this.el = Roo.get(el);
5378 Roo.apply(this, config);
5380 this.store.on('beforeload', this.onBeforeLoad, this);
5381 this.store.on('load', this.onLoad, this);
5382 this.store.on('loadexception', this.onLoadException, this);
5383 this.removeMask = false;
5385 var um = this.el.getUpdateManager();
5386 um.showLoadIndicator = false; // disable the default indicator
5387 um.on('beforeupdate', this.onBeforeLoad, this);
5388 um.on('update', this.onLoad, this);
5389 um.on('failure', this.onLoad, this);
5390 this.removeMask = true;
5394 Roo.LoadMask.prototype = {
5396 * @cfg {Boolean} removeMask
5397 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5402 * The text to display in a centered loading message box (defaults to 'Loading...')
5406 * @cfg {String} msgCls
5407 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5409 msgCls : 'x-mask-loading',
5412 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5418 * Disables the mask to prevent it from being displayed
5420 disable : function(){
5421 this.disabled = true;
5425 * Enables the mask so that it can be displayed
5427 enable : function(){
5428 this.disabled = false;
5431 onLoadException : function()
5435 if (typeof(arguments[3]) != 'undefined') {
5436 Roo.MessageBox.alert("Error loading",arguments[3]);
5440 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5450 this.el.unmask(this.removeMask);
5455 this.el.unmask(this.removeMask);
5459 onBeforeLoad : function(){
5461 this.el.mask(this.msg, this.msgCls);
5466 destroy : function(){
5468 this.store.un('beforeload', this.onBeforeLoad, this);
5469 this.store.un('load', this.onLoad, this);
5470 this.store.un('loadexception', this.onLoadException, this);
5472 var um = this.el.getUpdateManager();
5473 um.un('beforeupdate', this.onBeforeLoad, this);
5474 um.un('update', this.onLoad, this);
5475 um.un('failure', this.onLoad, this);
5486 * @class Roo.bootstrap.Table
5487 * @extends Roo.bootstrap.Component
5488 * Bootstrap Table class
5489 * @cfg {String} cls table class
5490 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491 * @cfg {String} bgcolor Specifies the background color for a table
5492 * @cfg {Number} border Specifies whether the table cells should have borders or not
5493 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494 * @cfg {Number} cellspacing Specifies the space between cells
5495 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497 * @cfg {String} sortable Specifies that the table should be sortable
5498 * @cfg {String} summary Specifies a summary of the content of a table
5499 * @cfg {Number} width Specifies the width of a table
5500 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5502 * @cfg {boolean} striped Should the rows be alternative striped
5503 * @cfg {boolean} bordered Add borders to the table
5504 * @cfg {boolean} hover Add hover highlighting
5505 * @cfg {boolean} condensed Format condensed
5506 * @cfg {boolean} responsive Format condensed
5507 * @cfg {Boolean} loadMask (true|false) default false
5508 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510 * @cfg {Boolean} rowSelection (true|false) default false
5511 * @cfg {Boolean} cellSelection (true|false) default false
5512 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5516 * Create a new Table
5517 * @param {Object} config The config object
5520 Roo.bootstrap.Table = function(config){
5521 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5524 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5531 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532 this.sm = this.selModel;
5533 this.sm.xmodule = this.xmodule || false;
5535 if (this.cm && typeof(this.cm.config) == 'undefined') {
5536 this.colModel = new Roo.grid.ColumnModel(this.cm);
5537 this.cm = this.colModel;
5538 this.cm.xmodule = this.xmodule || false;
5541 this.store= Roo.factory(this.store, Roo.data);
5542 this.ds = this.store;
5543 this.ds.xmodule = this.xmodule || false;
5546 if (this.footer && this.store) {
5547 this.footer.dataSource = this.ds;
5548 this.footer = Roo.factory(this.footer);
5555 * Fires when a cell is clicked
5556 * @param {Roo.bootstrap.Table} this
5557 * @param {Roo.Element} el
5558 * @param {Number} rowIndex
5559 * @param {Number} columnIndex
5560 * @param {Roo.EventObject} e
5564 * @event celldblclick
5565 * Fires when a cell is double clicked
5566 * @param {Roo.bootstrap.Table} this
5567 * @param {Roo.Element} el
5568 * @param {Number} rowIndex
5569 * @param {Number} columnIndex
5570 * @param {Roo.EventObject} e
5572 "celldblclick" : true,
5575 * Fires when a row is clicked
5576 * @param {Roo.bootstrap.Table} this
5577 * @param {Roo.Element} el
5578 * @param {Number} rowIndex
5579 * @param {Roo.EventObject} e
5583 * @event rowdblclick
5584 * Fires when a row is double clicked
5585 * @param {Roo.bootstrap.Table} this
5586 * @param {Roo.Element} el
5587 * @param {Number} rowIndex
5588 * @param {Roo.EventObject} e
5590 "rowdblclick" : true,
5593 * Fires when a mouseover occur
5594 * @param {Roo.bootstrap.Table} this
5595 * @param {Roo.Element} el
5596 * @param {Number} rowIndex
5597 * @param {Number} columnIndex
5598 * @param {Roo.EventObject} e
5603 * Fires when a mouseout occur
5604 * @param {Roo.bootstrap.Table} this
5605 * @param {Roo.Element} el
5606 * @param {Number} rowIndex
5607 * @param {Number} columnIndex
5608 * @param {Roo.EventObject} e
5613 * Fires when a row is rendered, so you can change add a style to it.
5614 * @param {Roo.bootstrap.Table} this
5615 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5619 * @event rowsrendered
5620 * Fires when all the rows have been rendered
5621 * @param {Roo.bootstrap.Table} this
5623 'rowsrendered' : true
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5653 rowSelection : false,
5654 cellSelection : false,
5657 // Roo.Element - the tbody
5660 getAutoCreate : function(){
5661 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5670 cfg.cls += ' table-striped';
5674 cfg.cls += ' table-hover';
5676 if (this.bordered) {
5677 cfg.cls += ' table-bordered';
5679 if (this.condensed) {
5680 cfg.cls += ' table-condensed';
5682 if (this.responsive) {
5683 cfg.cls += ' table-responsive';
5687 cfg.cls+= ' ' +this.cls;
5690 // this lot should be simplifed...
5693 cfg.align=this.align;
5696 cfg.bgcolor=this.bgcolor;
5699 cfg.border=this.border;
5701 if (this.cellpadding) {
5702 cfg.cellpadding=this.cellpadding;
5704 if (this.cellspacing) {
5705 cfg.cellspacing=this.cellspacing;
5708 cfg.frame=this.frame;
5711 cfg.rules=this.rules;
5713 if (this.sortable) {
5714 cfg.sortable=this.sortable;
5717 cfg.summary=this.summary;
5720 cfg.width=this.width;
5723 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5726 if(this.store || this.cm){
5727 if(this.headerShow){
5728 cfg.cn.push(this.renderHeader());
5731 cfg.cn.push(this.renderBody());
5733 if(this.footerShow){
5734 cfg.cn.push(this.renderFooter());
5737 cfg.cls+= ' TableGrid';
5740 return { cn : [ cfg ] };
5743 initEvents : function()
5745 if(!this.store || !this.cm){
5749 //Roo.log('initEvents with ds!!!!');
5751 this.mainBody = this.el.select('tbody', true).first();
5756 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757 e.on('click', _this.sort, _this);
5760 this.el.on("click", this.onClick, this);
5761 this.el.on("dblclick", this.onDblClick, this);
5763 // why is this done????? = it breaks dialogs??
5764 //this.parent().el.setStyle('position', 'relative');
5768 this.footer.parentId = this.id;
5769 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5772 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5774 this.store.on('load', this.onLoad, this);
5775 this.store.on('beforeload', this.onBeforeLoad, this);
5776 this.store.on('update', this.onUpdate, this);
5777 this.store.on('add', this.onAdd, this);
5781 onMouseover : function(e, el)
5783 var cell = Roo.get(el);
5789 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790 cell = cell.findParent('td', false, true);
5793 var row = cell.findParent('tr', false, true);
5794 var cellIndex = cell.dom.cellIndex;
5795 var rowIndex = row.dom.rowIndex - 1; // start from 0
5797 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5801 onMouseout : function(e, el)
5803 var cell = Roo.get(el);
5809 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810 cell = cell.findParent('td', false, true);
5813 var row = cell.findParent('tr', false, true);
5814 var cellIndex = cell.dom.cellIndex;
5815 var rowIndex = row.dom.rowIndex - 1; // start from 0
5817 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5821 onClick : function(e, el)
5823 var cell = Roo.get(el);
5825 if(!cell || (!this.cellSelection && !this.rowSelection)){
5829 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830 cell = cell.findParent('td', false, true);
5833 if(!cell || typeof(cell) == 'undefined'){
5837 var row = cell.findParent('tr', false, true);
5839 if(!row || typeof(row) == 'undefined'){
5843 var cellIndex = cell.dom.cellIndex;
5844 var rowIndex = this.getRowIndex(row);
5846 // why??? - should these not be based on SelectionModel?
5847 if(this.cellSelection){
5848 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5851 if(this.rowSelection){
5852 this.fireEvent('rowclick', this, row, rowIndex, e);
5858 onDblClick : function(e,el)
5860 var cell = Roo.get(el);
5862 if(!cell || (!this.CellSelection && !this.RowSelection)){
5866 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867 cell = cell.findParent('td', false, true);
5870 if(!cell || typeof(cell) == 'undefined'){
5874 var row = cell.findParent('tr', false, true);
5876 if(!row || typeof(row) == 'undefined'){
5880 var cellIndex = cell.dom.cellIndex;
5881 var rowIndex = this.getRowIndex(row);
5883 if(this.CellSelection){
5884 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5887 if(this.RowSelection){
5888 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5892 sort : function(e,el)
5894 var col = Roo.get(el);
5896 if(!col.hasClass('sortable')){
5900 var sort = col.attr('sort');
5903 if(col.hasClass('glyphicon-arrow-up')){
5907 this.store.sortInfo = {field : sort, direction : dir};
5910 Roo.log("calling footer first");
5911 this.footer.onClick('first');
5914 this.store.load({ params : { start : 0 } });
5918 renderHeader : function()
5927 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5929 var config = cm.config[i];
5934 html: cm.getColumnHeader(i)
5939 if(typeof(config.lgHeader) != 'undefined'){
5940 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5943 if(typeof(config.mdHeader) != 'undefined'){
5944 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5947 if(typeof(config.smHeader) != 'undefined'){
5948 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5951 if(typeof(config.xsHeader) != 'undefined'){
5952 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5959 if(typeof(config.tooltip) != 'undefined'){
5960 c.tooltip = config.tooltip;
5963 if(typeof(config.colspan) != 'undefined'){
5964 c.colspan = config.colspan;
5967 if(typeof(config.hidden) != 'undefined' && config.hidden){
5968 c.style += ' display:none;';
5971 if(typeof(config.dataIndex) != 'undefined'){
5972 c.sort = config.dataIndex;
5975 if(typeof(config.sortable) != 'undefined' && config.sortable){
5979 if(typeof(config.align) != 'undefined' && config.align.length){
5980 c.style += ' text-align:' + config.align + ';';
5983 if(typeof(config.width) != 'undefined'){
5984 c.style += ' width:' + config.width + 'px;';
5987 if(typeof(config.cls) != 'undefined'){
5988 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5991 ['xs','sm','md','lg'].map(function(size){
5993 if(typeof(config[size]) == 'undefined'){
5997 if (!config[size]) { // 0 = hidden
5998 cfg.cls += ' hidden-' + size;
6002 cfg.cls += ' col-' + size + '-' + config[size];
6012 renderBody : function()
6022 colspan : this.cm.getColumnCount()
6032 renderFooter : function()
6042 colspan : this.cm.getColumnCount()
6056 Roo.log('ds onload');
6061 var ds = this.store;
6063 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6066 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6070 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6075 var tbody = this.mainBody;
6077 if(ds.getCount() > 0){
6078 ds.data.each(function(d,rowIndex){
6079 var row = this.renderRow(cm, ds, rowIndex);
6081 tbody.createChild(row);
6085 if(row.cellObjects.length){
6086 Roo.each(row.cellObjects, function(r){
6087 _this.renderCellObject(r);
6094 Roo.each(this.el.select('tbody td', true).elements, function(e){
6095 e.on('mouseover', _this.onMouseover, _this);
6098 Roo.each(this.el.select('tbody td', true).elements, function(e){
6099 e.on('mouseout', _this.onMouseout, _this);
6101 this.fireEvent('rowsrendered', this);
6102 //if(this.loadMask){
6103 // this.maskEl.hide();
6108 onUpdate : function(ds,record)
6110 this.refreshRow(record);
6113 onRemove : function(ds, record, index, isUpdate){
6114 if(isUpdate !== true){
6115 this.fireEvent("beforerowremoved", this, index, record);
6117 var bt = this.mainBody.dom;
6119 var rows = this.el.select('tbody > tr', true).elements;
6121 if(typeof(rows[index]) != 'undefined'){
6122 bt.removeChild(rows[index].dom);
6125 // if(bt.rows[index]){
6126 // bt.removeChild(bt.rows[index]);
6129 if(isUpdate !== true){
6130 //this.stripeRows(index);
6131 //this.syncRowHeights(index, index);
6133 this.fireEvent("rowremoved", this, index, record);
6137 onAdd : function(ds, records, rowIndex)
6139 //Roo.log('on Add called');
6140 // - note this does not handle multiple adding very well..
6141 var bt = this.mainBody.dom;
6142 for (var i =0 ; i < records.length;i++) {
6143 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144 //Roo.log(records[i]);
6145 //Roo.log(this.store.getAt(rowIndex+i));
6146 this.insertRow(this.store, rowIndex + i, false);
6153 refreshRow : function(record){
6154 var ds = this.store, index;
6155 if(typeof record == 'number'){
6157 record = ds.getAt(index);
6159 index = ds.indexOf(record);
6161 this.insertRow(ds, index, true);
6162 this.onRemove(ds, record, index+1, true);
6163 //this.syncRowHeights(index, index);
6165 this.fireEvent("rowupdated", this, index, record);
6168 insertRow : function(dm, rowIndex, isUpdate){
6171 this.fireEvent("beforerowsinserted", this, rowIndex);
6173 //var s = this.getScrollState();
6174 var row = this.renderRow(this.cm, this.store, rowIndex);
6175 // insert before rowIndex..
6176 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6180 if(row.cellObjects.length){
6181 Roo.each(row.cellObjects, function(r){
6182 _this.renderCellObject(r);
6187 this.fireEvent("rowsinserted", this, rowIndex);
6188 //this.syncRowHeights(firstRow, lastRow);
6189 //this.stripeRows(firstRow);
6196 getRowDom : function(rowIndex)
6198 var rows = this.el.select('tbody > tr', true).elements;
6200 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6203 // returns the object tree for a tr..
6206 renderRow : function(cm, ds, rowIndex)
6209 var d = ds.getAt(rowIndex);
6216 var cellObjects = [];
6218 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219 var config = cm.config[i];
6221 var renderer = cm.getRenderer(i);
6225 if(typeof(renderer) !== 'undefined'){
6226 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6228 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229 // and are rendered into the cells after the row is rendered - using the id for the element.
6231 if(typeof(value) === 'object'){
6241 rowIndex : rowIndex,
6246 this.fireEvent('rowclass', this, rowcfg);
6250 cls : rowcfg.rowClass,
6252 html: (typeof(value) === 'object') ? '' : value
6259 if(typeof(config.colspan) != 'undefined'){
6260 td.colspan = config.colspan;
6263 if(typeof(config.hidden) != 'undefined' && config.hidden){
6264 td.style += ' display:none;';
6267 if(typeof(config.align) != 'undefined' && config.align.length){
6268 td.style += ' text-align:' + config.align + ';';
6271 if(typeof(config.width) != 'undefined'){
6272 td.style += ' width:' + config.width + 'px;';
6275 if(typeof(config.cursor) != 'undefined'){
6276 td.style += ' cursor:' + config.cursor + ';';
6279 if(typeof(config.cls) != 'undefined'){
6280 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6283 ['xs','sm','md','lg'].map(function(size){
6285 if(typeof(config[size]) == 'undefined'){
6289 if (!config[size]) { // 0 = hidden
6290 td.cls += ' hidden-' + size;
6294 td.cls += ' col-' + size + '-' + config[size];
6302 row.cellObjects = cellObjects;
6310 onBeforeLoad : function()
6312 //Roo.log('ds onBeforeLoad');
6316 //if(this.loadMask){
6317 // this.maskEl.show();
6325 this.el.select('tbody', true).first().dom.innerHTML = '';
6328 * Show or hide a row.
6329 * @param {Number} rowIndex to show or hide
6330 * @param {Boolean} state hide
6332 setRowVisibility : function(rowIndex, state)
6334 var bt = this.mainBody.dom;
6336 var rows = this.el.select('tbody > tr', true).elements;
6338 if(typeof(rows[rowIndex]) == 'undefined'){
6341 rows[rowIndex].dom.style.display = state ? '' : 'none';
6345 getSelectionModel : function(){
6347 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6349 return this.selModel;
6352 * Render the Roo.bootstrap object from renderder
6354 renderCellObject : function(r)
6358 var t = r.cfg.render(r.container);
6361 Roo.each(r.cfg.cn, function(c){
6363 container: t.getChildContainer(),
6366 _this.renderCellObject(child);
6371 getRowIndex : function(row)
6375 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6398 * @class Roo.bootstrap.TableCell
6399 * @extends Roo.bootstrap.Component
6400 * Bootstrap TableCell class
6401 * @cfg {String} html cell contain text
6402 * @cfg {String} cls cell class
6403 * @cfg {String} tag cell tag (td|th) default td
6404 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405 * @cfg {String} align Aligns the content in a cell
6406 * @cfg {String} axis Categorizes cells
6407 * @cfg {String} bgcolor Specifies the background color of a cell
6408 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409 * @cfg {Number} colspan Specifies the number of columns a cell should span
6410 * @cfg {String} headers Specifies one or more header cells a cell is related to
6411 * @cfg {Number} height Sets the height of a cell
6412 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413 * @cfg {Number} rowspan Sets the number of rows a cell should span
6414 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415 * @cfg {String} valign Vertical aligns the content in a cell
6416 * @cfg {Number} width Specifies the width of a cell
6419 * Create a new TableCell
6420 * @param {Object} config The config object
6423 Roo.bootstrap.TableCell = function(config){
6424 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6447 getAutoCreate : function(){
6448 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6468 cfg.align=this.align
6474 cfg.bgcolor=this.bgcolor
6477 cfg.charoff=this.charoff
6480 cfg.colspan=this.colspan
6483 cfg.headers=this.headers
6486 cfg.height=this.height
6489 cfg.nowrap=this.nowrap
6492 cfg.rowspan=this.rowspan
6495 cfg.scope=this.scope
6498 cfg.valign=this.valign
6501 cfg.width=this.width
6520 * @class Roo.bootstrap.TableRow
6521 * @extends Roo.bootstrap.Component
6522 * Bootstrap TableRow class
6523 * @cfg {String} cls row class
6524 * @cfg {String} align Aligns the content in a table row
6525 * @cfg {String} bgcolor Specifies a background color for a table row
6526 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527 * @cfg {String} valign Vertical aligns the content in a table row
6530 * Create a new TableRow
6531 * @param {Object} config The config object
6534 Roo.bootstrap.TableRow = function(config){
6535 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6546 getAutoCreate : function(){
6547 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6557 cfg.align = this.align;
6560 cfg.bgcolor = this.bgcolor;
6563 cfg.charoff = this.charoff;
6566 cfg.valign = this.valign;
6584 * @class Roo.bootstrap.TableBody
6585 * @extends Roo.bootstrap.Component
6586 * Bootstrap TableBody class
6587 * @cfg {String} cls element class
6588 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589 * @cfg {String} align Aligns the content inside the element
6590 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6594 * Create a new TableBody
6595 * @param {Object} config The config object
6598 Roo.bootstrap.TableBody = function(config){
6599 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6610 getAutoCreate : function(){
6611 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6625 cfg.align = this.align;
6628 cfg.charoff = this.charoff;
6631 cfg.valign = this.valign;
6638 // initEvents : function()
6645 // this.store = Roo.factory(this.store, Roo.data);
6646 // this.store.on('load', this.onLoad, this);
6648 // this.store.load();
6652 // onLoad: function ()
6654 // this.fireEvent('load', this);
6664 * Ext JS Library 1.1.1
6665 * Copyright(c) 2006-2007, Ext JS, LLC.
6667 * Originally Released Under LGPL - original licence link has changed is not relivant.
6670 * <script type="text/javascript">
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6676 * @class Roo.form.Action
6677 * Internal Class used to handle form actions
6679 * @param {Roo.form.BasicForm} el The form element or its id
6680 * @param {Object} config Configuration options
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6688 this.options = options || {};
6691 * Client Validation Failed
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6696 * Server Validation Failed
6699 Roo.form.Action.SERVER_INVALID = 'server';
6701 * Connect to Server Failed
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6706 * Reading Data from Server Failed
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6711 Roo.form.Action.prototype = {
6713 failureType : undefined,
6714 response : undefined,
6718 run : function(options){
6723 success : function(response){
6728 handleResponse : function(response){
6732 // default connection failure
6733 failure : function(response){
6735 this.response = response;
6736 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737 this.form.afterAction(this, false);
6740 processResponse : function(response){
6741 this.response = response;
6742 if(!response.responseText){
6745 this.result = this.handleResponse(response);
6749 // utility functions used internally
6750 getUrl : function(appendParams){
6751 var url = this.options.url || this.form.url || this.form.el.dom.action;
6753 var p = this.getParams();
6755 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6761 getMethod : function(){
6762 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6765 getParams : function(){
6766 var bp = this.form.baseParams;
6767 var p = this.options.params;
6769 if(typeof p == "object"){
6770 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771 }else if(typeof p == 'string' && bp){
6772 p += '&' + Roo.urlEncode(bp);
6775 p = Roo.urlEncode(bp);
6780 createCallback : function(){
6782 success: this.success,
6783 failure: this.failure,
6785 timeout: (this.form.timeout*1000),
6786 upload: this.form.fileUpload ? this.success : undefined
6791 Roo.form.Action.Submit = function(form, options){
6792 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6798 haveProgress : false,
6799 uploadComplete : false,
6801 // uploadProgress indicator.
6802 uploadProgress : function()
6804 if (!this.form.progressUrl) {
6808 if (!this.haveProgress) {
6809 Roo.MessageBox.progress("Uploading", "Uploading");
6811 if (this.uploadComplete) {
6812 Roo.MessageBox.hide();
6816 this.haveProgress = true;
6818 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6820 var c = new Roo.data.Connection();
6822 url : this.form.progressUrl,
6827 success : function(req){
6828 //console.log(data);
6832 rdata = Roo.decode(req.responseText)
6834 Roo.log("Invalid data from server..");
6838 if (!rdata || !rdata.success) {
6840 Roo.MessageBox.alert(Roo.encode(rdata));
6843 var data = rdata.data;
6845 if (this.uploadComplete) {
6846 Roo.MessageBox.hide();
6851 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6855 this.uploadProgress.defer(2000,this);
6858 failure: function(data) {
6859 Roo.log('progress url failed ');
6870 // run get Values on the form, so it syncs any secondary forms.
6871 this.form.getValues();
6873 var o = this.options;
6874 var method = this.getMethod();
6875 var isPost = method == 'POST';
6876 if(o.clientValidation === false || this.form.isValid()){
6878 if (this.form.progressUrl) {
6879 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880 (new Date() * 1) + '' + Math.random());
6885 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886 form:this.form.el.dom,
6887 url:this.getUrl(!isPost),
6889 params:isPost ? this.getParams() : null,
6890 isUpload: this.form.fileUpload
6893 this.uploadProgress();
6895 }else if (o.clientValidation !== false){ // client validation failed
6896 this.failureType = Roo.form.Action.CLIENT_INVALID;
6897 this.form.afterAction(this, false);
6901 success : function(response)
6903 this.uploadComplete= true;
6904 if (this.haveProgress) {
6905 Roo.MessageBox.hide();
6909 var result = this.processResponse(response);
6910 if(result === true || result.success){
6911 this.form.afterAction(this, true);
6915 this.form.markInvalid(result.errors);
6916 this.failureType = Roo.form.Action.SERVER_INVALID;
6918 this.form.afterAction(this, false);
6920 failure : function(response)
6922 this.uploadComplete= true;
6923 if (this.haveProgress) {
6924 Roo.MessageBox.hide();
6927 this.response = response;
6928 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929 this.form.afterAction(this, false);
6932 handleResponse : function(response){
6933 if(this.form.errorReader){
6934 var rs = this.form.errorReader.read(response);
6937 for(var i = 0, len = rs.records.length; i < len; i++) {
6938 var r = rs.records[i];
6942 if(errors.length < 1){
6946 success : rs.success,
6952 ret = Roo.decode(response.responseText);
6956 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6966 Roo.form.Action.Load = function(form, options){
6967 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968 this.reader = this.form.reader;
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6976 Roo.Ajax.request(Roo.apply(
6977 this.createCallback(), {
6978 method:this.getMethod(),
6979 url:this.getUrl(false),
6980 params:this.getParams()
6984 success : function(response){
6986 var result = this.processResponse(response);
6987 if(result === true || !result.success || !result.data){
6988 this.failureType = Roo.form.Action.LOAD_FAILURE;
6989 this.form.afterAction(this, false);
6992 this.form.clearInvalid();
6993 this.form.setValues(result.data);
6994 this.form.afterAction(this, true);
6997 handleResponse : function(response){
6998 if(this.form.reader){
6999 var rs = this.form.reader.read(response);
7000 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7002 success : rs.success,
7006 return Roo.decode(response.responseText);
7010 Roo.form.Action.ACTION_TYPES = {
7011 'load' : Roo.form.Action.Load,
7012 'submit' : Roo.form.Action.Submit
7021 * @class Roo.bootstrap.Form
7022 * @extends Roo.bootstrap.Component
7023 * Bootstrap Form class
7024 * @cfg {String} method GET | POST (default POST)
7025 * @cfg {String} labelAlign top | left (default top)
7026 * @cfg {String} align left | right - for navbars
7027 * @cfg {Boolean} loadMask load mask when submit (default true)
7032 * @param {Object} config The config object
7036 Roo.bootstrap.Form = function(config){
7037 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7040 * @event clientvalidation
7041 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042 * @param {Form} this
7043 * @param {Boolean} valid true if the form has passed client-side validation
7045 clientvalidation: true,
7047 * @event beforeaction
7048 * Fires before any action is performed. Return false to cancel the action.
7049 * @param {Form} this
7050 * @param {Action} action The action to be performed
7054 * @event actionfailed
7055 * Fires when an action fails.
7056 * @param {Form} this
7057 * @param {Action} action The action that failed
7059 actionfailed : true,
7061 * @event actioncomplete
7062 * Fires when an action is completed.
7063 * @param {Form} this
7064 * @param {Action} action The action that completed
7066 actioncomplete : true
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7074 * @cfg {String} method
7075 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7080 * The URL to use for form actions if one isn't supplied in the action options.
7083 * @cfg {Boolean} fileUpload
7084 * Set to true if this form is a file upload.
7088 * @cfg {Object} baseParams
7089 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7093 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7097 * @cfg {Sting} align (left|right) for navbar forms
7102 activeAction : null,
7105 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106 * element by passing it or its id or mask the form itself by passing in true.
7109 waitMsgTarget : false,
7113 getAutoCreate : function(){
7117 method : this.method || 'POST',
7118 id : this.id || Roo.id(),
7121 if (this.parent().xtype.match(/^Nav/)) {
7122 cfg.cls = 'navbar-form navbar-' + this.align;
7126 if (this.labelAlign == 'left' ) {
7127 cfg.cls += ' form-horizontal';
7133 initEvents : function()
7135 this.el.on('submit', this.onSubmit, this);
7136 // this was added as random key presses on the form where triggering form submit.
7137 this.el.on('keypress', function(e) {
7138 if (e.getCharCode() != 13) {
7141 // we might need to allow it for textareas.. and some other items.
7142 // check e.getTarget().
7144 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7148 Roo.log("keypress blocked");
7156 onSubmit : function(e){
7161 * Returns true if client-side validation on the form is successful.
7164 isValid : function(){
7165 var items = this.getItems();
7167 items.each(function(f){
7176 * Returns true if any fields in this form have changed since their original load.
7179 isDirty : function(){
7181 var items = this.getItems();
7182 items.each(function(f){
7192 * Performs a predefined action (submit or load) or custom actions you define on this form.
7193 * @param {String} actionName The name of the action type
7194 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7195 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196 * accept other config options):
7198 Property Type Description
7199 ---------------- --------------- ----------------------------------------------------------------------------------
7200 url String The url for the action (defaults to the form's url)
7201 method String The form method to use (defaults to the form's method, or POST if not defined)
7202 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7204 validate the form on the client (defaults to false)
7206 * @return {BasicForm} this
7208 doAction : function(action, options){
7209 if(typeof action == 'string'){
7210 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7212 if(this.fireEvent('beforeaction', this, action) !== false){
7213 this.beforeAction(action);
7214 action.run.defer(100, action);
7220 beforeAction : function(action){
7221 var o = action.options;
7224 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7226 // not really supported yet.. ??
7228 //if(this.waitMsgTarget === true){
7229 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230 //}else if(this.waitMsgTarget){
7231 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7234 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7240 afterAction : function(action, success){
7241 this.activeAction = null;
7242 var o = action.options;
7244 //if(this.waitMsgTarget === true){
7246 //}else if(this.waitMsgTarget){
7247 // this.waitMsgTarget.unmask();
7249 // Roo.MessageBox.updateProgress(1);
7250 // Roo.MessageBox.hide();
7257 Roo.callback(o.success, o.scope, [this, action]);
7258 this.fireEvent('actioncomplete', this, action);
7262 // failure condition..
7263 // we have a scenario where updates need confirming.
7264 // eg. if a locking scenario exists..
7265 // we look for { errors : { needs_confirm : true }} in the response.
7267 (typeof(action.result) != 'undefined') &&
7268 (typeof(action.result.errors) != 'undefined') &&
7269 (typeof(action.result.errors.needs_confirm) != 'undefined')
7272 Roo.log("not supported yet");
7275 Roo.MessageBox.confirm(
7276 "Change requires confirmation",
7277 action.result.errorMsg,
7282 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7292 Roo.callback(o.failure, o.scope, [this, action]);
7293 // show an error message if no failed handler is set..
7294 if (!this.hasListener('actionfailed')) {
7295 Roo.log("need to add dialog support");
7297 Roo.MessageBox.alert("Error",
7298 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299 action.result.errorMsg :
7300 "Saving Failed, please check your entries or try again"
7305 this.fireEvent('actionfailed', this, action);
7310 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311 * @param {String} id The value to search for
7314 findField : function(id){
7315 var items = this.getItems();
7316 var field = items.get(id);
7318 items.each(function(f){
7319 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7326 return field || null;
7329 * Mark fields in this form invalid in bulk.
7330 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331 * @return {BasicForm} this
7333 markInvalid : function(errors){
7334 if(errors instanceof Array){
7335 for(var i = 0, len = errors.length; i < len; i++){
7336 var fieldError = errors[i];
7337 var f = this.findField(fieldError.id);
7339 f.markInvalid(fieldError.msg);
7345 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346 field.markInvalid(errors[id]);
7350 //Roo.each(this.childForms || [], function (f) {
7351 // f.markInvalid(errors);
7358 * Set values for fields in this form in bulk.
7359 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360 * @return {BasicForm} this
7362 setValues : function(values){
7363 if(values instanceof Array){ // array of objects
7364 for(var i = 0, len = values.length; i < len; i++){
7366 var f = this.findField(v.id);
7368 f.setValue(v.value);
7369 if(this.trackResetOnLoad){
7370 f.originalValue = f.getValue();
7374 }else{ // object hash
7377 if(typeof values[id] != 'function' && (field = this.findField(id))){
7379 if (field.setFromData &&
7381 field.displayField &&
7382 // combos' with local stores can
7383 // be queried via setValue()
7384 // to set their value..
7385 (field.store && !field.store.isLocal)
7389 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391 field.setFromData(sd);
7394 field.setValue(values[id]);
7398 if(this.trackResetOnLoad){
7399 field.originalValue = field.getValue();
7405 //Roo.each(this.childForms || [], function (f) {
7406 // f.setValues(values);
7413 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414 * they are returned as an array.
7415 * @param {Boolean} asString
7418 getValues : function(asString){
7419 //if (this.childForms) {
7420 // copy values from the child forms
7421 // Roo.each(this.childForms, function (f) {
7422 // this.setValues(f.getValues());
7428 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429 if(asString === true){
7432 return Roo.urlDecode(fs);
7436 * Returns the fields in this form as an object with key/value pairs.
7437 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7440 getFieldValues : function(with_hidden)
7442 var items = this.getItems();
7444 items.each(function(f){
7448 var v = f.getValue();
7449 if (f.inputType =='radio') {
7450 if (typeof(ret[f.getName()]) == 'undefined') {
7451 ret[f.getName()] = ''; // empty..
7454 if (!f.el.dom.checked) {
7462 // not sure if this supported any more..
7463 if ((typeof(v) == 'object') && f.getRawValue) {
7464 v = f.getRawValue() ; // dates..
7466 // combo boxes where name != hiddenName...
7467 if (f.name != f.getName()) {
7468 ret[f.name] = f.getRawValue();
7470 ret[f.getName()] = v;
7477 * Clears all invalid messages in this form.
7478 * @return {BasicForm} this
7480 clearInvalid : function(){
7481 var items = this.getItems();
7483 items.each(function(f){
7494 * @return {BasicForm} this
7497 var items = this.getItems();
7498 items.each(function(f){
7502 Roo.each(this.childForms || [], function (f) {
7509 getItems : function()
7511 var r=new Roo.util.MixedCollection(false, function(o){
7512 return o.id || (o.id = Roo.id());
7514 var iter = function(el) {
7521 Roo.each(el.items,function(e) {
7541 * Ext JS Library 1.1.1
7542 * Copyright(c) 2006-2007, Ext JS, LLC.
7544 * Originally Released Under LGPL - original licence link has changed is not relivant.
7547 * <script type="text/javascript">
7550 * @class Roo.form.VTypes
7551 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7554 Roo.form.VTypes = function(){
7555 // closure these in so they are only created once.
7556 var alpha = /^[a-zA-Z_]+$/;
7557 var alphanum = /^[a-zA-Z0-9_]+$/;
7558 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7561 // All these messages and functions are configurable
7564 * The function used to validate email addresses
7565 * @param {String} value The email address
7567 'email' : function(v){
7568 return email.test(v);
7571 * The error text to display when the email validation function returns false
7574 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7576 * The keystroke filter mask to be applied on email input
7579 'emailMask' : /[a-z0-9_\.\-@]/i,
7582 * The function used to validate URLs
7583 * @param {String} value The URL
7585 'url' : function(v){
7589 * The error text to display when the url validation function returns false
7592 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7595 * The function used to validate alpha values
7596 * @param {String} value The value
7598 'alpha' : function(v){
7599 return alpha.test(v);
7602 * The error text to display when the alpha validation function returns false
7605 'alphaText' : 'This field should only contain letters and _',
7607 * The keystroke filter mask to be applied on alpha input
7610 'alphaMask' : /[a-z_]/i,
7613 * The function used to validate alphanumeric values
7614 * @param {String} value The value
7616 'alphanum' : function(v){
7617 return alphanum.test(v);
7620 * The error text to display when the alphanumeric validation function returns false
7623 'alphanumText' : 'This field should only contain letters, numbers and _',
7625 * The keystroke filter mask to be applied on alphanumeric input
7628 'alphanumMask' : /[a-z0-9_]/i
7638 * @class Roo.bootstrap.Input
7639 * @extends Roo.bootstrap.Component
7640 * Bootstrap Input class
7641 * @cfg {Boolean} disabled is it disabled
7642 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643 * @cfg {String} name name of the input
7644 * @cfg {string} fieldLabel - the label associated
7645 * @cfg {string} placeholder - placeholder to put in text.
7646 * @cfg {string} before - input group add on before
7647 * @cfg {string} after - input group add on after
7648 * @cfg {string} size - (lg|sm) or leave empty..
7649 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651 * @cfg {Number} md colspan out of 12 for computer-sized screens
7652 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653 * @cfg {string} value default value of the input
7654 * @cfg {Number} labelWidth set the width of label (0-12)
7655 * @cfg {String} labelAlign (top|left)
7656 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7659 * @cfg {String} align (left|center|right) Default left
7660 * @cfg {Boolean} forceFeedback (true|false) Default false
7666 * Create a new Input
7667 * @param {Object} config The config object
7670 Roo.bootstrap.Input = function(config){
7671 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7676 * Fires when this field receives input focus.
7677 * @param {Roo.form.Field} this
7682 * Fires when this field loses input focus.
7683 * @param {Roo.form.Field} this
7688 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7689 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690 * @param {Roo.form.Field} this
7691 * @param {Roo.EventObject} e The event object
7696 * Fires just before the field blurs if the field value has changed.
7697 * @param {Roo.form.Field} this
7698 * @param {Mixed} newValue The new value
7699 * @param {Mixed} oldValue The original value
7704 * Fires after the field has been marked as invalid.
7705 * @param {Roo.form.Field} this
7706 * @param {String} msg The validation message
7711 * Fires after the field has been validated with no errors.
7712 * @param {Roo.form.Field} this
7717 * Fires after the key up
7718 * @param {Roo.form.Field} this
7719 * @param {Roo.EventObject} e The event Object
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7727 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728 automatic validation (defaults to "keyup").
7730 validationEvent : "keyup",
7732 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7734 validateOnBlur : true,
7736 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7738 validationDelay : 250,
7740 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7742 focusClass : "x-form-focus", // not needed???
7746 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7748 invalidClass : "has-warning",
7751 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7753 validClass : "has-success",
7756 * @cfg {Boolean} hasFeedback (true|false) default true
7761 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7763 invalidFeedbackClass : "glyphicon-warning-sign",
7766 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7768 validFeedbackClass : "glyphicon-ok",
7771 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7773 selectOnFocus : false,
7776 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7780 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7785 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7787 disableKeyFilter : false,
7790 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7794 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7798 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7800 blankText : "This field is required",
7803 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7807 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7809 maxLength : Number.MAX_VALUE,
7811 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7813 minLengthText : "The minimum length for this field is {0}",
7815 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7817 maxLengthText : "The maximum length for this field is {0}",
7821 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822 * If available, this function will be called only after the basic validators all return true, and will be passed the
7823 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7827 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7833 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7837 autocomplete: false,
7856 formatedValue : false,
7857 forceFeedback : false,
7859 parentLabelAlign : function()
7862 while (parent.parent()) {
7863 parent = parent.parent();
7864 if (typeof(parent.labelAlign) !='undefined') {
7865 return parent.labelAlign;
7872 getAutoCreate : function(){
7874 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7880 if(this.inputType != 'hidden'){
7881 cfg.cls = 'form-group' //input-group
7887 type : this.inputType,
7889 cls : 'form-control',
7890 placeholder : this.placeholder || '',
7891 autocomplete : this.autocomplete || 'new-password'
7896 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7899 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900 input.maxLength = this.maxLength;
7903 if (this.disabled) {
7904 input.disabled=true;
7907 if (this.readOnly) {
7908 input.readonly=true;
7912 input.name = this.name;
7915 input.cls += ' input-' + this.size;
7918 ['xs','sm','md','lg'].map(function(size){
7919 if (settings[size]) {
7920 cfg.cls += ' col-' + size + '-' + settings[size];
7924 var inputblock = input;
7928 cls: 'glyphicon form-control-feedback'
7931 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7934 cls : 'has-feedback',
7942 if (this.before || this.after) {
7945 cls : 'input-group',
7949 if (this.before && typeof(this.before) == 'string') {
7951 inputblock.cn.push({
7953 cls : 'roo-input-before input-group-addon',
7957 if (this.before && typeof(this.before) == 'object') {
7958 this.before = Roo.factory(this.before);
7959 Roo.log(this.before);
7960 inputblock.cn.push({
7962 cls : 'roo-input-before input-group-' +
7963 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7967 inputblock.cn.push(input);
7969 if (this.after && typeof(this.after) == 'string') {
7970 inputblock.cn.push({
7972 cls : 'roo-input-after input-group-addon',
7976 if (this.after && typeof(this.after) == 'object') {
7977 this.after = Roo.factory(this.after);
7978 Roo.log(this.after);
7979 inputblock.cn.push({
7981 cls : 'roo-input-after input-group-' +
7982 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7986 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987 inputblock.cls += ' has-feedback';
7988 inputblock.cn.push(feedback);
7992 if (align ==='left' && this.fieldLabel.length) {
7993 Roo.log("left and has label");
7999 cls : 'control-label col-sm-' + this.labelWidth,
8000 html : this.fieldLabel
8004 cls : "col-sm-" + (12 - this.labelWidth),
8011 } else if ( this.fieldLabel.length) {
8017 //cls : 'input-group-addon',
8018 html : this.fieldLabel
8028 Roo.log(" no label && no align");
8037 Roo.log('input-parentType: ' + this.parentType);
8039 if (this.parentType === 'Navbar' && this.parent().bar) {
8040 cfg.cls += ' navbar-form';
8048 * return the real input element.
8050 inputEl: function ()
8052 return this.el.select('input.form-control',true).first();
8055 tooltipEl : function()
8057 return this.inputEl();
8060 setDisabled : function(v)
8062 var i = this.inputEl().dom;
8064 i.removeAttribute('disabled');
8068 i.setAttribute('disabled','true');
8070 initEvents : function()
8073 this.inputEl().on("keydown" , this.fireKey, this);
8074 this.inputEl().on("focus", this.onFocus, this);
8075 this.inputEl().on("blur", this.onBlur, this);
8077 this.inputEl().relayEvent('keyup', this);
8079 // reference to original value for reset
8080 this.originalValue = this.getValue();
8081 //Roo.form.TextField.superclass.initEvents.call(this);
8082 if(this.validationEvent == 'keyup'){
8083 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084 this.inputEl().on('keyup', this.filterValidation, this);
8086 else if(this.validationEvent !== false){
8087 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8090 if(this.selectOnFocus){
8091 this.on("focus", this.preFocus, this);
8094 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095 this.inputEl().on("keypress", this.filterKeys, this);
8098 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8099 this.el.on("click", this.autoSize, this);
8102 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8106 if (typeof(this.before) == 'object') {
8107 this.before.render(this.el.select('.roo-input-before',true).first());
8109 if (typeof(this.after) == 'object') {
8110 this.after.render(this.el.select('.roo-input-after',true).first());
8115 filterValidation : function(e){
8116 if(!e.isNavKeyPress()){
8117 this.validationTask.delay(this.validationDelay);
8121 * Validates the field value
8122 * @return {Boolean} True if the value is valid, else false
8124 validate : function(){
8125 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126 if(this.disabled || this.validateValue(this.getRawValue())){
8137 * Validates a value according to the field's validation rules and marks the field as invalid
8138 * if the validation fails
8139 * @param {Mixed} value The value to validate
8140 * @return {Boolean} True if the value is valid, else false
8142 validateValue : function(value){
8143 if(value.length < 1) { // if it's blank
8144 if(this.allowBlank){
8150 if(value.length < this.minLength){
8153 if(value.length > this.maxLength){
8157 var vt = Roo.form.VTypes;
8158 if(!vt[this.vtype](value, this)){
8162 if(typeof this.validator == "function"){
8163 var msg = this.validator(value);
8169 if(this.regex && !this.regex.test(value)){
8179 fireKey : function(e){
8180 //Roo.log('field ' + e.getKey());
8181 if(e.isNavKeyPress()){
8182 this.fireEvent("specialkey", this, e);
8185 focus : function (selectText){
8187 this.inputEl().focus();
8188 if(selectText === true){
8189 this.inputEl().dom.select();
8195 onFocus : function(){
8196 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197 // this.el.addClass(this.focusClass);
8200 this.hasFocus = true;
8201 this.startValue = this.getValue();
8202 this.fireEvent("focus", this);
8206 beforeBlur : Roo.emptyFn,
8210 onBlur : function(){
8212 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213 //this.el.removeClass(this.focusClass);
8215 this.hasFocus = false;
8216 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8219 var v = this.getValue();
8220 if(String(v) !== String(this.startValue)){
8221 this.fireEvent('change', this, v, this.startValue);
8223 this.fireEvent("blur", this);
8227 * Resets the current field value to the originally loaded value and clears any validation messages
8230 this.setValue(this.originalValue);
8234 * Returns the name of the field
8235 * @return {Mixed} name The name field
8237 getName: function(){
8241 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8242 * @return {Mixed} value The field value
8244 getValue : function(){
8246 var v = this.inputEl().getValue();
8251 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8252 * @return {Mixed} value The field value
8254 getRawValue : function(){
8255 var v = this.inputEl().getValue();
8261 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8262 * @param {Mixed} value The value to set
8264 setRawValue : function(v){
8265 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8268 selectText : function(start, end){
8269 var v = this.getRawValue();
8271 start = start === undefined ? 0 : start;
8272 end = end === undefined ? v.length : end;
8273 var d = this.inputEl().dom;
8274 if(d.setSelectionRange){
8275 d.setSelectionRange(start, end);
8276 }else if(d.createTextRange){
8277 var range = d.createTextRange();
8278 range.moveStart("character", start);
8279 range.moveEnd("character", v.length-end);
8286 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8287 * @param {Mixed} value The value to set
8289 setValue : function(v){
8292 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8298 processValue : function(value){
8299 if(this.stripCharsRe){
8300 var newValue = value.replace(this.stripCharsRe, '');
8301 if(newValue !== value){
8302 this.setRawValue(newValue);
8309 preFocus : function(){
8311 if(this.selectOnFocus){
8312 this.inputEl().dom.select();
8315 filterKeys : function(e){
8317 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8320 var c = e.getCharCode(), cc = String.fromCharCode(c);
8321 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8324 if(!this.maskRe.test(cc)){
8329 * Clear any invalid styles/messages for this field
8331 clearInvalid : function(){
8333 if(!this.el || this.preventMark){ // not rendered
8336 this.el.removeClass(this.invalidClass);
8338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8340 var feedback = this.el.select('.form-control-feedback', true).first();
8343 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8348 this.fireEvent('valid', this);
8352 * Mark this field as valid
8354 markValid : function()
8356 if(!this.el || this.preventMark){ // not rendered
8360 this.el.removeClass([this.invalidClass, this.validClass]);
8362 var feedback = this.el.select('.form-control-feedback', true).first();
8365 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8368 if(this.disabled || this.allowBlank){
8372 var formGroup = this.el.findParent('.form-group', false, true);
8376 var label = formGroup.select('label', true).first();
8377 var icon = formGroup.select('i.fa-star', true).first();
8384 this.el.addClass(this.validClass);
8386 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8388 var feedback = this.el.select('.form-control-feedback', true).first();
8391 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8392 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8397 this.fireEvent('valid', this);
8401 * Mark this field as invalid
8402 * @param {String} msg The validation message
8404 markInvalid : function(msg)
8406 if(!this.el || this.preventMark){ // not rendered
8410 this.el.removeClass([this.invalidClass, this.validClass]);
8412 var feedback = this.el.select('.form-control-feedback', true).first();
8415 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8418 if(this.disabled || this.allowBlank){
8422 var formGroup = this.el.findParent('.form-group', false, true);
8425 var label = formGroup.select('label', true).first();
8426 var icon = formGroup.select('i.fa-star', true).first();
8428 if(!this.getValue().length && label && !icon){
8429 this.el.findParent('.form-group', false, true).createChild({
8431 cls : 'text-danger fa fa-lg fa-star',
8432 tooltip : 'This field is required',
8433 style : 'margin-right:5px;'
8439 this.el.addClass(this.invalidClass);
8441 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8443 var feedback = this.el.select('.form-control-feedback', true).first();
8446 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8448 if(this.getValue().length || this.forceFeedback){
8449 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8456 this.fireEvent('invalid', this, msg);
8459 SafariOnKeyDown : function(event)
8461 // this is a workaround for a password hang bug on chrome/ webkit.
8463 var isSelectAll = false;
8465 if(this.inputEl().dom.selectionEnd > 0){
8466 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8468 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8469 event.preventDefault();
8474 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8476 event.preventDefault();
8477 // this is very hacky as keydown always get's upper case.
8479 var cc = String.fromCharCode(event.getCharCode());
8480 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8484 adjustWidth : function(tag, w){
8485 tag = tag.toLowerCase();
8486 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8487 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8491 if(tag == 'textarea'){
8494 }else if(Roo.isOpera){
8498 if(tag == 'textarea'){
8517 * @class Roo.bootstrap.TextArea
8518 * @extends Roo.bootstrap.Input
8519 * Bootstrap TextArea class
8520 * @cfg {Number} cols Specifies the visible width of a text area
8521 * @cfg {Number} rows Specifies the visible number of lines in a text area
8522 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8523 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8524 * @cfg {string} html text
8527 * Create a new TextArea
8528 * @param {Object} config The config object
8531 Roo.bootstrap.TextArea = function(config){
8532 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8536 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8546 getAutoCreate : function(){
8548 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8559 value : this.value || '',
8560 html: this.html || '',
8561 cls : 'form-control',
8562 placeholder : this.placeholder || ''
8566 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8567 input.maxLength = this.maxLength;
8571 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8575 input.cols = this.cols;
8578 if (this.readOnly) {
8579 input.readonly = true;
8583 input.name = this.name;
8587 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8591 ['xs','sm','md','lg'].map(function(size){
8592 if (settings[size]) {
8593 cfg.cls += ' col-' + size + '-' + settings[size];
8597 var inputblock = input;
8599 if(this.hasFeedback && !this.allowBlank){
8603 cls: 'glyphicon form-control-feedback'
8607 cls : 'has-feedback',
8616 if (this.before || this.after) {
8619 cls : 'input-group',
8623 inputblock.cn.push({
8625 cls : 'input-group-addon',
8630 inputblock.cn.push(input);
8632 if(this.hasFeedback && !this.allowBlank){
8633 inputblock.cls += ' has-feedback';
8634 inputblock.cn.push(feedback);
8638 inputblock.cn.push({
8640 cls : 'input-group-addon',
8647 if (align ==='left' && this.fieldLabel.length) {
8648 Roo.log("left and has label");
8654 cls : 'control-label col-sm-' + this.labelWidth,
8655 html : this.fieldLabel
8659 cls : "col-sm-" + (12 - this.labelWidth),
8666 } else if ( this.fieldLabel.length) {
8672 //cls : 'input-group-addon',
8673 html : this.fieldLabel
8683 Roo.log(" no label && no align");
8693 if (this.disabled) {
8694 input.disabled=true;
8701 * return the real textarea element.
8703 inputEl: function ()
8705 return this.el.select('textarea.form-control',true).first();
8713 * trigger field - base class for combo..
8718 * @class Roo.bootstrap.TriggerField
8719 * @extends Roo.bootstrap.Input
8720 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8721 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8722 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8723 * for which you can provide a custom implementation. For example:
8725 var trigger = new Roo.bootstrap.TriggerField();
8726 trigger.onTriggerClick = myTriggerFn;
8727 trigger.applyTo('my-field');
8730 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8731 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8732 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8733 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8734 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8737 * Create a new TriggerField.
8738 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8739 * to the base TextField)
8741 Roo.bootstrap.TriggerField = function(config){
8742 this.mimicing = false;
8743 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8746 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8748 * @cfg {String} triggerClass A CSS class to apply to the trigger
8751 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8756 * @cfg {Boolean} removable (true|false) special filter default false
8760 /** @cfg {Boolean} grow @hide */
8761 /** @cfg {Number} growMin @hide */
8762 /** @cfg {Number} growMax @hide */
8768 autoSize: Roo.emptyFn,
8775 actionMode : 'wrap',
8780 getAutoCreate : function(){
8782 var align = this.labelAlign || this.parentLabelAlign();
8787 cls: 'form-group' //input-group
8794 type : this.inputType,
8795 cls : 'form-control',
8796 autocomplete: 'new-password',
8797 placeholder : this.placeholder || ''
8801 input.name = this.name;
8804 input.cls += ' input-' + this.size;
8807 if (this.disabled) {
8808 input.disabled=true;
8811 var inputblock = input;
8813 if(this.hasFeedback && !this.allowBlank){
8817 cls: 'glyphicon form-control-feedback'
8820 if(this.removable && !this.editable && !this.tickable){
8822 cls : 'has-feedback',
8828 cls : 'roo-combo-removable-btn close'
8835 cls : 'has-feedback',
8844 if(this.removable && !this.editable && !this.tickable){
8846 cls : 'roo-removable',
8852 cls : 'roo-combo-removable-btn close'
8859 if (this.before || this.after) {
8862 cls : 'input-group',
8866 inputblock.cn.push({
8868 cls : 'input-group-addon',
8873 inputblock.cn.push(input);
8875 if(this.hasFeedback && !this.allowBlank){
8876 inputblock.cls += ' has-feedback';
8877 inputblock.cn.push(feedback);
8881 inputblock.cn.push({
8883 cls : 'input-group-addon',
8896 cls: 'form-hidden-field'
8904 Roo.log('multiple');
8912 cls: 'form-hidden-field'
8916 cls: 'select2-choices',
8920 cls: 'select2-search-field',
8933 cls: 'select2-container input-group',
8938 // cls: 'typeahead typeahead-long dropdown-menu',
8939 // style: 'display:none'
8944 if(!this.multiple && this.showToggleBtn){
8950 if (this.caret != false) {
8953 cls: 'fa fa-' + this.caret
8960 cls : 'input-group-addon btn dropdown-toggle',
8965 cls: 'combobox-clear',
8979 combobox.cls += ' select2-container-multi';
8982 if (align ==='left' && this.fieldLabel.length) {
8984 Roo.log("left and has label");
8990 cls : 'control-label col-sm-' + this.labelWidth,
8991 html : this.fieldLabel
8995 cls : "col-sm-" + (12 - this.labelWidth),
9002 } else if ( this.fieldLabel.length) {
9008 //cls : 'input-group-addon',
9009 html : this.fieldLabel
9019 Roo.log(" no label && no align");
9026 ['xs','sm','md','lg'].map(function(size){
9027 if (settings[size]) {
9028 cfg.cls += ' col-' + size + '-' + settings[size];
9039 onResize : function(w, h){
9040 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9041 // if(typeof w == 'number'){
9042 // var x = w - this.trigger.getWidth();
9043 // this.inputEl().setWidth(this.adjustWidth('input', x));
9044 // this.trigger.setStyle('left', x+'px');
9049 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9052 getResizeEl : function(){
9053 return this.inputEl();
9057 getPositionEl : function(){
9058 return this.inputEl();
9062 alignErrorIcon : function(){
9063 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9067 initEvents : function(){
9071 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9072 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9073 if(!this.multiple && this.showToggleBtn){
9074 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9075 if(this.hideTrigger){
9076 this.trigger.setDisplayed(false);
9078 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9082 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9085 if(this.removable && !this.editable && !this.tickable){
9086 var close = this.closeTriggerEl();
9089 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9090 close.on('click', this.removeBtnClick, this, close);
9094 //this.trigger.addClassOnOver('x-form-trigger-over');
9095 //this.trigger.addClassOnClick('x-form-trigger-click');
9098 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9102 closeTriggerEl : function()
9104 var close = this.el.select('.roo-combo-removable-btn', true).first();
9105 return close ? close : false;
9108 removeBtnClick : function(e, h, el)
9112 if(this.fireEvent("remove", this) !== false){
9117 createList : function()
9119 this.list = Roo.get(document.body).createChild({
9121 cls: 'typeahead typeahead-long dropdown-menu',
9122 style: 'display:none'
9125 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9130 initTrigger : function(){
9135 onDestroy : function(){
9137 this.trigger.removeAllListeners();
9138 // this.trigger.remove();
9141 // this.wrap.remove();
9143 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9147 onFocus : function(){
9148 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9151 this.wrap.addClass('x-trigger-wrap-focus');
9152 this.mimicing = true;
9153 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9154 if(this.monitorTab){
9155 this.el.on("keydown", this.checkTab, this);
9162 checkTab : function(e){
9163 if(e.getKey() == e.TAB){
9169 onBlur : function(){
9174 mimicBlur : function(e, t){
9176 if(!this.wrap.contains(t) && this.validateBlur()){
9183 triggerBlur : function(){
9184 this.mimicing = false;
9185 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9186 if(this.monitorTab){
9187 this.el.un("keydown", this.checkTab, this);
9189 //this.wrap.removeClass('x-trigger-wrap-focus');
9190 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9194 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9195 validateBlur : function(e, t){
9200 onDisable : function(){
9201 this.inputEl().dom.disabled = true;
9202 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9204 // this.wrap.addClass('x-item-disabled');
9209 onEnable : function(){
9210 this.inputEl().dom.disabled = false;
9211 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9213 // this.el.removeClass('x-item-disabled');
9218 onShow : function(){
9219 var ae = this.getActionEl();
9222 ae.dom.style.display = '';
9223 ae.dom.style.visibility = 'visible';
9229 onHide : function(){
9230 var ae = this.getActionEl();
9231 ae.dom.style.display = 'none';
9235 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9236 * by an implementing function.
9238 * @param {EventObject} e
9240 onTriggerClick : Roo.emptyFn
9244 * Ext JS Library 1.1.1
9245 * Copyright(c) 2006-2007, Ext JS, LLC.
9247 * Originally Released Under LGPL - original licence link has changed is not relivant.
9250 * <script type="text/javascript">
9255 * @class Roo.data.SortTypes
9257 * Defines the default sorting (casting?) comparison functions used when sorting data.
9259 Roo.data.SortTypes = {
9261 * Default sort that does nothing
9262 * @param {Mixed} s The value being converted
9263 * @return {Mixed} The comparison value
9270 * The regular expression used to strip tags
9274 stripTagsRE : /<\/?[^>]+>/gi,
9277 * Strips all HTML tags to sort on text only
9278 * @param {Mixed} s The value being converted
9279 * @return {String} The comparison value
9281 asText : function(s){
9282 return String(s).replace(this.stripTagsRE, "");
9286 * Strips all HTML tags to sort on text only - Case insensitive
9287 * @param {Mixed} s The value being converted
9288 * @return {String} The comparison value
9290 asUCText : function(s){
9291 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9295 * Case insensitive string
9296 * @param {Mixed} s The value being converted
9297 * @return {String} The comparison value
9299 asUCString : function(s) {
9300 return String(s).toUpperCase();
9305 * @param {Mixed} s The value being converted
9306 * @return {Number} The comparison value
9308 asDate : function(s) {
9312 if(s instanceof Date){
9315 return Date.parse(String(s));
9320 * @param {Mixed} s The value being converted
9321 * @return {Float} The comparison value
9323 asFloat : function(s) {
9324 var val = parseFloat(String(s).replace(/,/g, ""));
9325 if(isNaN(val)) val = 0;
9331 * @param {Mixed} s The value being converted
9332 * @return {Number} The comparison value
9334 asInt : function(s) {
9335 var val = parseInt(String(s).replace(/,/g, ""));
9336 if(isNaN(val)) val = 0;
9341 * Ext JS Library 1.1.1
9342 * Copyright(c) 2006-2007, Ext JS, LLC.
9344 * Originally Released Under LGPL - original licence link has changed is not relivant.
9347 * <script type="text/javascript">
9351 * @class Roo.data.Record
9352 * Instances of this class encapsulate both record <em>definition</em> information, and record
9353 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9354 * to access Records cached in an {@link Roo.data.Store} object.<br>
9356 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9357 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9360 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9362 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9363 * {@link #create}. The parameters are the same.
9364 * @param {Array} data An associative Array of data values keyed by the field name.
9365 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9366 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9367 * not specified an integer id is generated.
9369 Roo.data.Record = function(data, id){
9370 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9375 * Generate a constructor for a specific record layout.
9376 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9377 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9378 * Each field definition object may contain the following properties: <ul>
9379 * <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,
9380 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9381 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9382 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9383 * is being used, then this is a string containing the javascript expression to reference the data relative to
9384 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9385 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9386 * this may be omitted.</p></li>
9387 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9388 * <ul><li>auto (Default, implies no conversion)</li>
9393 * <li>date</li></ul></p></li>
9394 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9395 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9396 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9397 * by the Reader into an object that will be stored in the Record. It is passed the
9398 * following parameters:<ul>
9399 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9401 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9403 * <br>usage:<br><pre><code>
9404 var TopicRecord = Roo.data.Record.create(
9405 {name: 'title', mapping: 'topic_title'},
9406 {name: 'author', mapping: 'username'},
9407 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9408 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9409 {name: 'lastPoster', mapping: 'user2'},
9410 {name: 'excerpt', mapping: 'post_text'}
9413 var myNewRecord = new TopicRecord({
9414 title: 'Do my job please',
9417 lastPost: new Date(),
9418 lastPoster: 'Animal',
9419 excerpt: 'No way dude!'
9421 myStore.add(myNewRecord);
9426 Roo.data.Record.create = function(o){
9428 f.superclass.constructor.apply(this, arguments);
9430 Roo.extend(f, Roo.data.Record);
9431 var p = f.prototype;
9432 p.fields = new Roo.util.MixedCollection(false, function(field){
9435 for(var i = 0, len = o.length; i < len; i++){
9436 p.fields.add(new Roo.data.Field(o[i]));
9438 f.getField = function(name){
9439 return p.fields.get(name);
9444 Roo.data.Record.AUTO_ID = 1000;
9445 Roo.data.Record.EDIT = 'edit';
9446 Roo.data.Record.REJECT = 'reject';
9447 Roo.data.Record.COMMIT = 'commit';
9449 Roo.data.Record.prototype = {
9451 * Readonly flag - true if this record has been modified.
9460 join : function(store){
9465 * Set the named field to the specified value.
9466 * @param {String} name The name of the field to set.
9467 * @param {Object} value The value to set the field to.
9469 set : function(name, value){
9470 if(this.data[name] == value){
9477 if(typeof this.modified[name] == 'undefined'){
9478 this.modified[name] = this.data[name];
9480 this.data[name] = value;
9481 if(!this.editing && this.store){
9482 this.store.afterEdit(this);
9487 * Get the value of the named field.
9488 * @param {String} name The name of the field to get the value of.
9489 * @return {Object} The value of the field.
9491 get : function(name){
9492 return this.data[name];
9496 beginEdit : function(){
9497 this.editing = true;
9502 cancelEdit : function(){
9503 this.editing = false;
9504 delete this.modified;
9508 endEdit : function(){
9509 this.editing = false;
9510 if(this.dirty && this.store){
9511 this.store.afterEdit(this);
9516 * Usually called by the {@link Roo.data.Store} which owns the Record.
9517 * Rejects all changes made to the Record since either creation, or the last commit operation.
9518 * Modified fields are reverted to their original values.
9520 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9521 * of reject operations.
9523 reject : function(){
9524 var m = this.modified;
9526 if(typeof m[n] != "function"){
9527 this.data[n] = m[n];
9531 delete this.modified;
9532 this.editing = false;
9534 this.store.afterReject(this);
9539 * Usually called by the {@link Roo.data.Store} which owns the Record.
9540 * Commits all changes made to the Record since either creation, or the last commit operation.
9542 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9543 * of commit operations.
9545 commit : function(){
9547 delete this.modified;
9548 this.editing = false;
9550 this.store.afterCommit(this);
9555 hasError : function(){
9556 return this.error != null;
9560 clearError : function(){
9565 * Creates a copy of this record.
9566 * @param {String} id (optional) A new record id if you don't want to use this record's id
9569 copy : function(newId) {
9570 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9574 * Ext JS Library 1.1.1
9575 * Copyright(c) 2006-2007, Ext JS, LLC.
9577 * Originally Released Under LGPL - original licence link has changed is not relivant.
9580 * <script type="text/javascript">
9586 * @class Roo.data.Store
9587 * @extends Roo.util.Observable
9588 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9589 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9591 * 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
9592 * has no knowledge of the format of the data returned by the Proxy.<br>
9594 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9595 * instances from the data object. These records are cached and made available through accessor functions.
9597 * Creates a new Store.
9598 * @param {Object} config A config object containing the objects needed for the Store to access data,
9599 * and read the data into Records.
9601 Roo.data.Store = function(config){
9602 this.data = new Roo.util.MixedCollection(false);
9603 this.data.getKey = function(o){
9606 this.baseParams = {};
9613 "multisort" : "_multisort"
9616 if(config && config.data){
9617 this.inlineData = config.data;
9621 Roo.apply(this, config);
9623 if(this.reader){ // reader passed
9624 this.reader = Roo.factory(this.reader, Roo.data);
9625 this.reader.xmodule = this.xmodule || false;
9626 if(!this.recordType){
9627 this.recordType = this.reader.recordType;
9629 if(this.reader.onMetaChange){
9630 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9634 if(this.recordType){
9635 this.fields = this.recordType.prototype.fields;
9641 * @event datachanged
9642 * Fires when the data cache has changed, and a widget which is using this Store
9643 * as a Record cache should refresh its view.
9644 * @param {Store} this
9649 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9650 * @param {Store} this
9651 * @param {Object} meta The JSON metadata
9656 * Fires when Records have been added to the Store
9657 * @param {Store} this
9658 * @param {Roo.data.Record[]} records The array of Records added
9659 * @param {Number} index The index at which the record(s) were added
9664 * Fires when a Record has been removed from the Store
9665 * @param {Store} this
9666 * @param {Roo.data.Record} record The Record that was removed
9667 * @param {Number} index The index at which the record was removed
9672 * Fires when a Record has been updated
9673 * @param {Store} this
9674 * @param {Roo.data.Record} record The Record that was updated
9675 * @param {String} operation The update operation being performed. Value may be one of:
9677 Roo.data.Record.EDIT
9678 Roo.data.Record.REJECT
9679 Roo.data.Record.COMMIT
9685 * Fires when the data cache has been cleared.
9686 * @param {Store} this
9691 * Fires before a request is made for a new data object. If the beforeload handler returns false
9692 * the load action will be canceled.
9693 * @param {Store} this
9694 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9698 * @event beforeloadadd
9699 * Fires after a new set of Records has been loaded.
9700 * @param {Store} this
9701 * @param {Roo.data.Record[]} records The Records that were loaded
9702 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9704 beforeloadadd : true,
9707 * Fires after a new set of Records has been loaded, before they are added to the store.
9708 * @param {Store} this
9709 * @param {Roo.data.Record[]} records The Records that were loaded
9710 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9711 * @params {Object} return from reader
9715 * @event loadexception
9716 * Fires if an exception occurs in the Proxy during loading.
9717 * Called with the signature of the Proxy's "loadexception" event.
9718 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9721 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9722 * @param {Object} load options
9723 * @param {Object} jsonData from your request (normally this contains the Exception)
9725 loadexception : true
9729 this.proxy = Roo.factory(this.proxy, Roo.data);
9730 this.proxy.xmodule = this.xmodule || false;
9731 this.relayEvents(this.proxy, ["loadexception"]);
9733 this.sortToggle = {};
9734 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9736 Roo.data.Store.superclass.constructor.call(this);
9738 if(this.inlineData){
9739 this.loadData(this.inlineData);
9740 delete this.inlineData;
9744 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9746 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9747 * without a remote query - used by combo/forms at present.
9751 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9754 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9757 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9758 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9761 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9762 * on any HTTP request
9765 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9768 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9772 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9773 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9778 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9779 * loaded or when a record is removed. (defaults to false).
9781 pruneModifiedRecords : false,
9787 * Add Records to the Store and fires the add event.
9788 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9790 add : function(records){
9791 records = [].concat(records);
9792 for(var i = 0, len = records.length; i < len; i++){
9793 records[i].join(this);
9795 var index = this.data.length;
9796 this.data.addAll(records);
9797 this.fireEvent("add", this, records, index);
9801 * Remove a Record from the Store and fires the remove event.
9802 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9804 remove : function(record){
9805 var index = this.data.indexOf(record);
9806 this.data.removeAt(index);
9807 if(this.pruneModifiedRecords){
9808 this.modified.remove(record);
9810 this.fireEvent("remove", this, record, index);
9814 * Remove all Records from the Store and fires the clear event.
9816 removeAll : function(){
9818 if(this.pruneModifiedRecords){
9821 this.fireEvent("clear", this);
9825 * Inserts Records to the Store at the given index and fires the add event.
9826 * @param {Number} index The start index at which to insert the passed Records.
9827 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9829 insert : function(index, records){
9830 records = [].concat(records);
9831 for(var i = 0, len = records.length; i < len; i++){
9832 this.data.insert(index, records[i]);
9833 records[i].join(this);
9835 this.fireEvent("add", this, records, index);
9839 * Get the index within the cache of the passed Record.
9840 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9841 * @return {Number} The index of the passed Record. Returns -1 if not found.
9843 indexOf : function(record){
9844 return this.data.indexOf(record);
9848 * Get the index within the cache of the Record with the passed id.
9849 * @param {String} id The id of the Record to find.
9850 * @return {Number} The index of the Record. Returns -1 if not found.
9852 indexOfId : function(id){
9853 return this.data.indexOfKey(id);
9857 * Get the Record with the specified id.
9858 * @param {String} id The id of the Record to find.
9859 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9861 getById : function(id){
9862 return this.data.key(id);
9866 * Get the Record at the specified index.
9867 * @param {Number} index The index of the Record to find.
9868 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9870 getAt : function(index){
9871 return this.data.itemAt(index);
9875 * Returns a range of Records between specified indices.
9876 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9877 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9878 * @return {Roo.data.Record[]} An array of Records
9880 getRange : function(start, end){
9881 return this.data.getRange(start, end);
9885 storeOptions : function(o){
9886 o = Roo.apply({}, o);
9889 this.lastOptions = o;
9893 * Loads the Record cache from the configured Proxy using the configured Reader.
9895 * If using remote paging, then the first load call must specify the <em>start</em>
9896 * and <em>limit</em> properties in the options.params property to establish the initial
9897 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9899 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9900 * and this call will return before the new data has been loaded. Perform any post-processing
9901 * in a callback function, or in a "load" event handler.</strong>
9903 * @param {Object} options An object containing properties which control loading options:<ul>
9904 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9905 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9906 * passed the following arguments:<ul>
9907 * <li>r : Roo.data.Record[]</li>
9908 * <li>options: Options object from the load call</li>
9909 * <li>success: Boolean success indicator</li></ul></li>
9910 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9911 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9914 load : function(options){
9915 options = options || {};
9916 if(this.fireEvent("beforeload", this, options) !== false){
9917 this.storeOptions(options);
9918 var p = Roo.apply(options.params || {}, this.baseParams);
9919 // if meta was not loaded from remote source.. try requesting it.
9920 if (!this.reader.metaFromRemote) {
9923 if(this.sortInfo && this.remoteSort){
9924 var pn = this.paramNames;
9925 p[pn["sort"]] = this.sortInfo.field;
9926 p[pn["dir"]] = this.sortInfo.direction;
9928 if (this.multiSort) {
9929 var pn = this.paramNames;
9930 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9933 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9938 * Reloads the Record cache from the configured Proxy using the configured Reader and
9939 * the options from the last load operation performed.
9940 * @param {Object} options (optional) An object containing properties which may override the options
9941 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9942 * the most recently used options are reused).
9944 reload : function(options){
9945 this.load(Roo.applyIf(options||{}, this.lastOptions));
9949 // Called as a callback by the Reader during a load operation.
9950 loadRecords : function(o, options, success){
9951 if(!o || success === false){
9952 if(success !== false){
9953 this.fireEvent("load", this, [], options, o);
9955 if(options.callback){
9956 options.callback.call(options.scope || this, [], options, false);
9960 // if data returned failure - throw an exception.
9961 if (o.success === false) {
9962 // show a message if no listener is registered.
9963 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9964 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9966 // loadmask wil be hooked into this..
9967 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9970 var r = o.records, t = o.totalRecords || r.length;
9972 this.fireEvent("beforeloadadd", this, r, options, o);
9974 if(!options || options.add !== true){
9975 if(this.pruneModifiedRecords){
9978 for(var i = 0, len = r.length; i < len; i++){
9982 this.data = this.snapshot;
9983 delete this.snapshot;
9986 this.data.addAll(r);
9987 this.totalLength = t;
9989 this.fireEvent("datachanged", this);
9991 this.totalLength = Math.max(t, this.data.length+r.length);
9994 this.fireEvent("load", this, r, options, o);
9995 if(options.callback){
9996 options.callback.call(options.scope || this, r, options, true);
10002 * Loads data from a passed data block. A Reader which understands the format of the data
10003 * must have been configured in the constructor.
10004 * @param {Object} data The data block from which to read the Records. The format of the data expected
10005 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10006 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10008 loadData : function(o, append){
10009 var r = this.reader.readRecords(o);
10010 this.loadRecords(r, {add: append}, true);
10014 * Gets the number of cached records.
10016 * <em>If using paging, this may not be the total size of the dataset. If the data object
10017 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10018 * the data set size</em>
10020 getCount : function(){
10021 return this.data.length || 0;
10025 * Gets the total number of records in the dataset as returned by the server.
10027 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10028 * the dataset size</em>
10030 getTotalCount : function(){
10031 return this.totalLength || 0;
10035 * Returns the sort state of the Store as an object with two properties:
10037 field {String} The name of the field by which the Records are sorted
10038 direction {String} The sort order, "ASC" or "DESC"
10041 getSortState : function(){
10042 return this.sortInfo;
10046 applySort : function(){
10047 if(this.sortInfo && !this.remoteSort){
10048 var s = this.sortInfo, f = s.field;
10049 var st = this.fields.get(f).sortType;
10050 var fn = function(r1, r2){
10051 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10052 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10054 this.data.sort(s.direction, fn);
10055 if(this.snapshot && this.snapshot != this.data){
10056 this.snapshot.sort(s.direction, fn);
10062 * Sets the default sort column and order to be used by the next load operation.
10063 * @param {String} fieldName The name of the field to sort by.
10064 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10066 setDefaultSort : function(field, dir){
10067 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10071 * Sort the Records.
10072 * If remote sorting is used, the sort is performed on the server, and the cache is
10073 * reloaded. If local sorting is used, the cache is sorted internally.
10074 * @param {String} fieldName The name of the field to sort by.
10075 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10077 sort : function(fieldName, dir){
10078 var f = this.fields.get(fieldName);
10080 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10082 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10083 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10088 this.sortToggle[f.name] = dir;
10089 this.sortInfo = {field: f.name, direction: dir};
10090 if(!this.remoteSort){
10092 this.fireEvent("datachanged", this);
10094 this.load(this.lastOptions);
10099 * Calls the specified function for each of the Records in the cache.
10100 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10101 * Returning <em>false</em> aborts and exits the iteration.
10102 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10104 each : function(fn, scope){
10105 this.data.each(fn, scope);
10109 * Gets all records modified since the last commit. Modified records are persisted across load operations
10110 * (e.g., during paging).
10111 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10113 getModifiedRecords : function(){
10114 return this.modified;
10118 createFilterFn : function(property, value, anyMatch){
10119 if(!value.exec){ // not a regex
10120 value = String(value);
10121 if(value.length == 0){
10124 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10126 return function(r){
10127 return value.test(r.data[property]);
10132 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10133 * @param {String} property A field on your records
10134 * @param {Number} start The record index to start at (defaults to 0)
10135 * @param {Number} end The last record index to include (defaults to length - 1)
10136 * @return {Number} The sum
10138 sum : function(property, start, end){
10139 var rs = this.data.items, v = 0;
10140 start = start || 0;
10141 end = (end || end === 0) ? end : rs.length-1;
10143 for(var i = start; i <= end; i++){
10144 v += (rs[i].data[property] || 0);
10150 * Filter the records by a specified property.
10151 * @param {String} field A field on your records
10152 * @param {String/RegExp} value Either a string that the field
10153 * should start with or a RegExp to test against the field
10154 * @param {Boolean} anyMatch True to match any part not just the beginning
10156 filter : function(property, value, anyMatch){
10157 var fn = this.createFilterFn(property, value, anyMatch);
10158 return fn ? this.filterBy(fn) : this.clearFilter();
10162 * Filter by a function. The specified function will be called with each
10163 * record in this data source. If the function returns true the record is included,
10164 * otherwise it is filtered.
10165 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10166 * @param {Object} scope (optional) The scope of the function (defaults to this)
10168 filterBy : function(fn, scope){
10169 this.snapshot = this.snapshot || this.data;
10170 this.data = this.queryBy(fn, scope||this);
10171 this.fireEvent("datachanged", this);
10175 * Query the records by a specified property.
10176 * @param {String} field A field on your records
10177 * @param {String/RegExp} value Either a string that the field
10178 * should start with or a RegExp to test against the field
10179 * @param {Boolean} anyMatch True to match any part not just the beginning
10180 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10182 query : function(property, value, anyMatch){
10183 var fn = this.createFilterFn(property, value, anyMatch);
10184 return fn ? this.queryBy(fn) : this.data.clone();
10188 * Query by a function. The specified function will be called with each
10189 * record in this data source. If the function returns true the record is included
10191 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10192 * @param {Object} scope (optional) The scope of the function (defaults to this)
10193 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10195 queryBy : function(fn, scope){
10196 var data = this.snapshot || this.data;
10197 return data.filterBy(fn, scope||this);
10201 * Collects unique values for a particular dataIndex from this store.
10202 * @param {String} dataIndex The property to collect
10203 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10204 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10205 * @return {Array} An array of the unique values
10207 collect : function(dataIndex, allowNull, bypassFilter){
10208 var d = (bypassFilter === true && this.snapshot) ?
10209 this.snapshot.items : this.data.items;
10210 var v, sv, r = [], l = {};
10211 for(var i = 0, len = d.length; i < len; i++){
10212 v = d[i].data[dataIndex];
10214 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10223 * Revert to a view of the Record cache with no filtering applied.
10224 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10226 clearFilter : function(suppressEvent){
10227 if(this.snapshot && this.snapshot != this.data){
10228 this.data = this.snapshot;
10229 delete this.snapshot;
10230 if(suppressEvent !== true){
10231 this.fireEvent("datachanged", this);
10237 afterEdit : function(record){
10238 if(this.modified.indexOf(record) == -1){
10239 this.modified.push(record);
10241 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10245 afterReject : function(record){
10246 this.modified.remove(record);
10247 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10251 afterCommit : function(record){
10252 this.modified.remove(record);
10253 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10257 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10258 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10260 commitChanges : function(){
10261 var m = this.modified.slice(0);
10262 this.modified = [];
10263 for(var i = 0, len = m.length; i < len; i++){
10269 * Cancel outstanding changes on all changed records.
10271 rejectChanges : function(){
10272 var m = this.modified.slice(0);
10273 this.modified = [];
10274 for(var i = 0, len = m.length; i < len; i++){
10279 onMetaChange : function(meta, rtype, o){
10280 this.recordType = rtype;
10281 this.fields = rtype.prototype.fields;
10282 delete this.snapshot;
10283 this.sortInfo = meta.sortInfo || this.sortInfo;
10284 this.modified = [];
10285 this.fireEvent('metachange', this, this.reader.meta);
10288 moveIndex : function(data, type)
10290 var index = this.indexOf(data);
10292 var newIndex = index + type;
10296 this.insert(newIndex, data);
10301 * Ext JS Library 1.1.1
10302 * Copyright(c) 2006-2007, Ext JS, LLC.
10304 * Originally Released Under LGPL - original licence link has changed is not relivant.
10307 * <script type="text/javascript">
10311 * @class Roo.data.SimpleStore
10312 * @extends Roo.data.Store
10313 * Small helper class to make creating Stores from Array data easier.
10314 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10315 * @cfg {Array} fields An array of field definition objects, or field name strings.
10316 * @cfg {Array} data The multi-dimensional array of data
10318 * @param {Object} config
10320 Roo.data.SimpleStore = function(config){
10321 Roo.data.SimpleStore.superclass.constructor.call(this, {
10323 reader: new Roo.data.ArrayReader({
10326 Roo.data.Record.create(config.fields)
10328 proxy : new Roo.data.MemoryProxy(config.data)
10332 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10334 * Ext JS Library 1.1.1
10335 * Copyright(c) 2006-2007, Ext JS, LLC.
10337 * Originally Released Under LGPL - original licence link has changed is not relivant.
10340 * <script type="text/javascript">
10345 * @extends Roo.data.Store
10346 * @class Roo.data.JsonStore
10347 * Small helper class to make creating Stores for JSON data easier. <br/>
10349 var store = new Roo.data.JsonStore({
10350 url: 'get-images.php',
10352 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10355 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10356 * JsonReader and HttpProxy (unless inline data is provided).</b>
10357 * @cfg {Array} fields An array of field definition objects, or field name strings.
10359 * @param {Object} config
10361 Roo.data.JsonStore = function(c){
10362 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10363 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10364 reader: new Roo.data.JsonReader(c, c.fields)
10367 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10369 * Ext JS Library 1.1.1
10370 * Copyright(c) 2006-2007, Ext JS, LLC.
10372 * Originally Released Under LGPL - original licence link has changed is not relivant.
10375 * <script type="text/javascript">
10379 Roo.data.Field = function(config){
10380 if(typeof config == "string"){
10381 config = {name: config};
10383 Roo.apply(this, config);
10386 this.type = "auto";
10389 var st = Roo.data.SortTypes;
10390 // named sortTypes are supported, here we look them up
10391 if(typeof this.sortType == "string"){
10392 this.sortType = st[this.sortType];
10395 // set default sortType for strings and dates
10396 if(!this.sortType){
10399 this.sortType = st.asUCString;
10402 this.sortType = st.asDate;
10405 this.sortType = st.none;
10410 var stripRe = /[\$,%]/g;
10412 // prebuilt conversion function for this field, instead of
10413 // switching every time we're reading a value
10415 var cv, dateFormat = this.dateFormat;
10420 cv = function(v){ return v; };
10423 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10427 return v !== undefined && v !== null && v !== '' ?
10428 parseInt(String(v).replace(stripRe, ""), 10) : '';
10433 return v !== undefined && v !== null && v !== '' ?
10434 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10439 cv = function(v){ return v === true || v === "true" || v == 1; };
10446 if(v instanceof Date){
10450 if(dateFormat == "timestamp"){
10451 return new Date(v*1000);
10453 return Date.parseDate(v, dateFormat);
10455 var parsed = Date.parse(v);
10456 return parsed ? new Date(parsed) : null;
10465 Roo.data.Field.prototype = {
10473 * Ext JS Library 1.1.1
10474 * Copyright(c) 2006-2007, Ext JS, LLC.
10476 * Originally Released Under LGPL - original licence link has changed is not relivant.
10479 * <script type="text/javascript">
10482 // Base class for reading structured data from a data source. This class is intended to be
10483 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10486 * @class Roo.data.DataReader
10487 * Base class for reading structured data from a data source. This class is intended to be
10488 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10491 Roo.data.DataReader = function(meta, recordType){
10495 this.recordType = recordType instanceof Array ?
10496 Roo.data.Record.create(recordType) : recordType;
10499 Roo.data.DataReader.prototype = {
10501 * Create an empty record
10502 * @param {Object} data (optional) - overlay some values
10503 * @return {Roo.data.Record} record created.
10505 newRow : function(d) {
10507 this.recordType.prototype.fields.each(function(c) {
10509 case 'int' : da[c.name] = 0; break;
10510 case 'date' : da[c.name] = new Date(); break;
10511 case 'float' : da[c.name] = 0.0; break;
10512 case 'boolean' : da[c.name] = false; break;
10513 default : da[c.name] = ""; break;
10517 return new this.recordType(Roo.apply(da, d));
10522 * Ext JS Library 1.1.1
10523 * Copyright(c) 2006-2007, Ext JS, LLC.
10525 * Originally Released Under LGPL - original licence link has changed is not relivant.
10528 * <script type="text/javascript">
10532 * @class Roo.data.DataProxy
10533 * @extends Roo.data.Observable
10534 * This class is an abstract base class for implementations which provide retrieval of
10535 * unformatted data objects.<br>
10537 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10538 * (of the appropriate type which knows how to parse the data object) to provide a block of
10539 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10541 * Custom implementations must implement the load method as described in
10542 * {@link Roo.data.HttpProxy#load}.
10544 Roo.data.DataProxy = function(){
10547 * @event beforeload
10548 * Fires before a network request is made to retrieve a data object.
10549 * @param {Object} This DataProxy object.
10550 * @param {Object} params The params parameter to the load function.
10555 * Fires before the load method's callback is called.
10556 * @param {Object} This DataProxy object.
10557 * @param {Object} o The data object.
10558 * @param {Object} arg The callback argument object passed to the load function.
10562 * @event loadexception
10563 * Fires if an Exception occurs during data retrieval.
10564 * @param {Object} This DataProxy object.
10565 * @param {Object} o The data object.
10566 * @param {Object} arg The callback argument object passed to the load function.
10567 * @param {Object} e The Exception.
10569 loadexception : true
10571 Roo.data.DataProxy.superclass.constructor.call(this);
10574 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10577 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10581 * Ext JS Library 1.1.1
10582 * Copyright(c) 2006-2007, Ext JS, LLC.
10584 * Originally Released Under LGPL - original licence link has changed is not relivant.
10587 * <script type="text/javascript">
10590 * @class Roo.data.MemoryProxy
10591 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10592 * to the Reader when its load method is called.
10594 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10596 Roo.data.MemoryProxy = function(data){
10600 Roo.data.MemoryProxy.superclass.constructor.call(this);
10604 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10606 * Load data from the requested source (in this case an in-memory
10607 * data object passed to the constructor), read the data object into
10608 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10609 * process that block using the passed callback.
10610 * @param {Object} params This parameter is not used by the MemoryProxy class.
10611 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10612 * object into a block of Roo.data.Records.
10613 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10614 * The function must be passed <ul>
10615 * <li>The Record block object</li>
10616 * <li>The "arg" argument from the load function</li>
10617 * <li>A boolean success indicator</li>
10619 * @param {Object} scope The scope in which to call the callback
10620 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10622 load : function(params, reader, callback, scope, arg){
10623 params = params || {};
10626 result = reader.readRecords(this.data);
10628 this.fireEvent("loadexception", this, arg, null, e);
10629 callback.call(scope, null, arg, false);
10632 callback.call(scope, result, arg, true);
10636 update : function(params, records){
10641 * Ext JS Library 1.1.1
10642 * Copyright(c) 2006-2007, Ext JS, LLC.
10644 * Originally Released Under LGPL - original licence link has changed is not relivant.
10647 * <script type="text/javascript">
10650 * @class Roo.data.HttpProxy
10651 * @extends Roo.data.DataProxy
10652 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10653 * configured to reference a certain URL.<br><br>
10655 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10656 * from which the running page was served.<br><br>
10658 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10660 * Be aware that to enable the browser to parse an XML document, the server must set
10661 * the Content-Type header in the HTTP response to "text/xml".
10663 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10664 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10665 * will be used to make the request.
10667 Roo.data.HttpProxy = function(conn){
10668 Roo.data.HttpProxy.superclass.constructor.call(this);
10669 // is conn a conn config or a real conn?
10671 this.useAjax = !conn || !conn.events;
10675 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10676 // thse are take from connection...
10679 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10682 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10683 * extra parameters to each request made by this object. (defaults to undefined)
10686 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10687 * to each request made by this object. (defaults to undefined)
10690 * @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)
10693 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10696 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10702 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10706 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10707 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10708 * a finer-grained basis than the DataProxy events.
10710 getConnection : function(){
10711 return this.useAjax ? Roo.Ajax : this.conn;
10715 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10716 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10717 * process that block using the passed callback.
10718 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10719 * for the request to the remote server.
10720 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10721 * object into a block of Roo.data.Records.
10722 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10723 * The function must be passed <ul>
10724 * <li>The Record block object</li>
10725 * <li>The "arg" argument from the load function</li>
10726 * <li>A boolean success indicator</li>
10728 * @param {Object} scope The scope in which to call the callback
10729 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10731 load : function(params, reader, callback, scope, arg){
10732 if(this.fireEvent("beforeload", this, params) !== false){
10734 params : params || {},
10736 callback : callback,
10741 callback : this.loadResponse,
10745 Roo.applyIf(o, this.conn);
10746 if(this.activeRequest){
10747 Roo.Ajax.abort(this.activeRequest);
10749 this.activeRequest = Roo.Ajax.request(o);
10751 this.conn.request(o);
10754 callback.call(scope||this, null, arg, false);
10759 loadResponse : function(o, success, response){
10760 delete this.activeRequest;
10762 this.fireEvent("loadexception", this, o, response);
10763 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10768 result = o.reader.read(response);
10770 this.fireEvent("loadexception", this, o, response, e);
10771 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10775 this.fireEvent("load", this, o, o.request.arg);
10776 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10780 update : function(dataSet){
10785 updateResponse : function(dataSet){
10790 * Ext JS Library 1.1.1
10791 * Copyright(c) 2006-2007, Ext JS, LLC.
10793 * Originally Released Under LGPL - original licence link has changed is not relivant.
10796 * <script type="text/javascript">
10800 * @class Roo.data.ScriptTagProxy
10801 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10802 * other than the originating domain of the running page.<br><br>
10804 * <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
10805 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10807 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10808 * source code that is used as the source inside a <script> tag.<br><br>
10810 * In order for the browser to process the returned data, the server must wrap the data object
10811 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10812 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10813 * depending on whether the callback name was passed:
10816 boolean scriptTag = false;
10817 String cb = request.getParameter("callback");
10820 response.setContentType("text/javascript");
10822 response.setContentType("application/x-json");
10824 Writer out = response.getWriter();
10826 out.write(cb + "(");
10828 out.print(dataBlock.toJsonString());
10835 * @param {Object} config A configuration object.
10837 Roo.data.ScriptTagProxy = function(config){
10838 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10839 Roo.apply(this, config);
10840 this.head = document.getElementsByTagName("head")[0];
10843 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10845 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10847 * @cfg {String} url The URL from which to request the data object.
10850 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10854 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10855 * the server the name of the callback function set up by the load call to process the returned data object.
10856 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10857 * javascript output which calls this named function passing the data object as its only parameter.
10859 callbackParam : "callback",
10861 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10862 * name to the request.
10867 * Load data from the configured URL, read the data object into
10868 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10869 * process that block using the passed callback.
10870 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10871 * for the request to the remote server.
10872 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10873 * object into a block of Roo.data.Records.
10874 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10875 * The function must be passed <ul>
10876 * <li>The Record block object</li>
10877 * <li>The "arg" argument from the load function</li>
10878 * <li>A boolean success indicator</li>
10880 * @param {Object} scope The scope in which to call the callback
10881 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10883 load : function(params, reader, callback, scope, arg){
10884 if(this.fireEvent("beforeload", this, params) !== false){
10886 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10888 var url = this.url;
10889 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10891 url += "&_dc=" + (new Date().getTime());
10893 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10896 cb : "stcCallback"+transId,
10897 scriptId : "stcScript"+transId,
10901 callback : callback,
10907 window[trans.cb] = function(o){
10908 conn.handleResponse(o, trans);
10911 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10913 if(this.autoAbort !== false){
10917 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10919 var script = document.createElement("script");
10920 script.setAttribute("src", url);
10921 script.setAttribute("type", "text/javascript");
10922 script.setAttribute("id", trans.scriptId);
10923 this.head.appendChild(script);
10925 this.trans = trans;
10927 callback.call(scope||this, null, arg, false);
10932 isLoading : function(){
10933 return this.trans ? true : false;
10937 * Abort the current server request.
10939 abort : function(){
10940 if(this.isLoading()){
10941 this.destroyTrans(this.trans);
10946 destroyTrans : function(trans, isLoaded){
10947 this.head.removeChild(document.getElementById(trans.scriptId));
10948 clearTimeout(trans.timeoutId);
10950 window[trans.cb] = undefined;
10952 delete window[trans.cb];
10955 // if hasn't been loaded, wait for load to remove it to prevent script error
10956 window[trans.cb] = function(){
10957 window[trans.cb] = undefined;
10959 delete window[trans.cb];
10966 handleResponse : function(o, trans){
10967 this.trans = false;
10968 this.destroyTrans(trans, true);
10971 result = trans.reader.readRecords(o);
10973 this.fireEvent("loadexception", this, o, trans.arg, e);
10974 trans.callback.call(trans.scope||window, null, trans.arg, false);
10977 this.fireEvent("load", this, o, trans.arg);
10978 trans.callback.call(trans.scope||window, result, trans.arg, true);
10982 handleFailure : function(trans){
10983 this.trans = false;
10984 this.destroyTrans(trans, false);
10985 this.fireEvent("loadexception", this, null, trans.arg);
10986 trans.callback.call(trans.scope||window, null, trans.arg, false);
10990 * Ext JS Library 1.1.1
10991 * Copyright(c) 2006-2007, Ext JS, LLC.
10993 * Originally Released Under LGPL - original licence link has changed is not relivant.
10996 * <script type="text/javascript">
11000 * @class Roo.data.JsonReader
11001 * @extends Roo.data.DataReader
11002 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11003 * based on mappings in a provided Roo.data.Record constructor.
11005 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11006 * in the reply previously.
11011 var RecordDef = Roo.data.Record.create([
11012 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11013 {name: 'occupation'} // This field will use "occupation" as the mapping.
11015 var myReader = new Roo.data.JsonReader({
11016 totalProperty: "results", // The property which contains the total dataset size (optional)
11017 root: "rows", // The property which contains an Array of row objects
11018 id: "id" // The property within each row object that provides an ID for the record (optional)
11022 * This would consume a JSON file like this:
11024 { 'results': 2, 'rows': [
11025 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11026 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11029 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11030 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11031 * paged from the remote server.
11032 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11033 * @cfg {String} root name of the property which contains the Array of row objects.
11034 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11035 * @cfg {Array} fields Array of field definition objects
11037 * Create a new JsonReader
11038 * @param {Object} meta Metadata configuration options
11039 * @param {Object} recordType Either an Array of field definition objects,
11040 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11042 Roo.data.JsonReader = function(meta, recordType){
11045 // set some defaults:
11046 Roo.applyIf(meta, {
11047 totalProperty: 'total',
11048 successProperty : 'success',
11053 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11055 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11058 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11059 * Used by Store query builder to append _requestMeta to params.
11062 metaFromRemote : false,
11064 * This method is only used by a DataProxy which has retrieved data from a remote server.
11065 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11066 * @return {Object} data A data block which is used by an Roo.data.Store object as
11067 * a cache of Roo.data.Records.
11069 read : function(response){
11070 var json = response.responseText;
11072 var o = /* eval:var:o */ eval("("+json+")");
11074 throw {message: "JsonReader.read: Json object not found"};
11080 this.metaFromRemote = true;
11081 this.meta = o.metaData;
11082 this.recordType = Roo.data.Record.create(o.metaData.fields);
11083 this.onMetaChange(this.meta, this.recordType, o);
11085 return this.readRecords(o);
11088 // private function a store will implement
11089 onMetaChange : function(meta, recordType, o){
11096 simpleAccess: function(obj, subsc) {
11103 getJsonAccessor: function(){
11105 return function(expr) {
11107 return(re.test(expr))
11108 ? new Function("obj", "return obj." + expr)
11113 return Roo.emptyFn;
11118 * Create a data block containing Roo.data.Records from an XML document.
11119 * @param {Object} o An object which contains an Array of row objects in the property specified
11120 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11121 * which contains the total size of the dataset.
11122 * @return {Object} data A data block which is used by an Roo.data.Store object as
11123 * a cache of Roo.data.Records.
11125 readRecords : function(o){
11127 * After any data loads, the raw JSON data is available for further custom processing.
11131 var s = this.meta, Record = this.recordType,
11132 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11134 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11136 if(s.totalProperty) {
11137 this.getTotal = this.getJsonAccessor(s.totalProperty);
11139 if(s.successProperty) {
11140 this.getSuccess = this.getJsonAccessor(s.successProperty);
11142 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11144 var g = this.getJsonAccessor(s.id);
11145 this.getId = function(rec) {
11147 return (r === undefined || r === "") ? null : r;
11150 this.getId = function(){return null;};
11153 for(var jj = 0; jj < fl; jj++){
11155 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11156 this.ef[jj] = this.getJsonAccessor(map);
11160 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11161 if(s.totalProperty){
11162 var vt = parseInt(this.getTotal(o), 10);
11167 if(s.successProperty){
11168 var vs = this.getSuccess(o);
11169 if(vs === false || vs === 'false'){
11174 for(var i = 0; i < c; i++){
11177 var id = this.getId(n);
11178 for(var j = 0; j < fl; j++){
11180 var v = this.ef[j](n);
11182 Roo.log('missing convert for ' + f.name);
11186 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11188 var record = new Record(values, id);
11190 records[i] = record;
11196 totalRecords : totalRecords
11201 * Ext JS Library 1.1.1
11202 * Copyright(c) 2006-2007, Ext JS, LLC.
11204 * Originally Released Under LGPL - original licence link has changed is not relivant.
11207 * <script type="text/javascript">
11211 * @class Roo.data.ArrayReader
11212 * @extends Roo.data.DataReader
11213 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11214 * Each element of that Array represents a row of data fields. The
11215 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11216 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11220 var RecordDef = Roo.data.Record.create([
11221 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11222 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11224 var myReader = new Roo.data.ArrayReader({
11225 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11229 * This would consume an Array like this:
11231 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11233 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11235 * Create a new JsonReader
11236 * @param {Object} meta Metadata configuration options.
11237 * @param {Object} recordType Either an Array of field definition objects
11238 * as specified to {@link Roo.data.Record#create},
11239 * or an {@link Roo.data.Record} object
11240 * created using {@link Roo.data.Record#create}.
11242 Roo.data.ArrayReader = function(meta, recordType){
11243 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11246 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11248 * Create a data block containing Roo.data.Records from an XML document.
11249 * @param {Object} o An Array of row objects which represents the dataset.
11250 * @return {Object} data A data block which is used by an Roo.data.Store object as
11251 * a cache of Roo.data.Records.
11253 readRecords : function(o){
11254 var sid = this.meta ? this.meta.id : null;
11255 var recordType = this.recordType, fields = recordType.prototype.fields;
11258 for(var i = 0; i < root.length; i++){
11261 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11262 for(var j = 0, jlen = fields.length; j < jlen; j++){
11263 var f = fields.items[j];
11264 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11265 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11267 values[f.name] = v;
11269 var record = new recordType(values, id);
11271 records[records.length] = record;
11275 totalRecords : records.length
11284 * @class Roo.bootstrap.ComboBox
11285 * @extends Roo.bootstrap.TriggerField
11286 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11287 * @cfg {Boolean} append (true|false) default false
11288 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11289 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11290 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11291 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11292 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11293 * @cfg {Boolean} animate default true
11294 * @cfg {Boolean} emptyResultText only for touch device
11295 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11297 * Create a new ComboBox.
11298 * @param {Object} config Configuration options
11300 Roo.bootstrap.ComboBox = function(config){
11301 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11305 * Fires when the dropdown list is expanded
11306 * @param {Roo.bootstrap.ComboBox} combo This combo box
11311 * Fires when the dropdown list is collapsed
11312 * @param {Roo.bootstrap.ComboBox} combo This combo box
11316 * @event beforeselect
11317 * Fires before a list item is selected. Return false to cancel the selection.
11318 * @param {Roo.bootstrap.ComboBox} combo This combo box
11319 * @param {Roo.data.Record} record The data record returned from the underlying store
11320 * @param {Number} index The index of the selected item in the dropdown list
11322 'beforeselect' : true,
11325 * Fires when a list item is selected
11326 * @param {Roo.bootstrap.ComboBox} combo This combo box
11327 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11328 * @param {Number} index The index of the selected item in the dropdown list
11332 * @event beforequery
11333 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11334 * The event object passed has these properties:
11335 * @param {Roo.bootstrap.ComboBox} combo This combo box
11336 * @param {String} query The query
11337 * @param {Boolean} forceAll true to force "all" query
11338 * @param {Boolean} cancel true to cancel the query
11339 * @param {Object} e The query event object
11341 'beforequery': true,
11344 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11345 * @param {Roo.bootstrap.ComboBox} combo This combo box
11350 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11351 * @param {Roo.bootstrap.ComboBox} combo This combo box
11352 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11357 * Fires when the remove value from the combobox array
11358 * @param {Roo.bootstrap.ComboBox} combo This combo box
11362 * @event specialfilter
11363 * Fires when specialfilter
11364 * @param {Roo.bootstrap.ComboBox} combo This combo box
11366 'specialfilter' : true,
11369 * Fires when tick the element
11370 * @param {Roo.bootstrap.ComboBox} combo This combo box
11377 this.tickItems = [];
11379 this.selectedIndex = -1;
11380 if(this.mode == 'local'){
11381 if(config.queryDelay === undefined){
11382 this.queryDelay = 10;
11384 if(config.minChars === undefined){
11390 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11393 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11394 * rendering into an Roo.Editor, defaults to false)
11397 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11398 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11401 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11404 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11405 * the dropdown list (defaults to undefined, with no header element)
11409 * @cfg {String/Roo.Template} tpl The template to use to render the output
11413 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11415 listWidth: undefined,
11417 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11418 * mode = 'remote' or 'text' if mode = 'local')
11420 displayField: undefined,
11423 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11424 * mode = 'remote' or 'value' if mode = 'local').
11425 * Note: use of a valueField requires the user make a selection
11426 * in order for a value to be mapped.
11428 valueField: undefined,
11432 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11433 * field's data value (defaults to the underlying DOM element's name)
11435 hiddenName: undefined,
11437 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11441 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11443 selectedClass: 'active',
11446 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11450 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11451 * anchor positions (defaults to 'tl-bl')
11453 listAlign: 'tl-bl?',
11455 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11459 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11460 * query specified by the allQuery config option (defaults to 'query')
11462 triggerAction: 'query',
11464 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11465 * (defaults to 4, does not apply if editable = false)
11469 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11470 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11474 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11475 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11479 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11480 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11484 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11485 * when editable = true (defaults to false)
11487 selectOnFocus:false,
11489 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11491 queryParam: 'query',
11493 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11494 * when mode = 'remote' (defaults to 'Loading...')
11496 loadingText: 'Loading...',
11498 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11502 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11506 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11507 * traditional select (defaults to true)
11511 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11515 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11519 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11520 * listWidth has a higher value)
11524 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11525 * allow the user to set arbitrary text into the field (defaults to false)
11527 forceSelection:false,
11529 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11530 * if typeAhead = true (defaults to 250)
11532 typeAheadDelay : 250,
11534 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11535 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11537 valueNotFoundText : undefined,
11539 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11541 blockFocus : false,
11544 * @cfg {Boolean} disableClear Disable showing of clear button.
11546 disableClear : false,
11548 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11550 alwaysQuery : false,
11553 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11558 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11560 invalidClass : "has-warning",
11563 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11565 validClass : "has-success",
11568 * @cfg {Boolean} specialFilter (true|false) special filter default false
11570 specialFilter : false,
11573 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11575 mobileTouchView : true,
11587 btnPosition : 'right',
11588 triggerList : true,
11589 showToggleBtn : true,
11591 emptyResultText: 'Empty',
11592 triggerText : 'Select',
11594 // element that contains real text value.. (when hidden is used..)
11596 getAutoCreate : function()
11604 if(Roo.isTouch && this.mobileTouchView){
11605 cfg = this.getAutoCreateTouchView();
11612 if(!this.tickable){
11613 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11618 * ComboBox with tickable selections
11621 var align = this.labelAlign || this.parentLabelAlign();
11624 cls : 'form-group roo-combobox-tickable' //input-group
11629 cls : 'tickable-buttons',
11634 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11635 html : this.triggerText
11641 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11648 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11655 buttons.cn.unshift({
11657 cls: 'select2-search-field-input'
11663 Roo.each(buttons.cn, function(c){
11665 c.cls += ' btn-' + _this.size;
11668 if (_this.disabled) {
11679 cls: 'form-hidden-field'
11683 cls: 'select2-choices',
11687 cls: 'select2-search-field',
11699 cls: 'select2-container input-group select2-container-multi',
11704 // cls: 'typeahead typeahead-long dropdown-menu',
11705 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11710 if(this.hasFeedback && !this.allowBlank){
11714 cls: 'glyphicon form-control-feedback'
11717 combobox.cn.push(feedback);
11720 if (align ==='left' && this.fieldLabel.length) {
11722 Roo.log("left and has label");
11728 cls : 'control-label col-sm-' + this.labelWidth,
11729 html : this.fieldLabel
11733 cls : "col-sm-" + (12 - this.labelWidth),
11740 } else if ( this.fieldLabel.length) {
11746 //cls : 'input-group-addon',
11747 html : this.fieldLabel
11757 Roo.log(" no label && no align");
11764 ['xs','sm','md','lg'].map(function(size){
11765 if (settings[size]) {
11766 cfg.cls += ' col-' + size + '-' + settings[size];
11774 _initEventsCalled : false,
11777 initEvents: function()
11780 if (this._initEventsCalled) { // as we call render... prevent looping...
11783 this._initEventsCalled = true;
11786 throw "can not find store for combo";
11789 this.store = Roo.factory(this.store, Roo.data);
11791 // if we are building from html. then this element is so complex, that we can not really
11792 // use the rendered HTML.
11793 // so we have to trash and replace the previous code.
11794 if (Roo.XComponent.build_from_html) {
11796 // remove this element....
11797 var e = this.el.dom, k=0;
11798 while (e ) { e = e.previousSibling; ++k;}
11803 this.rendered = false;
11805 this.render(this.parent().getChildContainer(true), k);
11816 if(Roo.isTouch && this.mobileTouchView){
11817 this.initTouchView();
11822 this.initTickableEvents();
11826 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11828 if(this.hiddenName){
11830 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11832 this.hiddenField.dom.value =
11833 this.hiddenValue !== undefined ? this.hiddenValue :
11834 this.value !== undefined ? this.value : '';
11836 // prevent input submission
11837 this.el.dom.removeAttribute('name');
11838 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11843 // this.el.dom.setAttribute('autocomplete', 'off');
11846 var cls = 'x-combo-list';
11848 //this.list = new Roo.Layer({
11849 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11855 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11856 _this.list.setWidth(lw);
11859 this.list.on('mouseover', this.onViewOver, this);
11860 this.list.on('mousemove', this.onViewMove, this);
11862 this.list.on('scroll', this.onViewScroll, this);
11865 this.list.swallowEvent('mousewheel');
11866 this.assetHeight = 0;
11869 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11870 this.assetHeight += this.header.getHeight();
11873 this.innerList = this.list.createChild({cls:cls+'-inner'});
11874 this.innerList.on('mouseover', this.onViewOver, this);
11875 this.innerList.on('mousemove', this.onViewMove, this);
11876 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11878 if(this.allowBlank && !this.pageSize && !this.disableClear){
11879 this.footer = this.list.createChild({cls:cls+'-ft'});
11880 this.pageTb = new Roo.Toolbar(this.footer);
11884 this.footer = this.list.createChild({cls:cls+'-ft'});
11885 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11886 {pageSize: this.pageSize});
11890 if (this.pageTb && this.allowBlank && !this.disableClear) {
11892 this.pageTb.add(new Roo.Toolbar.Fill(), {
11893 cls: 'x-btn-icon x-btn-clear',
11895 handler: function()
11898 _this.clearValue();
11899 _this.onSelect(false, -1);
11904 this.assetHeight += this.footer.getHeight();
11909 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11912 this.view = new Roo.View(this.list, this.tpl, {
11913 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11915 //this.view.wrapEl.setDisplayed(false);
11916 this.view.on('click', this.onViewClick, this);
11920 this.store.on('beforeload', this.onBeforeLoad, this);
11921 this.store.on('load', this.onLoad, this);
11922 this.store.on('loadexception', this.onLoadException, this);
11924 if(this.resizable){
11925 this.resizer = new Roo.Resizable(this.list, {
11926 pinned:true, handles:'se'
11928 this.resizer.on('resize', function(r, w, h){
11929 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11930 this.listWidth = w;
11931 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11932 this.restrictHeight();
11934 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11937 if(!this.editable){
11938 this.editable = true;
11939 this.setEditable(false);
11944 if (typeof(this.events.add.listeners) != 'undefined') {
11946 this.addicon = this.wrap.createChild(
11947 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11949 this.addicon.on('click', function(e) {
11950 this.fireEvent('add', this);
11953 if (typeof(this.events.edit.listeners) != 'undefined') {
11955 this.editicon = this.wrap.createChild(
11956 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11957 if (this.addicon) {
11958 this.editicon.setStyle('margin-left', '40px');
11960 this.editicon.on('click', function(e) {
11962 // we fire even if inothing is selected..
11963 this.fireEvent('edit', this, this.lastData );
11969 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11970 "up" : function(e){
11971 this.inKeyMode = true;
11975 "down" : function(e){
11976 if(!this.isExpanded()){
11977 this.onTriggerClick();
11979 this.inKeyMode = true;
11984 "enter" : function(e){
11985 // this.onViewClick();
11989 if(this.fireEvent("specialkey", this, e)){
11990 this.onViewClick(false);
11996 "esc" : function(e){
12000 "tab" : function(e){
12003 if(this.fireEvent("specialkey", this, e)){
12004 this.onViewClick(false);
12012 doRelay : function(foo, bar, hname){
12013 if(hname == 'down' || this.scope.isExpanded()){
12014 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12023 this.queryDelay = Math.max(this.queryDelay || 10,
12024 this.mode == 'local' ? 10 : 250);
12027 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12029 if(this.typeAhead){
12030 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12032 if(this.editable !== false){
12033 this.inputEl().on("keyup", this.onKeyUp, this);
12035 if(this.forceSelection){
12036 this.inputEl().on('blur', this.doForce, this);
12040 this.choices = this.el.select('ul.select2-choices', true).first();
12041 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12045 initTickableEvents: function()
12049 if(this.hiddenName){
12051 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12053 this.hiddenField.dom.value =
12054 this.hiddenValue !== undefined ? this.hiddenValue :
12055 this.value !== undefined ? this.value : '';
12057 // prevent input submission
12058 this.el.dom.removeAttribute('name');
12059 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12064 // this.list = this.el.select('ul.dropdown-menu',true).first();
12066 this.choices = this.el.select('ul.select2-choices', true).first();
12067 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12068 if(this.triggerList){
12069 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12072 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12073 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12075 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12076 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12078 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12079 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12081 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12082 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12083 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12086 this.cancelBtn.hide();
12091 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12092 _this.list.setWidth(lw);
12095 this.list.on('mouseover', this.onViewOver, this);
12096 this.list.on('mousemove', this.onViewMove, this);
12098 this.list.on('scroll', this.onViewScroll, this);
12101 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>';
12104 this.view = new Roo.View(this.list, this.tpl, {
12105 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12108 //this.view.wrapEl.setDisplayed(false);
12109 this.view.on('click', this.onViewClick, this);
12113 this.store.on('beforeload', this.onBeforeLoad, this);
12114 this.store.on('load', this.onLoad, this);
12115 this.store.on('loadexception', this.onLoadException, this);
12118 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12119 "up" : function(e){
12120 this.inKeyMode = true;
12124 "down" : function(e){
12125 this.inKeyMode = true;
12129 "enter" : function(e){
12130 if(this.fireEvent("specialkey", this, e)){
12131 this.onViewClick(false);
12137 "esc" : function(e){
12138 this.onTickableFooterButtonClick(e, false, false);
12141 "tab" : function(e){
12142 this.fireEvent("specialkey", this, e);
12144 this.onTickableFooterButtonClick(e, false, false);
12151 doRelay : function(e, fn, key){
12152 if(this.scope.isExpanded()){
12153 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12162 this.queryDelay = Math.max(this.queryDelay || 10,
12163 this.mode == 'local' ? 10 : 250);
12166 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12168 if(this.typeAhead){
12169 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12172 if(this.editable !== false){
12173 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12178 onDestroy : function(){
12180 this.view.setStore(null);
12181 this.view.el.removeAllListeners();
12182 this.view.el.remove();
12183 this.view.purgeListeners();
12186 this.list.dom.innerHTML = '';
12190 this.store.un('beforeload', this.onBeforeLoad, this);
12191 this.store.un('load', this.onLoad, this);
12192 this.store.un('loadexception', this.onLoadException, this);
12194 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12198 fireKey : function(e){
12199 if(e.isNavKeyPress() && !this.list.isVisible()){
12200 this.fireEvent("specialkey", this, e);
12205 onResize: function(w, h){
12206 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12208 // if(typeof w != 'number'){
12209 // // we do not handle it!?!?
12212 // var tw = this.trigger.getWidth();
12213 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12214 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12216 // this.inputEl().setWidth( this.adjustWidth('input', x));
12218 // //this.trigger.setStyle('left', x+'px');
12220 // if(this.list && this.listWidth === undefined){
12221 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12222 // this.list.setWidth(lw);
12223 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12231 * Allow or prevent the user from directly editing the field text. If false is passed,
12232 * the user will only be able to select from the items defined in the dropdown list. This method
12233 * is the runtime equivalent of setting the 'editable' config option at config time.
12234 * @param {Boolean} value True to allow the user to directly edit the field text
12236 setEditable : function(value){
12237 if(value == this.editable){
12240 this.editable = value;
12242 this.inputEl().dom.setAttribute('readOnly', true);
12243 this.inputEl().on('mousedown', this.onTriggerClick, this);
12244 this.inputEl().addClass('x-combo-noedit');
12246 this.inputEl().dom.setAttribute('readOnly', false);
12247 this.inputEl().un('mousedown', this.onTriggerClick, this);
12248 this.inputEl().removeClass('x-combo-noedit');
12254 onBeforeLoad : function(combo,opts){
12255 if(!this.hasFocus){
12259 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12261 this.restrictHeight();
12262 this.selectedIndex = -1;
12266 onLoad : function(){
12268 this.hasQuery = false;
12270 if(!this.hasFocus){
12274 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12275 this.loading.hide();
12278 if(this.store.getCount() > 0){
12280 this.restrictHeight();
12281 if(this.lastQuery == this.allQuery){
12282 if(this.editable && !this.tickable){
12283 this.inputEl().dom.select();
12287 !this.selectByValue(this.value, true) &&
12290 !this.store.lastOptions ||
12291 typeof(this.store.lastOptions.add) == 'undefined' ||
12292 this.store.lastOptions.add != true
12295 this.select(0, true);
12298 if(this.autoFocus){
12301 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12302 this.taTask.delay(this.typeAheadDelay);
12306 this.onEmptyResults();
12312 onLoadException : function()
12314 this.hasQuery = false;
12316 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12317 this.loading.hide();
12320 if(this.tickable && this.editable){
12325 // only causes errors at present
12326 //Roo.log(this.store.reader.jsonData);
12327 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12329 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12335 onTypeAhead : function(){
12336 if(this.store.getCount() > 0){
12337 var r = this.store.getAt(0);
12338 var newValue = r.data[this.displayField];
12339 var len = newValue.length;
12340 var selStart = this.getRawValue().length;
12342 if(selStart != len){
12343 this.setRawValue(newValue);
12344 this.selectText(selStart, newValue.length);
12350 onSelect : function(record, index){
12352 if(this.fireEvent('beforeselect', this, record, index) !== false){
12354 this.setFromData(index > -1 ? record.data : false);
12357 this.fireEvent('select', this, record, index);
12362 * Returns the currently selected field value or empty string if no value is set.
12363 * @return {String} value The selected value
12365 getValue : function(){
12368 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12371 if(this.valueField){
12372 return typeof this.value != 'undefined' ? this.value : '';
12374 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12379 * Clears any text/value currently set in the field
12381 clearValue : function(){
12382 if(this.hiddenField){
12383 this.hiddenField.dom.value = '';
12386 this.setRawValue('');
12387 this.lastSelectionText = '';
12388 this.lastData = false;
12390 var close = this.closeTriggerEl();
12399 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12400 * will be displayed in the field. If the value does not match the data value of an existing item,
12401 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12402 * Otherwise the field will be blank (although the value will still be set).
12403 * @param {String} value The value to match
12405 setValue : function(v){
12412 if(this.valueField){
12413 var r = this.findRecord(this.valueField, v);
12415 text = r.data[this.displayField];
12416 }else if(this.valueNotFoundText !== undefined){
12417 text = this.valueNotFoundText;
12420 this.lastSelectionText = text;
12421 if(this.hiddenField){
12422 this.hiddenField.dom.value = v;
12424 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12427 var close = this.closeTriggerEl();
12430 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12434 * @property {Object} the last set data for the element
12439 * Sets the value of the field based on a object which is related to the record format for the store.
12440 * @param {Object} value the value to set as. or false on reset?
12442 setFromData : function(o){
12449 var dv = ''; // display value
12450 var vv = ''; // value value..
12452 if (this.displayField) {
12453 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12455 // this is an error condition!!!
12456 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12459 if(this.valueField){
12460 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12463 var close = this.closeTriggerEl();
12466 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12469 if(this.hiddenField){
12470 this.hiddenField.dom.value = vv;
12472 this.lastSelectionText = dv;
12473 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12477 // no hidden field.. - we store the value in 'value', but still display
12478 // display field!!!!
12479 this.lastSelectionText = dv;
12480 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12487 reset : function(){
12488 // overridden so that last data is reset..
12495 this.setValue(this.originalValue);
12496 this.clearInvalid();
12497 this.lastData = false;
12499 this.view.clearSelections();
12503 findRecord : function(prop, value){
12505 if(this.store.getCount() > 0){
12506 this.store.each(function(r){
12507 if(r.data[prop] == value){
12517 getName: function()
12519 // returns hidden if it's set..
12520 if (!this.rendered) {return ''};
12521 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12525 onViewMove : function(e, t){
12526 this.inKeyMode = false;
12530 onViewOver : function(e, t){
12531 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12534 var item = this.view.findItemFromChild(t);
12537 var index = this.view.indexOf(item);
12538 this.select(index, false);
12543 onViewClick : function(view, doFocus, el, e)
12545 var index = this.view.getSelectedIndexes()[0];
12547 var r = this.store.getAt(index);
12551 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12558 Roo.each(this.tickItems, function(v,k){
12560 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12562 _this.tickItems.splice(k, 1);
12564 if(typeof(e) == 'undefined' && view == false){
12565 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12577 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12578 this.tickItems.push(r.data);
12581 if(typeof(e) == 'undefined' && view == false){
12582 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12589 this.onSelect(r, index);
12591 if(doFocus !== false && !this.blockFocus){
12592 this.inputEl().focus();
12597 restrictHeight : function(){
12598 //this.innerList.dom.style.height = '';
12599 //var inner = this.innerList.dom;
12600 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12601 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12602 //this.list.beginUpdate();
12603 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12604 this.list.alignTo(this.inputEl(), this.listAlign);
12605 this.list.alignTo(this.inputEl(), this.listAlign);
12606 //this.list.endUpdate();
12610 onEmptyResults : function(){
12612 if(this.tickable && this.editable){
12613 this.restrictHeight();
12621 * Returns true if the dropdown list is expanded, else false.
12623 isExpanded : function(){
12624 return this.list.isVisible();
12628 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12629 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12630 * @param {String} value The data value of the item to select
12631 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12632 * selected item if it is not currently in view (defaults to true)
12633 * @return {Boolean} True if the value matched an item in the list, else false
12635 selectByValue : function(v, scrollIntoView){
12636 if(v !== undefined && v !== null){
12637 var r = this.findRecord(this.valueField || this.displayField, v);
12639 this.select(this.store.indexOf(r), scrollIntoView);
12647 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12648 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12649 * @param {Number} index The zero-based index of the list item to select
12650 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12651 * selected item if it is not currently in view (defaults to true)
12653 select : function(index, scrollIntoView){
12654 this.selectedIndex = index;
12655 this.view.select(index);
12656 if(scrollIntoView !== false){
12657 var el = this.view.getNode(index);
12659 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12662 this.list.scrollChildIntoView(el, false);
12668 selectNext : function(){
12669 var ct = this.store.getCount();
12671 if(this.selectedIndex == -1){
12673 }else if(this.selectedIndex < ct-1){
12674 this.select(this.selectedIndex+1);
12680 selectPrev : function(){
12681 var ct = this.store.getCount();
12683 if(this.selectedIndex == -1){
12685 }else if(this.selectedIndex != 0){
12686 this.select(this.selectedIndex-1);
12692 onKeyUp : function(e){
12693 if(this.editable !== false && !e.isSpecialKey()){
12694 this.lastKey = e.getKey();
12695 this.dqTask.delay(this.queryDelay);
12700 validateBlur : function(){
12701 return !this.list || !this.list.isVisible();
12705 initQuery : function(){
12707 var v = this.getRawValue();
12709 if(this.tickable && this.editable){
12710 v = this.tickableInputEl().getValue();
12717 doForce : function(){
12718 if(this.inputEl().dom.value.length > 0){
12719 this.inputEl().dom.value =
12720 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12726 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12727 * query allowing the query action to be canceled if needed.
12728 * @param {String} query The SQL query to execute
12729 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12730 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12731 * saved in the current store (defaults to false)
12733 doQuery : function(q, forceAll){
12735 if(q === undefined || q === null){
12740 forceAll: forceAll,
12744 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12749 forceAll = qe.forceAll;
12750 if(forceAll === true || (q.length >= this.minChars)){
12752 this.hasQuery = true;
12754 if(this.lastQuery != q || this.alwaysQuery){
12755 this.lastQuery = q;
12756 if(this.mode == 'local'){
12757 this.selectedIndex = -1;
12759 this.store.clearFilter();
12762 if(this.specialFilter){
12763 this.fireEvent('specialfilter', this);
12768 this.store.filter(this.displayField, q);
12771 this.store.fireEvent("datachanged", this.store);
12778 this.store.baseParams[this.queryParam] = q;
12780 var options = {params : this.getParams(q)};
12783 options.add = true;
12784 options.params.start = this.page * this.pageSize;
12787 this.store.load(options);
12790 * this code will make the page width larger, at the beginning, the list not align correctly,
12791 * we should expand the list on onLoad
12792 * so command out it
12797 this.selectedIndex = -1;
12802 this.loadNext = false;
12806 getParams : function(q){
12808 //p[this.queryParam] = q;
12812 p.limit = this.pageSize;
12818 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12820 collapse : function(){
12821 if(!this.isExpanded()){
12828 this.hasFocus = false;
12830 this.cancelBtn.hide();
12831 this.trigger.show();
12834 this.tickableInputEl().dom.value = '';
12835 this.tickableInputEl().blur();
12840 Roo.get(document).un('mousedown', this.collapseIf, this);
12841 Roo.get(document).un('mousewheel', this.collapseIf, this);
12842 if (!this.editable) {
12843 Roo.get(document).un('keydown', this.listKeyPress, this);
12845 this.fireEvent('collapse', this);
12849 collapseIf : function(e){
12850 var in_combo = e.within(this.el);
12851 var in_list = e.within(this.list);
12852 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12854 if (in_combo || in_list || is_list) {
12855 //e.stopPropagation();
12860 this.onTickableFooterButtonClick(e, false, false);
12868 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12870 expand : function(){
12872 if(this.isExpanded() || !this.hasFocus){
12876 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12877 this.list.setWidth(lw);
12884 this.restrictHeight();
12888 this.tickItems = Roo.apply([], this.item);
12891 this.cancelBtn.show();
12892 this.trigger.hide();
12895 this.tickableInputEl().focus();
12900 Roo.get(document).on('mousedown', this.collapseIf, this);
12901 Roo.get(document).on('mousewheel', this.collapseIf, this);
12902 if (!this.editable) {
12903 Roo.get(document).on('keydown', this.listKeyPress, this);
12906 this.fireEvent('expand', this);
12910 // Implements the default empty TriggerField.onTriggerClick function
12911 onTriggerClick : function(e)
12913 Roo.log('trigger click');
12915 if(this.disabled || !this.triggerList){
12920 this.loadNext = false;
12922 if(this.isExpanded()){
12924 if (!this.blockFocus) {
12925 this.inputEl().focus();
12929 this.hasFocus = true;
12930 if(this.triggerAction == 'all') {
12931 this.doQuery(this.allQuery, true);
12933 this.doQuery(this.getRawValue());
12935 if (!this.blockFocus) {
12936 this.inputEl().focus();
12941 onTickableTriggerClick : function(e)
12948 this.loadNext = false;
12949 this.hasFocus = true;
12951 if(this.triggerAction == 'all') {
12952 this.doQuery(this.allQuery, true);
12954 this.doQuery(this.getRawValue());
12958 onSearchFieldClick : function(e)
12960 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12961 this.onTickableFooterButtonClick(e, false, false);
12965 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12970 this.loadNext = false;
12971 this.hasFocus = true;
12973 if(this.triggerAction == 'all') {
12974 this.doQuery(this.allQuery, true);
12976 this.doQuery(this.getRawValue());
12980 listKeyPress : function(e)
12982 //Roo.log('listkeypress');
12983 // scroll to first matching element based on key pres..
12984 if (e.isSpecialKey()) {
12987 var k = String.fromCharCode(e.getKey()).toUpperCase();
12990 var csel = this.view.getSelectedNodes();
12991 var cselitem = false;
12993 var ix = this.view.indexOf(csel[0]);
12994 cselitem = this.store.getAt(ix);
12995 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13001 this.store.each(function(v) {
13003 // start at existing selection.
13004 if (cselitem.id == v.id) {
13010 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13011 match = this.store.indexOf(v);
13017 if (match === false) {
13018 return true; // no more action?
13021 this.view.select(match);
13022 var sn = Roo.get(this.view.getSelectedNodes()[0])
13023 sn.scrollIntoView(sn.dom.parentNode, false);
13026 onViewScroll : function(e, t){
13028 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){
13032 this.hasQuery = true;
13034 this.loading = this.list.select('.loading', true).first();
13036 if(this.loading === null){
13037 this.list.createChild({
13039 cls: 'loading select2-more-results select2-active',
13040 html: 'Loading more results...'
13043 this.loading = this.list.select('.loading', true).first();
13045 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13047 this.loading.hide();
13050 this.loading.show();
13055 this.loadNext = true;
13057 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13062 addItem : function(o)
13064 var dv = ''; // display value
13066 if (this.displayField) {
13067 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13069 // this is an error condition!!!
13070 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13077 var choice = this.choices.createChild({
13079 cls: 'select2-search-choice',
13088 cls: 'select2-search-choice-close',
13093 }, this.searchField);
13095 var close = choice.select('a.select2-search-choice-close', true).first()
13097 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13105 this.inputEl().dom.value = '';
13110 onRemoveItem : function(e, _self, o)
13112 e.preventDefault();
13114 this.lastItem = Roo.apply([], this.item);
13116 var index = this.item.indexOf(o.data) * 1;
13119 Roo.log('not this item?!');
13123 this.item.splice(index, 1);
13128 this.fireEvent('remove', this, e);
13134 syncValue : function()
13136 if(!this.item.length){
13143 Roo.each(this.item, function(i){
13144 if(_this.valueField){
13145 value.push(i[_this.valueField]);
13152 this.value = value.join(',');
13154 if(this.hiddenField){
13155 this.hiddenField.dom.value = this.value;
13158 this.store.fireEvent("datachanged", this.store);
13161 clearItem : function()
13163 if(!this.multiple){
13169 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13177 if(this.tickable && !Roo.isTouch){
13178 this.view.refresh();
13182 inputEl: function ()
13184 if(Roo.isTouch && this.mobileTouchView){
13185 return this.el.select('input.form-control',true).first();
13189 return this.searchField;
13192 return this.el.select('input.form-control',true).first();
13196 onTickableFooterButtonClick : function(e, btn, el)
13198 e.preventDefault();
13200 this.lastItem = Roo.apply([], this.item);
13202 if(btn && btn.name == 'cancel'){
13203 this.tickItems = Roo.apply([], this.item);
13212 Roo.each(this.tickItems, function(o){
13220 validate : function()
13222 var v = this.getRawValue();
13225 v = this.getValue();
13228 if(this.disabled || this.allowBlank || v.length){
13233 this.markInvalid();
13237 tickableInputEl : function()
13239 if(!this.tickable || !this.editable){
13240 return this.inputEl();
13243 return this.inputEl().select('.select2-search-field-input', true).first();
13247 getAutoCreateTouchView : function()
13252 cls: 'form-group' //input-group
13258 type : this.inputType,
13259 cls : 'form-control x-combo-noedit',
13260 autocomplete: 'new-password',
13261 placeholder : this.placeholder || '',
13266 input.name = this.name;
13270 input.cls += ' input-' + this.size;
13273 if (this.disabled) {
13274 input.disabled = true;
13285 inputblock.cls += ' input-group';
13287 inputblock.cn.unshift({
13289 cls : 'input-group-addon',
13294 if(this.removable && !this.multiple){
13295 inputblock.cls += ' roo-removable';
13297 inputblock.cn.push({
13300 cls : 'roo-combo-removable-btn close'
13304 if(this.hasFeedback && !this.allowBlank){
13306 inputblock.cls += ' has-feedback';
13308 inputblock.cn.push({
13310 cls: 'glyphicon form-control-feedback'
13317 inputblock.cls += (this.before) ? '' : ' input-group';
13319 inputblock.cn.push({
13321 cls : 'input-group-addon',
13332 cls: 'form-hidden-field'
13346 cls: 'form-hidden-field'
13350 cls: 'select2-choices',
13354 cls: 'select2-search-field',
13367 cls: 'select2-container input-group',
13374 combobox.cls += ' select2-container-multi';
13377 var align = this.labelAlign || this.parentLabelAlign();
13381 if(this.fieldLabel.length){
13383 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13384 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13389 cls : 'control-label ' + lw,
13390 html : this.fieldLabel
13402 var settings = this;
13404 ['xs','sm','md','lg'].map(function(size){
13405 if (settings[size]) {
13406 cfg.cls += ' col-' + size + '-' + settings[size];
13413 initTouchView : function()
13415 this.renderTouchView();
13417 this.touchViewEl.on('scroll', function(){
13418 this.el.dom.scrollTop = 0;
13421 this.inputEl().on("click", this.showTouchView, this);
13422 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13423 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13425 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13427 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13428 this.store.on('load', this.onTouchViewLoad, this);
13429 this.store.on('loadexception', this.onTouchViewLoadException, this);
13431 if(this.hiddenName){
13433 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13435 this.hiddenField.dom.value =
13436 this.hiddenValue !== undefined ? this.hiddenValue :
13437 this.value !== undefined ? this.value : '';
13439 this.el.dom.removeAttribute('name');
13440 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13444 this.choices = this.el.select('ul.select2-choices', true).first();
13445 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13448 if(this.removable && !this.multiple){
13449 var close = this.closeTriggerEl();
13451 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13452 close.on('click', this.removeBtnClick, this, close);
13461 renderTouchView : function()
13463 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13464 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13466 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13467 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13469 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13470 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13471 this.touchViewBodyEl.setStyle('overflow', 'auto');
13473 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13474 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13476 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13477 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13481 showTouchView : function()
13483 this.touchViewHeaderEl.hide();
13485 if(this.fieldLabel.length){
13486 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13487 this.touchViewHeaderEl.show();
13490 this.touchViewEl.show();
13492 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13493 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13495 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13497 if(this.fieldLabel.length){
13498 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13501 this.touchViewBodyEl.setHeight(bodyHeight);
13505 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13507 this.touchViewEl.addClass('in');
13510 this.doTouchViewQuery();
13514 hideTouchView : function()
13516 this.touchViewEl.removeClass('in');
13520 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13522 this.touchViewEl.setStyle('display', 'none');
13527 setTouchViewValue : function()
13534 Roo.each(this.tickItems, function(o){
13539 this.hideTouchView();
13542 doTouchViewQuery : function()
13551 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13555 if(!this.alwaysQuery || this.mode == 'local'){
13556 this.onTouchViewLoad();
13563 onTouchViewBeforeLoad : function(combo,opts)
13569 onTouchViewLoad : function()
13571 if(this.store.getCount() < 1){
13572 this.onTouchViewEmptyResults();
13576 this.clearTouchView();
13578 var rawValue = this.getRawValue();
13580 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13582 this.tickItems = [];
13584 this.store.data.each(function(d, rowIndex){
13585 var row = this.touchViewListGroup.createChild(template);
13587 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13588 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13591 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13592 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13595 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13596 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13597 this.tickItems.push(d.data);
13600 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13604 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13606 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13608 if(this.fieldLabel.length){
13609 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13612 var listHeight = this.touchViewListGroup.getHeight();
13616 if(firstChecked && listHeight > bodyHeight){
13617 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13622 onTouchViewLoadException : function()
13624 this.hideTouchView();
13627 onTouchViewEmptyResults : function()
13629 this.clearTouchView();
13631 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13633 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13637 clearTouchView : function()
13639 this.touchViewListGroup.dom.innerHTML = '';
13642 onTouchViewClick : function(e, el, o)
13644 e.preventDefault();
13647 var rowIndex = o.rowIndex;
13649 var r = this.store.getAt(rowIndex);
13651 if(!this.multiple){
13652 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13653 c.dom.removeAttribute('checked');
13656 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13658 this.setFromData(r.data);
13660 var close = this.closeTriggerEl();
13666 this.hideTouchView();
13668 this.fireEvent('select', this, r, rowIndex);
13673 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13674 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13675 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13679 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13680 this.addItem(r.data);
13681 this.tickItems.push(r.data);
13687 * @cfg {Boolean} grow
13691 * @cfg {Number} growMin
13695 * @cfg {Number} growMax
13704 Roo.apply(Roo.bootstrap.ComboBox, {
13708 cls: 'modal-header',
13730 cls: 'list-group-item',
13734 cls: 'roo-combobox-list-group-item-value'
13738 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13752 listItemCheckbox : {
13754 cls: 'list-group-item',
13758 cls: 'roo-combobox-list-group-item-value'
13762 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13778 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13783 cls: 'modal-footer',
13791 cls: 'col-xs-6 text-left',
13794 cls: 'btn btn-danger roo-touch-view-cancel',
13800 cls: 'col-xs-6 text-right',
13803 cls: 'btn btn-success roo-touch-view-ok',
13814 Roo.apply(Roo.bootstrap.ComboBox, {
13816 touchViewTemplate : {
13818 cls: 'modal fade roo-combobox-touch-view',
13822 cls: 'modal-dialog',
13826 cls: 'modal-content',
13828 Roo.bootstrap.ComboBox.header,
13829 Roo.bootstrap.ComboBox.body,
13830 Roo.bootstrap.ComboBox.footer
13839 * Ext JS Library 1.1.1
13840 * Copyright(c) 2006-2007, Ext JS, LLC.
13842 * Originally Released Under LGPL - original licence link has changed is not relivant.
13845 * <script type="text/javascript">
13850 * @extends Roo.util.Observable
13851 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13852 * This class also supports single and multi selection modes. <br>
13853 * Create a data model bound view:
13855 var store = new Roo.data.Store(...);
13857 var view = new Roo.View({
13859 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13861 singleSelect: true,
13862 selectedClass: "ydataview-selected",
13866 // listen for node click?
13867 view.on("click", function(vw, index, node, e){
13868 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13872 dataModel.load("foobar.xml");
13874 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13876 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13877 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13879 * Note: old style constructor is still suported (container, template, config)
13882 * Create a new View
13883 * @param {Object} config The config object
13886 Roo.View = function(config, depreciated_tpl, depreciated_config){
13888 this.parent = false;
13890 if (typeof(depreciated_tpl) == 'undefined') {
13891 // new way.. - universal constructor.
13892 Roo.apply(this, config);
13893 this.el = Roo.get(this.el);
13896 this.el = Roo.get(config);
13897 this.tpl = depreciated_tpl;
13898 Roo.apply(this, depreciated_config);
13900 this.wrapEl = this.el.wrap().wrap();
13901 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13904 if(typeof(this.tpl) == "string"){
13905 this.tpl = new Roo.Template(this.tpl);
13907 // support xtype ctors..
13908 this.tpl = new Roo.factory(this.tpl, Roo);
13912 this.tpl.compile();
13917 * @event beforeclick
13918 * Fires before a click is processed. Returns false to cancel the default action.
13919 * @param {Roo.View} this
13920 * @param {Number} index The index of the target node
13921 * @param {HTMLElement} node The target node
13922 * @param {Roo.EventObject} e The raw event object
13924 "beforeclick" : true,
13927 * Fires when a template node is clicked.
13928 * @param {Roo.View} this
13929 * @param {Number} index The index of the target node
13930 * @param {HTMLElement} node The target node
13931 * @param {Roo.EventObject} e The raw event object
13936 * Fires when a template node is double clicked.
13937 * @param {Roo.View} this
13938 * @param {Number} index The index of the target node
13939 * @param {HTMLElement} node The target node
13940 * @param {Roo.EventObject} e The raw event object
13944 * @event contextmenu
13945 * Fires when a template node is right clicked.
13946 * @param {Roo.View} this
13947 * @param {Number} index The index of the target node
13948 * @param {HTMLElement} node The target node
13949 * @param {Roo.EventObject} e The raw event object
13951 "contextmenu" : true,
13953 * @event selectionchange
13954 * Fires when the selected nodes change.
13955 * @param {Roo.View} this
13956 * @param {Array} selections Array of the selected nodes
13958 "selectionchange" : true,
13961 * @event beforeselect
13962 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13963 * @param {Roo.View} this
13964 * @param {HTMLElement} node The node to be selected
13965 * @param {Array} selections Array of currently selected nodes
13967 "beforeselect" : true,
13969 * @event preparedata
13970 * Fires on every row to render, to allow you to change the data.
13971 * @param {Roo.View} this
13972 * @param {Object} data to be rendered (change this)
13974 "preparedata" : true
13982 "click": this.onClick,
13983 "dblclick": this.onDblClick,
13984 "contextmenu": this.onContextMenu,
13988 this.selections = [];
13990 this.cmp = new Roo.CompositeElementLite([]);
13992 this.store = Roo.factory(this.store, Roo.data);
13993 this.setStore(this.store, true);
13996 if ( this.footer && this.footer.xtype) {
13998 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14000 this.footer.dataSource = this.store;
14001 this.footer.container = fctr;
14002 this.footer = Roo.factory(this.footer, Roo);
14003 fctr.insertFirst(this.el);
14005 // this is a bit insane - as the paging toolbar seems to detach the el..
14006 // dom.parentNode.parentNode.parentNode
14007 // they get detached?
14011 Roo.View.superclass.constructor.call(this);
14016 Roo.extend(Roo.View, Roo.util.Observable, {
14019 * @cfg {Roo.data.Store} store Data store to load data from.
14024 * @cfg {String|Roo.Element} el The container element.
14029 * @cfg {String|Roo.Template} tpl The template used by this View
14033 * @cfg {String} dataName the named area of the template to use as the data area
14034 * Works with domtemplates roo-name="name"
14038 * @cfg {String} selectedClass The css class to add to selected nodes
14040 selectedClass : "x-view-selected",
14042 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14047 * @cfg {String} text to display on mask (default Loading)
14051 * @cfg {Boolean} multiSelect Allow multiple selection
14053 multiSelect : false,
14055 * @cfg {Boolean} singleSelect Allow single selection
14057 singleSelect: false,
14060 * @cfg {Boolean} toggleSelect - selecting
14062 toggleSelect : false,
14065 * @cfg {Boolean} tickable - selecting
14070 * Returns the element this view is bound to.
14071 * @return {Roo.Element}
14073 getEl : function(){
14074 return this.wrapEl;
14080 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14082 refresh : function(){
14083 //Roo.log('refresh');
14086 // if we are using something like 'domtemplate', then
14087 // the what gets used is:
14088 // t.applySubtemplate(NAME, data, wrapping data..)
14089 // the outer template then get' applied with
14090 // the store 'extra data'
14091 // and the body get's added to the
14092 // roo-name="data" node?
14093 // <span class='roo-tpl-{name}'></span> ?????
14097 this.clearSelections();
14098 this.el.update("");
14100 var records = this.store.getRange();
14101 if(records.length < 1) {
14103 // is this valid?? = should it render a template??
14105 this.el.update(this.emptyText);
14109 if (this.dataName) {
14110 this.el.update(t.apply(this.store.meta)); //????
14111 el = this.el.child('.roo-tpl-' + this.dataName);
14114 for(var i = 0, len = records.length; i < len; i++){
14115 var data = this.prepareData(records[i].data, i, records[i]);
14116 this.fireEvent("preparedata", this, data, i, records[i]);
14118 var d = Roo.apply({}, data);
14121 Roo.apply(d, {'roo-id' : Roo.id()});
14125 Roo.each(this.parent.item, function(item){
14126 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14129 Roo.apply(d, {'roo-data-checked' : 'checked'});
14133 html[html.length] = Roo.util.Format.trim(
14135 t.applySubtemplate(this.dataName, d, this.store.meta) :
14142 el.update(html.join(""));
14143 this.nodes = el.dom.childNodes;
14144 this.updateIndexes(0);
14149 * Function to override to reformat the data that is sent to
14150 * the template for each node.
14151 * DEPRICATED - use the preparedata event handler.
14152 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14153 * a JSON object for an UpdateManager bound view).
14155 prepareData : function(data, index, record)
14157 this.fireEvent("preparedata", this, data, index, record);
14161 onUpdate : function(ds, record){
14162 // Roo.log('on update');
14163 this.clearSelections();
14164 var index = this.store.indexOf(record);
14165 var n = this.nodes[index];
14166 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14167 n.parentNode.removeChild(n);
14168 this.updateIndexes(index, index);
14174 onAdd : function(ds, records, index)
14176 //Roo.log(['on Add', ds, records, index] );
14177 this.clearSelections();
14178 if(this.nodes.length == 0){
14182 var n = this.nodes[index];
14183 for(var i = 0, len = records.length; i < len; i++){
14184 var d = this.prepareData(records[i].data, i, records[i]);
14186 this.tpl.insertBefore(n, d);
14189 this.tpl.append(this.el, d);
14192 this.updateIndexes(index);
14195 onRemove : function(ds, record, index){
14196 // Roo.log('onRemove');
14197 this.clearSelections();
14198 var el = this.dataName ?
14199 this.el.child('.roo-tpl-' + this.dataName) :
14202 el.dom.removeChild(this.nodes[index]);
14203 this.updateIndexes(index);
14207 * Refresh an individual node.
14208 * @param {Number} index
14210 refreshNode : function(index){
14211 this.onUpdate(this.store, this.store.getAt(index));
14214 updateIndexes : function(startIndex, endIndex){
14215 var ns = this.nodes;
14216 startIndex = startIndex || 0;
14217 endIndex = endIndex || ns.length - 1;
14218 for(var i = startIndex; i <= endIndex; i++){
14219 ns[i].nodeIndex = i;
14224 * Changes the data store this view uses and refresh the view.
14225 * @param {Store} store
14227 setStore : function(store, initial){
14228 if(!initial && this.store){
14229 this.store.un("datachanged", this.refresh);
14230 this.store.un("add", this.onAdd);
14231 this.store.un("remove", this.onRemove);
14232 this.store.un("update", this.onUpdate);
14233 this.store.un("clear", this.refresh);
14234 this.store.un("beforeload", this.onBeforeLoad);
14235 this.store.un("load", this.onLoad);
14236 this.store.un("loadexception", this.onLoad);
14240 store.on("datachanged", this.refresh, this);
14241 store.on("add", this.onAdd, this);
14242 store.on("remove", this.onRemove, this);
14243 store.on("update", this.onUpdate, this);
14244 store.on("clear", this.refresh, this);
14245 store.on("beforeload", this.onBeforeLoad, this);
14246 store.on("load", this.onLoad, this);
14247 store.on("loadexception", this.onLoad, this);
14255 * onbeforeLoad - masks the loading area.
14258 onBeforeLoad : function(store,opts)
14260 //Roo.log('onBeforeLoad');
14262 this.el.update("");
14264 this.el.mask(this.mask ? this.mask : "Loading" );
14266 onLoad : function ()
14273 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14274 * @param {HTMLElement} node
14275 * @return {HTMLElement} The template node
14277 findItemFromChild : function(node){
14278 var el = this.dataName ?
14279 this.el.child('.roo-tpl-' + this.dataName,true) :
14282 if(!node || node.parentNode == el){
14285 var p = node.parentNode;
14286 while(p && p != el){
14287 if(p.parentNode == el){
14296 onClick : function(e){
14297 var item = this.findItemFromChild(e.getTarget());
14299 var index = this.indexOf(item);
14300 if(this.onItemClick(item, index, e) !== false){
14301 this.fireEvent("click", this, index, item, e);
14304 this.clearSelections();
14309 onContextMenu : function(e){
14310 var item = this.findItemFromChild(e.getTarget());
14312 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14317 onDblClick : function(e){
14318 var item = this.findItemFromChild(e.getTarget());
14320 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14324 onItemClick : function(item, index, e)
14326 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14329 if (this.toggleSelect) {
14330 var m = this.isSelected(item) ? 'unselect' : 'select';
14333 _t[m](item, true, false);
14336 if(this.multiSelect || this.singleSelect){
14337 if(this.multiSelect && e.shiftKey && this.lastSelection){
14338 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14340 this.select(item, this.multiSelect && e.ctrlKey);
14341 this.lastSelection = item;
14344 if(!this.tickable){
14345 e.preventDefault();
14353 * Get the number of selected nodes.
14356 getSelectionCount : function(){
14357 return this.selections.length;
14361 * Get the currently selected nodes.
14362 * @return {Array} An array of HTMLElements
14364 getSelectedNodes : function(){
14365 return this.selections;
14369 * Get the indexes of the selected nodes.
14372 getSelectedIndexes : function(){
14373 var indexes = [], s = this.selections;
14374 for(var i = 0, len = s.length; i < len; i++){
14375 indexes.push(s[i].nodeIndex);
14381 * Clear all selections
14382 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14384 clearSelections : function(suppressEvent){
14385 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14386 this.cmp.elements = this.selections;
14387 this.cmp.removeClass(this.selectedClass);
14388 this.selections = [];
14389 if(!suppressEvent){
14390 this.fireEvent("selectionchange", this, this.selections);
14396 * Returns true if the passed node is selected
14397 * @param {HTMLElement/Number} node The node or node index
14398 * @return {Boolean}
14400 isSelected : function(node){
14401 var s = this.selections;
14405 node = this.getNode(node);
14406 return s.indexOf(node) !== -1;
14411 * @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
14412 * @param {Boolean} keepExisting (optional) true to keep existing selections
14413 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14415 select : function(nodeInfo, keepExisting, suppressEvent){
14416 if(nodeInfo instanceof Array){
14418 this.clearSelections(true);
14420 for(var i = 0, len = nodeInfo.length; i < len; i++){
14421 this.select(nodeInfo[i], true, true);
14425 var node = this.getNode(nodeInfo);
14426 if(!node || this.isSelected(node)){
14427 return; // already selected.
14430 this.clearSelections(true);
14433 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14434 Roo.fly(node).addClass(this.selectedClass);
14435 this.selections.push(node);
14436 if(!suppressEvent){
14437 this.fireEvent("selectionchange", this, this.selections);
14445 * @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
14446 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14447 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14449 unselect : function(nodeInfo, keepExisting, suppressEvent)
14451 if(nodeInfo instanceof Array){
14452 Roo.each(this.selections, function(s) {
14453 this.unselect(s, nodeInfo);
14457 var node = this.getNode(nodeInfo);
14458 if(!node || !this.isSelected(node)){
14459 //Roo.log("not selected");
14460 return; // not selected.
14464 Roo.each(this.selections, function(s) {
14466 Roo.fly(node).removeClass(this.selectedClass);
14473 this.selections= ns;
14474 this.fireEvent("selectionchange", this, this.selections);
14478 * Gets a template node.
14479 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14480 * @return {HTMLElement} The node or null if it wasn't found
14482 getNode : function(nodeInfo){
14483 if(typeof nodeInfo == "string"){
14484 return document.getElementById(nodeInfo);
14485 }else if(typeof nodeInfo == "number"){
14486 return this.nodes[nodeInfo];
14492 * Gets a range template nodes.
14493 * @param {Number} startIndex
14494 * @param {Number} endIndex
14495 * @return {Array} An array of nodes
14497 getNodes : function(start, end){
14498 var ns = this.nodes;
14499 start = start || 0;
14500 end = typeof end == "undefined" ? ns.length - 1 : end;
14503 for(var i = start; i <= end; i++){
14507 for(var i = start; i >= end; i--){
14515 * Finds the index of the passed node
14516 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14517 * @return {Number} The index of the node or -1
14519 indexOf : function(node){
14520 node = this.getNode(node);
14521 if(typeof node.nodeIndex == "number"){
14522 return node.nodeIndex;
14524 var ns = this.nodes;
14525 for(var i = 0, len = ns.length; i < len; i++){
14536 * based on jquery fullcalendar
14540 Roo.bootstrap = Roo.bootstrap || {};
14542 * @class Roo.bootstrap.Calendar
14543 * @extends Roo.bootstrap.Component
14544 * Bootstrap Calendar class
14545 * @cfg {Boolean} loadMask (true|false) default false
14546 * @cfg {Object} header generate the user specific header of the calendar, default false
14549 * Create a new Container
14550 * @param {Object} config The config object
14555 Roo.bootstrap.Calendar = function(config){
14556 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14560 * Fires when a date is selected
14561 * @param {DatePicker} this
14562 * @param {Date} date The selected date
14566 * @event monthchange
14567 * Fires when the displayed month changes
14568 * @param {DatePicker} this
14569 * @param {Date} date The selected month
14571 'monthchange': true,
14573 * @event evententer
14574 * Fires when mouse over an event
14575 * @param {Calendar} this
14576 * @param {event} Event
14578 'evententer': true,
14580 * @event eventleave
14581 * Fires when the mouse leaves an
14582 * @param {Calendar} this
14585 'eventleave': true,
14587 * @event eventclick
14588 * Fires when the mouse click an
14589 * @param {Calendar} this
14598 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14601 * @cfg {Number} startDay
14602 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14610 getAutoCreate : function(){
14613 var fc_button = function(name, corner, style, content ) {
14614 return Roo.apply({},{
14616 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14618 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14621 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14632 style : 'width:100%',
14639 cls : 'fc-header-left',
14641 fc_button('prev', 'left', 'arrow', '‹' ),
14642 fc_button('next', 'right', 'arrow', '›' ),
14643 { tag: 'span', cls: 'fc-header-space' },
14644 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14652 cls : 'fc-header-center',
14656 cls: 'fc-header-title',
14659 html : 'month / year'
14667 cls : 'fc-header-right',
14669 /* fc_button('month', 'left', '', 'month' ),
14670 fc_button('week', '', '', 'week' ),
14671 fc_button('day', 'right', '', 'day' )
14683 header = this.header;
14686 var cal_heads = function() {
14688 // fixme - handle this.
14690 for (var i =0; i < Date.dayNames.length; i++) {
14691 var d = Date.dayNames[i];
14694 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14695 html : d.substring(0,3)
14699 ret[0].cls += ' fc-first';
14700 ret[6].cls += ' fc-last';
14703 var cal_cell = function(n) {
14706 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14711 cls: 'fc-day-number',
14715 cls: 'fc-day-content',
14719 style: 'position: relative;' // height: 17px;
14731 var cal_rows = function() {
14734 for (var r = 0; r < 6; r++) {
14741 for (var i =0; i < Date.dayNames.length; i++) {
14742 var d = Date.dayNames[i];
14743 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14746 row.cn[0].cls+=' fc-first';
14747 row.cn[0].cn[0].style = 'min-height:90px';
14748 row.cn[6].cls+=' fc-last';
14752 ret[0].cls += ' fc-first';
14753 ret[4].cls += ' fc-prev-last';
14754 ret[5].cls += ' fc-last';
14761 cls: 'fc-border-separate',
14762 style : 'width:100%',
14770 cls : 'fc-first fc-last',
14788 cls : 'fc-content',
14789 style : "position: relative;",
14792 cls : 'fc-view fc-view-month fc-grid',
14793 style : 'position: relative',
14794 unselectable : 'on',
14797 cls : 'fc-event-container',
14798 style : 'position:absolute;z-index:8;top:0;left:0;'
14816 initEvents : function()
14819 throw "can not find store for calendar";
14825 style: "text-align:center",
14829 style: "background-color:white;width:50%;margin:250 auto",
14833 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14844 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14846 var size = this.el.select('.fc-content', true).first().getSize();
14847 this.maskEl.setSize(size.width, size.height);
14848 this.maskEl.enableDisplayMode("block");
14849 if(!this.loadMask){
14850 this.maskEl.hide();
14853 this.store = Roo.factory(this.store, Roo.data);
14854 this.store.on('load', this.onLoad, this);
14855 this.store.on('beforeload', this.onBeforeLoad, this);
14859 this.cells = this.el.select('.fc-day',true);
14860 //Roo.log(this.cells);
14861 this.textNodes = this.el.query('.fc-day-number');
14862 this.cells.addClassOnOver('fc-state-hover');
14864 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14865 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14866 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14867 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14869 this.on('monthchange', this.onMonthChange, this);
14871 this.update(new Date().clearTime());
14874 resize : function() {
14875 var sz = this.el.getSize();
14877 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14878 this.el.select('.fc-day-content div',true).setHeight(34);
14883 showPrevMonth : function(e){
14884 this.update(this.activeDate.add("mo", -1));
14886 showToday : function(e){
14887 this.update(new Date().clearTime());
14890 showNextMonth : function(e){
14891 this.update(this.activeDate.add("mo", 1));
14895 showPrevYear : function(){
14896 this.update(this.activeDate.add("y", -1));
14900 showNextYear : function(){
14901 this.update(this.activeDate.add("y", 1));
14906 update : function(date)
14908 var vd = this.activeDate;
14909 this.activeDate = date;
14910 // if(vd && this.el){
14911 // var t = date.getTime();
14912 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14913 // Roo.log('using add remove');
14915 // this.fireEvent('monthchange', this, date);
14917 // this.cells.removeClass("fc-state-highlight");
14918 // this.cells.each(function(c){
14919 // if(c.dateValue == t){
14920 // c.addClass("fc-state-highlight");
14921 // setTimeout(function(){
14922 // try{c.dom.firstChild.focus();}catch(e){}
14932 var days = date.getDaysInMonth();
14934 var firstOfMonth = date.getFirstDateOfMonth();
14935 var startingPos = firstOfMonth.getDay()-this.startDay;
14937 if(startingPos < this.startDay){
14941 var pm = date.add(Date.MONTH, -1);
14942 var prevStart = pm.getDaysInMonth()-startingPos;
14944 this.cells = this.el.select('.fc-day',true);
14945 this.textNodes = this.el.query('.fc-day-number');
14946 this.cells.addClassOnOver('fc-state-hover');
14948 var cells = this.cells.elements;
14949 var textEls = this.textNodes;
14951 Roo.each(cells, function(cell){
14952 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14955 days += startingPos;
14957 // convert everything to numbers so it's fast
14958 var day = 86400000;
14959 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14962 //Roo.log(prevStart);
14964 var today = new Date().clearTime().getTime();
14965 var sel = date.clearTime().getTime();
14966 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14967 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14968 var ddMatch = this.disabledDatesRE;
14969 var ddText = this.disabledDatesText;
14970 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14971 var ddaysText = this.disabledDaysText;
14972 var format = this.format;
14974 var setCellClass = function(cal, cell){
14978 //Roo.log('set Cell Class');
14980 var t = d.getTime();
14984 cell.dateValue = t;
14986 cell.className += " fc-today";
14987 cell.className += " fc-state-highlight";
14988 cell.title = cal.todayText;
14991 // disable highlight in other month..
14992 //cell.className += " fc-state-highlight";
14997 cell.className = " fc-state-disabled";
14998 cell.title = cal.minText;
15002 cell.className = " fc-state-disabled";
15003 cell.title = cal.maxText;
15007 if(ddays.indexOf(d.getDay()) != -1){
15008 cell.title = ddaysText;
15009 cell.className = " fc-state-disabled";
15012 if(ddMatch && format){
15013 var fvalue = d.dateFormat(format);
15014 if(ddMatch.test(fvalue)){
15015 cell.title = ddText.replace("%0", fvalue);
15016 cell.className = " fc-state-disabled";
15020 if (!cell.initialClassName) {
15021 cell.initialClassName = cell.dom.className;
15024 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15029 for(; i < startingPos; i++) {
15030 textEls[i].innerHTML = (++prevStart);
15031 d.setDate(d.getDate()+1);
15033 cells[i].className = "fc-past fc-other-month";
15034 setCellClass(this, cells[i]);
15039 for(; i < days; i++){
15040 intDay = i - startingPos + 1;
15041 textEls[i].innerHTML = (intDay);
15042 d.setDate(d.getDate()+1);
15044 cells[i].className = ''; // "x-date-active";
15045 setCellClass(this, cells[i]);
15049 for(; i < 42; i++) {
15050 textEls[i].innerHTML = (++extraDays);
15051 d.setDate(d.getDate()+1);
15053 cells[i].className = "fc-future fc-other-month";
15054 setCellClass(this, cells[i]);
15057 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15059 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15061 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15062 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15064 if(totalRows != 6){
15065 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15066 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15069 this.fireEvent('monthchange', this, date);
15073 if(!this.internalRender){
15074 var main = this.el.dom.firstChild;
15075 var w = main.offsetWidth;
15076 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15077 Roo.fly(main).setWidth(w);
15078 this.internalRender = true;
15079 // opera does not respect the auto grow header center column
15080 // then, after it gets a width opera refuses to recalculate
15081 // without a second pass
15082 if(Roo.isOpera && !this.secondPass){
15083 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15084 this.secondPass = true;
15085 this.update.defer(10, this, [date]);
15092 findCell : function(dt) {
15093 dt = dt.clearTime().getTime();
15095 this.cells.each(function(c){
15096 //Roo.log("check " +c.dateValue + '?=' + dt);
15097 if(c.dateValue == dt){
15107 findCells : function(ev) {
15108 var s = ev.start.clone().clearTime().getTime();
15110 var e= ev.end.clone().clearTime().getTime();
15113 this.cells.each(function(c){
15114 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15116 if(c.dateValue > e){
15119 if(c.dateValue < s){
15128 // findBestRow: function(cells)
15132 // for (var i =0 ; i < cells.length;i++) {
15133 // ret = Math.max(cells[i].rows || 0,ret);
15140 addItem : function(ev)
15142 // look for vertical location slot in
15143 var cells = this.findCells(ev);
15145 // ev.row = this.findBestRow(cells);
15147 // work out the location.
15151 for(var i =0; i < cells.length; i++) {
15153 cells[i].row = cells[0].row;
15156 cells[i].row = cells[i].row + 1;
15166 if (crow.start.getY() == cells[i].getY()) {
15168 crow.end = cells[i];
15185 cells[0].events.push(ev);
15187 this.calevents.push(ev);
15190 clearEvents: function() {
15192 if(!this.calevents){
15196 Roo.each(this.cells.elements, function(c){
15202 Roo.each(this.calevents, function(e) {
15203 Roo.each(e.els, function(el) {
15204 el.un('mouseenter' ,this.onEventEnter, this);
15205 el.un('mouseleave' ,this.onEventLeave, this);
15210 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15216 renderEvents: function()
15220 this.cells.each(function(c) {
15229 if(c.row != c.events.length){
15230 r = 4 - (4 - (c.row - c.events.length));
15233 c.events = ev.slice(0, r);
15234 c.more = ev.slice(r);
15236 if(c.more.length && c.more.length == 1){
15237 c.events.push(c.more.pop());
15240 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15244 this.cells.each(function(c) {
15246 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15249 for (var e = 0; e < c.events.length; e++){
15250 var ev = c.events[e];
15251 var rows = ev.rows;
15253 for(var i = 0; i < rows.length; i++) {
15255 // how many rows should it span..
15258 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15259 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15261 unselectable : "on",
15264 cls: 'fc-event-inner',
15268 // cls: 'fc-event-time',
15269 // html : cells.length > 1 ? '' : ev.time
15273 cls: 'fc-event-title',
15274 html : String.format('{0}', ev.title)
15281 cls: 'ui-resizable-handle ui-resizable-e',
15282 html : '  '
15289 cfg.cls += ' fc-event-start';
15291 if ((i+1) == rows.length) {
15292 cfg.cls += ' fc-event-end';
15295 var ctr = _this.el.select('.fc-event-container',true).first();
15296 var cg = ctr.createChild(cfg);
15298 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15299 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15301 var r = (c.more.length) ? 1 : 0;
15302 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15303 cg.setWidth(ebox.right - sbox.x -2);
15305 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15306 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15307 cg.on('click', _this.onEventClick, _this, ev);
15318 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15319 style : 'position: absolute',
15320 unselectable : "on",
15323 cls: 'fc-event-inner',
15327 cls: 'fc-event-title',
15335 cls: 'ui-resizable-handle ui-resizable-e',
15336 html : '  '
15342 var ctr = _this.el.select('.fc-event-container',true).first();
15343 var cg = ctr.createChild(cfg);
15345 var sbox = c.select('.fc-day-content',true).first().getBox();
15346 var ebox = c.select('.fc-day-content',true).first().getBox();
15348 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15349 cg.setWidth(ebox.right - sbox.x -2);
15351 cg.on('click', _this.onMoreEventClick, _this, c.more);
15361 onEventEnter: function (e, el,event,d) {
15362 this.fireEvent('evententer', this, el, event);
15365 onEventLeave: function (e, el,event,d) {
15366 this.fireEvent('eventleave', this, el, event);
15369 onEventClick: function (e, el,event,d) {
15370 this.fireEvent('eventclick', this, el, event);
15373 onMonthChange: function () {
15377 onMoreEventClick: function(e, el, more)
15381 this.calpopover.placement = 'right';
15382 this.calpopover.setTitle('More');
15384 this.calpopover.setContent('');
15386 var ctr = this.calpopover.el.select('.popover-content', true).first();
15388 Roo.each(more, function(m){
15390 cls : 'fc-event-hori fc-event-draggable',
15393 var cg = ctr.createChild(cfg);
15395 cg.on('click', _this.onEventClick, _this, m);
15398 this.calpopover.show(el);
15403 onLoad: function ()
15405 this.calevents = [];
15408 if(this.store.getCount() > 0){
15409 this.store.data.each(function(d){
15412 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15413 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15414 time : d.data.start_time,
15415 title : d.data.title,
15416 description : d.data.description,
15417 venue : d.data.venue
15422 this.renderEvents();
15424 if(this.calevents.length && this.loadMask){
15425 this.maskEl.hide();
15429 onBeforeLoad: function()
15431 this.clearEvents();
15433 this.maskEl.show();
15447 * @class Roo.bootstrap.Popover
15448 * @extends Roo.bootstrap.Component
15449 * Bootstrap Popover class
15450 * @cfg {String} html contents of the popover (or false to use children..)
15451 * @cfg {String} title of popover (or false to hide)
15452 * @cfg {String} placement how it is placed
15453 * @cfg {String} trigger click || hover (or false to trigger manually)
15454 * @cfg {String} over what (parent or false to trigger manually.)
15455 * @cfg {Number} delay - delay before showing
15458 * Create a new Popover
15459 * @param {Object} config The config object
15462 Roo.bootstrap.Popover = function(config){
15463 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15466 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15468 title: 'Fill in a title',
15471 placement : 'right',
15472 trigger : 'hover', // hover
15478 can_build_overlaid : false,
15480 getChildContainer : function()
15482 return this.el.select('.popover-content',true).first();
15485 getAutoCreate : function(){
15486 Roo.log('make popover?');
15488 cls : 'popover roo-dynamic',
15489 style: 'display:block',
15495 cls : 'popover-inner',
15499 cls: 'popover-title',
15503 cls : 'popover-content',
15514 setTitle: function(str)
15517 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15519 setContent: function(str)
15522 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15524 // as it get's added to the bottom of the page.
15525 onRender : function(ct, position)
15527 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15529 var cfg = Roo.apply({}, this.getAutoCreate());
15533 cfg.cls += ' ' + this.cls;
15536 cfg.style = this.style;
15538 Roo.log("adding to ")
15539 this.el = Roo.get(document.body).createChild(cfg, position);
15545 initEvents : function()
15547 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15548 this.el.enableDisplayMode('block');
15550 if (this.over === false) {
15553 if (this.triggers === false) {
15556 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15557 var triggers = this.trigger ? this.trigger.split(' ') : [];
15558 Roo.each(triggers, function(trigger) {
15560 if (trigger == 'click') {
15561 on_el.on('click', this.toggle, this);
15562 } else if (trigger != 'manual') {
15563 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15564 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15566 on_el.on(eventIn ,this.enter, this);
15567 on_el.on(eventOut, this.leave, this);
15578 toggle : function () {
15579 this.hoverState == 'in' ? this.leave() : this.enter();
15582 enter : function () {
15585 clearTimeout(this.timeout);
15587 this.hoverState = 'in';
15589 if (!this.delay || !this.delay.show) {
15594 this.timeout = setTimeout(function () {
15595 if (_t.hoverState == 'in') {
15598 }, this.delay.show)
15600 leave : function() {
15601 clearTimeout(this.timeout);
15603 this.hoverState = 'out';
15605 if (!this.delay || !this.delay.hide) {
15610 this.timeout = setTimeout(function () {
15611 if (_t.hoverState == 'out') {
15614 }, this.delay.hide)
15617 show : function (on_el)
15620 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15623 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15624 if (this.html !== false) {
15625 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15627 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15628 if (!this.title.length) {
15629 this.el.select('.popover-title',true).hide();
15632 var placement = typeof this.placement == 'function' ?
15633 this.placement.call(this, this.el, on_el) :
15636 var autoToken = /\s?auto?\s?/i;
15637 var autoPlace = autoToken.test(placement);
15639 placement = placement.replace(autoToken, '') || 'top';
15643 //this.el.setXY([0,0]);
15645 this.el.dom.style.display='block';
15646 this.el.addClass(placement);
15648 //this.el.appendTo(on_el);
15650 var p = this.getPosition();
15651 var box = this.el.getBox();
15656 var align = Roo.bootstrap.Popover.alignment[placement];
15657 this.el.alignTo(on_el, align[0],align[1]);
15658 //var arrow = this.el.select('.arrow',true).first();
15659 //arrow.set(align[2],
15661 this.el.addClass('in');
15664 if (this.el.hasClass('fade')) {
15671 this.el.setXY([0,0]);
15672 this.el.removeClass('in');
15674 this.hoverState = null;
15680 Roo.bootstrap.Popover.alignment = {
15681 'left' : ['r-l', [-10,0], 'right'],
15682 'right' : ['l-r', [10,0], 'left'],
15683 'bottom' : ['t-b', [0,10], 'top'],
15684 'top' : [ 'b-t', [0,-10], 'bottom']
15695 * @class Roo.bootstrap.Progress
15696 * @extends Roo.bootstrap.Component
15697 * Bootstrap Progress class
15698 * @cfg {Boolean} striped striped of the progress bar
15699 * @cfg {Boolean} active animated of the progress bar
15703 * Create a new Progress
15704 * @param {Object} config The config object
15707 Roo.bootstrap.Progress = function(config){
15708 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15711 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15716 getAutoCreate : function(){
15724 cfg.cls += ' progress-striped';
15728 cfg.cls += ' active';
15747 * @class Roo.bootstrap.ProgressBar
15748 * @extends Roo.bootstrap.Component
15749 * Bootstrap ProgressBar class
15750 * @cfg {Number} aria_valuenow aria-value now
15751 * @cfg {Number} aria_valuemin aria-value min
15752 * @cfg {Number} aria_valuemax aria-value max
15753 * @cfg {String} label label for the progress bar
15754 * @cfg {String} panel (success | info | warning | danger )
15755 * @cfg {String} role role of the progress bar
15756 * @cfg {String} sr_only text
15760 * Create a new ProgressBar
15761 * @param {Object} config The config object
15764 Roo.bootstrap.ProgressBar = function(config){
15765 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15768 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15772 aria_valuemax : 100,
15778 getAutoCreate : function()
15783 cls: 'progress-bar',
15784 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15796 cfg.role = this.role;
15799 if(this.aria_valuenow){
15800 cfg['aria-valuenow'] = this.aria_valuenow;
15803 if(this.aria_valuemin){
15804 cfg['aria-valuemin'] = this.aria_valuemin;
15807 if(this.aria_valuemax){
15808 cfg['aria-valuemax'] = this.aria_valuemax;
15811 if(this.label && !this.sr_only){
15812 cfg.html = this.label;
15816 cfg.cls += ' progress-bar-' + this.panel;
15822 update : function(aria_valuenow)
15824 this.aria_valuenow = aria_valuenow;
15826 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15841 * @class Roo.bootstrap.TabGroup
15842 * @extends Roo.bootstrap.Column
15843 * Bootstrap Column class
15844 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15845 * @cfg {Boolean} carousel true to make the group behave like a carousel
15846 * @cfg {Boolean} bullets show bullets for the panels
15847 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15848 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15849 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15852 * Create a new TabGroup
15853 * @param {Object} config The config object
15856 Roo.bootstrap.TabGroup = function(config){
15857 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15859 this.navId = Roo.id();
15862 Roo.bootstrap.TabGroup.register(this);
15866 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15869 transition : false,
15874 slideOnTouch : false,
15876 getAutoCreate : function()
15878 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15880 cfg.cls += ' tab-content';
15882 Roo.log('get auto create...............');
15884 if (this.carousel) {
15885 cfg.cls += ' carousel slide';
15888 cls : 'carousel-inner'
15891 if(this.bullets && !Roo.isTouch){
15894 cls : 'carousel-bullets',
15898 if(this.bullets_cls){
15899 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15902 for (var i = 0; i < this.bullets; i++){
15904 cls : 'bullet bullet-' + i
15912 cfg.cn[0].cn = bullets;
15919 initEvents: function()
15921 Roo.log('-------- init events on tab group ---------');
15927 if(Roo.isTouch && this.slideOnTouch){
15928 this.el.on("touchstart", this.onTouchStart, this);
15931 if(this.autoslide){
15934 this.slideFn = window.setInterval(function() {
15935 _this.showPanelNext();
15941 onTouchStart : function(e, el, o)
15943 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15947 this.showPanelNext();
15950 getChildContainer : function()
15952 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15956 * register a Navigation item
15957 * @param {Roo.bootstrap.NavItem} the navitem to add
15959 register : function(item)
15961 this.tabs.push( item);
15962 item.navId = this.navId; // not really needed..
15967 getActivePanel : function()
15970 Roo.each(this.tabs, function(t) {
15980 getPanelByName : function(n)
15983 Roo.each(this.tabs, function(t) {
15984 if (t.tabId == n) {
15992 indexOfPanel : function(p)
15995 Roo.each(this.tabs, function(t,i) {
15996 if (t.tabId == p.tabId) {
16005 * show a specific panel
16006 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16007 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16009 showPanel : function (pan)
16011 if(this.transition){
16012 Roo.log("waiting for the transitionend");
16016 if (typeof(pan) == 'number') {
16017 pan = this.tabs[pan];
16019 if (typeof(pan) == 'string') {
16020 pan = this.getPanelByName(pan);
16022 if (pan.tabId == this.getActivePanel().tabId) {
16025 var cur = this.getActivePanel();
16027 if (false === cur.fireEvent('beforedeactivate')) {
16031 if(this.bullets > 0 && !Roo.isTouch){
16032 this.setActiveBullet(this.indexOfPanel(pan));
16035 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16037 this.transition = true;
16038 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16039 var lr = dir == 'next' ? 'left' : 'right';
16040 pan.el.addClass(dir); // or prev
16041 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16042 cur.el.addClass(lr); // or right
16043 pan.el.addClass(lr);
16046 cur.el.on('transitionend', function() {
16047 Roo.log("trans end?");
16049 pan.el.removeClass([lr,dir]);
16050 pan.setActive(true);
16052 cur.el.removeClass([lr]);
16053 cur.setActive(false);
16055 _this.transition = false;
16057 }, this, { single: true } );
16062 cur.setActive(false);
16063 pan.setActive(true);
16068 showPanelNext : function()
16070 var i = this.indexOfPanel(this.getActivePanel());
16072 if (i >= this.tabs.length - 1 && !this.autoslide) {
16076 if (i >= this.tabs.length - 1 && this.autoslide) {
16080 this.showPanel(this.tabs[i+1]);
16083 showPanelPrev : function()
16085 var i = this.indexOfPanel(this.getActivePanel());
16087 if (i < 1 && !this.autoslide) {
16091 if (i < 1 && this.autoslide) {
16092 i = this.tabs.length;
16095 this.showPanel(this.tabs[i-1]);
16099 addBullet: function()
16101 if(!this.bullets || Roo.isTouch){
16104 var ctr = this.el.select('.carousel-bullets',true).first();
16105 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16106 var bullet = ctr.createChild({
16107 cls : 'bullet bullet-' + i
16108 },ctr.dom.lastChild);
16110 bullet.on('click', (function(e, el, o, ii, t){
16112 e.preventDefault();
16114 this.showPanel(ii);
16116 if(this.autoslide && this.slideFn){
16117 clearInterval(this.slideFn);
16118 this.slideFn = window.setInterval(function() {
16119 this.showPanelNext();
16123 }).createDelegate(this, [i, bullet], true));
16128 setActiveBullet : function(i)
16134 Roo.each(this.el.select('.bullet', true).elements, function(el){
16135 el.removeClass('selected');
16138 var bullet = this.el.select('.bullet-' + i, true).first();
16144 bullet.addClass('selected');
16155 Roo.apply(Roo.bootstrap.TabGroup, {
16159 * register a Navigation Group
16160 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16162 register : function(navgrp)
16164 this.groups[navgrp.navId] = navgrp;
16168 * fetch a Navigation Group based on the navigation ID
16169 * if one does not exist , it will get created.
16170 * @param {string} the navgroup to add
16171 * @returns {Roo.bootstrap.NavGroup} the navgroup
16173 get: function(navId) {
16174 if (typeof(this.groups[navId]) == 'undefined') {
16175 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16177 return this.groups[navId] ;
16192 * @class Roo.bootstrap.TabPanel
16193 * @extends Roo.bootstrap.Component
16194 * Bootstrap TabPanel class
16195 * @cfg {Boolean} active panel active
16196 * @cfg {String} html panel content
16197 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16198 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16202 * Create a new TabPanel
16203 * @param {Object} config The config object
16206 Roo.bootstrap.TabPanel = function(config){
16207 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16211 * Fires when the active status changes
16212 * @param {Roo.bootstrap.TabPanel} this
16213 * @param {Boolean} state the new state
16218 * @event beforedeactivate
16219 * Fires before a tab is de-activated - can be used to do validation on a form.
16220 * @param {Roo.bootstrap.TabPanel} this
16221 * @return {Boolean} false if there is an error
16224 'beforedeactivate': true
16227 this.tabId = this.tabId || Roo.id();
16231 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16238 getAutoCreate : function(){
16241 // item is needed for carousel - not sure if it has any effect otherwise
16242 cls: 'tab-pane item',
16243 html: this.html || ''
16247 cfg.cls += ' active';
16251 cfg.tabId = this.tabId;
16258 initEvents: function()
16260 Roo.log('-------- init events on tab panel ---------');
16262 var p = this.parent();
16263 this.navId = this.navId || p.navId;
16265 if (typeof(this.navId) != 'undefined') {
16266 // not really needed.. but just in case.. parent should be a NavGroup.
16267 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16268 Roo.log(['register', tg, this]);
16271 var i = tg.tabs.length - 1;
16273 if(this.active && tg.bullets > 0 && i < tg.bullets){
16274 tg.setActiveBullet(i);
16281 onRender : function(ct, position)
16283 // Roo.log("Call onRender: " + this.xtype);
16285 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16293 setActive: function(state)
16295 Roo.log("panel - set active " + this.tabId + "=" + state);
16297 this.active = state;
16299 this.el.removeClass('active');
16301 } else if (!this.el.hasClass('active')) {
16302 this.el.addClass('active');
16305 this.fireEvent('changed', this, state);
16322 * @class Roo.bootstrap.DateField
16323 * @extends Roo.bootstrap.Input
16324 * Bootstrap DateField class
16325 * @cfg {Number} weekStart default 0
16326 * @cfg {String} viewMode default empty, (months|years)
16327 * @cfg {String} minViewMode default empty, (months|years)
16328 * @cfg {Number} startDate default -Infinity
16329 * @cfg {Number} endDate default Infinity
16330 * @cfg {Boolean} todayHighlight default false
16331 * @cfg {Boolean} todayBtn default false
16332 * @cfg {Boolean} calendarWeeks default false
16333 * @cfg {Object} daysOfWeekDisabled default empty
16334 * @cfg {Boolean} singleMode default false (true | false)
16336 * @cfg {Boolean} keyboardNavigation default true
16337 * @cfg {String} language default en
16340 * Create a new DateField
16341 * @param {Object} config The config object
16344 Roo.bootstrap.DateField = function(config){
16345 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16349 * Fires when this field show.
16350 * @param {Roo.bootstrap.DateField} this
16351 * @param {Mixed} date The date value
16356 * Fires when this field hide.
16357 * @param {Roo.bootstrap.DateField} this
16358 * @param {Mixed} date The date value
16363 * Fires when select a date.
16364 * @param {Roo.bootstrap.DateField} this
16365 * @param {Mixed} date The date value
16369 * @event beforeselect
16370 * Fires when before select a date.
16371 * @param {Roo.bootstrap.DateField} this
16372 * @param {Mixed} date The date value
16374 beforeselect : true
16378 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16381 * @cfg {String} format
16382 * The default date format string which can be overriden for localization support. The format must be
16383 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16387 * @cfg {String} altFormats
16388 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16389 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16391 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16399 todayHighlight : false,
16405 keyboardNavigation: true,
16407 calendarWeeks: false,
16409 startDate: -Infinity,
16413 daysOfWeekDisabled: [],
16417 singleMode : false,
16419 UTCDate: function()
16421 return new Date(Date.UTC.apply(Date, arguments));
16424 UTCToday: function()
16426 var today = new Date();
16427 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16430 getDate: function() {
16431 var d = this.getUTCDate();
16432 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16435 getUTCDate: function() {
16439 setDate: function(d) {
16440 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16443 setUTCDate: function(d) {
16445 this.setValue(this.formatDate(this.date));
16448 onRender: function(ct, position)
16451 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16453 this.language = this.language || 'en';
16454 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16455 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16457 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16458 this.format = this.format || 'm/d/y';
16459 this.isInline = false;
16460 this.isInput = true;
16461 this.component = this.el.select('.add-on', true).first() || false;
16462 this.component = (this.component && this.component.length === 0) ? false : this.component;
16463 this.hasInput = this.component && this.inputEL().length;
16465 if (typeof(this.minViewMode === 'string')) {
16466 switch (this.minViewMode) {
16468 this.minViewMode = 1;
16471 this.minViewMode = 2;
16474 this.minViewMode = 0;
16479 if (typeof(this.viewMode === 'string')) {
16480 switch (this.viewMode) {
16493 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16495 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16497 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16499 this.picker().on('mousedown', this.onMousedown, this);
16500 this.picker().on('click', this.onClick, this);
16502 this.picker().addClass('datepicker-dropdown');
16504 this.startViewMode = this.viewMode;
16506 if(this.singleMode){
16507 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16508 v.setVisibilityMode(Roo.Element.DISPLAY)
16512 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16513 v.setStyle('width', '189px');
16517 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16518 if(!this.calendarWeeks){
16523 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16524 v.attr('colspan', function(i, val){
16525 return parseInt(val) + 1;
16530 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16532 this.setStartDate(this.startDate);
16533 this.setEndDate(this.endDate);
16535 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16542 if(this.isInline) {
16547 picker : function()
16549 return this.pickerEl;
16550 // return this.el.select('.datepicker', true).first();
16553 fillDow: function()
16555 var dowCnt = this.weekStart;
16564 if(this.calendarWeeks){
16572 while (dowCnt < this.weekStart + 7) {
16576 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16580 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16583 fillMonths: function()
16586 var months = this.picker().select('>.datepicker-months td', true).first();
16588 months.dom.innerHTML = '';
16594 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16597 months.createChild(month);
16604 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;
16606 if (this.date < this.startDate) {
16607 this.viewDate = new Date(this.startDate);
16608 } else if (this.date > this.endDate) {
16609 this.viewDate = new Date(this.endDate);
16611 this.viewDate = new Date(this.date);
16619 var d = new Date(this.viewDate),
16620 year = d.getUTCFullYear(),
16621 month = d.getUTCMonth(),
16622 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16623 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16624 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16625 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16626 currentDate = this.date && this.date.valueOf(),
16627 today = this.UTCToday();
16629 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16631 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16633 // this.picker.select('>tfoot th.today').
16634 // .text(dates[this.language].today)
16635 // .toggle(this.todayBtn !== false);
16637 this.updateNavArrows();
16640 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16642 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16644 prevMonth.setUTCDate(day);
16646 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16648 var nextMonth = new Date(prevMonth);
16650 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16652 nextMonth = nextMonth.valueOf();
16654 var fillMonths = false;
16656 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16658 while(prevMonth.valueOf() < nextMonth) {
16661 if (prevMonth.getUTCDay() === this.weekStart) {
16663 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16671 if(this.calendarWeeks){
16672 // ISO 8601: First week contains first thursday.
16673 // ISO also states week starts on Monday, but we can be more abstract here.
16675 // Start of current week: based on weekstart/current date
16676 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16677 // Thursday of this week
16678 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16679 // First Thursday of year, year from thursday
16680 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16681 // Calendar week: ms between thursdays, div ms per day, div 7 days
16682 calWeek = (th - yth) / 864e5 / 7 + 1;
16684 fillMonths.cn.push({
16692 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16694 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16697 if (this.todayHighlight &&
16698 prevMonth.getUTCFullYear() == today.getFullYear() &&
16699 prevMonth.getUTCMonth() == today.getMonth() &&
16700 prevMonth.getUTCDate() == today.getDate()) {
16701 clsName += ' today';
16704 if (currentDate && prevMonth.valueOf() === currentDate) {
16705 clsName += ' active';
16708 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16709 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16710 clsName += ' disabled';
16713 fillMonths.cn.push({
16715 cls: 'day ' + clsName,
16716 html: prevMonth.getDate()
16719 prevMonth.setDate(prevMonth.getDate()+1);
16722 var currentYear = this.date && this.date.getUTCFullYear();
16723 var currentMonth = this.date && this.date.getUTCMonth();
16725 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16727 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16728 v.removeClass('active');
16730 if(currentYear === year && k === currentMonth){
16731 v.addClass('active');
16734 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16735 v.addClass('disabled');
16741 year = parseInt(year/10, 10) * 10;
16743 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16745 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16748 for (var i = -1; i < 11; i++) {
16749 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16751 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16759 showMode: function(dir)
16762 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16765 Roo.each(this.picker().select('>div',true).elements, function(v){
16766 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16769 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16774 if(this.isInline) return;
16776 this.picker().removeClass(['bottom', 'top']);
16778 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16780 * place to the top of element!
16784 this.picker().addClass('top');
16785 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16790 this.picker().addClass('bottom');
16792 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16795 parseDate : function(value)
16797 if(!value || value instanceof Date){
16800 var v = Date.parseDate(value, this.format);
16801 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16802 v = Date.parseDate(value, 'Y-m-d');
16804 if(!v && this.altFormats){
16805 if(!this.altFormatsArray){
16806 this.altFormatsArray = this.altFormats.split("|");
16808 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16809 v = Date.parseDate(value, this.altFormatsArray[i]);
16815 formatDate : function(date, fmt)
16817 return (!date || !(date instanceof Date)) ?
16818 date : date.dateFormat(fmt || this.format);
16821 onFocus : function()
16823 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16827 onBlur : function()
16829 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16831 var d = this.inputEl().getValue();
16840 this.picker().show();
16844 this.fireEvent('show', this, this.date);
16849 if(this.isInline) return;
16850 this.picker().hide();
16851 this.viewMode = this.startViewMode;
16854 this.fireEvent('hide', this, this.date);
16858 onMousedown: function(e)
16860 e.stopPropagation();
16861 e.preventDefault();
16866 Roo.bootstrap.DateField.superclass.keyup.call(this);
16870 setValue: function(v)
16872 if(this.fireEvent('beforeselect', this, v) !== false){
16873 var d = new Date(this.parseDate(v) ).clearTime();
16875 if(isNaN(d.getTime())){
16876 this.date = this.viewDate = '';
16877 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16881 v = this.formatDate(d);
16883 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16885 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16889 this.fireEvent('select', this, this.date);
16893 getValue: function()
16895 return this.formatDate(this.date);
16898 fireKey: function(e)
16900 if (!this.picker().isVisible()){
16901 if (e.keyCode == 27) // allow escape to hide and re-show picker
16906 var dateChanged = false,
16908 newDate, newViewDate;
16913 e.preventDefault();
16917 if (!this.keyboardNavigation) break;
16918 dir = e.keyCode == 37 ? -1 : 1;
16921 newDate = this.moveYear(this.date, dir);
16922 newViewDate = this.moveYear(this.viewDate, dir);
16923 } else if (e.shiftKey){
16924 newDate = this.moveMonth(this.date, dir);
16925 newViewDate = this.moveMonth(this.viewDate, dir);
16927 newDate = new Date(this.date);
16928 newDate.setUTCDate(this.date.getUTCDate() + dir);
16929 newViewDate = new Date(this.viewDate);
16930 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16932 if (this.dateWithinRange(newDate)){
16933 this.date = newDate;
16934 this.viewDate = newViewDate;
16935 this.setValue(this.formatDate(this.date));
16937 e.preventDefault();
16938 dateChanged = true;
16943 if (!this.keyboardNavigation) break;
16944 dir = e.keyCode == 38 ? -1 : 1;
16946 newDate = this.moveYear(this.date, dir);
16947 newViewDate = this.moveYear(this.viewDate, dir);
16948 } else if (e.shiftKey){
16949 newDate = this.moveMonth(this.date, dir);
16950 newViewDate = this.moveMonth(this.viewDate, dir);
16952 newDate = new Date(this.date);
16953 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16954 newViewDate = new Date(this.viewDate);
16955 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16957 if (this.dateWithinRange(newDate)){
16958 this.date = newDate;
16959 this.viewDate = newViewDate;
16960 this.setValue(this.formatDate(this.date));
16962 e.preventDefault();
16963 dateChanged = true;
16967 this.setValue(this.formatDate(this.date));
16969 e.preventDefault();
16972 this.setValue(this.formatDate(this.date));
16986 onClick: function(e)
16988 e.stopPropagation();
16989 e.preventDefault();
16991 var target = e.getTarget();
16993 if(target.nodeName.toLowerCase() === 'i'){
16994 target = Roo.get(target).dom.parentNode;
16997 var nodeName = target.nodeName;
16998 var className = target.className;
16999 var html = target.innerHTML;
17000 //Roo.log(nodeName);
17002 switch(nodeName.toLowerCase()) {
17004 switch(className) {
17010 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17011 switch(this.viewMode){
17013 this.viewDate = this.moveMonth(this.viewDate, dir);
17017 this.viewDate = this.moveYear(this.viewDate, dir);
17023 var date = new Date();
17024 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17026 this.setValue(this.formatDate(this.date));
17033 if (className.indexOf('disabled') < 0) {
17034 this.viewDate.setUTCDate(1);
17035 if (className.indexOf('month') > -1) {
17036 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17038 var year = parseInt(html, 10) || 0;
17039 this.viewDate.setUTCFullYear(year);
17043 if(this.singleMode){
17044 this.setValue(this.formatDate(this.viewDate));
17055 //Roo.log(className);
17056 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17057 var day = parseInt(html, 10) || 1;
17058 var year = this.viewDate.getUTCFullYear(),
17059 month = this.viewDate.getUTCMonth();
17061 if (className.indexOf('old') > -1) {
17068 } else if (className.indexOf('new') > -1) {
17076 //Roo.log([year,month,day]);
17077 this.date = this.UTCDate(year, month, day,0,0,0,0);
17078 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17080 //Roo.log(this.formatDate(this.date));
17081 this.setValue(this.formatDate(this.date));
17088 setStartDate: function(startDate)
17090 this.startDate = startDate || -Infinity;
17091 if (this.startDate !== -Infinity) {
17092 this.startDate = this.parseDate(this.startDate);
17095 this.updateNavArrows();
17098 setEndDate: function(endDate)
17100 this.endDate = endDate || Infinity;
17101 if (this.endDate !== Infinity) {
17102 this.endDate = this.parseDate(this.endDate);
17105 this.updateNavArrows();
17108 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17110 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17111 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17112 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17114 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17115 return parseInt(d, 10);
17118 this.updateNavArrows();
17121 updateNavArrows: function()
17123 if(this.singleMode){
17127 var d = new Date(this.viewDate),
17128 year = d.getUTCFullYear(),
17129 month = d.getUTCMonth();
17131 Roo.each(this.picker().select('.prev', true).elements, function(v){
17133 switch (this.viewMode) {
17136 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17142 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17149 Roo.each(this.picker().select('.next', true).elements, function(v){
17151 switch (this.viewMode) {
17154 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17160 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17168 moveMonth: function(date, dir)
17170 if (!dir) return date;
17171 var new_date = new Date(date.valueOf()),
17172 day = new_date.getUTCDate(),
17173 month = new_date.getUTCMonth(),
17174 mag = Math.abs(dir),
17176 dir = dir > 0 ? 1 : -1;
17179 // If going back one month, make sure month is not current month
17180 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17182 return new_date.getUTCMonth() == month;
17184 // If going forward one month, make sure month is as expected
17185 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17187 return new_date.getUTCMonth() != new_month;
17189 new_month = month + dir;
17190 new_date.setUTCMonth(new_month);
17191 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17192 if (new_month < 0 || new_month > 11)
17193 new_month = (new_month + 12) % 12;
17195 // For magnitudes >1, move one month at a time...
17196 for (var i=0; i<mag; i++)
17197 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17198 new_date = this.moveMonth(new_date, dir);
17199 // ...then reset the day, keeping it in the new month
17200 new_month = new_date.getUTCMonth();
17201 new_date.setUTCDate(day);
17203 return new_month != new_date.getUTCMonth();
17206 // Common date-resetting loop -- if date is beyond end of month, make it
17209 new_date.setUTCDate(--day);
17210 new_date.setUTCMonth(new_month);
17215 moveYear: function(date, dir)
17217 return this.moveMonth(date, dir*12);
17220 dateWithinRange: function(date)
17222 return date >= this.startDate && date <= this.endDate;
17228 this.picker().remove();
17233 Roo.apply(Roo.bootstrap.DateField, {
17244 html: '<i class="fa fa-arrow-left"/>'
17254 html: '<i class="fa fa-arrow-right"/>'
17296 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17297 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17298 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17299 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17300 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17313 navFnc: 'FullYear',
17318 navFnc: 'FullYear',
17323 Roo.apply(Roo.bootstrap.DateField, {
17327 cls: 'datepicker dropdown-menu roo-dynamic',
17331 cls: 'datepicker-days',
17335 cls: 'table-condensed',
17337 Roo.bootstrap.DateField.head,
17341 Roo.bootstrap.DateField.footer
17348 cls: 'datepicker-months',
17352 cls: 'table-condensed',
17354 Roo.bootstrap.DateField.head,
17355 Roo.bootstrap.DateField.content,
17356 Roo.bootstrap.DateField.footer
17363 cls: 'datepicker-years',
17367 cls: 'table-condensed',
17369 Roo.bootstrap.DateField.head,
17370 Roo.bootstrap.DateField.content,
17371 Roo.bootstrap.DateField.footer
17390 * @class Roo.bootstrap.TimeField
17391 * @extends Roo.bootstrap.Input
17392 * Bootstrap DateField class
17396 * Create a new TimeField
17397 * @param {Object} config The config object
17400 Roo.bootstrap.TimeField = function(config){
17401 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17405 * Fires when this field show.
17406 * @param {Roo.bootstrap.DateField} thisthis
17407 * @param {Mixed} date The date value
17412 * Fires when this field hide.
17413 * @param {Roo.bootstrap.DateField} this
17414 * @param {Mixed} date The date value
17419 * Fires when select a date.
17420 * @param {Roo.bootstrap.DateField} this
17421 * @param {Mixed} date The date value
17427 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17430 * @cfg {String} format
17431 * The default time format string which can be overriden for localization support. The format must be
17432 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17436 onRender: function(ct, position)
17439 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17441 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17443 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17445 this.pop = this.picker().select('>.datepicker-time',true).first();
17446 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17448 this.picker().on('mousedown', this.onMousedown, this);
17449 this.picker().on('click', this.onClick, this);
17451 this.picker().addClass('datepicker-dropdown');
17456 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17457 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17458 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17459 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17460 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17461 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17465 fireKey: function(e){
17466 if (!this.picker().isVisible()){
17467 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17473 e.preventDefault();
17481 this.onTogglePeriod();
17484 this.onIncrementMinutes();
17487 this.onDecrementMinutes();
17496 onClick: function(e) {
17497 e.stopPropagation();
17498 e.preventDefault();
17501 picker : function()
17503 return this.el.select('.datepicker', true).first();
17506 fillTime: function()
17508 var time = this.pop.select('tbody', true).first();
17510 time.dom.innerHTML = '';
17525 cls: 'hours-up glyphicon glyphicon-chevron-up'
17545 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17566 cls: 'timepicker-hour',
17581 cls: 'timepicker-minute',
17596 cls: 'btn btn-primary period',
17618 cls: 'hours-down glyphicon glyphicon-chevron-down'
17638 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17656 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17663 var hours = this.time.getHours();
17664 var minutes = this.time.getMinutes();
17677 hours = hours - 12;
17681 hours = '0' + hours;
17685 minutes = '0' + minutes;
17688 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17689 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17690 this.pop.select('button', true).first().dom.innerHTML = period;
17696 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17698 var cls = ['bottom'];
17700 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17707 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17712 this.picker().addClass(cls.join('-'));
17716 Roo.each(cls, function(c){
17718 _this.picker().setTop(_this.inputEl().getHeight());
17722 _this.picker().setTop(0 - _this.picker().getHeight());
17727 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17731 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17738 onFocus : function()
17740 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17744 onBlur : function()
17746 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17752 this.picker().show();
17757 this.fireEvent('show', this, this.date);
17762 this.picker().hide();
17765 this.fireEvent('hide', this, this.date);
17768 setTime : function()
17771 this.setValue(this.time.format(this.format));
17773 this.fireEvent('select', this, this.date);
17778 onMousedown: function(e){
17779 e.stopPropagation();
17780 e.preventDefault();
17783 onIncrementHours: function()
17785 Roo.log('onIncrementHours');
17786 this.time = this.time.add(Date.HOUR, 1);
17791 onDecrementHours: function()
17793 Roo.log('onDecrementHours');
17794 this.time = this.time.add(Date.HOUR, -1);
17798 onIncrementMinutes: function()
17800 Roo.log('onIncrementMinutes');
17801 this.time = this.time.add(Date.MINUTE, 1);
17805 onDecrementMinutes: function()
17807 Roo.log('onDecrementMinutes');
17808 this.time = this.time.add(Date.MINUTE, -1);
17812 onTogglePeriod: function()
17814 Roo.log('onTogglePeriod');
17815 this.time = this.time.add(Date.HOUR, 12);
17822 Roo.apply(Roo.bootstrap.TimeField, {
17852 cls: 'btn btn-info ok',
17864 Roo.apply(Roo.bootstrap.TimeField, {
17868 cls: 'datepicker dropdown-menu',
17872 cls: 'datepicker-time',
17876 cls: 'table-condensed',
17878 Roo.bootstrap.TimeField.content,
17879 Roo.bootstrap.TimeField.footer
17898 * @class Roo.bootstrap.MonthField
17899 * @extends Roo.bootstrap.Input
17900 * Bootstrap MonthField class
17902 * @cfg {String} language default en
17905 * Create a new MonthField
17906 * @param {Object} config The config object
17909 Roo.bootstrap.MonthField = function(config){
17910 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17915 * Fires when this field show.
17916 * @param {Roo.bootstrap.MonthField} this
17917 * @param {Mixed} date The date value
17922 * Fires when this field hide.
17923 * @param {Roo.bootstrap.MonthField} this
17924 * @param {Mixed} date The date value
17929 * Fires when select a date.
17930 * @param {Roo.bootstrap.MonthField} this
17931 * @param {String} oldvalue The old value
17932 * @param {String} newvalue The new value
17938 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17940 onRender: function(ct, position)
17943 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17945 this.language = this.language || 'en';
17946 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17947 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17949 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17950 this.isInline = false;
17951 this.isInput = true;
17952 this.component = this.el.select('.add-on', true).first() || false;
17953 this.component = (this.component && this.component.length === 0) ? false : this.component;
17954 this.hasInput = this.component && this.inputEL().length;
17956 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17958 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17960 this.picker().on('mousedown', this.onMousedown, this);
17961 this.picker().on('click', this.onClick, this);
17963 this.picker().addClass('datepicker-dropdown');
17965 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17966 v.setStyle('width', '189px');
17973 if(this.isInline) {
17979 setValue: function(v, suppressEvent)
17981 var o = this.getValue();
17983 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17987 if(suppressEvent !== true){
17988 this.fireEvent('select', this, o, v);
17993 getValue: function()
17998 onClick: function(e)
18000 e.stopPropagation();
18001 e.preventDefault();
18003 var target = e.getTarget();
18005 if(target.nodeName.toLowerCase() === 'i'){
18006 target = Roo.get(target).dom.parentNode;
18009 var nodeName = target.nodeName;
18010 var className = target.className;
18011 var html = target.innerHTML;
18013 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18017 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18019 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18025 picker : function()
18027 return this.pickerEl;
18030 fillMonths: function()
18033 var months = this.picker().select('>.datepicker-months td', true).first();
18035 months.dom.innerHTML = '';
18041 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18044 months.createChild(month);
18053 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18054 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18057 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18058 e.removeClass('active');
18060 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18061 e.addClass('active');
18068 if(this.isInline) return;
18070 this.picker().removeClass(['bottom', 'top']);
18072 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18074 * place to the top of element!
18078 this.picker().addClass('top');
18079 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18084 this.picker().addClass('bottom');
18086 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18089 onFocus : function()
18091 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18095 onBlur : function()
18097 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18099 var d = this.inputEl().getValue();
18108 this.picker().show();
18109 this.picker().select('>.datepicker-months', true).first().show();
18113 this.fireEvent('show', this, this.date);
18118 if(this.isInline) return;
18119 this.picker().hide();
18120 this.fireEvent('hide', this, this.date);
18124 onMousedown: function(e)
18126 e.stopPropagation();
18127 e.preventDefault();
18132 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18136 fireKey: function(e)
18138 if (!this.picker().isVisible()){
18139 if (e.keyCode == 27) // allow escape to hide and re-show picker
18149 e.preventDefault();
18153 dir = e.keyCode == 37 ? -1 : 1;
18155 this.vIndex = this.vIndex + dir;
18157 if(this.vIndex < 0){
18161 if(this.vIndex > 11){
18165 if(isNaN(this.vIndex)){
18169 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18175 dir = e.keyCode == 38 ? -1 : 1;
18177 this.vIndex = this.vIndex + dir * 4;
18179 if(this.vIndex < 0){
18183 if(this.vIndex > 11){
18187 if(isNaN(this.vIndex)){
18191 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18196 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18197 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18201 e.preventDefault();
18204 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18205 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18221 this.picker().remove();
18226 Roo.apply(Roo.bootstrap.MonthField, {
18245 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18246 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18251 Roo.apply(Roo.bootstrap.MonthField, {
18255 cls: 'datepicker dropdown-menu roo-dynamic',
18259 cls: 'datepicker-months',
18263 cls: 'table-condensed',
18265 Roo.bootstrap.DateField.content
18285 * @class Roo.bootstrap.CheckBox
18286 * @extends Roo.bootstrap.Input
18287 * Bootstrap CheckBox class
18289 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18290 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18291 * @cfg {String} boxLabel The text that appears beside the checkbox
18292 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18293 * @cfg {Boolean} checked initnal the element
18294 * @cfg {Boolean} inline inline the element (default false)
18295 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18298 * Create a new CheckBox
18299 * @param {Object} config The config object
18302 Roo.bootstrap.CheckBox = function(config){
18303 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18308 * Fires when the element is checked or unchecked.
18309 * @param {Roo.bootstrap.CheckBox} this This input
18310 * @param {Boolean} checked The new checked value
18317 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18319 inputType: 'checkbox',
18327 getAutoCreate : function()
18329 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18335 cfg.cls = 'form-group ' + this.inputType; //input-group
18338 cfg.cls += ' ' + this.inputType + '-inline';
18344 type : this.inputType,
18345 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18346 cls : 'roo-' + this.inputType, //'form-box',
18347 placeholder : this.placeholder || ''
18351 if (this.weight) { // Validity check?
18352 cfg.cls += " " + this.inputType + "-" + this.weight;
18355 if (this.disabled) {
18356 input.disabled=true;
18360 input.checked = this.checked;
18364 input.name = this.name;
18368 input.cls += ' input-' + this.size;
18373 ['xs','sm','md','lg'].map(function(size){
18374 if (settings[size]) {
18375 cfg.cls += ' col-' + size + '-' + settings[size];
18379 var inputblock = input;
18381 if (this.before || this.after) {
18384 cls : 'input-group',
18389 inputblock.cn.push({
18391 cls : 'input-group-addon',
18396 inputblock.cn.push(input);
18399 inputblock.cn.push({
18401 cls : 'input-group-addon',
18408 if (align ==='left' && this.fieldLabel.length) {
18409 Roo.log("left and has label");
18415 cls : 'control-label col-md-' + this.labelWidth,
18416 html : this.fieldLabel
18420 cls : "col-md-" + (12 - this.labelWidth),
18427 } else if ( this.fieldLabel.length) {
18432 tag: this.boxLabel ? 'span' : 'label',
18434 cls: 'control-label box-input-label',
18435 //cls : 'input-group-addon',
18436 html : this.fieldLabel
18446 Roo.log(" no label && no align");
18447 cfg.cn = [ inputblock ] ;
18452 var boxLabelCfg = {
18454 //'for': id, // box label is handled by onclick - so no for...
18456 html: this.boxLabel
18460 boxLabelCfg.tooltip = this.tooltip;
18463 cfg.cn.push(boxLabelCfg);
18473 * return the real input element.
18475 inputEl: function ()
18477 return this.el.select('input.roo-' + this.inputType,true).first();
18480 labelEl: function()
18482 return this.el.select('label.control-label',true).first();
18484 /* depricated... */
18488 return this.labelEl();
18491 initEvents : function()
18493 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18495 this.inputEl().on('click', this.onClick, this);
18497 if (this.boxLabel) {
18498 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18501 this.startValue = this.getValue();
18504 Roo.bootstrap.CheckBox.register(this);
18508 onClick : function()
18510 this.setChecked(!this.checked);
18513 setChecked : function(state,suppressEvent)
18515 this.startValue = this.getValue();
18517 if(this.inputType == 'radio'){
18519 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18520 e.dom.checked = false;
18523 this.inputEl().dom.checked = true;
18525 this.inputEl().dom.value = this.inputValue;
18527 if(suppressEvent !== true){
18528 this.fireEvent('check', this, true);
18536 this.checked = state;
18538 this.inputEl().dom.checked = state;
18540 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18542 if(suppressEvent !== true){
18543 this.fireEvent('check', this, state);
18549 getValue : function()
18551 if(this.inputType == 'radio'){
18552 return this.getGroupValue();
18555 return this.inputEl().getValue();
18559 getGroupValue : function()
18561 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18565 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18568 setValue : function(v,suppressEvent)
18570 if(this.inputType == 'radio'){
18571 this.setGroupValue(v, suppressEvent);
18575 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18580 setGroupValue : function(v, suppressEvent)
18582 this.startValue = this.getValue();
18584 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18585 e.dom.checked = false;
18587 if(e.dom.value == v){
18588 e.dom.checked = true;
18592 if(suppressEvent !== true){
18593 this.fireEvent('check', this, true);
18601 validate : function()
18605 (this.inputType == 'radio' && this.validateRadio()) ||
18606 (this.inputType == 'checkbox' && this.validateCheckbox())
18612 this.markInvalid();
18616 validateRadio : function()
18620 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18621 if(!e.dom.checked){
18633 validateCheckbox : function()
18636 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18639 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18647 for(var i in group){
18652 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18659 * Mark this field as valid
18661 markValid : function()
18663 if(this.allowBlank){
18669 this.fireEvent('valid', this);
18671 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18674 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18681 if(this.inputType == 'radio'){
18682 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18683 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18684 e.findParent('.form-group', false, true).addClass(_this.validClass);
18691 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18692 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18696 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18702 for(var i in group){
18703 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18704 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18709 * Mark this field as invalid
18710 * @param {String} msg The validation message
18712 markInvalid : function(msg)
18714 if(this.allowBlank){
18720 this.fireEvent('invalid', this, msg);
18722 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18725 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18729 label.markInvalid();
18732 if(this.inputType == 'radio'){
18733 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18734 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18735 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18742 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18743 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18747 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18753 for(var i in group){
18754 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18755 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18762 Roo.apply(Roo.bootstrap.CheckBox, {
18767 * register a CheckBox Group
18768 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18770 register : function(checkbox)
18772 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18773 this.groups[checkbox.groupId] = {};
18776 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18780 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18784 * fetch a CheckBox Group based on the group ID
18785 * @param {string} the group ID
18786 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18788 get: function(groupId) {
18789 if (typeof(this.groups[groupId]) == 'undefined') {
18793 return this.groups[groupId] ;
18805 *<div class="radio">
18807 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18808 Option one is this and that—be sure to include why it's great
18815 *<label class="radio-inline">fieldLabel</label>
18816 *<label class="radio-inline">
18817 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18825 * @class Roo.bootstrap.Radio
18826 * @extends Roo.bootstrap.CheckBox
18827 * Bootstrap Radio class
18830 * Create a new Radio
18831 * @param {Object} config The config object
18834 Roo.bootstrap.Radio = function(config){
18835 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18839 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18841 inputType: 'radio',
18845 getAutoCreate : function()
18847 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18848 align = align || 'left'; // default...
18855 tag : this.inline ? 'span' : 'div',
18860 var inline = this.inline ? ' radio-inline' : '';
18864 // does not need for, as we wrap the input with it..
18866 cls : 'control-label box-label' + inline,
18869 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18873 //cls : 'control-label' + inline,
18874 html : this.fieldLabel,
18875 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18884 type : this.inputType,
18885 //value : (!this.checked) ? this.valueOff : this.inputValue,
18886 value : this.inputValue,
18888 placeholder : this.placeholder || '' // ?? needed????
18891 if (this.weight) { // Validity check?
18892 input.cls += " radio-" + this.weight;
18894 if (this.disabled) {
18895 input.disabled=true;
18899 input.checked = this.checked;
18903 input.name = this.name;
18907 input.cls += ' input-' + this.size;
18910 //?? can span's inline have a width??
18913 ['xs','sm','md','lg'].map(function(size){
18914 if (settings[size]) {
18915 cfg.cls += ' col-' + size + '-' + settings[size];
18919 var inputblock = input;
18921 if (this.before || this.after) {
18924 cls : 'input-group',
18929 inputblock.cn.push({
18931 cls : 'input-group-addon',
18935 inputblock.cn.push(input);
18937 inputblock.cn.push({
18939 cls : 'input-group-addon',
18947 if (this.fieldLabel && this.fieldLabel.length) {
18948 cfg.cn.push(fieldLabel);
18951 // normal bootstrap puts the input inside the label.
18952 // however with our styled version - it has to go after the input.
18954 //lbl.cn.push(inputblock);
18958 cls: 'radio' + inline,
18965 cfg.cn.push( lblwrap);
18970 html: this.boxLabel
18979 initEvents : function()
18981 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18983 this.inputEl().on('click', this.onClick, this);
18984 if (this.boxLabel) {
18985 Roo.log('find label')
18986 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18991 inputEl: function ()
18993 return this.el.select('input.roo-radio',true).first();
18995 onClick : function()
18998 this.setChecked(true);
19001 setChecked : function(state,suppressEvent)
19004 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19005 v.dom.checked = false;
19008 Roo.log(this.inputEl().dom);
19009 this.checked = state;
19010 this.inputEl().dom.checked = state;
19012 if(suppressEvent !== true){
19013 this.fireEvent('check', this, state);
19016 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19020 getGroupValue : function()
19023 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19024 if(v.dom.checked == true){
19025 value = v.dom.value;
19033 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19034 * @return {Mixed} value The field value
19036 getValue : function(){
19037 return this.getGroupValue();
19043 //<script type="text/javascript">
19046 * Based Ext JS Library 1.1.1
19047 * Copyright(c) 2006-2007, Ext JS, LLC.
19053 * @class Roo.HtmlEditorCore
19054 * @extends Roo.Component
19055 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19057 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19060 Roo.HtmlEditorCore = function(config){
19063 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19068 * @event initialize
19069 * Fires when the editor is fully initialized (including the iframe)
19070 * @param {Roo.HtmlEditorCore} this
19075 * Fires when the editor is first receives the focus. Any insertion must wait
19076 * until after this event.
19077 * @param {Roo.HtmlEditorCore} this
19081 * @event beforesync
19082 * Fires before the textarea is updated with content from the editor iframe. Return false
19083 * to cancel the sync.
19084 * @param {Roo.HtmlEditorCore} this
19085 * @param {String} html
19089 * @event beforepush
19090 * Fires before the iframe editor is updated with content from the textarea. Return false
19091 * to cancel the push.
19092 * @param {Roo.HtmlEditorCore} this
19093 * @param {String} html
19098 * Fires when the textarea is updated with content from the editor iframe.
19099 * @param {Roo.HtmlEditorCore} this
19100 * @param {String} html
19105 * Fires when the iframe editor is updated with content from the textarea.
19106 * @param {Roo.HtmlEditorCore} this
19107 * @param {String} html
19112 * @event editorevent
19113 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19114 * @param {Roo.HtmlEditorCore} this
19120 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19122 // defaults : white / black...
19123 this.applyBlacklists();
19130 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19134 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19140 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19145 * @cfg {Number} height (in pixels)
19149 * @cfg {Number} width (in pixels)
19154 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19157 stylesheets: false,
19162 // private properties
19163 validationEvent : false,
19165 initialized : false,
19167 sourceEditMode : false,
19168 onFocus : Roo.emptyFn,
19170 hideMode:'offsets',
19174 // blacklist + whitelisted elements..
19181 * Protected method that will not generally be called directly. It
19182 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19183 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19185 getDocMarkup : function(){
19189 // inherit styels from page...??
19190 if (this.stylesheets === false) {
19192 Roo.get(document.head).select('style').each(function(node) {
19193 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19196 Roo.get(document.head).select('link').each(function(node) {
19197 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19200 } else if (!this.stylesheets.length) {
19202 st = '<style type="text/css">' +
19203 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19209 st += '<style type="text/css">' +
19210 'IMG { cursor: pointer } ' +
19214 return '<html><head>' + st +
19215 //<style type="text/css">' +
19216 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19218 ' </head><body class="roo-htmleditor-body"></body></html>';
19222 onRender : function(ct, position)
19225 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19226 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19229 this.el.dom.style.border = '0 none';
19230 this.el.dom.setAttribute('tabIndex', -1);
19231 this.el.addClass('x-hidden hide');
19235 if(Roo.isIE){ // fix IE 1px bogus margin
19236 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19240 this.frameId = Roo.id();
19244 var iframe = this.owner.wrap.createChild({
19246 cls: 'form-control', // bootstrap..
19248 name: this.frameId,
19249 frameBorder : 'no',
19250 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19255 this.iframe = iframe.dom;
19257 this.assignDocWin();
19259 this.doc.designMode = 'on';
19262 this.doc.write(this.getDocMarkup());
19266 var task = { // must defer to wait for browser to be ready
19268 //console.log("run task?" + this.doc.readyState);
19269 this.assignDocWin();
19270 if(this.doc.body || this.doc.readyState == 'complete'){
19272 this.doc.designMode="on";
19276 Roo.TaskMgr.stop(task);
19277 this.initEditor.defer(10, this);
19284 Roo.TaskMgr.start(task);
19289 onResize : function(w, h)
19291 Roo.log('resize: ' +w + ',' + h );
19292 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19296 if(typeof w == 'number'){
19298 this.iframe.style.width = w + 'px';
19300 if(typeof h == 'number'){
19302 this.iframe.style.height = h + 'px';
19304 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19311 * Toggles the editor between standard and source edit mode.
19312 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19314 toggleSourceEdit : function(sourceEditMode){
19316 this.sourceEditMode = sourceEditMode === true;
19318 if(this.sourceEditMode){
19320 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19323 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19324 //this.iframe.className = '';
19327 //this.setSize(this.owner.wrap.getSize());
19328 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19335 * Protected method that will not generally be called directly. If you need/want
19336 * custom HTML cleanup, this is the method you should override.
19337 * @param {String} html The HTML to be cleaned
19338 * return {String} The cleaned HTML
19340 cleanHtml : function(html){
19341 html = String(html);
19342 if(html.length > 5){
19343 if(Roo.isSafari){ // strip safari nonsense
19344 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19347 if(html == ' '){
19354 * HTML Editor -> Textarea
19355 * Protected method that will not generally be called directly. Syncs the contents
19356 * of the editor iframe with the textarea.
19358 syncValue : function(){
19359 if(this.initialized){
19360 var bd = (this.doc.body || this.doc.documentElement);
19361 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19362 var html = bd.innerHTML;
19364 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19365 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19367 html = '<div style="'+m[0]+'">' + html + '</div>';
19370 html = this.cleanHtml(html);
19371 // fix up the special chars.. normaly like back quotes in word...
19372 // however we do not want to do this with chinese..
19373 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19374 var cc = b.charCodeAt();
19376 (cc >= 0x4E00 && cc < 0xA000 ) ||
19377 (cc >= 0x3400 && cc < 0x4E00 ) ||
19378 (cc >= 0xf900 && cc < 0xfb00 )
19384 if(this.owner.fireEvent('beforesync', this, html) !== false){
19385 this.el.dom.value = html;
19386 this.owner.fireEvent('sync', this, html);
19392 * Protected method that will not generally be called directly. Pushes the value of the textarea
19393 * into the iframe editor.
19395 pushValue : function(){
19396 if(this.initialized){
19397 var v = this.el.dom.value.trim();
19399 // if(v.length < 1){
19403 if(this.owner.fireEvent('beforepush', this, v) !== false){
19404 var d = (this.doc.body || this.doc.documentElement);
19406 this.cleanUpPaste();
19407 this.el.dom.value = d.innerHTML;
19408 this.owner.fireEvent('push', this, v);
19414 deferFocus : function(){
19415 this.focus.defer(10, this);
19419 focus : function(){
19420 if(this.win && !this.sourceEditMode){
19427 assignDocWin: function()
19429 var iframe = this.iframe;
19432 this.doc = iframe.contentWindow.document;
19433 this.win = iframe.contentWindow;
19435 // if (!Roo.get(this.frameId)) {
19438 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19439 // this.win = Roo.get(this.frameId).dom.contentWindow;
19441 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19445 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19446 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19451 initEditor : function(){
19452 //console.log("INIT EDITOR");
19453 this.assignDocWin();
19457 this.doc.designMode="on";
19459 this.doc.write(this.getDocMarkup());
19462 var dbody = (this.doc.body || this.doc.documentElement);
19463 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19464 // this copies styles from the containing element into thsi one..
19465 // not sure why we need all of this..
19466 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19468 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19469 //ss['background-attachment'] = 'fixed'; // w3c
19470 dbody.bgProperties = 'fixed'; // ie
19471 //Roo.DomHelper.applyStyles(dbody, ss);
19472 Roo.EventManager.on(this.doc, {
19473 //'mousedown': this.onEditorEvent,
19474 'mouseup': this.onEditorEvent,
19475 'dblclick': this.onEditorEvent,
19476 'click': this.onEditorEvent,
19477 'keyup': this.onEditorEvent,
19482 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19484 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19485 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19487 this.initialized = true;
19489 this.owner.fireEvent('initialize', this);
19494 onDestroy : function(){
19500 //for (var i =0; i < this.toolbars.length;i++) {
19501 // // fixme - ask toolbars for heights?
19502 // this.toolbars[i].onDestroy();
19505 //this.wrap.dom.innerHTML = '';
19506 //this.wrap.remove();
19511 onFirstFocus : function(){
19513 this.assignDocWin();
19516 this.activated = true;
19519 if(Roo.isGecko){ // prevent silly gecko errors
19521 var s = this.win.getSelection();
19522 if(!s.focusNode || s.focusNode.nodeType != 3){
19523 var r = s.getRangeAt(0);
19524 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19529 this.execCmd('useCSS', true);
19530 this.execCmd('styleWithCSS', false);
19533 this.owner.fireEvent('activate', this);
19537 adjustFont: function(btn){
19538 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19539 //if(Roo.isSafari){ // safari
19542 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19543 if(Roo.isSafari){ // safari
19544 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19545 v = (v < 10) ? 10 : v;
19546 v = (v > 48) ? 48 : v;
19547 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19552 v = Math.max(1, v+adjust);
19554 this.execCmd('FontSize', v );
19557 onEditorEvent : function(e)
19559 this.owner.fireEvent('editorevent', this, e);
19560 // this.updateToolbar();
19561 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19564 insertTag : function(tg)
19566 // could be a bit smarter... -> wrap the current selected tRoo..
19567 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19569 range = this.createRange(this.getSelection());
19570 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19571 wrappingNode.appendChild(range.extractContents());
19572 range.insertNode(wrappingNode);
19579 this.execCmd("formatblock", tg);
19583 insertText : function(txt)
19587 var range = this.createRange();
19588 range.deleteContents();
19589 //alert(Sender.getAttribute('label'));
19591 range.insertNode(this.doc.createTextNode(txt));
19597 * Executes a Midas editor command on the editor document and performs necessary focus and
19598 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19599 * @param {String} cmd The Midas command
19600 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19602 relayCmd : function(cmd, value){
19604 this.execCmd(cmd, value);
19605 this.owner.fireEvent('editorevent', this);
19606 //this.updateToolbar();
19607 this.owner.deferFocus();
19611 * Executes a Midas editor command directly on the editor document.
19612 * For visual commands, you should use {@link #relayCmd} instead.
19613 * <b>This should only be called after the editor is initialized.</b>
19614 * @param {String} cmd The Midas command
19615 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19617 execCmd : function(cmd, value){
19618 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19625 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19627 * @param {String} text | dom node..
19629 insertAtCursor : function(text)
19634 if(!this.activated){
19640 var r = this.doc.selection.createRange();
19651 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19655 // from jquery ui (MIT licenced)
19657 var win = this.win;
19659 if (win.getSelection && win.getSelection().getRangeAt) {
19660 range = win.getSelection().getRangeAt(0);
19661 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19662 range.insertNode(node);
19663 } else if (win.document.selection && win.document.selection.createRange) {
19664 // no firefox support
19665 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19666 win.document.selection.createRange().pasteHTML(txt);
19668 // no firefox support
19669 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19670 this.execCmd('InsertHTML', txt);
19679 mozKeyPress : function(e){
19681 var c = e.getCharCode(), cmd;
19684 c = String.fromCharCode(c).toLowerCase();
19698 this.cleanUpPaste.defer(100, this);
19706 e.preventDefault();
19714 fixKeys : function(){ // load time branching for fastest keydown performance
19716 return function(e){
19717 var k = e.getKey(), r;
19720 r = this.doc.selection.createRange();
19723 r.pasteHTML('    ');
19730 r = this.doc.selection.createRange();
19732 var target = r.parentElement();
19733 if(!target || target.tagName.toLowerCase() != 'li'){
19735 r.pasteHTML('<br />');
19741 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19742 this.cleanUpPaste.defer(100, this);
19748 }else if(Roo.isOpera){
19749 return function(e){
19750 var k = e.getKey();
19754 this.execCmd('InsertHTML','    ');
19757 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19758 this.cleanUpPaste.defer(100, this);
19763 }else if(Roo.isSafari){
19764 return function(e){
19765 var k = e.getKey();
19769 this.execCmd('InsertText','\t');
19773 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19774 this.cleanUpPaste.defer(100, this);
19782 getAllAncestors: function()
19784 var p = this.getSelectedNode();
19787 a.push(p); // push blank onto stack..
19788 p = this.getParentElement();
19792 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19796 a.push(this.doc.body);
19800 lastSelNode : false,
19803 getSelection : function()
19805 this.assignDocWin();
19806 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19809 getSelectedNode: function()
19811 // this may only work on Gecko!!!
19813 // should we cache this!!!!
19818 var range = this.createRange(this.getSelection()).cloneRange();
19821 var parent = range.parentElement();
19823 var testRange = range.duplicate();
19824 testRange.moveToElementText(parent);
19825 if (testRange.inRange(range)) {
19828 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19831 parent = parent.parentElement;
19836 // is ancestor a text element.
19837 var ac = range.commonAncestorContainer;
19838 if (ac.nodeType == 3) {
19839 ac = ac.parentNode;
19842 var ar = ac.childNodes;
19845 var other_nodes = [];
19846 var has_other_nodes = false;
19847 for (var i=0;i<ar.length;i++) {
19848 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19851 // fullly contained node.
19853 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19858 // probably selected..
19859 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19860 other_nodes.push(ar[i]);
19864 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19869 has_other_nodes = true;
19871 if (!nodes.length && other_nodes.length) {
19872 nodes= other_nodes;
19874 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19880 createRange: function(sel)
19882 // this has strange effects when using with
19883 // top toolbar - not sure if it's a great idea.
19884 //this.editor.contentWindow.focus();
19885 if (typeof sel != "undefined") {
19887 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19889 return this.doc.createRange();
19892 return this.doc.createRange();
19895 getParentElement: function()
19898 this.assignDocWin();
19899 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19901 var range = this.createRange(sel);
19904 var p = range.commonAncestorContainer;
19905 while (p.nodeType == 3) { // text node
19916 * Range intersection.. the hard stuff...
19920 * [ -- selected range --- ]
19924 * if end is before start or hits it. fail.
19925 * if start is after end or hits it fail.
19927 * if either hits (but other is outside. - then it's not
19933 // @see http://www.thismuchiknow.co.uk/?p=64.
19934 rangeIntersectsNode : function(range, node)
19936 var nodeRange = node.ownerDocument.createRange();
19938 nodeRange.selectNode(node);
19940 nodeRange.selectNodeContents(node);
19943 var rangeStartRange = range.cloneRange();
19944 rangeStartRange.collapse(true);
19946 var rangeEndRange = range.cloneRange();
19947 rangeEndRange.collapse(false);
19949 var nodeStartRange = nodeRange.cloneRange();
19950 nodeStartRange.collapse(true);
19952 var nodeEndRange = nodeRange.cloneRange();
19953 nodeEndRange.collapse(false);
19955 return rangeStartRange.compareBoundaryPoints(
19956 Range.START_TO_START, nodeEndRange) == -1 &&
19957 rangeEndRange.compareBoundaryPoints(
19958 Range.START_TO_START, nodeStartRange) == 1;
19962 rangeCompareNode : function(range, node)
19964 var nodeRange = node.ownerDocument.createRange();
19966 nodeRange.selectNode(node);
19968 nodeRange.selectNodeContents(node);
19972 range.collapse(true);
19974 nodeRange.collapse(true);
19976 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19977 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19979 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19981 var nodeIsBefore = ss == 1;
19982 var nodeIsAfter = ee == -1;
19984 if (nodeIsBefore && nodeIsAfter)
19986 if (!nodeIsBefore && nodeIsAfter)
19987 return 1; //right trailed.
19989 if (nodeIsBefore && !nodeIsAfter)
19990 return 2; // left trailed.
19995 // private? - in a new class?
19996 cleanUpPaste : function()
19998 // cleans up the whole document..
19999 Roo.log('cleanuppaste');
20001 this.cleanUpChildren(this.doc.body);
20002 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20003 if (clean != this.doc.body.innerHTML) {
20004 this.doc.body.innerHTML = clean;
20009 cleanWordChars : function(input) {// change the chars to hex code
20010 var he = Roo.HtmlEditorCore;
20012 var output = input;
20013 Roo.each(he.swapCodes, function(sw) {
20014 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20016 output = output.replace(swapper, sw[1]);
20023 cleanUpChildren : function (n)
20025 if (!n.childNodes.length) {
20028 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20029 this.cleanUpChild(n.childNodes[i]);
20036 cleanUpChild : function (node)
20039 //console.log(node);
20040 if (node.nodeName == "#text") {
20041 // clean up silly Windows -- stuff?
20044 if (node.nodeName == "#comment") {
20045 node.parentNode.removeChild(node);
20046 // clean up silly Windows -- stuff?
20049 var lcname = node.tagName.toLowerCase();
20050 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20051 // whitelist of tags..
20053 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20055 node.parentNode.removeChild(node);
20060 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20062 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20063 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20065 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20066 // remove_keep_children = true;
20069 if (remove_keep_children) {
20070 this.cleanUpChildren(node);
20071 // inserts everything just before this node...
20072 while (node.childNodes.length) {
20073 var cn = node.childNodes[0];
20074 node.removeChild(cn);
20075 node.parentNode.insertBefore(cn, node);
20077 node.parentNode.removeChild(node);
20081 if (!node.attributes || !node.attributes.length) {
20082 this.cleanUpChildren(node);
20086 function cleanAttr(n,v)
20089 if (v.match(/^\./) || v.match(/^\//)) {
20092 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20095 if (v.match(/^#/)) {
20098 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20099 node.removeAttribute(n);
20103 var cwhite = this.cwhite;
20104 var cblack = this.cblack;
20106 function cleanStyle(n,v)
20108 if (v.match(/expression/)) { //XSS?? should we even bother..
20109 node.removeAttribute(n);
20113 var parts = v.split(/;/);
20116 Roo.each(parts, function(p) {
20117 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20121 var l = p.split(':').shift().replace(/\s+/g,'');
20122 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20124 if ( cwhite.length && cblack.indexOf(l) > -1) {
20125 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20126 //node.removeAttribute(n);
20130 // only allow 'c whitelisted system attributes'
20131 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20132 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20133 //node.removeAttribute(n);
20143 if (clean.length) {
20144 node.setAttribute(n, clean.join(';'));
20146 node.removeAttribute(n);
20152 for (var i = node.attributes.length-1; i > -1 ; i--) {
20153 var a = node.attributes[i];
20156 if (a.name.toLowerCase().substr(0,2)=='on') {
20157 node.removeAttribute(a.name);
20160 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20161 node.removeAttribute(a.name);
20164 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20165 cleanAttr(a.name,a.value); // fixme..
20168 if (a.name == 'style') {
20169 cleanStyle(a.name,a.value);
20172 /// clean up MS crap..
20173 // tecnically this should be a list of valid class'es..
20176 if (a.name == 'class') {
20177 if (a.value.match(/^Mso/)) {
20178 node.className = '';
20181 if (a.value.match(/body/)) {
20182 node.className = '';
20193 this.cleanUpChildren(node);
20199 * Clean up MS wordisms...
20201 cleanWord : function(node)
20206 this.cleanWord(this.doc.body);
20209 if (node.nodeName == "#text") {
20210 // clean up silly Windows -- stuff?
20213 if (node.nodeName == "#comment") {
20214 node.parentNode.removeChild(node);
20215 // clean up silly Windows -- stuff?
20219 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20220 node.parentNode.removeChild(node);
20224 // remove - but keep children..
20225 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20226 while (node.childNodes.length) {
20227 var cn = node.childNodes[0];
20228 node.removeChild(cn);
20229 node.parentNode.insertBefore(cn, node);
20231 node.parentNode.removeChild(node);
20232 this.iterateChildren(node, this.cleanWord);
20236 if (node.className.length) {
20238 var cn = node.className.split(/\W+/);
20240 Roo.each(cn, function(cls) {
20241 if (cls.match(/Mso[a-zA-Z]+/)) {
20246 node.className = cna.length ? cna.join(' ') : '';
20248 node.removeAttribute("class");
20252 if (node.hasAttribute("lang")) {
20253 node.removeAttribute("lang");
20256 if (node.hasAttribute("style")) {
20258 var styles = node.getAttribute("style").split(";");
20260 Roo.each(styles, function(s) {
20261 if (!s.match(/:/)) {
20264 var kv = s.split(":");
20265 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20268 // what ever is left... we allow.
20271 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20272 if (!nstyle.length) {
20273 node.removeAttribute('style');
20276 this.iterateChildren(node, this.cleanWord);
20282 * iterateChildren of a Node, calling fn each time, using this as the scole..
20283 * @param {DomNode} node node to iterate children of.
20284 * @param {Function} fn method of this class to call on each item.
20286 iterateChildren : function(node, fn)
20288 if (!node.childNodes.length) {
20291 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20292 fn.call(this, node.childNodes[i])
20298 * cleanTableWidths.
20300 * Quite often pasting from word etc.. results in tables with column and widths.
20301 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20304 cleanTableWidths : function(node)
20309 this.cleanTableWidths(this.doc.body);
20314 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20317 Roo.log(node.tagName);
20318 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20319 this.iterateChildren(node, this.cleanTableWidths);
20322 if (node.hasAttribute('width')) {
20323 node.removeAttribute('width');
20327 if (node.hasAttribute("style")) {
20330 var styles = node.getAttribute("style").split(";");
20332 Roo.each(styles, function(s) {
20333 if (!s.match(/:/)) {
20336 var kv = s.split(":");
20337 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20340 // what ever is left... we allow.
20343 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20344 if (!nstyle.length) {
20345 node.removeAttribute('style');
20349 this.iterateChildren(node, this.cleanTableWidths);
20357 domToHTML : function(currentElement, depth, nopadtext) {
20359 depth = depth || 0;
20360 nopadtext = nopadtext || false;
20362 if (!currentElement) {
20363 return this.domToHTML(this.doc.body);
20366 //Roo.log(currentElement);
20368 var allText = false;
20369 var nodeName = currentElement.nodeName;
20370 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20372 if (nodeName == '#text') {
20374 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20379 if (nodeName != 'BODY') {
20382 // Prints the node tagName, such as <A>, <IMG>, etc
20385 for(i = 0; i < currentElement.attributes.length;i++) {
20387 var aname = currentElement.attributes.item(i).name;
20388 if (!currentElement.attributes.item(i).value.length) {
20391 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20394 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20403 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20406 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20411 // Traverse the tree
20413 var currentElementChild = currentElement.childNodes.item(i);
20414 var allText = true;
20415 var innerHTML = '';
20417 while (currentElementChild) {
20418 // Formatting code (indent the tree so it looks nice on the screen)
20419 var nopad = nopadtext;
20420 if (lastnode == 'SPAN') {
20424 if (currentElementChild.nodeName == '#text') {
20425 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20426 toadd = nopadtext ? toadd : toadd.trim();
20427 if (!nopad && toadd.length > 80) {
20428 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20430 innerHTML += toadd;
20433 currentElementChild = currentElement.childNodes.item(i);
20439 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20441 // Recursively traverse the tree structure of the child node
20442 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20443 lastnode = currentElementChild.nodeName;
20445 currentElementChild=currentElement.childNodes.item(i);
20451 // The remaining code is mostly for formatting the tree
20452 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20457 ret+= "</"+tagName+">";
20463 applyBlacklists : function()
20465 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20466 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20470 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20471 if (b.indexOf(tag) > -1) {
20474 this.white.push(tag);
20478 Roo.each(w, function(tag) {
20479 if (b.indexOf(tag) > -1) {
20482 if (this.white.indexOf(tag) > -1) {
20485 this.white.push(tag);
20490 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20491 if (w.indexOf(tag) > -1) {
20494 this.black.push(tag);
20498 Roo.each(b, function(tag) {
20499 if (w.indexOf(tag) > -1) {
20502 if (this.black.indexOf(tag) > -1) {
20505 this.black.push(tag);
20510 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20511 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20515 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20516 if (b.indexOf(tag) > -1) {
20519 this.cwhite.push(tag);
20523 Roo.each(w, function(tag) {
20524 if (b.indexOf(tag) > -1) {
20527 if (this.cwhite.indexOf(tag) > -1) {
20530 this.cwhite.push(tag);
20535 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20536 if (w.indexOf(tag) > -1) {
20539 this.cblack.push(tag);
20543 Roo.each(b, function(tag) {
20544 if (w.indexOf(tag) > -1) {
20547 if (this.cblack.indexOf(tag) > -1) {
20550 this.cblack.push(tag);
20555 setStylesheets : function(stylesheets)
20557 if(typeof(stylesheets) == 'string'){
20558 Roo.get(this.iframe.contentDocument.head).createChild({
20560 rel : 'stylesheet',
20569 Roo.each(stylesheets, function(s) {
20574 Roo.get(_this.iframe.contentDocument.head).createChild({
20576 rel : 'stylesheet',
20585 removeStylesheets : function()
20589 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20594 // hide stuff that is not compatible
20608 * @event specialkey
20612 * @cfg {String} fieldClass @hide
20615 * @cfg {String} focusClass @hide
20618 * @cfg {String} autoCreate @hide
20621 * @cfg {String} inputType @hide
20624 * @cfg {String} invalidClass @hide
20627 * @cfg {String} invalidText @hide
20630 * @cfg {String} msgFx @hide
20633 * @cfg {String} validateOnBlur @hide
20637 Roo.HtmlEditorCore.white = [
20638 'area', 'br', 'img', 'input', 'hr', 'wbr',
20640 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20641 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20642 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20643 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20644 'table', 'ul', 'xmp',
20646 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20649 'dir', 'menu', 'ol', 'ul', 'dl',
20655 Roo.HtmlEditorCore.black = [
20656 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20658 'base', 'basefont', 'bgsound', 'blink', 'body',
20659 'frame', 'frameset', 'head', 'html', 'ilayer',
20660 'iframe', 'layer', 'link', 'meta', 'object',
20661 'script', 'style' ,'title', 'xml' // clean later..
20663 Roo.HtmlEditorCore.clean = [
20664 'script', 'style', 'title', 'xml'
20666 Roo.HtmlEditorCore.remove = [
20671 Roo.HtmlEditorCore.ablack = [
20675 Roo.HtmlEditorCore.aclean = [
20676 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20680 Roo.HtmlEditorCore.pwhite= [
20681 'http', 'https', 'mailto'
20684 // white listed style attributes.
20685 Roo.HtmlEditorCore.cwhite= [
20686 // 'text-align', /// default is to allow most things..
20692 // black listed style attributes.
20693 Roo.HtmlEditorCore.cblack= [
20694 // 'font-size' -- this can be set by the project
20698 Roo.HtmlEditorCore.swapCodes =[
20717 * @class Roo.bootstrap.HtmlEditor
20718 * @extends Roo.bootstrap.TextArea
20719 * Bootstrap HtmlEditor class
20722 * Create a new HtmlEditor
20723 * @param {Object} config The config object
20726 Roo.bootstrap.HtmlEditor = function(config){
20727 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20728 if (!this.toolbars) {
20729 this.toolbars = [];
20731 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20734 * @event initialize
20735 * Fires when the editor is fully initialized (including the iframe)
20736 * @param {HtmlEditor} this
20741 * Fires when the editor is first receives the focus. Any insertion must wait
20742 * until after this event.
20743 * @param {HtmlEditor} this
20747 * @event beforesync
20748 * Fires before the textarea is updated with content from the editor iframe. Return false
20749 * to cancel the sync.
20750 * @param {HtmlEditor} this
20751 * @param {String} html
20755 * @event beforepush
20756 * Fires before the iframe editor is updated with content from the textarea. Return false
20757 * to cancel the push.
20758 * @param {HtmlEditor} this
20759 * @param {String} html
20764 * Fires when the textarea is updated with content from the editor iframe.
20765 * @param {HtmlEditor} this
20766 * @param {String} html
20771 * Fires when the iframe editor is updated with content from the textarea.
20772 * @param {HtmlEditor} this
20773 * @param {String} html
20777 * @event editmodechange
20778 * Fires when the editor switches edit modes
20779 * @param {HtmlEditor} this
20780 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20782 editmodechange: true,
20784 * @event editorevent
20785 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20786 * @param {HtmlEditor} this
20790 * @event firstfocus
20791 * Fires when on first focus - needed by toolbars..
20792 * @param {HtmlEditor} this
20797 * Auto save the htmlEditor value as a file into Events
20798 * @param {HtmlEditor} this
20802 * @event savedpreview
20803 * preview the saved version of htmlEditor
20804 * @param {HtmlEditor} this
20811 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20815 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20820 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20825 * @cfg {Number} height (in pixels)
20829 * @cfg {Number} width (in pixels)
20834 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20837 stylesheets: false,
20842 // private properties
20843 validationEvent : false,
20845 initialized : false,
20848 onFocus : Roo.emptyFn,
20850 hideMode:'offsets',
20853 tbContainer : false,
20855 toolbarContainer :function() {
20856 return this.wrap.select('.x-html-editor-tb',true).first();
20860 * Protected method that will not generally be called directly. It
20861 * is called when the editor creates its toolbar. Override this method if you need to
20862 * add custom toolbar buttons.
20863 * @param {HtmlEditor} editor
20865 createToolbar : function(){
20867 Roo.log("create toolbars");
20869 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20870 this.toolbars[0].render(this.toolbarContainer());
20874 // if (!editor.toolbars || !editor.toolbars.length) {
20875 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20878 // for (var i =0 ; i < editor.toolbars.length;i++) {
20879 // editor.toolbars[i] = Roo.factory(
20880 // typeof(editor.toolbars[i]) == 'string' ?
20881 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20882 // Roo.bootstrap.HtmlEditor);
20883 // editor.toolbars[i].init(editor);
20889 onRender : function(ct, position)
20891 // Roo.log("Call onRender: " + this.xtype);
20893 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20895 this.wrap = this.inputEl().wrap({
20896 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20899 this.editorcore.onRender(ct, position);
20901 if (this.resizable) {
20902 this.resizeEl = new Roo.Resizable(this.wrap, {
20906 minHeight : this.height,
20907 height: this.height,
20908 handles : this.resizable,
20911 resize : function(r, w, h) {
20912 _t.onResize(w,h); // -something
20918 this.createToolbar(this);
20921 if(!this.width && this.resizable){
20922 this.setSize(this.wrap.getSize());
20924 if (this.resizeEl) {
20925 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20926 // should trigger onReize..
20932 onResize : function(w, h)
20934 Roo.log('resize: ' +w + ',' + h );
20935 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20939 if(this.inputEl() ){
20940 if(typeof w == 'number'){
20941 var aw = w - this.wrap.getFrameWidth('lr');
20942 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20945 if(typeof h == 'number'){
20946 var tbh = -11; // fixme it needs to tool bar size!
20947 for (var i =0; i < this.toolbars.length;i++) {
20948 // fixme - ask toolbars for heights?
20949 tbh += this.toolbars[i].el.getHeight();
20950 //if (this.toolbars[i].footer) {
20951 // tbh += this.toolbars[i].footer.el.getHeight();
20959 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20960 ah -= 5; // knock a few pixes off for look..
20961 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20965 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20966 this.editorcore.onResize(ew,eh);
20971 * Toggles the editor between standard and source edit mode.
20972 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20974 toggleSourceEdit : function(sourceEditMode)
20976 this.editorcore.toggleSourceEdit(sourceEditMode);
20978 if(this.editorcore.sourceEditMode){
20979 Roo.log('editor - showing textarea');
20982 // Roo.log(this.syncValue());
20984 this.inputEl().removeClass(['hide', 'x-hidden']);
20985 this.inputEl().dom.removeAttribute('tabIndex');
20986 this.inputEl().focus();
20988 Roo.log('editor - hiding textarea');
20990 // Roo.log(this.pushValue());
20993 this.inputEl().addClass(['hide', 'x-hidden']);
20994 this.inputEl().dom.setAttribute('tabIndex', -1);
20995 //this.deferFocus();
20998 if(this.resizable){
20999 this.setSize(this.wrap.getSize());
21002 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21005 // private (for BoxComponent)
21006 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21008 // private (for BoxComponent)
21009 getResizeEl : function(){
21013 // private (for BoxComponent)
21014 getPositionEl : function(){
21019 initEvents : function(){
21020 this.originalValue = this.getValue();
21024 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21027 // markInvalid : Roo.emptyFn,
21029 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21032 // clearInvalid : Roo.emptyFn,
21034 setValue : function(v){
21035 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21036 this.editorcore.pushValue();
21041 deferFocus : function(){
21042 this.focus.defer(10, this);
21046 focus : function(){
21047 this.editorcore.focus();
21053 onDestroy : function(){
21059 for (var i =0; i < this.toolbars.length;i++) {
21060 // fixme - ask toolbars for heights?
21061 this.toolbars[i].onDestroy();
21064 this.wrap.dom.innerHTML = '';
21065 this.wrap.remove();
21070 onFirstFocus : function(){
21071 //Roo.log("onFirstFocus");
21072 this.editorcore.onFirstFocus();
21073 for (var i =0; i < this.toolbars.length;i++) {
21074 this.toolbars[i].onFirstFocus();
21080 syncValue : function()
21082 this.editorcore.syncValue();
21085 pushValue : function()
21087 this.editorcore.pushValue();
21091 // hide stuff that is not compatible
21105 * @event specialkey
21109 * @cfg {String} fieldClass @hide
21112 * @cfg {String} focusClass @hide
21115 * @cfg {String} autoCreate @hide
21118 * @cfg {String} inputType @hide
21121 * @cfg {String} invalidClass @hide
21124 * @cfg {String} invalidText @hide
21127 * @cfg {String} msgFx @hide
21130 * @cfg {String} validateOnBlur @hide
21139 Roo.namespace('Roo.bootstrap.htmleditor');
21141 * @class Roo.bootstrap.HtmlEditorToolbar1
21146 new Roo.bootstrap.HtmlEditor({
21149 new Roo.bootstrap.HtmlEditorToolbar1({
21150 disable : { fonts: 1 , format: 1, ..., ... , ...],
21156 * @cfg {Object} disable List of elements to disable..
21157 * @cfg {Array} btns List of additional buttons.
21161 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21164 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21167 Roo.apply(this, config);
21169 // default disabled, based on 'good practice'..
21170 this.disable = this.disable || {};
21171 Roo.applyIf(this.disable, {
21174 specialElements : true
21176 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21178 this.editor = config.editor;
21179 this.editorcore = config.editor.editorcore;
21181 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21183 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21184 // dont call parent... till later.
21186 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21191 editorcore : false,
21196 "h1","h2","h3","h4","h5","h6",
21198 "abbr", "acronym", "address", "cite", "samp", "var",
21202 onRender : function(ct, position)
21204 // Roo.log("Call onRender: " + this.xtype);
21206 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21208 this.el.dom.style.marginBottom = '0';
21210 var editorcore = this.editorcore;
21211 var editor= this.editor;
21214 var btn = function(id,cmd , toggle, handler){
21216 var event = toggle ? 'toggle' : 'click';
21221 xns: Roo.bootstrap,
21224 enableToggle:toggle !== false,
21226 pressed : toggle ? false : null,
21229 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21230 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21239 xns: Roo.bootstrap,
21240 glyphicon : 'font',
21244 xns: Roo.bootstrap,
21248 Roo.each(this.formats, function(f) {
21249 style.menu.items.push({
21251 xns: Roo.bootstrap,
21252 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21257 editorcore.insertTag(this.tagname);
21264 children.push(style);
21267 btn('bold',false,true);
21268 btn('italic',false,true);
21269 btn('align-left', 'justifyleft',true);
21270 btn('align-center', 'justifycenter',true);
21271 btn('align-right' , 'justifyright',true);
21272 btn('link', false, false, function(btn) {
21273 //Roo.log("create link?");
21274 var url = prompt(this.createLinkText, this.defaultLinkValue);
21275 if(url && url != 'http:/'+'/'){
21276 this.editorcore.relayCmd('createlink', url);
21279 btn('list','insertunorderedlist',true);
21280 btn('pencil', false,true, function(btn){
21283 this.toggleSourceEdit(btn.pressed);
21289 xns: Roo.bootstrap,
21294 xns: Roo.bootstrap,
21299 cog.menu.items.push({
21301 xns: Roo.bootstrap,
21302 html : Clean styles,
21307 editorcore.insertTag(this.tagname);
21316 this.xtype = 'NavSimplebar';
21318 for(var i=0;i< children.length;i++) {
21320 this.buttons.add(this.addxtypeChild(children[i]));
21324 editor.on('editorevent', this.updateToolbar, this);
21326 onBtnClick : function(id)
21328 this.editorcore.relayCmd(id);
21329 this.editorcore.focus();
21333 * Protected method that will not generally be called directly. It triggers
21334 * a toolbar update by reading the markup state of the current selection in the editor.
21336 updateToolbar: function(){
21338 if(!this.editorcore.activated){
21339 this.editor.onFirstFocus(); // is this neeed?
21343 var btns = this.buttons;
21344 var doc = this.editorcore.doc;
21345 btns.get('bold').setActive(doc.queryCommandState('bold'));
21346 btns.get('italic').setActive(doc.queryCommandState('italic'));
21347 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21349 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21350 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21351 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21353 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21354 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21357 var ans = this.editorcore.getAllAncestors();
21358 if (this.formatCombo) {
21361 var store = this.formatCombo.store;
21362 this.formatCombo.setValue("");
21363 for (var i =0; i < ans.length;i++) {
21364 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21366 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21374 // hides menus... - so this cant be on a menu...
21375 Roo.bootstrap.MenuMgr.hideAll();
21377 Roo.bootstrap.MenuMgr.hideAll();
21378 //this.editorsyncValue();
21380 onFirstFocus: function() {
21381 this.buttons.each(function(item){
21385 toggleSourceEdit : function(sourceEditMode){
21388 if(sourceEditMode){
21389 Roo.log("disabling buttons");
21390 this.buttons.each( function(item){
21391 if(item.cmd != 'pencil'){
21397 Roo.log("enabling buttons");
21398 if(this.editorcore.initialized){
21399 this.buttons.each( function(item){
21405 Roo.log("calling toggole on editor");
21406 // tell the editor that it's been pressed..
21407 this.editor.toggleSourceEdit(sourceEditMode);
21417 * @class Roo.bootstrap.Table.AbstractSelectionModel
21418 * @extends Roo.util.Observable
21419 * Abstract base class for grid SelectionModels. It provides the interface that should be
21420 * implemented by descendant classes. This class should not be directly instantiated.
21423 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21424 this.locked = false;
21425 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21429 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21430 /** @ignore Called by the grid automatically. Do not call directly. */
21431 init : function(grid){
21437 * Locks the selections.
21440 this.locked = true;
21444 * Unlocks the selections.
21446 unlock : function(){
21447 this.locked = false;
21451 * Returns true if the selections are locked.
21452 * @return {Boolean}
21454 isLocked : function(){
21455 return this.locked;
21459 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21460 * @class Roo.bootstrap.Table.RowSelectionModel
21461 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21462 * It supports multiple selections and keyboard selection/navigation.
21464 * @param {Object} config
21467 Roo.bootstrap.Table.RowSelectionModel = function(config){
21468 Roo.apply(this, config);
21469 this.selections = new Roo.util.MixedCollection(false, function(o){
21474 this.lastActive = false;
21478 * @event selectionchange
21479 * Fires when the selection changes
21480 * @param {SelectionModel} this
21482 "selectionchange" : true,
21484 * @event afterselectionchange
21485 * Fires after the selection changes (eg. by key press or clicking)
21486 * @param {SelectionModel} this
21488 "afterselectionchange" : true,
21490 * @event beforerowselect
21491 * Fires when a row is selected being selected, return false to cancel.
21492 * @param {SelectionModel} this
21493 * @param {Number} rowIndex The selected index
21494 * @param {Boolean} keepExisting False if other selections will be cleared
21496 "beforerowselect" : true,
21499 * Fires when a row is selected.
21500 * @param {SelectionModel} this
21501 * @param {Number} rowIndex The selected index
21502 * @param {Roo.data.Record} r The record
21504 "rowselect" : true,
21506 * @event rowdeselect
21507 * Fires when a row is deselected.
21508 * @param {SelectionModel} this
21509 * @param {Number} rowIndex The selected index
21511 "rowdeselect" : true
21513 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21514 this.locked = false;
21517 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21519 * @cfg {Boolean} singleSelect
21520 * True to allow selection of only one row at a time (defaults to false)
21522 singleSelect : false,
21525 initEvents : function(){
21527 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21528 this.grid.on("mousedown", this.handleMouseDown, this);
21529 }else{ // allow click to work like normal
21530 this.grid.on("rowclick", this.handleDragableRowClick, this);
21533 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21534 "up" : function(e){
21536 this.selectPrevious(e.shiftKey);
21537 }else if(this.last !== false && this.lastActive !== false){
21538 var last = this.last;
21539 this.selectRange(this.last, this.lastActive-1);
21540 this.grid.getView().focusRow(this.lastActive);
21541 if(last !== false){
21545 this.selectFirstRow();
21547 this.fireEvent("afterselectionchange", this);
21549 "down" : function(e){
21551 this.selectNext(e.shiftKey);
21552 }else if(this.last !== false && this.lastActive !== false){
21553 var last = this.last;
21554 this.selectRange(this.last, this.lastActive+1);
21555 this.grid.getView().focusRow(this.lastActive);
21556 if(last !== false){
21560 this.selectFirstRow();
21562 this.fireEvent("afterselectionchange", this);
21567 var view = this.grid.view;
21568 view.on("refresh", this.onRefresh, this);
21569 view.on("rowupdated", this.onRowUpdated, this);
21570 view.on("rowremoved", this.onRemove, this);
21574 onRefresh : function(){
21575 var ds = this.grid.dataSource, i, v = this.grid.view;
21576 var s = this.selections;
21577 s.each(function(r){
21578 if((i = ds.indexOfId(r.id)) != -1){
21587 onRemove : function(v, index, r){
21588 this.selections.remove(r);
21592 onRowUpdated : function(v, index, r){
21593 if(this.isSelected(r)){
21594 v.onRowSelect(index);
21600 * @param {Array} records The records to select
21601 * @param {Boolean} keepExisting (optional) True to keep existing selections
21603 selectRecords : function(records, keepExisting){
21605 this.clearSelections();
21607 var ds = this.grid.dataSource;
21608 for(var i = 0, len = records.length; i < len; i++){
21609 this.selectRow(ds.indexOf(records[i]), true);
21614 * Gets the number of selected rows.
21617 getCount : function(){
21618 return this.selections.length;
21622 * Selects the first row in the grid.
21624 selectFirstRow : function(){
21629 * Select the last row.
21630 * @param {Boolean} keepExisting (optional) True to keep existing selections
21632 selectLastRow : function(keepExisting){
21633 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21637 * Selects the row immediately following the last selected row.
21638 * @param {Boolean} keepExisting (optional) True to keep existing selections
21640 selectNext : function(keepExisting){
21641 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21642 this.selectRow(this.last+1, keepExisting);
21643 this.grid.getView().focusRow(this.last);
21648 * Selects the row that precedes the last selected row.
21649 * @param {Boolean} keepExisting (optional) True to keep existing selections
21651 selectPrevious : function(keepExisting){
21653 this.selectRow(this.last-1, keepExisting);
21654 this.grid.getView().focusRow(this.last);
21659 * Returns the selected records
21660 * @return {Array} Array of selected records
21662 getSelections : function(){
21663 return [].concat(this.selections.items);
21667 * Returns the first selected record.
21670 getSelected : function(){
21671 return this.selections.itemAt(0);
21676 * Clears all selections.
21678 clearSelections : function(fast){
21679 if(this.locked) return;
21681 var ds = this.grid.dataSource;
21682 var s = this.selections;
21683 s.each(function(r){
21684 this.deselectRow(ds.indexOfId(r.id));
21688 this.selections.clear();
21695 * Selects all rows.
21697 selectAll : function(){
21698 if(this.locked) return;
21699 this.selections.clear();
21700 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21701 this.selectRow(i, true);
21706 * Returns True if there is a selection.
21707 * @return {Boolean}
21709 hasSelection : function(){
21710 return this.selections.length > 0;
21714 * Returns True if the specified row is selected.
21715 * @param {Number/Record} record The record or index of the record to check
21716 * @return {Boolean}
21718 isSelected : function(index){
21719 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21720 return (r && this.selections.key(r.id) ? true : false);
21724 * Returns True if the specified record id is selected.
21725 * @param {String} id The id of record to check
21726 * @return {Boolean}
21728 isIdSelected : function(id){
21729 return (this.selections.key(id) ? true : false);
21733 handleMouseDown : function(e, t){
21734 var view = this.grid.getView(), rowIndex;
21735 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21738 if(e.shiftKey && this.last !== false){
21739 var last = this.last;
21740 this.selectRange(last, rowIndex, e.ctrlKey);
21741 this.last = last; // reset the last
21742 view.focusRow(rowIndex);
21744 var isSelected = this.isSelected(rowIndex);
21745 if(e.button !== 0 && isSelected){
21746 view.focusRow(rowIndex);
21747 }else if(e.ctrlKey && isSelected){
21748 this.deselectRow(rowIndex);
21749 }else if(!isSelected){
21750 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21751 view.focusRow(rowIndex);
21754 this.fireEvent("afterselectionchange", this);
21757 handleDragableRowClick : function(grid, rowIndex, e)
21759 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21760 this.selectRow(rowIndex, false);
21761 grid.view.focusRow(rowIndex);
21762 this.fireEvent("afterselectionchange", this);
21767 * Selects multiple rows.
21768 * @param {Array} rows Array of the indexes of the row to select
21769 * @param {Boolean} keepExisting (optional) True to keep existing selections
21771 selectRows : function(rows, keepExisting){
21773 this.clearSelections();
21775 for(var i = 0, len = rows.length; i < len; i++){
21776 this.selectRow(rows[i], true);
21781 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21782 * @param {Number} startRow The index of the first row in the range
21783 * @param {Number} endRow The index of the last row in the range
21784 * @param {Boolean} keepExisting (optional) True to retain existing selections
21786 selectRange : function(startRow, endRow, keepExisting){
21787 if(this.locked) return;
21789 this.clearSelections();
21791 if(startRow <= endRow){
21792 for(var i = startRow; i <= endRow; i++){
21793 this.selectRow(i, true);
21796 for(var i = startRow; i >= endRow; i--){
21797 this.selectRow(i, true);
21803 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21804 * @param {Number} startRow The index of the first row in the range
21805 * @param {Number} endRow The index of the last row in the range
21807 deselectRange : function(startRow, endRow, preventViewNotify){
21808 if(this.locked) return;
21809 for(var i = startRow; i <= endRow; i++){
21810 this.deselectRow(i, preventViewNotify);
21816 * @param {Number} row The index of the row to select
21817 * @param {Boolean} keepExisting (optional) True to keep existing selections
21819 selectRow : function(index, keepExisting, preventViewNotify){
21820 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21821 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21822 if(!keepExisting || this.singleSelect){
21823 this.clearSelections();
21825 var r = this.grid.dataSource.getAt(index);
21826 this.selections.add(r);
21827 this.last = this.lastActive = index;
21828 if(!preventViewNotify){
21829 this.grid.getView().onRowSelect(index);
21831 this.fireEvent("rowselect", this, index, r);
21832 this.fireEvent("selectionchange", this);
21838 * @param {Number} row The index of the row to deselect
21840 deselectRow : function(index, preventViewNotify){
21841 if(this.locked) return;
21842 if(this.last == index){
21845 if(this.lastActive == index){
21846 this.lastActive = false;
21848 var r = this.grid.dataSource.getAt(index);
21849 this.selections.remove(r);
21850 if(!preventViewNotify){
21851 this.grid.getView().onRowDeselect(index);
21853 this.fireEvent("rowdeselect", this, index);
21854 this.fireEvent("selectionchange", this);
21858 restoreLast : function(){
21860 this.last = this._last;
21865 acceptsNav : function(row, col, cm){
21866 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21870 onEditorKey : function(field, e){
21871 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21876 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21878 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21880 }else if(k == e.ENTER && !e.ctrlKey){
21884 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21886 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21888 }else if(k == e.ESC){
21892 g.startEditing(newCell[0], newCell[1]);
21897 * Ext JS Library 1.1.1
21898 * Copyright(c) 2006-2007, Ext JS, LLC.
21900 * Originally Released Under LGPL - original licence link has changed is not relivant.
21903 * <script type="text/javascript">
21907 * @class Roo.bootstrap.PagingToolbar
21908 * @extends Roo.bootstrap.NavSimplebar
21909 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21911 * Create a new PagingToolbar
21912 * @param {Object} config The config object
21913 * @param {Roo.data.Store} store
21915 Roo.bootstrap.PagingToolbar = function(config)
21917 // old args format still supported... - xtype is prefered..
21918 // created from xtype...
21920 this.ds = config.dataSource;
21922 if (config.store && !this.ds) {
21923 this.store= Roo.factory(config.store, Roo.data);
21924 this.ds = this.store;
21925 this.ds.xmodule = this.xmodule || false;
21928 this.toolbarItems = [];
21929 if (config.items) {
21930 this.toolbarItems = config.items;
21933 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21938 this.bind(this.ds);
21941 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21945 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21947 * @cfg {Roo.data.Store} dataSource
21948 * The underlying data store providing the paged data
21951 * @cfg {String/HTMLElement/Element} container
21952 * container The id or element that will contain the toolbar
21955 * @cfg {Boolean} displayInfo
21956 * True to display the displayMsg (defaults to false)
21959 * @cfg {Number} pageSize
21960 * The number of records to display per page (defaults to 20)
21964 * @cfg {String} displayMsg
21965 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21967 displayMsg : 'Displaying {0} - {1} of {2}',
21969 * @cfg {String} emptyMsg
21970 * The message to display when no records are found (defaults to "No data to display")
21972 emptyMsg : 'No data to display',
21974 * Customizable piece of the default paging text (defaults to "Page")
21977 beforePageText : "Page",
21979 * Customizable piece of the default paging text (defaults to "of %0")
21982 afterPageText : "of {0}",
21984 * Customizable piece of the default paging text (defaults to "First Page")
21987 firstText : "First Page",
21989 * Customizable piece of the default paging text (defaults to "Previous Page")
21992 prevText : "Previous Page",
21994 * Customizable piece of the default paging text (defaults to "Next Page")
21997 nextText : "Next Page",
21999 * Customizable piece of the default paging text (defaults to "Last Page")
22002 lastText : "Last Page",
22004 * Customizable piece of the default paging text (defaults to "Refresh")
22007 refreshText : "Refresh",
22011 onRender : function(ct, position)
22013 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22014 this.navgroup.parentId = this.id;
22015 this.navgroup.onRender(this.el, null);
22016 // add the buttons to the navgroup
22018 if(this.displayInfo){
22019 Roo.log(this.el.select('ul.navbar-nav',true).first());
22020 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22021 this.displayEl = this.el.select('.x-paging-info', true).first();
22022 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22023 // this.displayEl = navel.el.select('span',true).first();
22029 Roo.each(_this.buttons, function(e){ // this might need to use render????
22030 Roo.factory(e).onRender(_this.el, null);
22034 Roo.each(_this.toolbarItems, function(e) {
22035 _this.navgroup.addItem(e);
22039 this.first = this.navgroup.addItem({
22040 tooltip: this.firstText,
22042 icon : 'fa fa-backward',
22044 preventDefault: true,
22045 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22048 this.prev = this.navgroup.addItem({
22049 tooltip: this.prevText,
22051 icon : 'fa fa-step-backward',
22053 preventDefault: true,
22054 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22056 //this.addSeparator();
22059 var field = this.navgroup.addItem( {
22061 cls : 'x-paging-position',
22063 html : this.beforePageText +
22064 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22065 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22068 this.field = field.el.select('input', true).first();
22069 this.field.on("keydown", this.onPagingKeydown, this);
22070 this.field.on("focus", function(){this.dom.select();});
22073 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22074 //this.field.setHeight(18);
22075 //this.addSeparator();
22076 this.next = this.navgroup.addItem({
22077 tooltip: this.nextText,
22079 html : ' <i class="fa fa-step-forward">',
22081 preventDefault: true,
22082 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22084 this.last = this.navgroup.addItem({
22085 tooltip: this.lastText,
22086 icon : 'fa fa-forward',
22089 preventDefault: true,
22090 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22092 //this.addSeparator();
22093 this.loading = this.navgroup.addItem({
22094 tooltip: this.refreshText,
22095 icon: 'fa fa-refresh',
22096 preventDefault: true,
22097 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22103 updateInfo : function(){
22104 if(this.displayEl){
22105 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22106 var msg = count == 0 ?
22110 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22112 this.displayEl.update(msg);
22117 onLoad : function(ds, r, o){
22118 this.cursor = o.params ? o.params.start : 0;
22119 var d = this.getPageData(),
22123 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22124 this.field.dom.value = ap;
22125 this.first.setDisabled(ap == 1);
22126 this.prev.setDisabled(ap == 1);
22127 this.next.setDisabled(ap == ps);
22128 this.last.setDisabled(ap == ps);
22129 this.loading.enable();
22134 getPageData : function(){
22135 var total = this.ds.getTotalCount();
22138 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22139 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22144 onLoadError : function(){
22145 this.loading.enable();
22149 onPagingKeydown : function(e){
22150 var k = e.getKey();
22151 var d = this.getPageData();
22153 var v = this.field.dom.value, pageNum;
22154 if(!v || isNaN(pageNum = parseInt(v, 10))){
22155 this.field.dom.value = d.activePage;
22158 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22159 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22162 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))
22164 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22165 this.field.dom.value = pageNum;
22166 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22169 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22171 var v = this.field.dom.value, pageNum;
22172 var increment = (e.shiftKey) ? 10 : 1;
22173 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22175 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22176 this.field.dom.value = d.activePage;
22179 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22181 this.field.dom.value = parseInt(v, 10) + increment;
22182 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22183 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22190 beforeLoad : function(){
22192 this.loading.disable();
22197 onClick : function(which){
22206 ds.load({params:{start: 0, limit: this.pageSize}});
22209 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22212 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22215 var total = ds.getTotalCount();
22216 var extra = total % this.pageSize;
22217 var lastStart = extra ? (total - extra) : total-this.pageSize;
22218 ds.load({params:{start: lastStart, limit: this.pageSize}});
22221 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22227 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22228 * @param {Roo.data.Store} store The data store to unbind
22230 unbind : function(ds){
22231 ds.un("beforeload", this.beforeLoad, this);
22232 ds.un("load", this.onLoad, this);
22233 ds.un("loadexception", this.onLoadError, this);
22234 ds.un("remove", this.updateInfo, this);
22235 ds.un("add", this.updateInfo, this);
22236 this.ds = undefined;
22240 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22241 * @param {Roo.data.Store} store The data store to bind
22243 bind : function(ds){
22244 ds.on("beforeload", this.beforeLoad, this);
22245 ds.on("load", this.onLoad, this);
22246 ds.on("loadexception", this.onLoadError, this);
22247 ds.on("remove", this.updateInfo, this);
22248 ds.on("add", this.updateInfo, this);
22259 * @class Roo.bootstrap.MessageBar
22260 * @extends Roo.bootstrap.Component
22261 * Bootstrap MessageBar class
22262 * @cfg {String} html contents of the MessageBar
22263 * @cfg {String} weight (info | success | warning | danger) default info
22264 * @cfg {String} beforeClass insert the bar before the given class
22265 * @cfg {Boolean} closable (true | false) default false
22266 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22269 * Create a new Element
22270 * @param {Object} config The config object
22273 Roo.bootstrap.MessageBar = function(config){
22274 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22277 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22283 beforeClass: 'bootstrap-sticky-wrap',
22285 getAutoCreate : function(){
22289 cls: 'alert alert-dismissable alert-' + this.weight,
22294 html: this.html || ''
22300 cfg.cls += ' alert-messages-fixed';
22314 onRender : function(ct, position)
22316 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22319 var cfg = Roo.apply({}, this.getAutoCreate());
22323 cfg.cls += ' ' + this.cls;
22326 cfg.style = this.style;
22328 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22330 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22333 this.el.select('>button.close').on('click', this.hide, this);
22339 if (!this.rendered) {
22345 this.fireEvent('show', this);
22351 if (!this.rendered) {
22357 this.fireEvent('hide', this);
22360 update : function()
22362 // var e = this.el.dom.firstChild;
22364 // if(this.closable){
22365 // e = e.nextSibling;
22368 // e.data = this.html || '';
22370 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22386 * @class Roo.bootstrap.Graph
22387 * @extends Roo.bootstrap.Component
22388 * Bootstrap Graph class
22392 @cfg {String} graphtype bar | vbar | pie
22393 @cfg {number} g_x coodinator | centre x (pie)
22394 @cfg {number} g_y coodinator | centre y (pie)
22395 @cfg {number} g_r radius (pie)
22396 @cfg {number} g_height height of the chart (respected by all elements in the set)
22397 @cfg {number} g_width width of the chart (respected by all elements in the set)
22398 @cfg {Object} title The title of the chart
22401 -opts (object) options for the chart
22403 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22404 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22406 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.
22407 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22409 o stretch (boolean)
22411 -opts (object) options for the pie
22414 o startAngle (number)
22415 o endAngle (number)
22419 * Create a new Input
22420 * @param {Object} config The config object
22423 Roo.bootstrap.Graph = function(config){
22424 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22430 * The img click event for the img.
22431 * @param {Roo.EventObject} e
22437 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22448 //g_colors: this.colors,
22455 getAutoCreate : function(){
22466 onRender : function(ct,position){
22467 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22468 this.raphael = Raphael(this.el.dom);
22470 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22471 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22472 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22473 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22475 r.text(160, 10, "Single Series Chart").attr(txtattr);
22476 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22477 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22478 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22480 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22481 r.barchart(330, 10, 300, 220, data1);
22482 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22483 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22486 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22487 // r.barchart(30, 30, 560, 250, xdata, {
22488 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22489 // axis : "0 0 1 1",
22490 // axisxlabels : xdata
22491 // //yvalues : cols,
22494 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22496 // this.load(null,xdata,{
22497 // axis : "0 0 1 1",
22498 // axisxlabels : xdata
22503 load : function(graphtype,xdata,opts){
22504 this.raphael.clear();
22506 graphtype = this.graphtype;
22511 var r = this.raphael,
22512 fin = function () {
22513 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22515 fout = function () {
22516 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22518 pfin = function() {
22519 this.sector.stop();
22520 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22523 this.label[0].stop();
22524 this.label[0].attr({ r: 7.5 });
22525 this.label[1].attr({ "font-weight": 800 });
22528 pfout = function() {
22529 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22532 this.label[0].animate({ r: 5 }, 500, "bounce");
22533 this.label[1].attr({ "font-weight": 400 });
22539 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22542 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22545 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22546 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22548 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22555 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22560 setTitle: function(o)
22565 initEvents: function() {
22568 this.el.on('click', this.onClick, this);
22572 onClick : function(e)
22574 Roo.log('img onclick');
22575 this.fireEvent('click', this, e);
22587 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22590 * @class Roo.bootstrap.dash.NumberBox
22591 * @extends Roo.bootstrap.Component
22592 * Bootstrap NumberBox class
22593 * @cfg {String} headline Box headline
22594 * @cfg {String} content Box content
22595 * @cfg {String} icon Box icon
22596 * @cfg {String} footer Footer text
22597 * @cfg {String} fhref Footer href
22600 * Create a new NumberBox
22601 * @param {Object} config The config object
22605 Roo.bootstrap.dash.NumberBox = function(config){
22606 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22610 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22619 getAutoCreate : function(){
22623 cls : 'small-box ',
22631 cls : 'roo-headline',
22632 html : this.headline
22636 cls : 'roo-content',
22637 html : this.content
22651 cls : 'ion ' + this.icon
22660 cls : 'small-box-footer',
22661 href : this.fhref || '#',
22665 cfg.cn.push(footer);
22672 onRender : function(ct,position){
22673 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22680 setHeadline: function (value)
22682 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22685 setFooter: function (value, href)
22687 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22690 this.el.select('a.small-box-footer',true).first().attr('href', href);
22695 setContent: function (value)
22697 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22700 initEvents: function()
22714 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22717 * @class Roo.bootstrap.dash.TabBox
22718 * @extends Roo.bootstrap.Component
22719 * Bootstrap TabBox class
22720 * @cfg {String} title Title of the TabBox
22721 * @cfg {String} icon Icon of the TabBox
22722 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22723 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22726 * Create a new TabBox
22727 * @param {Object} config The config object
22731 Roo.bootstrap.dash.TabBox = function(config){
22732 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22737 * When a pane is added
22738 * @param {Roo.bootstrap.dash.TabPane} pane
22742 * @event activatepane
22743 * When a pane is activated
22744 * @param {Roo.bootstrap.dash.TabPane} pane
22746 "activatepane" : true
22754 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22759 tabScrollable : false,
22761 getChildContainer : function()
22763 return this.el.select('.tab-content', true).first();
22766 getAutoCreate : function(){
22770 cls: 'pull-left header',
22778 cls: 'fa ' + this.icon
22784 cls: 'nav nav-tabs pull-right',
22790 if(this.tabScrollable){
22797 cls: 'nav nav-tabs pull-right',
22808 cls: 'nav-tabs-custom',
22813 cls: 'tab-content no-padding',
22821 initEvents : function()
22823 //Roo.log('add add pane handler');
22824 this.on('addpane', this.onAddPane, this);
22827 * Updates the box title
22828 * @param {String} html to set the title to.
22830 setTitle : function(value)
22832 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22834 onAddPane : function(pane)
22836 this.panes.push(pane);
22837 //Roo.log('addpane');
22839 // tabs are rendere left to right..
22840 if(!this.showtabs){
22844 var ctr = this.el.select('.nav-tabs', true).first();
22847 var existing = ctr.select('.nav-tab',true);
22848 var qty = existing.getCount();;
22851 var tab = ctr.createChild({
22853 cls : 'nav-tab' + (qty ? '' : ' active'),
22861 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22864 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22866 pane.el.addClass('active');
22871 onTabClick : function(ev,un,ob,pane)
22873 //Roo.log('tab - prev default');
22874 ev.preventDefault();
22877 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22878 pane.tab.addClass('active');
22879 //Roo.log(pane.title);
22880 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22881 // technically we should have a deactivate event.. but maybe add later.
22882 // and it should not de-activate the selected tab...
22883 this.fireEvent('activatepane', pane);
22884 pane.el.addClass('active');
22885 pane.fireEvent('activate');
22890 getActivePane : function()
22893 Roo.each(this.panes, function(p) {
22894 if(p.el.hasClass('active')){
22915 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22917 * @class Roo.bootstrap.TabPane
22918 * @extends Roo.bootstrap.Component
22919 * Bootstrap TabPane class
22920 * @cfg {Boolean} active (false | true) Default false
22921 * @cfg {String} title title of panel
22925 * Create a new TabPane
22926 * @param {Object} config The config object
22929 Roo.bootstrap.dash.TabPane = function(config){
22930 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22936 * When a pane is activated
22937 * @param {Roo.bootstrap.dash.TabPane} pane
22944 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22949 // the tabBox that this is attached to.
22952 getAutoCreate : function()
22960 cfg.cls += ' active';
22965 initEvents : function()
22967 //Roo.log('trigger add pane handler');
22968 this.parent().fireEvent('addpane', this)
22972 * Updates the tab title
22973 * @param {String} html to set the title to.
22975 setTitle: function(str)
22981 this.tab.select('a', true).first().dom.innerHTML = str;
22998 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23001 * @class Roo.bootstrap.menu.Menu
23002 * @extends Roo.bootstrap.Component
23003 * Bootstrap Menu class - container for Menu
23004 * @cfg {String} html Text of the menu
23005 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23006 * @cfg {String} icon Font awesome icon
23007 * @cfg {String} pos Menu align to (top | bottom) default bottom
23011 * Create a new Menu
23012 * @param {Object} config The config object
23016 Roo.bootstrap.menu.Menu = function(config){
23017 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23021 * @event beforeshow
23022 * Fires before this menu is displayed
23023 * @param {Roo.bootstrap.menu.Menu} this
23027 * @event beforehide
23028 * Fires before this menu is hidden
23029 * @param {Roo.bootstrap.menu.Menu} this
23034 * Fires after this menu is displayed
23035 * @param {Roo.bootstrap.menu.Menu} this
23040 * Fires after this menu is hidden
23041 * @param {Roo.bootstrap.menu.Menu} this
23046 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23047 * @param {Roo.bootstrap.menu.Menu} this
23048 * @param {Roo.EventObject} e
23055 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23059 weight : 'default',
23064 getChildContainer : function() {
23065 if(this.isSubMenu){
23069 return this.el.select('ul.dropdown-menu', true).first();
23072 getAutoCreate : function()
23077 cls : 'roo-menu-text',
23085 cls : 'fa ' + this.icon
23096 cls : 'dropdown-button btn btn-' + this.weight,
23101 cls : 'dropdown-toggle btn btn-' + this.weight,
23111 cls : 'dropdown-menu'
23117 if(this.pos == 'top'){
23118 cfg.cls += ' dropup';
23121 if(this.isSubMenu){
23124 cls : 'dropdown-menu'
23131 onRender : function(ct, position)
23133 this.isSubMenu = ct.hasClass('dropdown-submenu');
23135 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23138 initEvents : function()
23140 if(this.isSubMenu){
23144 this.hidden = true;
23146 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23147 this.triggerEl.on('click', this.onTriggerPress, this);
23149 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23150 this.buttonEl.on('click', this.onClick, this);
23156 if(this.isSubMenu){
23160 return this.el.select('ul.dropdown-menu', true).first();
23163 onClick : function(e)
23165 this.fireEvent("click", this, e);
23168 onTriggerPress : function(e)
23170 if (this.isVisible()) {
23177 isVisible : function(){
23178 return !this.hidden;
23183 this.fireEvent("beforeshow", this);
23185 this.hidden = false;
23186 this.el.addClass('open');
23188 Roo.get(document).on("mouseup", this.onMouseUp, this);
23190 this.fireEvent("show", this);
23197 this.fireEvent("beforehide", this);
23199 this.hidden = true;
23200 this.el.removeClass('open');
23202 Roo.get(document).un("mouseup", this.onMouseUp);
23204 this.fireEvent("hide", this);
23207 onMouseUp : function()
23221 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23224 * @class Roo.bootstrap.menu.Item
23225 * @extends Roo.bootstrap.Component
23226 * Bootstrap MenuItem class
23227 * @cfg {Boolean} submenu (true | false) default false
23228 * @cfg {String} html text of the item
23229 * @cfg {String} href the link
23230 * @cfg {Boolean} disable (true | false) default false
23231 * @cfg {Boolean} preventDefault (true | false) default true
23232 * @cfg {String} icon Font awesome icon
23233 * @cfg {String} pos Submenu align to (left | right) default right
23237 * Create a new Item
23238 * @param {Object} config The config object
23242 Roo.bootstrap.menu.Item = function(config){
23243 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23247 * Fires when the mouse is hovering over this menu
23248 * @param {Roo.bootstrap.menu.Item} this
23249 * @param {Roo.EventObject} e
23254 * Fires when the mouse exits this menu
23255 * @param {Roo.bootstrap.menu.Item} this
23256 * @param {Roo.EventObject} e
23262 * The raw click event for the entire grid.
23263 * @param {Roo.EventObject} e
23269 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23274 preventDefault: true,
23279 getAutoCreate : function()
23284 cls : 'roo-menu-item-text',
23292 cls : 'fa ' + this.icon
23301 href : this.href || '#',
23308 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23312 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23314 if(this.pos == 'left'){
23315 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23322 initEvents : function()
23324 this.el.on('mouseover', this.onMouseOver, this);
23325 this.el.on('mouseout', this.onMouseOut, this);
23327 this.el.select('a', true).first().on('click', this.onClick, this);
23331 onClick : function(e)
23333 if(this.preventDefault){
23334 e.preventDefault();
23337 this.fireEvent("click", this, e);
23340 onMouseOver : function(e)
23342 if(this.submenu && this.pos == 'left'){
23343 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23346 this.fireEvent("mouseover", this, e);
23349 onMouseOut : function(e)
23351 this.fireEvent("mouseout", this, e);
23363 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23366 * @class Roo.bootstrap.menu.Separator
23367 * @extends Roo.bootstrap.Component
23368 * Bootstrap Separator class
23371 * Create a new Separator
23372 * @param {Object} config The config object
23376 Roo.bootstrap.menu.Separator = function(config){
23377 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23380 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23382 getAutoCreate : function(){
23403 * @class Roo.bootstrap.Tooltip
23404 * Bootstrap Tooltip class
23405 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23406 * to determine which dom element triggers the tooltip.
23408 * It needs to add support for additional attributes like tooltip-position
23411 * Create a new Toolti
23412 * @param {Object} config The config object
23415 Roo.bootstrap.Tooltip = function(config){
23416 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23419 Roo.apply(Roo.bootstrap.Tooltip, {
23421 * @function init initialize tooltip monitoring.
23425 currentTip : false,
23426 currentRegion : false,
23432 Roo.get(document).on('mouseover', this.enter ,this);
23433 Roo.get(document).on('mouseout', this.leave, this);
23436 this.currentTip = new Roo.bootstrap.Tooltip();
23439 enter : function(ev)
23441 var dom = ev.getTarget();
23443 //Roo.log(['enter',dom]);
23444 var el = Roo.fly(dom);
23445 if (this.currentEl) {
23447 //Roo.log(this.currentEl);
23448 //Roo.log(this.currentEl.contains(dom));
23449 if (this.currentEl == el) {
23452 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23458 if (this.currentTip.el) {
23459 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23464 // you can not look for children, as if el is the body.. then everythign is the child..
23465 if (!el.attr('tooltip')) { //
23466 if (!el.select("[tooltip]").elements.length) {
23469 // is the mouse over this child...?
23470 bindEl = el.select("[tooltip]").first();
23471 var xy = ev.getXY();
23472 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23473 //Roo.log("not in region.");
23476 //Roo.log("child element over..");
23479 this.currentEl = bindEl;
23480 this.currentTip.bind(bindEl);
23481 this.currentRegion = Roo.lib.Region.getRegion(dom);
23482 this.currentTip.enter();
23485 leave : function(ev)
23487 var dom = ev.getTarget();
23488 //Roo.log(['leave',dom]);
23489 if (!this.currentEl) {
23494 if (dom != this.currentEl.dom) {
23497 var xy = ev.getXY();
23498 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23501 // only activate leave if mouse cursor is outside... bounding box..
23506 if (this.currentTip) {
23507 this.currentTip.leave();
23509 //Roo.log('clear currentEl');
23510 this.currentEl = false;
23515 'left' : ['r-l', [-2,0], 'right'],
23516 'right' : ['l-r', [2,0], 'left'],
23517 'bottom' : ['t-b', [0,2], 'top'],
23518 'top' : [ 'b-t', [0,-2], 'bottom']
23524 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23529 delay : null, // can be { show : 300 , hide: 500}
23533 hoverState : null, //???
23535 placement : 'bottom',
23537 getAutoCreate : function(){
23544 cls : 'tooltip-arrow'
23547 cls : 'tooltip-inner'
23554 bind : function(el)
23560 enter : function () {
23562 if (this.timeout != null) {
23563 clearTimeout(this.timeout);
23566 this.hoverState = 'in';
23567 //Roo.log("enter - show");
23568 if (!this.delay || !this.delay.show) {
23573 this.timeout = setTimeout(function () {
23574 if (_t.hoverState == 'in') {
23577 }, this.delay.show);
23581 clearTimeout(this.timeout);
23583 this.hoverState = 'out';
23584 if (!this.delay || !this.delay.hide) {
23590 this.timeout = setTimeout(function () {
23591 //Roo.log("leave - timeout");
23593 if (_t.hoverState == 'out') {
23595 Roo.bootstrap.Tooltip.currentEl = false;
23603 this.render(document.body);
23606 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23608 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23610 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23612 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23614 var placement = typeof this.placement == 'function' ?
23615 this.placement.call(this, this.el, on_el) :
23618 var autoToken = /\s?auto?\s?/i;
23619 var autoPlace = autoToken.test(placement);
23621 placement = placement.replace(autoToken, '') || 'top';
23625 //this.el.setXY([0,0]);
23627 //this.el.dom.style.display='block';
23629 //this.el.appendTo(on_el);
23631 var p = this.getPosition();
23632 var box = this.el.getBox();
23638 var align = Roo.bootstrap.Tooltip.alignment[placement];
23640 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23642 if(placement == 'top' || placement == 'bottom'){
23644 placement = 'right';
23647 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23648 placement = 'left';
23652 align = Roo.bootstrap.Tooltip.alignment[placement];
23654 this.el.alignTo(this.bindEl, align[0],align[1]);
23655 //var arrow = this.el.select('.arrow',true).first();
23656 //arrow.set(align[2],
23658 this.el.addClass(placement);
23660 this.el.addClass('in fade');
23662 this.hoverState = null;
23664 if (this.el.hasClass('fade')) {
23675 //this.el.setXY([0,0]);
23676 this.el.removeClass('in');
23692 * @class Roo.bootstrap.LocationPicker
23693 * @extends Roo.bootstrap.Component
23694 * Bootstrap LocationPicker class
23695 * @cfg {Number} latitude Position when init default 0
23696 * @cfg {Number} longitude Position when init default 0
23697 * @cfg {Number} zoom default 15
23698 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23699 * @cfg {Boolean} mapTypeControl default false
23700 * @cfg {Boolean} disableDoubleClickZoom default false
23701 * @cfg {Boolean} scrollwheel default true
23702 * @cfg {Boolean} streetViewControl default false
23703 * @cfg {Number} radius default 0
23704 * @cfg {String} locationName
23705 * @cfg {Boolean} draggable default true
23706 * @cfg {Boolean} enableAutocomplete default false
23707 * @cfg {Boolean} enableReverseGeocode default true
23708 * @cfg {String} markerTitle
23711 * Create a new LocationPicker
23712 * @param {Object} config The config object
23716 Roo.bootstrap.LocationPicker = function(config){
23718 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23723 * Fires when the picker initialized.
23724 * @param {Roo.bootstrap.LocationPicker} this
23725 * @param {Google Location} location
23729 * @event positionchanged
23730 * Fires when the picker position changed.
23731 * @param {Roo.bootstrap.LocationPicker} this
23732 * @param {Google Location} location
23734 positionchanged : true,
23737 * Fires when the map resize.
23738 * @param {Roo.bootstrap.LocationPicker} this
23743 * Fires when the map show.
23744 * @param {Roo.bootstrap.LocationPicker} this
23749 * Fires when the map hide.
23750 * @param {Roo.bootstrap.LocationPicker} this
23755 * Fires when click the map.
23756 * @param {Roo.bootstrap.LocationPicker} this
23757 * @param {Map event} e
23761 * @event mapRightClick
23762 * Fires when right click the map.
23763 * @param {Roo.bootstrap.LocationPicker} this
23764 * @param {Map event} e
23766 mapRightClick : true,
23768 * @event markerClick
23769 * Fires when click the marker.
23770 * @param {Roo.bootstrap.LocationPicker} this
23771 * @param {Map event} e
23773 markerClick : true,
23775 * @event markerRightClick
23776 * Fires when right click the marker.
23777 * @param {Roo.bootstrap.LocationPicker} this
23778 * @param {Map event} e
23780 markerRightClick : true,
23782 * @event OverlayViewDraw
23783 * Fires when OverlayView Draw
23784 * @param {Roo.bootstrap.LocationPicker} this
23786 OverlayViewDraw : true,
23788 * @event OverlayViewOnAdd
23789 * Fires when OverlayView Draw
23790 * @param {Roo.bootstrap.LocationPicker} this
23792 OverlayViewOnAdd : true,
23794 * @event OverlayViewOnRemove
23795 * Fires when OverlayView Draw
23796 * @param {Roo.bootstrap.LocationPicker} this
23798 OverlayViewOnRemove : true,
23800 * @event OverlayViewShow
23801 * Fires when OverlayView Draw
23802 * @param {Roo.bootstrap.LocationPicker} this
23803 * @param {Pixel} cpx
23805 OverlayViewShow : true,
23807 * @event OverlayViewHide
23808 * Fires when OverlayView Draw
23809 * @param {Roo.bootstrap.LocationPicker} this
23811 OverlayViewHide : true,
23813 * @event loadexception
23814 * Fires when load google lib failed.
23815 * @param {Roo.bootstrap.LocationPicker} this
23817 loadexception : true
23822 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23824 gMapContext: false,
23830 mapTypeControl: false,
23831 disableDoubleClickZoom: false,
23833 streetViewControl: false,
23837 enableAutocomplete: false,
23838 enableReverseGeocode: true,
23841 getAutoCreate: function()
23846 cls: 'roo-location-picker'
23852 initEvents: function(ct, position)
23854 if(!this.el.getWidth() || this.isApplied()){
23858 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23863 initial: function()
23865 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23866 this.fireEvent('loadexception', this);
23870 if(!this.mapTypeId){
23871 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23874 this.gMapContext = this.GMapContext();
23876 this.initOverlayView();
23878 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23882 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23883 _this.setPosition(_this.gMapContext.marker.position);
23886 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23887 _this.fireEvent('mapClick', this, event);
23891 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23892 _this.fireEvent('mapRightClick', this, event);
23896 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23897 _this.fireEvent('markerClick', this, event);
23901 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23902 _this.fireEvent('markerRightClick', this, event);
23906 this.setPosition(this.gMapContext.location);
23908 this.fireEvent('initial', this, this.gMapContext.location);
23911 initOverlayView: function()
23915 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23919 _this.fireEvent('OverlayViewDraw', _this);
23924 _this.fireEvent('OverlayViewOnAdd', _this);
23927 onRemove: function()
23929 _this.fireEvent('OverlayViewOnRemove', _this);
23932 show: function(cpx)
23934 _this.fireEvent('OverlayViewShow', _this, cpx);
23939 _this.fireEvent('OverlayViewHide', _this);
23945 fromLatLngToContainerPixel: function(event)
23947 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23950 isApplied: function()
23952 return this.getGmapContext() == false ? false : true;
23955 getGmapContext: function()
23957 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23960 GMapContext: function()
23962 var position = new google.maps.LatLng(this.latitude, this.longitude);
23964 var _map = new google.maps.Map(this.el.dom, {
23967 mapTypeId: this.mapTypeId,
23968 mapTypeControl: this.mapTypeControl,
23969 disableDoubleClickZoom: this.disableDoubleClickZoom,
23970 scrollwheel: this.scrollwheel,
23971 streetViewControl: this.streetViewControl,
23972 locationName: this.locationName,
23973 draggable: this.draggable,
23974 enableAutocomplete: this.enableAutocomplete,
23975 enableReverseGeocode: this.enableReverseGeocode
23978 var _marker = new google.maps.Marker({
23979 position: position,
23981 title: this.markerTitle,
23982 draggable: this.draggable
23989 location: position,
23990 radius: this.radius,
23991 locationName: this.locationName,
23992 addressComponents: {
23993 formatted_address: null,
23994 addressLine1: null,
23995 addressLine2: null,
23997 streetNumber: null,
24001 stateOrProvince: null
24004 domContainer: this.el.dom,
24005 geodecoder: new google.maps.Geocoder()
24009 drawCircle: function(center, radius, options)
24011 if (this.gMapContext.circle != null) {
24012 this.gMapContext.circle.setMap(null);
24016 options = Roo.apply({}, options, {
24017 strokeColor: "#0000FF",
24018 strokeOpacity: .35,
24020 fillColor: "#0000FF",
24024 options.map = this.gMapContext.map;
24025 options.radius = radius;
24026 options.center = center;
24027 this.gMapContext.circle = new google.maps.Circle(options);
24028 return this.gMapContext.circle;
24034 setPosition: function(location)
24036 this.gMapContext.location = location;
24037 this.gMapContext.marker.setPosition(location);
24038 this.gMapContext.map.panTo(location);
24039 this.drawCircle(location, this.gMapContext.radius, {});
24043 if (this.gMapContext.settings.enableReverseGeocode) {
24044 this.gMapContext.geodecoder.geocode({
24045 latLng: this.gMapContext.location
24046 }, function(results, status) {
24048 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24049 _this.gMapContext.locationName = results[0].formatted_address;
24050 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24052 _this.fireEvent('positionchanged', this, location);
24059 this.fireEvent('positionchanged', this, location);
24064 google.maps.event.trigger(this.gMapContext.map, "resize");
24066 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24068 this.fireEvent('resize', this);
24071 setPositionByLatLng: function(latitude, longitude)
24073 this.setPosition(new google.maps.LatLng(latitude, longitude));
24076 getCurrentPosition: function()
24079 latitude: this.gMapContext.location.lat(),
24080 longitude: this.gMapContext.location.lng()
24084 getAddressName: function()
24086 return this.gMapContext.locationName;
24089 getAddressComponents: function()
24091 return this.gMapContext.addressComponents;
24094 address_component_from_google_geocode: function(address_components)
24098 for (var i = 0; i < address_components.length; i++) {
24099 var component = address_components[i];
24100 if (component.types.indexOf("postal_code") >= 0) {
24101 result.postalCode = component.short_name;
24102 } else if (component.types.indexOf("street_number") >= 0) {
24103 result.streetNumber = component.short_name;
24104 } else if (component.types.indexOf("route") >= 0) {
24105 result.streetName = component.short_name;
24106 } else if (component.types.indexOf("neighborhood") >= 0) {
24107 result.city = component.short_name;
24108 } else if (component.types.indexOf("locality") >= 0) {
24109 result.city = component.short_name;
24110 } else if (component.types.indexOf("sublocality") >= 0) {
24111 result.district = component.short_name;
24112 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24113 result.stateOrProvince = component.short_name;
24114 } else if (component.types.indexOf("country") >= 0) {
24115 result.country = component.short_name;
24119 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24120 result.addressLine2 = "";
24124 setZoomLevel: function(zoom)
24126 this.gMapContext.map.setZoom(zoom);
24139 this.fireEvent('show', this);
24150 this.fireEvent('hide', this);
24155 Roo.apply(Roo.bootstrap.LocationPicker, {
24157 OverlayView : function(map, options)
24159 options = options || {};
24173 * @class Roo.bootstrap.Alert
24174 * @extends Roo.bootstrap.Component
24175 * Bootstrap Alert class
24176 * @cfg {String} title The title of alert
24177 * @cfg {String} html The content of alert
24178 * @cfg {String} weight ( success | info | warning | danger )
24179 * @cfg {String} faicon font-awesomeicon
24182 * Create a new alert
24183 * @param {Object} config The config object
24187 Roo.bootstrap.Alert = function(config){
24188 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24192 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24199 getAutoCreate : function()
24208 cls : 'roo-alert-icon'
24213 cls : 'roo-alert-title',
24218 cls : 'roo-alert-text',
24225 cfg.cn[0].cls += ' fa ' + this.faicon;
24229 cfg.cls += ' alert-' + this.weight;
24235 initEvents: function()
24237 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24240 setTitle : function(str)
24242 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24245 setText : function(str)
24247 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24250 setWeight : function(weight)
24253 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24256 this.weight = weight;
24258 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24261 setIcon : function(icon)
24264 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24267 this.faicon = icon;
24269 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24290 * @class Roo.bootstrap.UploadCropbox
24291 * @extends Roo.bootstrap.Component
24292 * Bootstrap UploadCropbox class
24293 * @cfg {String} emptyText show when image has been loaded
24294 * @cfg {String} rotateNotify show when image too small to rotate
24295 * @cfg {Number} errorTimeout default 3000
24296 * @cfg {Number} minWidth default 300
24297 * @cfg {Number} minHeight default 300
24298 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24299 * @cfg {Boolean} isDocument (true|false) default false
24300 * @cfg {String} url action url
24301 * @cfg {String} paramName default 'imageUpload'
24302 * @cfg {String} method default POST
24303 * @cfg {Boolean} loadMask (true|false) default true
24304 * @cfg {Boolean} loadingText default 'Loading...'
24307 * Create a new UploadCropbox
24308 * @param {Object} config The config object
24311 Roo.bootstrap.UploadCropbox = function(config){
24312 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24316 * @event beforeselectfile
24317 * Fire before select file
24318 * @param {Roo.bootstrap.UploadCropbox} this
24320 "beforeselectfile" : true,
24323 * Fire after initEvent
24324 * @param {Roo.bootstrap.UploadCropbox} this
24329 * Fire after initEvent
24330 * @param {Roo.bootstrap.UploadCropbox} this
24331 * @param {String} data
24336 * Fire when preparing the file data
24337 * @param {Roo.bootstrap.UploadCropbox} this
24338 * @param {Object} file
24343 * Fire when get exception
24344 * @param {Roo.bootstrap.UploadCropbox} this
24345 * @param {XMLHttpRequest} xhr
24347 "exception" : true,
24349 * @event beforeloadcanvas
24350 * Fire before load the canvas
24351 * @param {Roo.bootstrap.UploadCropbox} this
24352 * @param {String} src
24354 "beforeloadcanvas" : true,
24357 * Fire when trash image
24358 * @param {Roo.bootstrap.UploadCropbox} this
24363 * Fire when download the image
24364 * @param {Roo.bootstrap.UploadCropbox} this
24368 * @event footerbuttonclick
24369 * Fire when footerbuttonclick
24370 * @param {Roo.bootstrap.UploadCropbox} this
24371 * @param {String} type
24373 "footerbuttonclick" : true,
24377 * @param {Roo.bootstrap.UploadCropbox} this
24382 * Fire when rotate the image
24383 * @param {Roo.bootstrap.UploadCropbox} this
24384 * @param {String} pos
24389 * Fire when inspect the file
24390 * @param {Roo.bootstrap.UploadCropbox} this
24391 * @param {Object} file
24396 * Fire when xhr upload the file
24397 * @param {Roo.bootstrap.UploadCropbox} this
24398 * @param {Object} data
24403 * Fire when arrange the file data
24404 * @param {Roo.bootstrap.UploadCropbox} this
24405 * @param {Object} formData
24410 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24413 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24415 emptyText : 'Click to upload image',
24416 rotateNotify : 'Image is too small to rotate',
24417 errorTimeout : 3000,
24431 cropType : 'image/jpeg',
24433 canvasLoaded : false,
24434 isDocument : false,
24436 paramName : 'imageUpload',
24438 loadingText : 'Loading...',
24441 getAutoCreate : function()
24445 cls : 'roo-upload-cropbox',
24449 cls : 'roo-upload-cropbox-selector',
24454 cls : 'roo-upload-cropbox-body',
24455 style : 'cursor:pointer',
24459 cls : 'roo-upload-cropbox-preview'
24463 cls : 'roo-upload-cropbox-thumb'
24467 cls : 'roo-upload-cropbox-empty-notify',
24468 html : this.emptyText
24472 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24473 html : this.rotateNotify
24479 cls : 'roo-upload-cropbox-footer',
24482 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24492 onRender : function(ct, position)
24494 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24496 if (this.buttons.length) {
24498 Roo.each(this.buttons, function(bb) {
24500 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24502 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24508 this.maskEl = this.el;
24512 initEvents : function()
24514 this.urlAPI = (window.createObjectURL && window) ||
24515 (window.URL && URL.revokeObjectURL && URL) ||
24516 (window.webkitURL && webkitURL);
24518 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24519 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24521 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24522 this.selectorEl.hide();
24524 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24525 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24527 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24528 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24529 this.thumbEl.hide();
24531 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24532 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24534 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24535 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24536 this.errorEl.hide();
24538 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24539 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24540 this.footerEl.hide();
24542 this.setThumbBoxSize();
24548 this.fireEvent('initial', this);
24555 window.addEventListener("resize", function() { _this.resize(); } );
24557 this.bodyEl.on('click', this.beforeSelectFile, this);
24560 this.bodyEl.on('touchstart', this.onTouchStart, this);
24561 this.bodyEl.on('touchmove', this.onTouchMove, this);
24562 this.bodyEl.on('touchend', this.onTouchEnd, this);
24566 this.bodyEl.on('mousedown', this.onMouseDown, this);
24567 this.bodyEl.on('mousemove', this.onMouseMove, this);
24568 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24569 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24570 Roo.get(document).on('mouseup', this.onMouseUp, this);
24573 this.selectorEl.on('change', this.onFileSelected, this);
24579 this.baseScale = 1;
24581 this.baseRotate = 1;
24582 this.dragable = false;
24583 this.pinching = false;
24586 this.cropData = false;
24587 this.notifyEl.dom.innerHTML = this.emptyText;
24589 this.selectorEl.dom.value = '';
24593 resize : function()
24595 if(this.fireEvent('resize', this) != false){
24596 this.setThumbBoxPosition();
24597 this.setCanvasPosition();
24601 onFooterButtonClick : function(e, el, o, type)
24604 case 'rotate-left' :
24605 this.onRotateLeft(e);
24607 case 'rotate-right' :
24608 this.onRotateRight(e);
24611 this.beforeSelectFile(e);
24626 this.fireEvent('footerbuttonclick', this, type);
24629 beforeSelectFile : function(e)
24631 e.preventDefault();
24633 if(this.fireEvent('beforeselectfile', this) != false){
24634 this.selectorEl.dom.click();
24638 onFileSelected : function(e)
24640 e.preventDefault();
24642 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24646 var file = this.selectorEl.dom.files[0];
24648 if(this.fireEvent('inspect', this, file) != false){
24649 this.prepare(file);
24654 trash : function(e)
24656 this.fireEvent('trash', this);
24659 download : function(e)
24661 this.fireEvent('download', this);
24664 loadCanvas : function(src)
24666 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24670 this.imageEl = document.createElement('img');
24674 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24676 this.imageEl.src = src;
24680 onLoadCanvas : function()
24682 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24683 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24685 this.bodyEl.un('click', this.beforeSelectFile, this);
24687 this.notifyEl.hide();
24688 this.thumbEl.show();
24689 this.footerEl.show();
24691 this.baseRotateLevel();
24693 if(this.isDocument){
24694 this.setThumbBoxSize();
24697 this.setThumbBoxPosition();
24699 this.baseScaleLevel();
24705 this.canvasLoaded = true;
24708 this.maskEl.unmask();
24713 setCanvasPosition : function()
24715 if(!this.canvasEl){
24719 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24720 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24722 this.previewEl.setLeft(pw);
24723 this.previewEl.setTop(ph);
24727 onMouseDown : function(e)
24731 this.dragable = true;
24732 this.pinching = false;
24734 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24735 this.dragable = false;
24739 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24740 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24744 onMouseMove : function(e)
24748 if(!this.canvasLoaded){
24752 if (!this.dragable){
24756 var minX = Math.ceil(this.thumbEl.getLeft(true));
24757 var minY = Math.ceil(this.thumbEl.getTop(true));
24759 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24760 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24762 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24763 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24765 x = x - this.mouseX;
24766 y = y - this.mouseY;
24768 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24769 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24771 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24772 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24774 this.previewEl.setLeft(bgX);
24775 this.previewEl.setTop(bgY);
24777 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24778 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24781 onMouseUp : function(e)
24785 this.dragable = false;
24788 onMouseWheel : function(e)
24792 this.startScale = this.scale;
24794 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24796 if(!this.zoomable()){
24797 this.scale = this.startScale;
24806 zoomable : function()
24808 var minScale = this.thumbEl.getWidth() / this.minWidth;
24810 if(this.minWidth < this.minHeight){
24811 minScale = this.thumbEl.getHeight() / this.minHeight;
24814 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24815 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24819 (this.rotate == 0 || this.rotate == 180) &&
24821 width > this.imageEl.OriginWidth ||
24822 height > this.imageEl.OriginHeight ||
24823 (width < this.minWidth && height < this.minHeight)
24831 (this.rotate == 90 || this.rotate == 270) &&
24833 width > this.imageEl.OriginWidth ||
24834 height > this.imageEl.OriginHeight ||
24835 (width < this.minHeight && height < this.minWidth)
24842 !this.isDocument &&
24843 (this.rotate == 0 || this.rotate == 180) &&
24845 width < this.minWidth ||
24846 width > this.imageEl.OriginWidth ||
24847 height < this.minHeight ||
24848 height > this.imageEl.OriginHeight
24855 !this.isDocument &&
24856 (this.rotate == 90 || this.rotate == 270) &&
24858 width < this.minHeight ||
24859 width > this.imageEl.OriginWidth ||
24860 height < this.minWidth ||
24861 height > this.imageEl.OriginHeight
24871 onRotateLeft : function(e)
24873 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24875 var minScale = this.thumbEl.getWidth() / this.minWidth;
24877 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24878 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24880 this.startScale = this.scale;
24882 while (this.getScaleLevel() < minScale){
24884 this.scale = this.scale + 1;
24886 if(!this.zoomable()){
24891 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24892 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24897 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24904 this.scale = this.startScale;
24906 this.onRotateFail();
24911 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24913 if(this.isDocument){
24914 this.setThumbBoxSize();
24915 this.setThumbBoxPosition();
24916 this.setCanvasPosition();
24921 this.fireEvent('rotate', this, 'left');
24925 onRotateRight : function(e)
24927 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24929 var minScale = this.thumbEl.getWidth() / this.minWidth;
24931 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24932 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24934 this.startScale = this.scale;
24936 while (this.getScaleLevel() < minScale){
24938 this.scale = this.scale + 1;
24940 if(!this.zoomable()){
24945 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24946 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24951 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24958 this.scale = this.startScale;
24960 this.onRotateFail();
24965 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24967 if(this.isDocument){
24968 this.setThumbBoxSize();
24969 this.setThumbBoxPosition();
24970 this.setCanvasPosition();
24975 this.fireEvent('rotate', this, 'right');
24978 onRotateFail : function()
24980 this.errorEl.show(true);
24984 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24989 this.previewEl.dom.innerHTML = '';
24991 var canvasEl = document.createElement("canvas");
24993 var contextEl = canvasEl.getContext("2d");
24995 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24996 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24997 var center = this.imageEl.OriginWidth / 2;
24999 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25000 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25001 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25002 center = this.imageEl.OriginHeight / 2;
25005 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25007 contextEl.translate(center, center);
25008 contextEl.rotate(this.rotate * Math.PI / 180);
25010 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25012 this.canvasEl = document.createElement("canvas");
25014 this.contextEl = this.canvasEl.getContext("2d");
25016 switch (this.rotate) {
25019 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25020 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25022 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25027 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25028 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25030 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25031 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);
25035 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25040 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25041 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25043 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25044 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);
25048 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);
25053 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25054 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25056 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25057 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25061 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);
25068 this.previewEl.appendChild(this.canvasEl);
25070 this.setCanvasPosition();
25075 if(!this.canvasLoaded){
25079 var imageCanvas = document.createElement("canvas");
25081 var imageContext = imageCanvas.getContext("2d");
25083 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25084 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25086 var center = imageCanvas.width / 2;
25088 imageContext.translate(center, center);
25090 imageContext.rotate(this.rotate * Math.PI / 180);
25092 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25094 var canvas = document.createElement("canvas");
25096 var context = canvas.getContext("2d");
25098 canvas.width = this.minWidth;
25099 canvas.height = this.minHeight;
25101 switch (this.rotate) {
25104 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25105 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25107 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25108 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25110 var targetWidth = this.minWidth - 2 * x;
25111 var targetHeight = this.minHeight - 2 * y;
25115 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25116 scale = targetWidth / width;
25119 if(x > 0 && y == 0){
25120 scale = targetHeight / height;
25123 if(x > 0 && y > 0){
25124 scale = targetWidth / width;
25126 if(width < height){
25127 scale = targetHeight / height;
25131 context.scale(scale, scale);
25133 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25134 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25136 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25137 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25139 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25144 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25145 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25147 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25148 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25150 var targetWidth = this.minWidth - 2 * x;
25151 var targetHeight = this.minHeight - 2 * y;
25155 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25156 scale = targetWidth / width;
25159 if(x > 0 && y == 0){
25160 scale = targetHeight / height;
25163 if(x > 0 && y > 0){
25164 scale = targetWidth / width;
25166 if(width < height){
25167 scale = targetHeight / height;
25171 context.scale(scale, scale);
25173 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25174 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25176 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25177 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25179 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25181 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25186 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25187 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25189 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25190 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25192 var targetWidth = this.minWidth - 2 * x;
25193 var targetHeight = this.minHeight - 2 * y;
25197 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25198 scale = targetWidth / width;
25201 if(x > 0 && y == 0){
25202 scale = targetHeight / height;
25205 if(x > 0 && y > 0){
25206 scale = targetWidth / width;
25208 if(width < height){
25209 scale = targetHeight / height;
25213 context.scale(scale, scale);
25215 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25216 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25218 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25219 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25221 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25222 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25224 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25229 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25230 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25232 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25233 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25235 var targetWidth = this.minWidth - 2 * x;
25236 var targetHeight = this.minHeight - 2 * y;
25240 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25241 scale = targetWidth / width;
25244 if(x > 0 && y == 0){
25245 scale = targetHeight / height;
25248 if(x > 0 && y > 0){
25249 scale = targetWidth / width;
25251 if(width < height){
25252 scale = targetHeight / height;
25256 context.scale(scale, scale);
25258 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25259 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25261 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25262 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25264 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25266 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25273 this.cropData = canvas.toDataURL(this.cropType);
25275 if(this.fireEvent('crop', this, this.cropData) !== false){
25276 this.process(this.file, this.cropData);
25283 setThumbBoxSize : function()
25287 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25288 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25289 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25291 this.minWidth = width;
25292 this.minHeight = height;
25294 if(this.rotate == 90 || this.rotate == 270){
25295 this.minWidth = height;
25296 this.minHeight = width;
25301 width = Math.ceil(this.minWidth * height / this.minHeight);
25303 if(this.minWidth > this.minHeight){
25305 height = Math.ceil(this.minHeight * width / this.minWidth);
25308 this.thumbEl.setStyle({
25309 width : width + 'px',
25310 height : height + 'px'
25317 setThumbBoxPosition : function()
25319 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25320 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25322 this.thumbEl.setLeft(x);
25323 this.thumbEl.setTop(y);
25327 baseRotateLevel : function()
25329 this.baseRotate = 1;
25332 typeof(this.exif) != 'undefined' &&
25333 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25334 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25336 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25339 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25343 baseScaleLevel : function()
25347 if(this.isDocument){
25349 if(this.baseRotate == 6 || this.baseRotate == 8){
25351 height = this.thumbEl.getHeight();
25352 this.baseScale = height / this.imageEl.OriginWidth;
25354 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25355 width = this.thumbEl.getWidth();
25356 this.baseScale = width / this.imageEl.OriginHeight;
25362 height = this.thumbEl.getHeight();
25363 this.baseScale = height / this.imageEl.OriginHeight;
25365 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25366 width = this.thumbEl.getWidth();
25367 this.baseScale = width / this.imageEl.OriginWidth;
25373 if(this.baseRotate == 6 || this.baseRotate == 8){
25375 width = this.thumbEl.getHeight();
25376 this.baseScale = width / this.imageEl.OriginHeight;
25378 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25379 height = this.thumbEl.getWidth();
25380 this.baseScale = height / this.imageEl.OriginHeight;
25383 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25384 height = this.thumbEl.getWidth();
25385 this.baseScale = height / this.imageEl.OriginHeight;
25387 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25388 width = this.thumbEl.getHeight();
25389 this.baseScale = width / this.imageEl.OriginWidth;
25396 width = this.thumbEl.getWidth();
25397 this.baseScale = width / this.imageEl.OriginWidth;
25399 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25400 height = this.thumbEl.getHeight();
25401 this.baseScale = height / this.imageEl.OriginHeight;
25404 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25406 height = this.thumbEl.getHeight();
25407 this.baseScale = height / this.imageEl.OriginHeight;
25409 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25410 width = this.thumbEl.getWidth();
25411 this.baseScale = width / this.imageEl.OriginWidth;
25419 getScaleLevel : function()
25421 return this.baseScale * Math.pow(1.1, this.scale);
25424 onTouchStart : function(e)
25426 if(!this.canvasLoaded){
25427 this.beforeSelectFile(e);
25431 var touches = e.browserEvent.touches;
25437 if(touches.length == 1){
25438 this.onMouseDown(e);
25442 if(touches.length != 2){
25448 for(var i = 0, finger; finger = touches[i]; i++){
25449 coords.push(finger.pageX, finger.pageY);
25452 var x = Math.pow(coords[0] - coords[2], 2);
25453 var y = Math.pow(coords[1] - coords[3], 2);
25455 this.startDistance = Math.sqrt(x + y);
25457 this.startScale = this.scale;
25459 this.pinching = true;
25460 this.dragable = false;
25464 onTouchMove : function(e)
25466 if(!this.pinching && !this.dragable){
25470 var touches = e.browserEvent.touches;
25477 this.onMouseMove(e);
25483 for(var i = 0, finger; finger = touches[i]; i++){
25484 coords.push(finger.pageX, finger.pageY);
25487 var x = Math.pow(coords[0] - coords[2], 2);
25488 var y = Math.pow(coords[1] - coords[3], 2);
25490 this.endDistance = Math.sqrt(x + y);
25492 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25494 if(!this.zoomable()){
25495 this.scale = this.startScale;
25503 onTouchEnd : function(e)
25505 this.pinching = false;
25506 this.dragable = false;
25510 process : function(file, crop)
25513 this.maskEl.mask(this.loadingText);
25516 this.xhr = new XMLHttpRequest();
25518 file.xhr = this.xhr;
25520 this.xhr.open(this.method, this.url, true);
25523 "Accept": "application/json",
25524 "Cache-Control": "no-cache",
25525 "X-Requested-With": "XMLHttpRequest"
25528 for (var headerName in headers) {
25529 var headerValue = headers[headerName];
25531 this.xhr.setRequestHeader(headerName, headerValue);
25537 this.xhr.onload = function()
25539 _this.xhrOnLoad(_this.xhr);
25542 this.xhr.onerror = function()
25544 _this.xhrOnError(_this.xhr);
25547 var formData = new FormData();
25549 formData.append('returnHTML', 'NO');
25552 formData.append('crop', crop);
25555 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25556 formData.append(this.paramName, file, file.name);
25559 if(typeof(file.filename) != 'undefined'){
25560 formData.append('filename', file.filename);
25563 if(typeof(file.mimetype) != 'undefined'){
25564 formData.append('mimetype', file.mimetype);
25567 if(this.fireEvent('arrange', this, formData) != false){
25568 this.xhr.send(formData);
25572 xhrOnLoad : function(xhr)
25575 this.maskEl.unmask();
25578 if (xhr.readyState !== 4) {
25579 this.fireEvent('exception', this, xhr);
25583 var response = Roo.decode(xhr.responseText);
25585 if(!response.success){
25586 this.fireEvent('exception', this, xhr);
25590 var response = Roo.decode(xhr.responseText);
25592 this.fireEvent('upload', this, response);
25596 xhrOnError : function()
25599 this.maskEl.unmask();
25602 Roo.log('xhr on error');
25604 var response = Roo.decode(xhr.responseText);
25610 prepare : function(file)
25613 this.maskEl.mask(this.loadingText);
25619 if(typeof(file) === 'string'){
25620 this.loadCanvas(file);
25624 if(!file || !this.urlAPI){
25629 this.cropType = file.type;
25633 if(this.fireEvent('prepare', this, this.file) != false){
25635 var reader = new FileReader();
25637 reader.onload = function (e) {
25638 if (e.target.error) {
25639 Roo.log(e.target.error);
25643 var buffer = e.target.result,
25644 dataView = new DataView(buffer),
25646 maxOffset = dataView.byteLength - 4,
25650 if (dataView.getUint16(0) === 0xffd8) {
25651 while (offset < maxOffset) {
25652 markerBytes = dataView.getUint16(offset);
25654 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25655 markerLength = dataView.getUint16(offset + 2) + 2;
25656 if (offset + markerLength > dataView.byteLength) {
25657 Roo.log('Invalid meta data: Invalid segment size.');
25661 if(markerBytes == 0xffe1){
25662 _this.parseExifData(
25669 offset += markerLength;
25679 var url = _this.urlAPI.createObjectURL(_this.file);
25681 _this.loadCanvas(url);
25686 reader.readAsArrayBuffer(this.file);
25692 parseExifData : function(dataView, offset, length)
25694 var tiffOffset = offset + 10,
25698 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25699 // No Exif data, might be XMP data instead
25703 // Check for the ASCII code for "Exif" (0x45786966):
25704 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25705 // No Exif data, might be XMP data instead
25708 if (tiffOffset + 8 > dataView.byteLength) {
25709 Roo.log('Invalid Exif data: Invalid segment size.');
25712 // Check for the two null bytes:
25713 if (dataView.getUint16(offset + 8) !== 0x0000) {
25714 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25717 // Check the byte alignment:
25718 switch (dataView.getUint16(tiffOffset)) {
25720 littleEndian = true;
25723 littleEndian = false;
25726 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25729 // Check for the TIFF tag marker (0x002A):
25730 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25731 Roo.log('Invalid Exif data: Missing TIFF marker.');
25734 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25735 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25737 this.parseExifTags(
25740 tiffOffset + dirOffset,
25745 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25750 if (dirOffset + 6 > dataView.byteLength) {
25751 Roo.log('Invalid Exif data: Invalid directory offset.');
25754 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25755 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25756 if (dirEndOffset + 4 > dataView.byteLength) {
25757 Roo.log('Invalid Exif data: Invalid directory size.');
25760 for (i = 0; i < tagsNumber; i += 1) {
25764 dirOffset + 2 + 12 * i, // tag offset
25768 // Return the offset to the next directory:
25769 return dataView.getUint32(dirEndOffset, littleEndian);
25772 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25774 var tag = dataView.getUint16(offset, littleEndian);
25776 this.exif[tag] = this.getExifValue(
25780 dataView.getUint16(offset + 2, littleEndian), // tag type
25781 dataView.getUint32(offset + 4, littleEndian), // tag length
25786 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25788 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25797 Roo.log('Invalid Exif data: Invalid tag type.');
25801 tagSize = tagType.size * length;
25802 // Determine if the value is contained in the dataOffset bytes,
25803 // or if the value at the dataOffset is a pointer to the actual data:
25804 dataOffset = tagSize > 4 ?
25805 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25806 if (dataOffset + tagSize > dataView.byteLength) {
25807 Roo.log('Invalid Exif data: Invalid data offset.');
25810 if (length === 1) {
25811 return tagType.getValue(dataView, dataOffset, littleEndian);
25814 for (i = 0; i < length; i += 1) {
25815 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25818 if (tagType.ascii) {
25820 // Concatenate the chars:
25821 for (i = 0; i < values.length; i += 1) {
25823 // Ignore the terminating NULL byte(s):
25824 if (c === '\u0000') {
25836 Roo.apply(Roo.bootstrap.UploadCropbox, {
25838 'Orientation': 0x0112
25842 1: 0, //'top-left',
25844 3: 180, //'bottom-right',
25845 // 4: 'bottom-left',
25847 6: 90, //'right-top',
25848 // 7: 'right-bottom',
25849 8: 270 //'left-bottom'
25853 // byte, 8-bit unsigned int:
25855 getValue: function (dataView, dataOffset) {
25856 return dataView.getUint8(dataOffset);
25860 // ascii, 8-bit byte:
25862 getValue: function (dataView, dataOffset) {
25863 return String.fromCharCode(dataView.getUint8(dataOffset));
25868 // short, 16 bit int:
25870 getValue: function (dataView, dataOffset, littleEndian) {
25871 return dataView.getUint16(dataOffset, littleEndian);
25875 // long, 32 bit int:
25877 getValue: function (dataView, dataOffset, littleEndian) {
25878 return dataView.getUint32(dataOffset, littleEndian);
25882 // rational = two long values, first is numerator, second is denominator:
25884 getValue: function (dataView, dataOffset, littleEndian) {
25885 return dataView.getUint32(dataOffset, littleEndian) /
25886 dataView.getUint32(dataOffset + 4, littleEndian);
25890 // slong, 32 bit signed int:
25892 getValue: function (dataView, dataOffset, littleEndian) {
25893 return dataView.getInt32(dataOffset, littleEndian);
25897 // srational, two slongs, first is numerator, second is denominator:
25899 getValue: function (dataView, dataOffset, littleEndian) {
25900 return dataView.getInt32(dataOffset, littleEndian) /
25901 dataView.getInt32(dataOffset + 4, littleEndian);
25911 cls : 'btn-group roo-upload-cropbox-rotate-left',
25912 action : 'rotate-left',
25916 cls : 'btn btn-default',
25917 html : '<i class="fa fa-undo"></i>'
25923 cls : 'btn-group roo-upload-cropbox-picture',
25924 action : 'picture',
25928 cls : 'btn btn-default',
25929 html : '<i class="fa fa-picture-o"></i>'
25935 cls : 'btn-group roo-upload-cropbox-rotate-right',
25936 action : 'rotate-right',
25940 cls : 'btn btn-default',
25941 html : '<i class="fa fa-repeat"></i>'
25949 cls : 'btn-group roo-upload-cropbox-rotate-left',
25950 action : 'rotate-left',
25954 cls : 'btn btn-default',
25955 html : '<i class="fa fa-undo"></i>'
25961 cls : 'btn-group roo-upload-cropbox-download',
25962 action : 'download',
25966 cls : 'btn btn-default',
25967 html : '<i class="fa fa-download"></i>'
25973 cls : 'btn-group roo-upload-cropbox-crop',
25978 cls : 'btn btn-default',
25979 html : '<i class="fa fa-crop"></i>'
25985 cls : 'btn-group roo-upload-cropbox-trash',
25990 cls : 'btn btn-default',
25991 html : '<i class="fa fa-trash"></i>'
25997 cls : 'btn-group roo-upload-cropbox-rotate-right',
25998 action : 'rotate-right',
26002 cls : 'btn btn-default',
26003 html : '<i class="fa fa-repeat"></i>'
26011 cls : 'btn-group roo-upload-cropbox-rotate-left',
26012 action : 'rotate-left',
26016 cls : 'btn btn-default',
26017 html : '<i class="fa fa-undo"></i>'
26023 cls : 'btn-group roo-upload-cropbox-rotate-right',
26024 action : 'rotate-right',
26028 cls : 'btn btn-default',
26029 html : '<i class="fa fa-repeat"></i>'
26042 * @class Roo.bootstrap.DocumentManager
26043 * @extends Roo.bootstrap.Component
26044 * Bootstrap DocumentManager class
26045 * @cfg {String} paramName default 'imageUpload'
26046 * @cfg {String} method default POST
26047 * @cfg {String} url action url
26048 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26049 * @cfg {Boolean} multiple multiple upload default true
26050 * @cfg {Number} thumbSize default 300
26051 * @cfg {String} fieldLabel
26052 * @cfg {Number} labelWidth default 4
26053 * @cfg {String} labelAlign (left|top) default left
26054 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26057 * Create a new DocumentManager
26058 * @param {Object} config The config object
26061 Roo.bootstrap.DocumentManager = function(config){
26062 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26067 * Fire when initial the DocumentManager
26068 * @param {Roo.bootstrap.DocumentManager} this
26073 * inspect selected file
26074 * @param {Roo.bootstrap.DocumentManager} this
26075 * @param {File} file
26080 * Fire when xhr load exception
26081 * @param {Roo.bootstrap.DocumentManager} this
26082 * @param {XMLHttpRequest} xhr
26084 "exception" : true,
26087 * prepare the form data
26088 * @param {Roo.bootstrap.DocumentManager} this
26089 * @param {Object} formData
26094 * Fire when remove the file
26095 * @param {Roo.bootstrap.DocumentManager} this
26096 * @param {Object} file
26101 * Fire after refresh the file
26102 * @param {Roo.bootstrap.DocumentManager} this
26107 * Fire after click the image
26108 * @param {Roo.bootstrap.DocumentManager} this
26109 * @param {Object} file
26114 * Fire when upload a image and editable set to true
26115 * @param {Roo.bootstrap.DocumentManager} this
26116 * @param {Object} file
26120 * @event beforeselectfile
26121 * Fire before select file
26122 * @param {Roo.bootstrap.DocumentManager} this
26124 "beforeselectfile" : true,
26127 * Fire before process file
26128 * @param {Roo.bootstrap.DocumentManager} this
26129 * @param {Object} file
26136 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26145 paramName : 'imageUpload',
26148 labelAlign : 'left',
26155 getAutoCreate : function()
26157 var managerWidget = {
26159 cls : 'roo-document-manager',
26163 cls : 'roo-document-manager-selector',
26168 cls : 'roo-document-manager-uploader',
26172 cls : 'roo-document-manager-upload-btn',
26173 html : '<i class="fa fa-plus"></i>'
26184 cls : 'column col-md-12',
26189 if(this.fieldLabel.length){
26194 cls : 'column col-md-12',
26195 html : this.fieldLabel
26199 cls : 'column col-md-12',
26204 if(this.labelAlign == 'left'){
26208 cls : 'column col-md-' + this.labelWidth,
26209 html : this.fieldLabel
26213 cls : 'column col-md-' + (12 - this.labelWidth),
26223 cls : 'row clearfix',
26231 initEvents : function()
26233 this.managerEl = this.el.select('.roo-document-manager', true).first();
26234 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26236 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26237 this.selectorEl.hide();
26240 this.selectorEl.attr('multiple', 'multiple');
26243 this.selectorEl.on('change', this.onFileSelected, this);
26245 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26246 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26248 this.uploader.on('click', this.onUploaderClick, this);
26250 this.renderProgressDialog();
26254 window.addEventListener("resize", function() { _this.refresh(); } );
26256 this.fireEvent('initial', this);
26259 renderProgressDialog : function()
26263 this.progressDialog = new Roo.bootstrap.Modal({
26264 cls : 'roo-document-manager-progress-dialog',
26265 allow_close : false,
26275 btnclick : function() {
26276 _this.uploadCancel();
26282 this.progressDialog.render(Roo.get(document.body));
26284 this.progress = new Roo.bootstrap.Progress({
26285 cls : 'roo-document-manager-progress',
26290 this.progress.render(this.progressDialog.getChildContainer());
26292 this.progressBar = new Roo.bootstrap.ProgressBar({
26293 cls : 'roo-document-manager-progress-bar',
26296 aria_valuemax : 12,
26300 this.progressBar.render(this.progress.getChildContainer());
26303 onUploaderClick : function(e)
26305 e.preventDefault();
26307 if(this.fireEvent('beforeselectfile', this) != false){
26308 this.selectorEl.dom.click();
26313 onFileSelected : function(e)
26315 e.preventDefault();
26317 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26321 Roo.each(this.selectorEl.dom.files, function(file){
26322 if(this.fireEvent('inspect', this, file) != false){
26323 this.files.push(file);
26333 this.selectorEl.dom.value = '';
26335 if(!this.files.length){
26339 if(this.boxes > 0 && this.files.length > this.boxes){
26340 this.files = this.files.slice(0, this.boxes);
26343 this.uploader.show();
26345 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26346 this.uploader.hide();
26355 Roo.each(this.files, function(file){
26357 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26358 var f = this.renderPreview(file);
26363 if(file.type.indexOf('image') != -1){
26364 this.delegates.push(
26366 _this.process(file);
26367 }).createDelegate(this)
26375 _this.process(file);
26376 }).createDelegate(this)
26381 this.files = files;
26383 this.delegates = this.delegates.concat(docs);
26385 if(!this.delegates.length){
26390 this.progressBar.aria_valuemax = this.delegates.length;
26397 arrange : function()
26399 if(!this.delegates.length){
26400 this.progressDialog.hide();
26405 var delegate = this.delegates.shift();
26407 this.progressDialog.show();
26409 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26411 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26416 refresh : function()
26418 this.uploader.show();
26420 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26421 this.uploader.hide();
26424 Roo.isTouch ? this.closable(false) : this.closable(true);
26426 this.fireEvent('refresh', this);
26429 onRemove : function(e, el, o)
26431 e.preventDefault();
26433 this.fireEvent('remove', this, o);
26437 remove : function(o)
26441 Roo.each(this.files, function(file){
26442 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26451 this.files = files;
26458 Roo.each(this.files, function(file){
26463 file.target.remove();
26472 onClick : function(e, el, o)
26474 e.preventDefault();
26476 this.fireEvent('click', this, o);
26480 closable : function(closable)
26482 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26484 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26496 xhrOnLoad : function(xhr)
26498 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26502 if (xhr.readyState !== 4) {
26504 this.fireEvent('exception', this, xhr);
26508 var response = Roo.decode(xhr.responseText);
26510 if(!response.success){
26512 this.fireEvent('exception', this, xhr);
26516 var file = this.renderPreview(response.data);
26518 this.files.push(file);
26524 xhrOnError : function()
26526 Roo.log('xhr on error');
26528 var response = Roo.decode(xhr.responseText);
26535 process : function(file)
26537 if(this.fireEvent('process', this, file) !== false){
26538 if(this.editable && file.type.indexOf('image') != -1){
26539 this.fireEvent('edit', this, file);
26543 this.uploadStart(file, false);
26550 uploadStart : function(file, crop)
26552 this.xhr = new XMLHttpRequest();
26554 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26559 file.xhr = this.xhr;
26561 this.managerEl.createChild({
26563 cls : 'roo-document-manager-loading',
26567 tooltip : file.name,
26568 cls : 'roo-document-manager-thumb',
26569 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26575 this.xhr.open(this.method, this.url, true);
26578 "Accept": "application/json",
26579 "Cache-Control": "no-cache",
26580 "X-Requested-With": "XMLHttpRequest"
26583 for (var headerName in headers) {
26584 var headerValue = headers[headerName];
26586 this.xhr.setRequestHeader(headerName, headerValue);
26592 this.xhr.onload = function()
26594 _this.xhrOnLoad(_this.xhr);
26597 this.xhr.onerror = function()
26599 _this.xhrOnError(_this.xhr);
26602 var formData = new FormData();
26604 formData.append('returnHTML', 'NO');
26607 formData.append('crop', crop);
26610 formData.append(this.paramName, file, file.name);
26612 if(this.fireEvent('prepare', this, formData) != false){
26613 this.xhr.send(formData);
26617 uploadCancel : function()
26624 this.delegates = [];
26626 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26633 renderPreview : function(file)
26635 if(typeof(file.target) != 'undefined' && file.target){
26639 var previewEl = this.managerEl.createChild({
26641 cls : 'roo-document-manager-preview',
26645 tooltip : file.filename,
26646 cls : 'roo-document-manager-thumb',
26647 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26652 html : '<i class="fa fa-times-circle"></i>'
26657 var close = previewEl.select('button.close', true).first();
26659 close.on('click', this.onRemove, this, file);
26661 file.target = previewEl;
26663 var image = previewEl.select('img', true).first();
26667 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26669 image.on('click', this.onClick, this, file);
26675 onPreviewLoad : function(file, image)
26677 if(typeof(file.target) == 'undefined' || !file.target){
26681 var width = image.dom.naturalWidth || image.dom.width;
26682 var height = image.dom.naturalHeight || image.dom.height;
26684 if(width > height){
26685 file.target.addClass('wide');
26689 file.target.addClass('tall');
26694 uploadFromSource : function(file, crop)
26696 this.xhr = new XMLHttpRequest();
26698 this.managerEl.createChild({
26700 cls : 'roo-document-manager-loading',
26704 tooltip : file.name,
26705 cls : 'roo-document-manager-thumb',
26706 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26712 this.xhr.open(this.method, this.url, true);
26715 "Accept": "application/json",
26716 "Cache-Control": "no-cache",
26717 "X-Requested-With": "XMLHttpRequest"
26720 for (var headerName in headers) {
26721 var headerValue = headers[headerName];
26723 this.xhr.setRequestHeader(headerName, headerValue);
26729 this.xhr.onload = function()
26731 _this.xhrOnLoad(_this.xhr);
26734 this.xhr.onerror = function()
26736 _this.xhrOnError(_this.xhr);
26739 var formData = new FormData();
26741 formData.append('returnHTML', 'NO');
26743 formData.append('crop', crop);
26745 if(typeof(file.filename) != 'undefined'){
26746 formData.append('filename', file.filename);
26749 if(typeof(file.mimetype) != 'undefined'){
26750 formData.append('mimetype', file.mimetype);
26753 if(this.fireEvent('prepare', this, formData) != false){
26754 this.xhr.send(formData);
26764 * @class Roo.bootstrap.DocumentViewer
26765 * @extends Roo.bootstrap.Component
26766 * Bootstrap DocumentViewer class
26769 * Create a new DocumentViewer
26770 * @param {Object} config The config object
26773 Roo.bootstrap.DocumentViewer = function(config){
26774 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26779 * Fire after initEvent
26780 * @param {Roo.bootstrap.DocumentViewer} this
26786 * @param {Roo.bootstrap.DocumentViewer} this
26791 * Fire after trash button
26792 * @param {Roo.bootstrap.DocumentViewer} this
26799 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26801 getAutoCreate : function()
26805 cls : 'roo-document-viewer',
26809 cls : 'roo-document-viewer-body',
26813 cls : 'roo-document-viewer-thumb',
26817 cls : 'roo-document-viewer-image'
26825 cls : 'roo-document-viewer-footer',
26828 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26836 cls : 'btn btn-default roo-document-viewer-trash',
26837 html : '<i class="fa fa-trash"></i>'
26850 initEvents : function()
26853 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26854 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26856 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26857 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26859 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26860 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26862 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26863 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26865 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26866 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26868 this.bodyEl.on('click', this.onClick, this);
26870 this.trashBtn.on('click', this.onTrash, this);
26874 initial : function()
26876 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26879 this.fireEvent('initial', this);
26883 onClick : function(e)
26885 e.preventDefault();
26887 this.fireEvent('click', this);
26890 onTrash : function(e)
26892 e.preventDefault();
26894 this.fireEvent('trash', this);
26906 * @class Roo.bootstrap.NavProgressBar
26907 * @extends Roo.bootstrap.Component
26908 * Bootstrap NavProgressBar class
26911 * Create a new nav progress bar
26912 * @param {Object} config The config object
26915 Roo.bootstrap.NavProgressBar = function(config){
26916 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26918 this.bullets = this.bullets || [];
26920 // Roo.bootstrap.NavProgressBar.register(this);
26924 * Fires when the active item changes
26925 * @param {Roo.bootstrap.NavProgressBar} this
26926 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26927 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26934 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26939 getAutoCreate : function()
26941 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26945 cls : 'roo-navigation-bar-group',
26949 cls : 'roo-navigation-top-bar'
26953 cls : 'roo-navigation-bullets-bar',
26957 cls : 'roo-navigation-bar'
26964 cls : 'roo-navigation-bottom-bar'
26974 initEvents: function()
26979 onRender : function(ct, position)
26981 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26983 if(this.bullets.length){
26984 Roo.each(this.bullets, function(b){
26993 addItem : function(cfg)
26995 var item = new Roo.bootstrap.NavProgressItem(cfg);
26997 item.parentId = this.id;
26998 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27001 var top = new Roo.bootstrap.Element({
27003 cls : 'roo-navigation-bar-text'
27006 var bottom = new Roo.bootstrap.Element({
27008 cls : 'roo-navigation-bar-text'
27011 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27012 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27014 var topText = new Roo.bootstrap.Element({
27016 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27019 var bottomText = new Roo.bootstrap.Element({
27021 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27024 topText.onRender(top.el, null);
27025 bottomText.onRender(bottom.el, null);
27028 item.bottomEl = bottom;
27031 this.barItems.push(item);
27036 getActive : function()
27038 var active = false;
27040 Roo.each(this.barItems, function(v){
27042 if (!v.isActive()) {
27054 setActiveItem : function(item)
27058 Roo.each(this.barItems, function(v){
27059 if (v.rid == item.rid) {
27063 if (v.isActive()) {
27064 v.setActive(false);
27069 item.setActive(true);
27071 this.fireEvent('changed', this, item, prev);
27074 getBarItem: function(rid)
27078 Roo.each(this.barItems, function(e) {
27079 if (e.rid != rid) {
27090 indexOfItem : function(item)
27094 Roo.each(this.barItems, function(v, i){
27096 if (v.rid != item.rid) {
27107 setActiveNext : function()
27109 var i = this.indexOfItem(this.getActive());
27111 if (i > this.barItems.length) {
27115 this.setActiveItem(this.barItems[i+1]);
27118 setActivePrev : function()
27120 var i = this.indexOfItem(this.getActive());
27126 this.setActiveItem(this.barItems[i-1]);
27129 format : function()
27131 if(!this.barItems.length){
27135 var width = 100 / this.barItems.length;
27137 Roo.each(this.barItems, function(i){
27138 i.el.setStyle('width', width + '%');
27139 i.topEl.el.setStyle('width', width + '%');
27140 i.bottomEl.el.setStyle('width', width + '%');
27149 * Nav Progress Item
27154 * @class Roo.bootstrap.NavProgressItem
27155 * @extends Roo.bootstrap.Component
27156 * Bootstrap NavProgressItem class
27157 * @cfg {String} rid the reference id
27158 * @cfg {Boolean} active (true|false) Is item active default false
27159 * @cfg {Boolean} disabled (true|false) Is item active default false
27160 * @cfg {String} html
27161 * @cfg {String} position (top|bottom) text position default bottom
27162 * @cfg {String} icon show icon instead of number
27165 * Create a new NavProgressItem
27166 * @param {Object} config The config object
27168 Roo.bootstrap.NavProgressItem = function(config){
27169 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27174 * The raw click event for the entire grid.
27175 * @param {Roo.bootstrap.NavProgressItem} this
27176 * @param {Roo.EventObject} e
27183 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27189 position : 'bottom',
27192 getAutoCreate : function()
27194 var iconCls = 'roo-navigation-bar-item-icon';
27196 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27200 cls: 'roo-navigation-bar-item',
27210 cfg.cls += ' active';
27213 cfg.cls += ' disabled';
27219 disable : function()
27221 this.setDisabled(true);
27224 enable : function()
27226 this.setDisabled(false);
27229 initEvents: function()
27231 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27233 this.iconEl.on('click', this.onClick, this);
27236 onClick : function(e)
27238 e.preventDefault();
27244 if(this.fireEvent('click', this, e) === false){
27248 this.parent().setActiveItem(this);
27251 isActive: function ()
27253 return this.active;
27256 setActive : function(state)
27258 if(this.active == state){
27262 this.active = state;
27265 this.el.addClass('active');
27269 this.el.removeClass('active');
27274 setDisabled : function(state)
27276 if(this.disabled == state){
27280 this.disabled = state;
27283 this.el.addClass('disabled');
27287 this.el.removeClass('disabled');
27290 tooltipEl : function()
27292 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27305 * @class Roo.bootstrap.FieldLabel
27306 * @extends Roo.bootstrap.Component
27307 * Bootstrap FieldLabel class
27308 * @cfg {String} html contents of the element
27309 * @cfg {String} tag tag of the element default label
27310 * @cfg {String} cls class of the element
27311 * @cfg {String} target label target
27312 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27313 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27314 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27315 * @cfg {String} iconTooltip default "This field is required"
27318 * Create a new FieldLabel
27319 * @param {Object} config The config object
27322 Roo.bootstrap.FieldLabel = function(config){
27323 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27328 * Fires after the field has been marked as invalid.
27329 * @param {Roo.form.FieldLabel} this
27330 * @param {String} msg The validation message
27335 * Fires after the field has been validated with no errors.
27336 * @param {Roo.form.FieldLabel} this
27342 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27349 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27350 validClass : 'text-success fa fa-lg fa-check',
27351 iconTooltip : 'This field is required',
27353 getAutoCreate : function(){
27357 cls : 'roo-bootstrap-field-label ' + this.cls,
27363 tooltip : this.iconTooltip
27375 initEvents: function()
27377 Roo.bootstrap.Element.superclass.initEvents.call(this);
27379 this.iconEl = this.el.select('i', true).first();
27381 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27383 Roo.bootstrap.FieldLabel.register(this);
27387 * Mark this field as valid
27389 markValid : function()
27391 this.iconEl.show();
27393 this.iconEl.removeClass(this.invalidClass);
27395 this.iconEl.addClass(this.validClass);
27397 this.fireEvent('valid', this);
27401 * Mark this field as invalid
27402 * @param {String} msg The validation message
27404 markInvalid : function(msg)
27406 this.iconEl.show();
27408 this.iconEl.removeClass(this.validClass);
27410 this.iconEl.addClass(this.invalidClass);
27412 this.fireEvent('invalid', this, msg);
27418 Roo.apply(Roo.bootstrap.FieldLabel, {
27423 * register a FieldLabel Group
27424 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27426 register : function(label)
27428 if(this.groups.hasOwnProperty(label.target)){
27432 this.groups[label.target] = label;
27436 * fetch a FieldLabel Group based on the target
27437 * @param {string} target
27438 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27440 get: function(target) {
27441 if (typeof(this.groups[target]) == 'undefined') {
27445 return this.groups[target] ;
27454 * page DateSplitField.
27460 * @class Roo.bootstrap.DateSplitField
27461 * @extends Roo.bootstrap.Component
27462 * Bootstrap DateSplitField class
27463 * @cfg {string} fieldLabel - the label associated
27464 * @cfg {Number} labelWidth set the width of label (0-12)
27465 * @cfg {String} labelAlign (top|left)
27466 * @cfg {Boolean} dayAllowBlank (true|false) default false
27467 * @cfg {Boolean} monthAllowBlank (true|false) default false
27468 * @cfg {Boolean} yearAllowBlank (true|false) default false
27469 * @cfg {string} dayPlaceholder
27470 * @cfg {string} monthPlaceholder
27471 * @cfg {string} yearPlaceholder
27472 * @cfg {string} dayFormat default 'd'
27473 * @cfg {string} monthFormat default 'm'
27474 * @cfg {string} yearFormat default 'Y'
27478 * Create a new DateSplitField
27479 * @param {Object} config The config object
27482 Roo.bootstrap.DateSplitField = function(config){
27483 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27489 * getting the data of years
27490 * @param {Roo.bootstrap.DateSplitField} this
27491 * @param {Object} years
27496 * getting the data of days
27497 * @param {Roo.bootstrap.DateSplitField} this
27498 * @param {Object} days
27503 * Fires after the field has been marked as invalid.
27504 * @param {Roo.form.Field} this
27505 * @param {String} msg The validation message
27510 * Fires after the field has been validated with no errors.
27511 * @param {Roo.form.Field} this
27517 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27520 labelAlign : 'top',
27522 dayAllowBlank : false,
27523 monthAllowBlank : false,
27524 yearAllowBlank : false,
27525 dayPlaceholder : '',
27526 monthPlaceholder : '',
27527 yearPlaceholder : '',
27531 isFormField : true,
27533 getAutoCreate : function()
27537 cls : 'row roo-date-split-field-group',
27542 cls : 'form-hidden-field roo-date-split-field-group-value',
27548 if(this.fieldLabel){
27551 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27555 html : this.fieldLabel
27561 Roo.each(['day', 'month', 'year'], function(t){
27564 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27571 inputEl: function ()
27573 return this.el.select('.roo-date-split-field-group-value', true).first();
27576 onRender : function(ct, position)
27580 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27582 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27584 this.dayField = new Roo.bootstrap.ComboBox({
27585 allowBlank : this.dayAllowBlank,
27586 alwaysQuery : true,
27587 displayField : 'value',
27590 forceSelection : true,
27592 placeholder : this.dayPlaceholder,
27593 selectOnFocus : true,
27594 tpl : '<div class="select2-result"><b>{value}</b></div>',
27595 triggerAction : 'all',
27597 valueField : 'value',
27598 store : new Roo.data.SimpleStore({
27599 data : (function() {
27601 _this.fireEvent('days', _this, days);
27604 fields : [ 'value' ]
27607 select : function (_self, record, index)
27609 _this.setValue(_this.getValue());
27614 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27616 this.monthField = new Roo.bootstrap.MonthField({
27617 after : '<i class=\"fa fa-calendar\"></i>',
27618 allowBlank : this.monthAllowBlank,
27619 placeholder : this.monthPlaceholder,
27622 render : function (_self)
27624 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27625 e.preventDefault();
27629 select : function (_self, oldvalue, newvalue)
27631 _this.setValue(_this.getValue());
27636 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27638 this.yearField = new Roo.bootstrap.ComboBox({
27639 allowBlank : this.yearAllowBlank,
27640 alwaysQuery : true,
27641 displayField : 'value',
27644 forceSelection : true,
27646 placeholder : this.yearPlaceholder,
27647 selectOnFocus : true,
27648 tpl : '<div class="select2-result"><b>{value}</b></div>',
27649 triggerAction : 'all',
27651 valueField : 'value',
27652 store : new Roo.data.SimpleStore({
27653 data : (function() {
27655 _this.fireEvent('years', _this, years);
27658 fields : [ 'value' ]
27661 select : function (_self, record, index)
27663 _this.setValue(_this.getValue());
27668 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27671 setValue : function(v, format)
27673 this.inputEl.dom.value = v;
27675 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27677 var d = Date.parseDate(v, f);
27684 this.setDay(d.format(this.dayFormat));
27685 this.setMonth(d.format(this.monthFormat));
27686 this.setYear(d.format(this.yearFormat));
27693 setDay : function(v)
27695 this.dayField.setValue(v);
27696 this.inputEl.dom.value = this.getValue();
27701 setMonth : function(v)
27703 this.monthField.setValue(v, true);
27704 this.inputEl.dom.value = this.getValue();
27709 setYear : function(v)
27711 this.yearField.setValue(v);
27712 this.inputEl.dom.value = this.getValue();
27717 getDay : function()
27719 return this.dayField.getValue();
27722 getMonth : function()
27724 return this.monthField.getValue();
27727 getYear : function()
27729 return this.yearField.getValue();
27732 getValue : function()
27734 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27736 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27746 this.inputEl.dom.value = '';
27751 validate : function()
27753 var d = this.dayField.validate();
27754 var m = this.monthField.validate();
27755 var y = this.yearField.validate();
27760 (!this.dayAllowBlank && !d) ||
27761 (!this.monthAllowBlank && !m) ||
27762 (!this.yearAllowBlank && !y)
27767 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27776 this.markInvalid();
27781 markValid : function()
27784 var label = this.el.select('label', true).first();
27785 var icon = this.el.select('i.fa-star', true).first();
27791 this.fireEvent('valid', this);
27795 * Mark this field as invalid
27796 * @param {String} msg The validation message
27798 markInvalid : function(msg)
27801 var label = this.el.select('label', true).first();
27802 var icon = this.el.select('i.fa-star', true).first();
27804 if(label && !icon){
27805 this.el.select('.roo-date-split-field-label', true).createChild({
27807 cls : 'text-danger fa fa-lg fa-star',
27808 tooltip : 'This field is required',
27809 style : 'margin-right:5px;'
27813 this.fireEvent('invalid', this, msg);
27816 clearInvalid : function()
27818 var label = this.el.select('label', true).first();
27819 var icon = this.el.select('i.fa-star', true).first();
27825 this.fireEvent('valid', this);
27828 getName: function()