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' : '',
1442 src : 'about:blank' // just incase src get's set to undefined?!?
1445 cfg.html = this.html || cfg.html;
1447 cfg.src = this.src || cfg.src;
1449 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1450 cfg.cls += ' img-' + this.border;
1467 a.target = this.target;
1472 return (this.href) ? a : cfg;
1475 initEvents: function()
1478 this.el.on('click', this.onClick, this);
1483 onClick : function(e)
1485 Roo.log('img onclick');
1486 this.fireEvent('click', this, e);
1500 * @class Roo.bootstrap.Link
1501 * @extends Roo.bootstrap.Component
1502 * Bootstrap Link Class
1503 * @cfg {String} alt image alternative text
1504 * @cfg {String} href a tag href
1505 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1506 * @cfg {String} html the content of the link.
1507 * @cfg {String} anchor name for the anchor link
1509 * @cfg {Boolean} preventDefault (true | false) default false
1513 * Create a new Input
1514 * @param {Object} config The config object
1517 Roo.bootstrap.Link = function(config){
1518 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1524 * The img click event for the img.
1525 * @param {Roo.EventObject} e
1531 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1535 preventDefault: false,
1539 getAutoCreate : function()
1545 // anchor's do not require html/href...
1546 if (this.anchor === false) {
1547 cfg.html = this.html || '';
1548 cfg.href = this.href || '#';
1550 cfg.name = this.anchor;
1551 if (this.html !== false) {
1552 cfg.html = this.html;
1554 if (this.href !== false) {
1555 cfg.href = this.href;
1559 if(this.alt !== false){
1564 if(this.target !== false) {
1565 cfg.target = this.target;
1571 initEvents: function() {
1573 if(!this.href || this.preventDefault){
1574 this.el.on('click', this.onClick, this);
1578 onClick : function(e)
1580 if(this.preventDefault){
1583 //Roo.log('img onclick');
1584 this.fireEvent('click', this, e);
1597 * @class Roo.bootstrap.Header
1598 * @extends Roo.bootstrap.Component
1599 * Bootstrap Header class
1600 * @cfg {String} html content of header
1601 * @cfg {Number} level (1|2|3|4|5|6) default 1
1604 * Create a new Header
1605 * @param {Object} config The config object
1609 Roo.bootstrap.Header = function(config){
1610 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1613 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1621 getAutoCreate : function(){
1626 tag: 'h' + (1 *this.level),
1627 html: this.html || ''
1639 * Ext JS Library 1.1.1
1640 * Copyright(c) 2006-2007, Ext JS, LLC.
1642 * Originally Released Under LGPL - original licence link has changed is not relivant.
1645 * <script type="text/javascript">
1649 * @class Roo.bootstrap.MenuMgr
1650 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1653 Roo.bootstrap.MenuMgr = function(){
1654 var menus, active, groups = {}, attached = false, lastShow = new Date();
1656 // private - called when first menu is created
1659 active = new Roo.util.MixedCollection();
1660 Roo.get(document).addKeyListener(27, function(){
1661 if(active.length > 0){
1669 if(active && active.length > 0){
1670 var c = active.clone();
1680 if(active.length < 1){
1681 Roo.get(document).un("mouseup", onMouseDown);
1689 var last = active.last();
1690 lastShow = new Date();
1693 Roo.get(document).on("mouseup", onMouseDown);
1698 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1699 m.parentMenu.activeChild = m;
1700 }else if(last && last.isVisible()){
1701 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1706 function onBeforeHide(m){
1708 m.activeChild.hide();
1710 if(m.autoHideTimer){
1711 clearTimeout(m.autoHideTimer);
1712 delete m.autoHideTimer;
1717 function onBeforeShow(m){
1718 var pm = m.parentMenu;
1719 if(!pm && !m.allowOtherMenus){
1721 }else if(pm && pm.activeChild && active != m){
1722 pm.activeChild.hide();
1726 // private this should really trigger on mouseup..
1727 function onMouseDown(e){
1728 Roo.log("on Mouse Up");
1729 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1739 function onBeforeCheck(mi, state){
1741 var g = groups[mi.group];
1742 for(var i = 0, l = g.length; i < l; i++){
1744 g[i].setChecked(false);
1753 * Hides all menus that are currently visible
1755 hideAll : function(){
1760 register : function(menu){
1764 menus[menu.id] = menu;
1765 menu.on("beforehide", onBeforeHide);
1766 menu.on("hide", onHide);
1767 menu.on("beforeshow", onBeforeShow);
1768 menu.on("show", onShow);
1770 if(g && menu.events["checkchange"]){
1774 groups[g].push(menu);
1775 menu.on("checkchange", onCheck);
1780 * Returns a {@link Roo.menu.Menu} object
1781 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1782 * be used to generate and return a new Menu instance.
1784 get : function(menu){
1785 if(typeof menu == "string"){ // menu id
1787 }else if(menu.events){ // menu instance
1790 /*else if(typeof menu.length == 'number'){ // array of menu items?
1791 return new Roo.bootstrap.Menu({items:menu});
1792 }else{ // otherwise, must be a config
1793 return new Roo.bootstrap.Menu(menu);
1800 unregister : function(menu){
1801 delete menus[menu.id];
1802 menu.un("beforehide", onBeforeHide);
1803 menu.un("hide", onHide);
1804 menu.un("beforeshow", onBeforeShow);
1805 menu.un("show", onShow);
1807 if(g && menu.events["checkchange"]){
1808 groups[g].remove(menu);
1809 menu.un("checkchange", onCheck);
1814 registerCheckable : function(menuItem){
1815 var g = menuItem.group;
1820 groups[g].push(menuItem);
1821 menuItem.on("beforecheckchange", onBeforeCheck);
1826 unregisterCheckable : function(menuItem){
1827 var g = menuItem.group;
1829 groups[g].remove(menuItem);
1830 menuItem.un("beforecheckchange", onBeforeCheck);
1842 * @class Roo.bootstrap.Menu
1843 * @extends Roo.bootstrap.Component
1844 * Bootstrap Menu class - container for MenuItems
1845 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1849 * @param {Object} config The config object
1853 Roo.bootstrap.Menu = function(config){
1854 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1855 if (this.registerMenu) {
1856 Roo.bootstrap.MenuMgr.register(this);
1861 * Fires before this menu is displayed
1862 * @param {Roo.menu.Menu} this
1867 * Fires before this menu is hidden
1868 * @param {Roo.menu.Menu} this
1873 * Fires after this menu is displayed
1874 * @param {Roo.menu.Menu} this
1879 * Fires after this menu is hidden
1880 * @param {Roo.menu.Menu} this
1885 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1886 * @param {Roo.menu.Menu} this
1887 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1888 * @param {Roo.EventObject} e
1893 * Fires when the mouse is hovering over this menu
1894 * @param {Roo.menu.Menu} this
1895 * @param {Roo.EventObject} e
1896 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1901 * Fires when the mouse exits this menu
1902 * @param {Roo.menu.Menu} this
1903 * @param {Roo.EventObject} e
1904 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1909 * Fires when a menu item contained in this menu is clicked
1910 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1911 * @param {Roo.EventObject} e
1915 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1918 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1922 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1925 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1927 registerMenu : true,
1929 menuItems :false, // stores the menu items..
1935 getChildContainer : function() {
1939 getAutoCreate : function(){
1941 //if (['right'].indexOf(this.align)!==-1) {
1942 // cfg.cn[1].cls += ' pull-right'
1948 cls : 'dropdown-menu' ,
1949 style : 'z-index:1000'
1953 if (this.type === 'submenu') {
1954 cfg.cls = 'submenu active';
1956 if (this.type === 'treeview') {
1957 cfg.cls = 'treeview-menu';
1962 initEvents : function() {
1964 // Roo.log("ADD event");
1965 // Roo.log(this.triggerEl.dom);
1966 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1968 this.triggerEl.addClass('dropdown-toggle');
1974 this.el.on('touchstart' , this.onTouch, this);
1976 this.el.on('click' , this.onClick, this);
1978 this.el.on("mouseover", this.onMouseOver, this);
1979 this.el.on("mouseout", this.onMouseOut, this);
1983 findTargetItem : function(e){
1984 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1988 //Roo.log(t); Roo.log(t.id);
1990 //Roo.log(this.menuitems);
1991 return this.menuitems.get(t.id);
1993 //return this.items.get(t.menuItemId);
1999 onTouch : function(e) {
2004 onClick : function(e){
2005 Roo.log("menu.onClick");
2006 var t = this.findTargetItem(e);
2007 if(!t || t.isContainer){
2012 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2013 if(t == this.activeItem && t.shouldDeactivate(e)){
2014 this.activeItem.deactivate();
2015 delete this.activeItem;
2019 this.setActiveItem(t, true);
2027 Roo.log('pass click event');
2031 this.fireEvent("click", this, t, e);
2035 onMouseOver : function(e){
2036 var t = this.findTargetItem(e);
2039 // if(t.canActivate && !t.disabled){
2040 // this.setActiveItem(t, true);
2044 this.fireEvent("mouseover", this, e, t);
2046 isVisible : function(){
2047 return !this.hidden;
2049 onMouseOut : function(e){
2050 var t = this.findTargetItem(e);
2053 // if(t == this.activeItem && t.shouldDeactivate(e)){
2054 // this.activeItem.deactivate();
2055 // delete this.activeItem;
2058 this.fireEvent("mouseout", this, e, t);
2063 * Displays this menu relative to another element
2064 * @param {String/HTMLElement/Roo.Element} element The element to align to
2065 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2066 * the element (defaults to this.defaultAlign)
2067 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2069 show : function(el, pos, parentMenu){
2070 this.parentMenu = parentMenu;
2074 this.fireEvent("beforeshow", this);
2075 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2078 * Displays this menu at a specific xy position
2079 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2080 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2082 showAt : function(xy, parentMenu, /* private: */_e){
2083 this.parentMenu = parentMenu;
2088 this.fireEvent("beforeshow", this);
2089 //xy = this.el.adjustForConstraints(xy);
2093 this.hideMenuItems();
2094 this.hidden = false;
2095 this.triggerEl.addClass('open');
2097 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2098 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2103 this.fireEvent("show", this);
2109 this.doFocus.defer(50, this);
2113 doFocus : function(){
2115 this.focusEl.focus();
2120 * Hides this menu and optionally all parent menus
2121 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2123 hide : function(deep){
2125 this.hideMenuItems();
2126 if(this.el && this.isVisible()){
2127 this.fireEvent("beforehide", this);
2128 if(this.activeItem){
2129 this.activeItem.deactivate();
2130 this.activeItem = null;
2132 this.triggerEl.removeClass('open');;
2134 this.fireEvent("hide", this);
2136 if(deep === true && this.parentMenu){
2137 this.parentMenu.hide(true);
2141 onTriggerPress : function(e)
2144 Roo.log('trigger press');
2145 //Roo.log(e.getTarget());
2146 // Roo.log(this.triggerEl.dom);
2147 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2151 if (this.isVisible()) {
2156 this.show(this.triggerEl, false, false);
2165 hideMenuItems : function()
2167 //$(backdrop).remove()
2168 Roo.select('.open',true).each(function(aa) {
2170 aa.removeClass('open');
2171 //var parent = getParent($(this))
2172 //var relatedTarget = { relatedTarget: this }
2174 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2175 //if (e.isDefaultPrevented()) return
2176 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2179 addxtypeChild : function (tree, cntr) {
2180 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2182 this.menuitems.add(comp);
2203 * @class Roo.bootstrap.MenuItem
2204 * @extends Roo.bootstrap.Component
2205 * Bootstrap MenuItem class
2206 * @cfg {String} html the menu label
2207 * @cfg {String} href the link
2208 * @cfg {Boolean} preventDefault (true | false) default true
2209 * @cfg {Boolean} isContainer (true | false) default false
2213 * Create a new MenuItem
2214 * @param {Object} config The config object
2218 Roo.bootstrap.MenuItem = function(config){
2219 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2224 * The raw click event for the entire grid.
2225 * @param {Roo.bootstrap.MenuItem} this
2226 * @param {Roo.EventObject} e
2232 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2236 preventDefault: true,
2237 isContainer : false,
2239 getAutoCreate : function(){
2241 if(this.isContainer){
2244 cls: 'dropdown-menu-item'
2250 cls: 'dropdown-menu-item',
2259 if (this.parent().type == 'treeview') {
2260 cfg.cls = 'treeview-menu';
2263 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2264 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2268 initEvents: function() {
2270 //this.el.select('a').on('click', this.onClick, this);
2273 onClick : function(e)
2275 Roo.log('item on click ');
2276 //if(this.preventDefault){
2277 // e.preventDefault();
2279 //this.parent().hideMenuItems();
2281 this.fireEvent('click', this, e);
2300 * @class Roo.bootstrap.MenuSeparator
2301 * @extends Roo.bootstrap.Component
2302 * Bootstrap MenuSeparator class
2305 * Create a new MenuItem
2306 * @param {Object} config The config object
2310 Roo.bootstrap.MenuSeparator = function(config){
2311 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2314 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2316 getAutoCreate : function(){
2335 * @class Roo.bootstrap.Modal
2336 * @extends Roo.bootstrap.Component
2337 * Bootstrap Modal class
2338 * @cfg {String} title Title of dialog
2339 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2340 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2341 * @cfg {Boolean} specificTitle default false
2342 * @cfg {Array} buttons Array of buttons or standard button set..
2343 * @cfg {String} buttonPosition (left|right|center) default right
2344 * @cfg {Boolean} animate default true
2345 * @cfg {Boolean} allow_close default true
2348 * Create a new Modal Dialog
2349 * @param {Object} config The config object
2352 Roo.bootstrap.Modal = function(config){
2353 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2358 * The raw btnclick event for the button
2359 * @param {Roo.EventObject} e
2363 this.buttons = this.buttons || [];
2366 this.tmpl = Roo.factory(this.tmpl);
2371 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2373 title : 'test dialog',
2383 specificTitle: false,
2385 buttonPosition: 'right',
2399 onRender : function(ct, position)
2401 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2404 var cfg = Roo.apply({}, this.getAutoCreate());
2407 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2409 //if (!cfg.name.length) {
2413 cfg.cls += ' ' + this.cls;
2416 cfg.style = this.style;
2418 this.el = Roo.get(document.body).createChild(cfg, position);
2420 //var type = this.el.dom.type;
2425 if(this.tabIndex !== undefined){
2426 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2430 this.bodyEl = this.el.select('.modal-body',true).first();
2431 this.closeEl = this.el.select('.modal-header .close', true).first();
2432 this.footerEl = this.el.select('.modal-footer',true).first();
2433 this.titleEl = this.el.select('.modal-title',true).first();
2437 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2438 this.maskEl.enableDisplayMode("block");
2440 //this.el.addClass("x-dlg-modal");
2442 if (this.buttons.length) {
2443 Roo.each(this.buttons, function(bb) {
2444 var b = Roo.apply({}, bb);
2445 b.xns = b.xns || Roo.bootstrap;
2446 b.xtype = b.xtype || 'Button';
2447 if (typeof(b.listeners) == 'undefined') {
2448 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2451 var btn = Roo.factory(b);
2453 btn.onRender(this.el.select('.modal-footer div').first());
2457 // render the children.
2460 if(typeof(this.items) != 'undefined'){
2461 var items = this.items;
2464 for(var i =0;i < items.length;i++) {
2465 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2469 this.items = nitems;
2471 // where are these used - they used to be body/close/footer
2475 //this.el.addClass([this.fieldClass, this.cls]);
2479 getAutoCreate : function(){
2484 html : this.html || ''
2489 cls : 'modal-title',
2493 if(this.specificTitle){
2499 if (this.allow_close) {
2510 style : 'display: none',
2513 cls: "modal-dialog",
2516 cls : "modal-content",
2519 cls : 'modal-header',
2524 cls : 'modal-footer',
2528 cls: 'btn-' + this.buttonPosition
2545 modal.cls += ' fade';
2551 getChildContainer : function() {
2556 getButtonContainer : function() {
2557 return this.el.select('.modal-footer div',true).first();
2560 initEvents : function()
2562 if (this.allow_close) {
2563 this.closeEl.on('click', this.hide, this);
2568 window.addEventListener("resize", function() { _this.resize(); } );
2574 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2579 if (!this.rendered) {
2583 this.el.setStyle('display', 'block');
2587 (function(){ _this.el.addClass('in'); }).defer(50);
2589 this.el.addClass('in');
2592 // not sure how we can show data in here..
2594 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2597 Roo.get(document.body).addClass("x-body-masked");
2598 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2600 this.el.setStyle('zIndex', '10001');
2602 this.fireEvent('show', this);
2609 Roo.get(document.body).removeClass("x-body-masked");
2610 this.el.removeClass('in');
2614 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2616 this.el.setStyle('display', 'none');
2619 this.fireEvent('hide', this);
2622 addButton : function(str, cb)
2626 var b = Roo.apply({}, { html : str } );
2627 b.xns = b.xns || Roo.bootstrap;
2628 b.xtype = b.xtype || 'Button';
2629 if (typeof(b.listeners) == 'undefined') {
2630 b.listeners = { click : cb.createDelegate(this) };
2633 var btn = Roo.factory(b);
2635 btn.onRender(this.el.select('.modal-footer div').first());
2641 setDefaultButton : function(btn)
2643 //this.el.select('.modal-footer').()
2645 resizeTo: function(w,h)
2649 setContentSize : function(w, h)
2653 onButtonClick: function(btn,e)
2656 this.fireEvent('btnclick', btn.name, e);
2659 * Set the title of the Dialog
2660 * @param {String} str new Title
2662 setTitle: function(str) {
2663 this.titleEl.dom.innerHTML = str;
2666 * Set the body of the Dialog
2667 * @param {String} str new Title
2669 setBody: function(str) {
2670 this.bodyEl.dom.innerHTML = str;
2673 * Set the body of the Dialog using the template
2674 * @param {Obj} data - apply this data to the template and replace the body contents.
2676 applyBody: function(obj)
2679 Roo.log("Error - using apply Body without a template");
2682 this.tmpl.overwrite(this.bodyEl, obj);
2688 Roo.apply(Roo.bootstrap.Modal, {
2690 * Button config that displays a single OK button
2699 * Button config that displays Yes and No buttons
2715 * Button config that displays OK and Cancel buttons
2730 * Button config that displays Yes, No and Cancel buttons
2753 * messagebox - can be used as a replace
2757 * @class Roo.MessageBox
2758 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2762 Roo.Msg.alert('Status', 'Changes saved successfully.');
2764 // Prompt for user data:
2765 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2767 // process text value...
2771 // Show a dialog using config options:
2773 title:'Save Changes?',
2774 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2775 buttons: Roo.Msg.YESNOCANCEL,
2782 Roo.bootstrap.MessageBox = function(){
2783 var dlg, opt, mask, waitTimer;
2784 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2785 var buttons, activeTextEl, bwidth;
2789 var handleButton = function(button){
2791 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2795 var handleHide = function(){
2797 dlg.el.removeClass(opt.cls);
2800 // Roo.TaskMgr.stop(waitTimer);
2801 // waitTimer = null;
2806 var updateButtons = function(b){
2809 buttons["ok"].hide();
2810 buttons["cancel"].hide();
2811 buttons["yes"].hide();
2812 buttons["no"].hide();
2813 //dlg.footer.dom.style.display = 'none';
2816 dlg.footerEl.dom.style.display = '';
2817 for(var k in buttons){
2818 if(typeof buttons[k] != "function"){
2821 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2822 width += buttons[k].el.getWidth()+15;
2832 var handleEsc = function(d, k, e){
2833 if(opt && opt.closable !== false){
2843 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2844 * @return {Roo.BasicDialog} The BasicDialog element
2846 getDialog : function(){
2848 dlg = new Roo.bootstrap.Modal( {
2851 //constraintoviewport:false,
2853 //collapsible : false,
2858 //buttonAlign:"center",
2859 closeClick : function(){
2860 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2863 handleButton("cancel");
2868 dlg.on("hide", handleHide);
2870 //dlg.addKeyListener(27, handleEsc);
2872 this.buttons = buttons;
2873 var bt = this.buttonText;
2874 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2875 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2876 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2877 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2879 bodyEl = dlg.bodyEl.createChild({
2881 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2882 '<textarea class="roo-mb-textarea"></textarea>' +
2883 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2885 msgEl = bodyEl.dom.firstChild;
2886 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2887 textboxEl.enableDisplayMode();
2888 textboxEl.addKeyListener([10,13], function(){
2889 if(dlg.isVisible() && opt && opt.buttons){
2892 }else if(opt.buttons.yes){
2893 handleButton("yes");
2897 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2898 textareaEl.enableDisplayMode();
2899 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2900 progressEl.enableDisplayMode();
2901 var pf = progressEl.dom.firstChild;
2903 pp = Roo.get(pf.firstChild);
2904 pp.setHeight(pf.offsetHeight);
2912 * Updates the message box body text
2913 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2914 * the XHTML-compliant non-breaking space character '&#160;')
2915 * @return {Roo.MessageBox} This message box
2917 updateText : function(text){
2918 if(!dlg.isVisible() && !opt.width){
2919 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2921 msgEl.innerHTML = text || ' ';
2923 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2924 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2926 Math.min(opt.width || cw , this.maxWidth),
2927 Math.max(opt.minWidth || this.minWidth, bwidth)
2930 activeTextEl.setWidth(w);
2932 if(dlg.isVisible()){
2933 dlg.fixedcenter = false;
2935 // to big, make it scroll. = But as usual stupid IE does not support
2938 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2939 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2940 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2942 bodyEl.dom.style.height = '';
2943 bodyEl.dom.style.overflowY = '';
2946 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2948 bodyEl.dom.style.overflowX = '';
2951 dlg.setContentSize(w, bodyEl.getHeight());
2952 if(dlg.isVisible()){
2953 dlg.fixedcenter = true;
2959 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2960 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2961 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2962 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2963 * @return {Roo.MessageBox} This message box
2965 updateProgress : function(value, text){
2967 this.updateText(text);
2969 if (pp) { // weird bug on my firefox - for some reason this is not defined
2970 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2976 * Returns true if the message box is currently displayed
2977 * @return {Boolean} True if the message box is visible, else false
2979 isVisible : function(){
2980 return dlg && dlg.isVisible();
2984 * Hides the message box if it is displayed
2987 if(this.isVisible()){
2993 * Displays a new message box, or reinitializes an existing message box, based on the config options
2994 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2995 * The following config object properties are supported:
2997 Property Type Description
2998 ---------- --------------- ------------------------------------------------------------------------------------
2999 animEl String/Element An id or Element from which the message box should animate as it opens and
3000 closes (defaults to undefined)
3001 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3002 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3003 closable Boolean False to hide the top-right close button (defaults to true). Note that
3004 progress and wait dialogs will ignore this property and always hide the
3005 close button as they can only be closed programmatically.
3006 cls String A custom CSS class to apply to the message box element
3007 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3008 displayed (defaults to 75)
3009 fn Function A callback function to execute after closing the dialog. The arguments to the
3010 function will be btn (the name of the button that was clicked, if applicable,
3011 e.g. "ok"), and text (the value of the active text field, if applicable).
3012 Progress and wait dialogs will ignore this option since they do not respond to
3013 user actions and can only be closed programmatically, so any required function
3014 should be called by the same code after it closes the dialog.
3015 icon String A CSS class that provides a background image to be used as an icon for
3016 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3017 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3018 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3019 modal Boolean False to allow user interaction with the page while the message box is
3020 displayed (defaults to true)
3021 msg String A string that will replace the existing message box body text (defaults
3022 to the XHTML-compliant non-breaking space character ' ')
3023 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3024 progress Boolean True to display a progress bar (defaults to false)
3025 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3026 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3027 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3028 title String The title text
3029 value String The string value to set into the active textbox element if displayed
3030 wait Boolean True to display a progress bar (defaults to false)
3031 width Number The width of the dialog in pixels
3038 msg: 'Please enter your address:',
3040 buttons: Roo.MessageBox.OKCANCEL,
3043 animEl: 'addAddressBtn'
3046 * @param {Object} config Configuration options
3047 * @return {Roo.MessageBox} This message box
3049 show : function(options)
3052 // this causes nightmares if you show one dialog after another
3053 // especially on callbacks..
3055 if(this.isVisible()){
3058 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3059 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3060 Roo.log("New Dialog Message:" + options.msg )
3061 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3062 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3065 var d = this.getDialog();
3067 d.setTitle(opt.title || " ");
3068 d.closeEl.setDisplayed(opt.closable !== false);
3069 activeTextEl = textboxEl;
3070 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3075 textareaEl.setHeight(typeof opt.multiline == "number" ?
3076 opt.multiline : this.defaultTextHeight);
3077 activeTextEl = textareaEl;
3086 progressEl.setDisplayed(opt.progress === true);
3087 this.updateProgress(0);
3088 activeTextEl.dom.value = opt.value || "";
3090 dlg.setDefaultButton(activeTextEl);
3092 var bs = opt.buttons;
3096 }else if(bs && bs.yes){
3097 db = buttons["yes"];
3099 dlg.setDefaultButton(db);
3101 bwidth = updateButtons(opt.buttons);
3102 this.updateText(opt.msg);
3104 d.el.addClass(opt.cls);
3106 d.proxyDrag = opt.proxyDrag === true;
3107 d.modal = opt.modal !== false;
3108 d.mask = opt.modal !== false ? mask : false;
3110 // force it to the end of the z-index stack so it gets a cursor in FF
3111 document.body.appendChild(dlg.el.dom);
3112 d.animateTarget = null;
3113 d.show(options.animEl);
3119 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3120 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3121 * and closing the message box when the process is complete.
3122 * @param {String} title The title bar text
3123 * @param {String} msg The message box body text
3124 * @return {Roo.MessageBox} This message box
3126 progress : function(title, msg){
3133 minWidth: this.minProgressWidth,
3140 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3141 * If a callback function is passed it will be called after the user clicks the button, and the
3142 * id of the button that was clicked will be passed as the only parameter to the callback
3143 * (could also be the top-right close button).
3144 * @param {String} title The title bar text
3145 * @param {String} msg The message box body text
3146 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3147 * @param {Object} scope (optional) The scope of the callback function
3148 * @return {Roo.MessageBox} This message box
3150 alert : function(title, msg, fn, scope){
3163 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3164 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3165 * You are responsible for closing the message box when the process is complete.
3166 * @param {String} msg The message box body text
3167 * @param {String} title (optional) The title bar text
3168 * @return {Roo.MessageBox} This message box
3170 wait : function(msg, title){
3181 waitTimer = Roo.TaskMgr.start({
3183 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3191 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3192 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3193 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3194 * @param {String} title The title bar text
3195 * @param {String} msg The message box body text
3196 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3197 * @param {Object} scope (optional) The scope of the callback function
3198 * @return {Roo.MessageBox} This message box
3200 confirm : function(title, msg, fn, scope){
3204 buttons: this.YESNO,
3213 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3214 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3215 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3216 * (could also be the top-right close button) and the text that was entered will be passed as the two
3217 * parameters to the callback.
3218 * @param {String} title The title bar text
3219 * @param {String} msg The message box body text
3220 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3221 * @param {Object} scope (optional) The scope of the callback function
3222 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3223 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3224 * @return {Roo.MessageBox} This message box
3226 prompt : function(title, msg, fn, scope, multiline){
3230 buttons: this.OKCANCEL,
3235 multiline: multiline,
3242 * Button config that displays a single OK button
3247 * Button config that displays Yes and No buttons
3250 YESNO : {yes:true, no:true},
3252 * Button config that displays OK and Cancel buttons
3255 OKCANCEL : {ok:true, cancel:true},
3257 * Button config that displays Yes, No and Cancel buttons
3260 YESNOCANCEL : {yes:true, no:true, cancel:true},
3263 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3266 defaultTextHeight : 75,
3268 * The maximum width in pixels of the message box (defaults to 600)
3273 * The minimum width in pixels of the message box (defaults to 100)
3278 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3279 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3282 minProgressWidth : 250,
3284 * An object containing the default button text strings that can be overriden for localized language support.
3285 * Supported properties are: ok, cancel, yes and no.
3286 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3299 * Shorthand for {@link Roo.MessageBox}
3301 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3302 Roo.Msg = Roo.Msg || Roo.MessageBox;
3311 * @class Roo.bootstrap.Navbar
3312 * @extends Roo.bootstrap.Component
3313 * Bootstrap Navbar class
3316 * Create a new Navbar
3317 * @param {Object} config The config object
3321 Roo.bootstrap.Navbar = function(config){
3322 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3326 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3335 getAutoCreate : function(){
3338 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3342 initEvents :function ()
3344 //Roo.log(this.el.select('.navbar-toggle',true));
3345 this.el.select('.navbar-toggle',true).on('click', function() {
3346 // Roo.log('click');
3347 this.el.select('.navbar-collapse',true).toggleClass('in');
3355 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3357 var size = this.el.getSize();
3358 this.maskEl.setSize(size.width, size.height);
3359 this.maskEl.enableDisplayMode("block");
3368 getChildContainer : function()
3370 if (this.el.select('.collapse').getCount()) {
3371 return this.el.select('.collapse',true).first();
3404 * @class Roo.bootstrap.NavSimplebar
3405 * @extends Roo.bootstrap.Navbar
3406 * Bootstrap Sidebar class
3408 * @cfg {Boolean} inverse is inverted color
3410 * @cfg {String} type (nav | pills | tabs)
3411 * @cfg {Boolean} arrangement stacked | justified
3412 * @cfg {String} align (left | right) alignment
3414 * @cfg {Boolean} main (true|false) main nav bar? default false
3415 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3417 * @cfg {String} tag (header|footer|nav|div) default is nav
3423 * Create a new Sidebar
3424 * @param {Object} config The config object
3428 Roo.bootstrap.NavSimplebar = function(config){
3429 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3432 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3448 getAutoCreate : function(){
3452 tag : this.tag || 'div',
3465 this.type = this.type || 'nav';
3466 if (['tabs','pills'].indexOf(this.type)!==-1) {
3467 cfg.cn[0].cls += ' nav-' + this.type
3471 if (this.type!=='nav') {
3472 Roo.log('nav type must be nav/tabs/pills')
3474 cfg.cn[0].cls += ' navbar-nav'
3480 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3481 cfg.cn[0].cls += ' nav-' + this.arrangement;
3485 if (this.align === 'right') {
3486 cfg.cn[0].cls += ' navbar-right';
3490 cfg.cls += ' navbar-inverse';
3517 * @class Roo.bootstrap.NavHeaderbar
3518 * @extends Roo.bootstrap.NavSimplebar
3519 * Bootstrap Sidebar class
3521 * @cfg {String} brand what is brand
3522 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3523 * @cfg {String} brand_href href of the brand
3524 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3525 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3526 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3527 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3530 * Create a new Sidebar
3531 * @param {Object} config The config object
3535 Roo.bootstrap.NavHeaderbar = function(config){
3536 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3540 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3547 desktopCenter : false,
3550 getAutoCreate : function(){
3553 tag: this.nav || 'nav',
3560 if (this.desktopCenter) {
3561 cn.push({cls : 'container', cn : []});
3568 cls: 'navbar-header',
3573 cls: 'navbar-toggle',
3574 'data-toggle': 'collapse',
3579 html: 'Toggle navigation'
3601 cls: 'collapse navbar-collapse',
3605 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3607 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3608 cfg.cls += ' navbar-' + this.position;
3610 // tag can override this..
3612 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3615 if (this.brand !== '') {
3618 href: this.brand_href ? this.brand_href : '#',
3619 cls: 'navbar-brand',
3627 cfg.cls += ' main-nav';
3635 getHeaderChildContainer : function()
3637 if (this.el.select('.navbar-header').getCount()) {
3638 return this.el.select('.navbar-header',true).first();
3641 return this.getChildContainer();
3645 initEvents : function()
3647 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3649 if (this.autohide) {
3654 Roo.get(document).on('scroll',function(e) {
3655 var ns = Roo.get(document).getScroll().top;
3656 var os = prevScroll;
3660 ft.removeClass('slideDown');
3661 ft.addClass('slideUp');
3664 ft.removeClass('slideUp');
3665 ft.addClass('slideDown');
3686 * @class Roo.bootstrap.NavSidebar
3687 * @extends Roo.bootstrap.Navbar
3688 * Bootstrap Sidebar class
3691 * Create a new Sidebar
3692 * @param {Object} config The config object
3696 Roo.bootstrap.NavSidebar = function(config){
3697 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3700 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3702 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3704 getAutoCreate : function(){
3709 cls: 'sidebar sidebar-nav'
3731 * @class Roo.bootstrap.NavGroup
3732 * @extends Roo.bootstrap.Component
3733 * Bootstrap NavGroup class
3734 * @cfg {String} align (left|right)
3735 * @cfg {Boolean} inverse
3736 * @cfg {String} type (nav|pills|tab) default nav
3737 * @cfg {String} navId - reference Id for navbar.
3741 * Create a new nav group
3742 * @param {Object} config The config object
3745 Roo.bootstrap.NavGroup = function(config){
3746 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3749 Roo.bootstrap.NavGroup.register(this);
3753 * Fires when the active item changes
3754 * @param {Roo.bootstrap.NavGroup} this
3755 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3756 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3763 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3774 getAutoCreate : function()
3776 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3783 if (['tabs','pills'].indexOf(this.type)!==-1) {
3784 cfg.cls += ' nav-' + this.type
3786 if (this.type!=='nav') {
3787 Roo.log('nav type must be nav/tabs/pills')
3789 cfg.cls += ' navbar-nav'
3792 if (this.parent().sidebar) {
3795 cls: 'dashboard-menu sidebar-menu'
3801 if (this.form === true) {
3807 if (this.align === 'right') {
3808 cfg.cls += ' navbar-right';
3810 cfg.cls += ' navbar-left';
3814 if (this.align === 'right') {
3815 cfg.cls += ' navbar-right';
3819 cfg.cls += ' navbar-inverse';
3827 * sets the active Navigation item
3828 * @param {Roo.bootstrap.NavItem} the new current navitem
3830 setActiveItem : function(item)
3833 Roo.each(this.navItems, function(v){
3838 v.setActive(false, true);
3845 item.setActive(true, true);
3846 this.fireEvent('changed', this, item, prev);
3851 * gets the active Navigation item
3852 * @return {Roo.bootstrap.NavItem} the current navitem
3854 getActive : function()
3858 Roo.each(this.navItems, function(v){
3869 indexOfNav : function()
3873 Roo.each(this.navItems, function(v,i){
3884 * adds a Navigation item
3885 * @param {Roo.bootstrap.NavItem} the navitem to add
3887 addItem : function(cfg)
3889 var cn = new Roo.bootstrap.NavItem(cfg);
3891 cn.parentId = this.id;
3892 cn.onRender(this.el, null);
3896 * register a Navigation item
3897 * @param {Roo.bootstrap.NavItem} the navitem to add
3899 register : function(item)
3901 this.navItems.push( item);
3902 item.navId = this.navId;
3907 * clear all the Navigation item
3910 clearAll : function()
3913 this.el.dom.innerHTML = '';
3916 getNavItem: function(tabId)
3919 Roo.each(this.navItems, function(e) {
3920 if (e.tabId == tabId) {
3930 setActiveNext : function()
3932 var i = this.indexOfNav(this.getActive());
3933 if (i > this.navItems.length) {
3936 this.setActiveItem(this.navItems[i+1]);
3938 setActivePrev : function()
3940 var i = this.indexOfNav(this.getActive());
3944 this.setActiveItem(this.navItems[i-1]);
3946 clearWasActive : function(except) {
3947 Roo.each(this.navItems, function(e) {
3948 if (e.tabId != except.tabId && e.was_active) {
3949 e.was_active = false;
3956 getWasActive : function ()
3959 Roo.each(this.navItems, function(e) {
3974 Roo.apply(Roo.bootstrap.NavGroup, {
3978 * register a Navigation Group
3979 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3981 register : function(navgrp)
3983 this.groups[navgrp.navId] = navgrp;
3987 * fetch a Navigation Group based on the navigation ID
3988 * @param {string} the navgroup to add
3989 * @returns {Roo.bootstrap.NavGroup} the navgroup
3991 get: function(navId) {
3992 if (typeof(this.groups[navId]) == 'undefined') {
3994 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3996 return this.groups[navId] ;
4011 * @class Roo.bootstrap.NavItem
4012 * @extends Roo.bootstrap.Component
4013 * Bootstrap Navbar.NavItem class
4014 * @cfg {String} href link to
4015 * @cfg {String} html content of button
4016 * @cfg {String} badge text inside badge
4017 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4018 * @cfg {String} glyphicon name of glyphicon
4019 * @cfg {String} icon name of font awesome icon
4020 * @cfg {Boolean} active Is item active
4021 * @cfg {Boolean} disabled Is item disabled
4023 * @cfg {Boolean} preventDefault (true | false) default false
4024 * @cfg {String} tabId the tab that this item activates.
4025 * @cfg {String} tagtype (a|span) render as a href or span?
4026 * @cfg {Boolean} animateRef (true|false) link to element default false
4029 * Create a new Navbar Item
4030 * @param {Object} config The config object
4032 Roo.bootstrap.NavItem = function(config){
4033 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4038 * The raw click event for the entire grid.
4039 * @param {Roo.EventObject} e
4044 * Fires when the active item active state changes
4045 * @param {Roo.bootstrap.NavItem} this
4046 * @param {boolean} state the new state
4052 * Fires when scroll to element
4053 * @param {Roo.bootstrap.NavItem} this
4054 * @param {Object} options
4055 * @param {Roo.EventObject} e
4063 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4071 preventDefault : false,
4078 getAutoCreate : function(){
4087 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4089 if (this.disabled) {
4090 cfg.cls += ' disabled';
4093 if (this.href || this.html || this.glyphicon || this.icon) {
4097 href : this.href || "#",
4098 html: this.html || ''
4103 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4106 if(this.glyphicon) {
4107 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4112 cfg.cn[0].html += " <span class='caret'></span>";
4116 if (this.badge !== '') {
4118 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4126 initEvents: function()
4128 if (typeof (this.menu) != 'undefined') {
4129 this.menu.parentType = this.xtype;
4130 this.menu.triggerEl = this.el;
4131 this.menu = this.addxtype(Roo.apply({}, this.menu));
4134 this.el.select('a',true).on('click', this.onClick, this);
4136 if(this.tagtype == 'span'){
4137 this.el.select('span',true).on('click', this.onClick, this);
4140 // at this point parent should be available..
4141 this.parent().register(this);
4144 onClick : function(e)
4147 this.preventDefault ||
4154 if (this.disabled) {
4158 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4159 if (tg && tg.transition) {
4160 Roo.log("waiting for the transitionend");
4166 //Roo.log("fire event clicked");
4167 if(this.fireEvent('click', this, e) === false){
4171 if(this.tagtype == 'span'){
4175 //Roo.log(this.href);
4176 var ael = this.el.select('a',true).first();
4179 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4180 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4181 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4182 return; // ignore... - it's a 'hash' to another page.
4186 this.scrollToElement(e);
4190 var p = this.parent();
4192 if (['tabs','pills'].indexOf(p.type)!==-1) {
4193 if (typeof(p.setActiveItem) !== 'undefined') {
4194 p.setActiveItem(this);
4198 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4199 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4200 // remove the collapsed menu expand...
4201 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4205 isActive: function () {
4208 setActive : function(state, fire, is_was_active)
4210 if (this.active && !state && this.navId) {
4211 this.was_active = true;
4212 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4214 nv.clearWasActive(this);
4218 this.active = state;
4221 this.el.removeClass('active');
4222 } else if (!this.el.hasClass('active')) {
4223 this.el.addClass('active');
4226 this.fireEvent('changed', this, state);
4229 // show a panel if it's registered and related..
4231 if (!this.navId || !this.tabId || !state || is_was_active) {
4235 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4239 var pan = tg.getPanelByName(this.tabId);
4243 // if we can not flip to new panel - go back to old nav highlight..
4244 if (false == tg.showPanel(pan)) {
4245 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4247 var onav = nv.getWasActive();
4249 onav.setActive(true, false, true);
4258 // this should not be here...
4259 setDisabled : function(state)
4261 this.disabled = state;
4263 this.el.removeClass('disabled');
4264 } else if (!this.el.hasClass('disabled')) {
4265 this.el.addClass('disabled');
4271 * Fetch the element to display the tooltip on.
4272 * @return {Roo.Element} defaults to this.el
4274 tooltipEl : function()
4276 return this.el.select('' + this.tagtype + '', true).first();
4279 scrollToElement : function(e)
4281 var c = document.body;
4284 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4286 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4287 c = document.documentElement;
4290 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4296 var o = target.calcOffsetsTo(c);
4303 this.fireEvent('scrollto', this, options, e);
4305 Roo.get(c).scrollTo('top', options.value, true);
4318 * <span> icon </span>
4319 * <span> text </span>
4320 * <span>badge </span>
4324 * @class Roo.bootstrap.NavSidebarItem
4325 * @extends Roo.bootstrap.NavItem
4326 * Bootstrap Navbar.NavSidebarItem class
4327 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4329 * Create a new Navbar Button
4330 * @param {Object} config The config object
4332 Roo.bootstrap.NavSidebarItem = function(config){
4333 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4338 * The raw click event for the entire grid.
4339 * @param {Roo.EventObject} e
4344 * Fires when the active item active state changes
4345 * @param {Roo.bootstrap.NavSidebarItem} this
4346 * @param {boolean} state the new state
4354 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4356 badgeWeight : 'default',
4358 getAutoCreate : function(){
4363 href : this.href || '#',
4375 html : this.html || ''
4380 cfg.cls += ' active';
4383 if (this.disabled) {
4384 cfg.cls += ' disabled';
4388 if (this.glyphicon || this.icon) {
4389 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4390 a.cn.push({ tag : 'i', cls : c }) ;
4395 if (this.badge !== '') {
4397 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4401 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4402 a.cls += 'dropdown-toggle treeview' ;
4413 initEvents : function()
4415 this.el.on('click', this.onClick, this);
4418 if(this.badge !== ''){
4420 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4425 onClick : function(e)
4432 if(this.preventDefault){
4436 this.fireEvent('click', this);
4439 disable : function()
4441 this.setDisabled(true);
4446 this.setDisabled(false);
4449 setDisabled : function(state)
4451 if(this.disabled == state){
4455 this.disabled = state;
4458 this.el.addClass('disabled');
4462 this.el.removeClass('disabled');
4467 setActive : function(state)
4469 if(this.active == state){
4473 this.active = state;
4476 this.el.addClass('active');
4480 this.el.removeClass('active');
4485 isActive: function ()
4490 setBadge : function(str)
4496 this.badgeEl.dom.innerHTML = str;
4513 * @class Roo.bootstrap.Row
4514 * @extends Roo.bootstrap.Component
4515 * Bootstrap Row class (contains columns...)
4519 * @param {Object} config The config object
4522 Roo.bootstrap.Row = function(config){
4523 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4526 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4528 getAutoCreate : function(){
4547 * @class Roo.bootstrap.Element
4548 * @extends Roo.bootstrap.Component
4549 * Bootstrap Element class
4550 * @cfg {String} html contents of the element
4551 * @cfg {String} tag tag of the element
4552 * @cfg {String} cls class of the element
4553 * @cfg {Boolean} preventDefault (true|false) default false
4554 * @cfg {Boolean} clickable (true|false) default false
4557 * Create a new Element
4558 * @param {Object} config The config object
4561 Roo.bootstrap.Element = function(config){
4562 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4568 * When a element is chick
4569 * @param {Roo.bootstrap.Element} this
4570 * @param {Roo.EventObject} e
4576 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4581 preventDefault: false,
4584 getAutoCreate : function(){
4595 initEvents: function()
4597 Roo.bootstrap.Element.superclass.initEvents.call(this);
4600 this.el.on('click', this.onClick, this);
4605 onClick : function(e)
4607 if(this.preventDefault){
4611 this.fireEvent('click', this, e);
4614 getValue : function()
4616 return this.el.dom.innerHTML;
4619 setValue : function(value)
4621 this.el.dom.innerHTML = value;
4636 * @class Roo.bootstrap.Pagination
4637 * @extends Roo.bootstrap.Component
4638 * Bootstrap Pagination class
4639 * @cfg {String} size xs | sm | md | lg
4640 * @cfg {Boolean} inverse false | true
4643 * Create a new Pagination
4644 * @param {Object} config The config object
4647 Roo.bootstrap.Pagination = function(config){
4648 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4651 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4657 getAutoCreate : function(){
4663 cfg.cls += ' inverse';
4669 cfg.cls += " " + this.cls;
4687 * @class Roo.bootstrap.PaginationItem
4688 * @extends Roo.bootstrap.Component
4689 * Bootstrap PaginationItem class
4690 * @cfg {String} html text
4691 * @cfg {String} href the link
4692 * @cfg {Boolean} preventDefault (true | false) default true
4693 * @cfg {Boolean} active (true | false) default false
4694 * @cfg {Boolean} disabled default false
4698 * Create a new PaginationItem
4699 * @param {Object} config The config object
4703 Roo.bootstrap.PaginationItem = function(config){
4704 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4709 * The raw click event for the entire grid.
4710 * @param {Roo.EventObject} e
4716 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4720 preventDefault: true,
4725 getAutoCreate : function(){
4731 href : this.href ? this.href : '#',
4732 html : this.html ? this.html : ''
4742 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4746 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4752 initEvents: function() {
4754 this.el.on('click', this.onClick, this);
4757 onClick : function(e)
4759 Roo.log('PaginationItem on click ');
4760 if(this.preventDefault){
4768 this.fireEvent('click', this, e);
4784 * @class Roo.bootstrap.Slider
4785 * @extends Roo.bootstrap.Component
4786 * Bootstrap Slider class
4789 * Create a new Slider
4790 * @param {Object} config The config object
4793 Roo.bootstrap.Slider = function(config){
4794 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4797 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4799 getAutoCreate : function(){
4803 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4807 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4819 * Ext JS Library 1.1.1
4820 * Copyright(c) 2006-2007, Ext JS, LLC.
4822 * Originally Released Under LGPL - original licence link has changed is not relivant.
4825 * <script type="text/javascript">
4830 * @class Roo.grid.ColumnModel
4831 * @extends Roo.util.Observable
4832 * This is the default implementation of a ColumnModel used by the Grid. It defines
4833 * the columns in the grid.
4836 var colModel = new Roo.grid.ColumnModel([
4837 {header: "Ticker", width: 60, sortable: true, locked: true},
4838 {header: "Company Name", width: 150, sortable: true},
4839 {header: "Market Cap.", width: 100, sortable: true},
4840 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4841 {header: "Employees", width: 100, sortable: true, resizable: false}
4846 * The config options listed for this class are options which may appear in each
4847 * individual column definition.
4848 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4850 * @param {Object} config An Array of column config objects. See this class's
4851 * config objects for details.
4853 Roo.grid.ColumnModel = function(config){
4855 * The config passed into the constructor
4857 this.config = config;
4860 // if no id, create one
4861 // if the column does not have a dataIndex mapping,
4862 // map it to the order it is in the config
4863 for(var i = 0, len = config.length; i < len; i++){
4865 if(typeof c.dataIndex == "undefined"){
4868 if(typeof c.renderer == "string"){
4869 c.renderer = Roo.util.Format[c.renderer];
4871 if(typeof c.id == "undefined"){
4874 if(c.editor && c.editor.xtype){
4875 c.editor = Roo.factory(c.editor, Roo.grid);
4877 if(c.editor && c.editor.isFormField){
4878 c.editor = new Roo.grid.GridEditor(c.editor);
4880 this.lookup[c.id] = c;
4884 * The width of columns which have no width specified (defaults to 100)
4887 this.defaultWidth = 100;
4890 * Default sortable of columns which have no sortable specified (defaults to false)
4893 this.defaultSortable = false;
4897 * @event widthchange
4898 * Fires when the width of a column changes.
4899 * @param {ColumnModel} this
4900 * @param {Number} columnIndex The column index
4901 * @param {Number} newWidth The new width
4903 "widthchange": true,
4905 * @event headerchange
4906 * Fires when the text of a header changes.
4907 * @param {ColumnModel} this
4908 * @param {Number} columnIndex The column index
4909 * @param {Number} newText The new header text
4911 "headerchange": true,
4913 * @event hiddenchange
4914 * Fires when a column is hidden or "unhidden".
4915 * @param {ColumnModel} this
4916 * @param {Number} columnIndex The column index
4917 * @param {Boolean} hidden true if hidden, false otherwise
4919 "hiddenchange": true,
4921 * @event columnmoved
4922 * Fires when a column is moved.
4923 * @param {ColumnModel} this
4924 * @param {Number} oldIndex
4925 * @param {Number} newIndex
4927 "columnmoved" : true,
4929 * @event columlockchange
4930 * Fires when a column's locked state is changed
4931 * @param {ColumnModel} this
4932 * @param {Number} colIndex
4933 * @param {Boolean} locked true if locked
4935 "columnlockchange" : true
4937 Roo.grid.ColumnModel.superclass.constructor.call(this);
4939 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4941 * @cfg {String} header The header text to display in the Grid view.
4944 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4945 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4946 * specified, the column's index is used as an index into the Record's data Array.
4949 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4950 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4953 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4954 * Defaults to the value of the {@link #defaultSortable} property.
4955 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4958 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4961 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4964 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4967 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4970 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4971 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4972 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4973 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4976 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4979 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4982 * @cfg {String} cursor (Optional)
4985 * @cfg {String} tooltip (Optional)
4988 * @cfg {Number} xs (Optional)
4991 * @cfg {Number} sm (Optional)
4994 * @cfg {Number} md (Optional)
4997 * @cfg {Number} lg (Optional)
5000 * Returns the id of the column at the specified index.
5001 * @param {Number} index The column index
5002 * @return {String} the id
5004 getColumnId : function(index){
5005 return this.config[index].id;
5009 * Returns the column for a specified id.
5010 * @param {String} id The column id
5011 * @return {Object} the column
5013 getColumnById : function(id){
5014 return this.lookup[id];
5019 * Returns the column for a specified dataIndex.
5020 * @param {String} dataIndex The column dataIndex
5021 * @return {Object|Boolean} the column or false if not found
5023 getColumnByDataIndex: function(dataIndex){
5024 var index = this.findColumnIndex(dataIndex);
5025 return index > -1 ? this.config[index] : false;
5029 * Returns the index for a specified column id.
5030 * @param {String} id The column id
5031 * @return {Number} the index, or -1 if not found
5033 getIndexById : function(id){
5034 for(var i = 0, len = this.config.length; i < len; i++){
5035 if(this.config[i].id == id){
5043 * Returns the index for a specified column dataIndex.
5044 * @param {String} dataIndex The column dataIndex
5045 * @return {Number} the index, or -1 if not found
5048 findColumnIndex : function(dataIndex){
5049 for(var i = 0, len = this.config.length; i < len; i++){
5050 if(this.config[i].dataIndex == dataIndex){
5058 moveColumn : function(oldIndex, newIndex){
5059 var c = this.config[oldIndex];
5060 this.config.splice(oldIndex, 1);
5061 this.config.splice(newIndex, 0, c);
5062 this.dataMap = null;
5063 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5066 isLocked : function(colIndex){
5067 return this.config[colIndex].locked === true;
5070 setLocked : function(colIndex, value, suppressEvent){
5071 if(this.isLocked(colIndex) == value){
5074 this.config[colIndex].locked = value;
5076 this.fireEvent("columnlockchange", this, colIndex, value);
5080 getTotalLockedWidth : function(){
5082 for(var i = 0; i < this.config.length; i++){
5083 if(this.isLocked(i) && !this.isHidden(i)){
5084 this.totalWidth += this.getColumnWidth(i);
5090 getLockedCount : function(){
5091 for(var i = 0, len = this.config.length; i < len; i++){
5092 if(!this.isLocked(i)){
5099 * Returns the number of columns.
5102 getColumnCount : function(visibleOnly){
5103 if(visibleOnly === true){
5105 for(var i = 0, len = this.config.length; i < len; i++){
5106 if(!this.isHidden(i)){
5112 return this.config.length;
5116 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5117 * @param {Function} fn
5118 * @param {Object} scope (optional)
5119 * @return {Array} result
5121 getColumnsBy : function(fn, scope){
5123 for(var i = 0, len = this.config.length; i < len; i++){
5124 var c = this.config[i];
5125 if(fn.call(scope||this, c, i) === true){
5133 * Returns true if the specified column is sortable.
5134 * @param {Number} col The column index
5137 isSortable : function(col){
5138 if(typeof this.config[col].sortable == "undefined"){
5139 return this.defaultSortable;
5141 return this.config[col].sortable;
5145 * Returns the rendering (formatting) function defined for the column.
5146 * @param {Number} col The column index.
5147 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5149 getRenderer : function(col){
5150 if(!this.config[col].renderer){
5151 return Roo.grid.ColumnModel.defaultRenderer;
5153 return this.config[col].renderer;
5157 * Sets the rendering (formatting) function for a column.
5158 * @param {Number} col The column index
5159 * @param {Function} fn The function to use to process the cell's raw data
5160 * to return HTML markup for the grid view. The render function is called with
5161 * the following parameters:<ul>
5162 * <li>Data value.</li>
5163 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5164 * <li>css A CSS style string to apply to the table cell.</li>
5165 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5166 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5167 * <li>Row index</li>
5168 * <li>Column index</li>
5169 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5171 setRenderer : function(col, fn){
5172 this.config[col].renderer = fn;
5176 * Returns the width for the specified column.
5177 * @param {Number} col The column index
5180 getColumnWidth : function(col){
5181 return this.config[col].width * 1 || this.defaultWidth;
5185 * Sets the width for a column.
5186 * @param {Number} col The column index
5187 * @param {Number} width The new width
5189 setColumnWidth : function(col, width, suppressEvent){
5190 this.config[col].width = width;
5191 this.totalWidth = null;
5193 this.fireEvent("widthchange", this, col, width);
5198 * Returns the total width of all columns.
5199 * @param {Boolean} includeHidden True to include hidden column widths
5202 getTotalWidth : function(includeHidden){
5203 if(!this.totalWidth){
5204 this.totalWidth = 0;
5205 for(var i = 0, len = this.config.length; i < len; i++){
5206 if(includeHidden || !this.isHidden(i)){
5207 this.totalWidth += this.getColumnWidth(i);
5211 return this.totalWidth;
5215 * Returns the header for the specified column.
5216 * @param {Number} col The column index
5219 getColumnHeader : function(col){
5220 return this.config[col].header;
5224 * Sets the header for a column.
5225 * @param {Number} col The column index
5226 * @param {String} header The new header
5228 setColumnHeader : function(col, header){
5229 this.config[col].header = header;
5230 this.fireEvent("headerchange", this, col, header);
5234 * Returns the tooltip for the specified column.
5235 * @param {Number} col The column index
5238 getColumnTooltip : function(col){
5239 return this.config[col].tooltip;
5242 * Sets the tooltip for a column.
5243 * @param {Number} col The column index
5244 * @param {String} tooltip The new tooltip
5246 setColumnTooltip : function(col, tooltip){
5247 this.config[col].tooltip = tooltip;
5251 * Returns the dataIndex for the specified column.
5252 * @param {Number} col The column index
5255 getDataIndex : function(col){
5256 return this.config[col].dataIndex;
5260 * Sets the dataIndex for a column.
5261 * @param {Number} col The column index
5262 * @param {Number} dataIndex The new dataIndex
5264 setDataIndex : function(col, dataIndex){
5265 this.config[col].dataIndex = dataIndex;
5271 * Returns true if the cell is editable.
5272 * @param {Number} colIndex The column index
5273 * @param {Number} rowIndex The row index
5276 isCellEditable : function(colIndex, rowIndex){
5277 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5281 * Returns the editor defined for the cell/column.
5282 * return false or null to disable editing.
5283 * @param {Number} colIndex The column index
5284 * @param {Number} rowIndex The row index
5287 getCellEditor : function(colIndex, rowIndex){
5288 return this.config[colIndex].editor;
5292 * Sets if a column is editable.
5293 * @param {Number} col The column index
5294 * @param {Boolean} editable True if the column is editable
5296 setEditable : function(col, editable){
5297 this.config[col].editable = editable;
5302 * Returns true if the column is hidden.
5303 * @param {Number} colIndex The column index
5306 isHidden : function(colIndex){
5307 return this.config[colIndex].hidden;
5312 * Returns true if the column width cannot be changed
5314 isFixed : function(colIndex){
5315 return this.config[colIndex].fixed;
5319 * Returns true if the column can be resized
5322 isResizable : function(colIndex){
5323 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5326 * Sets if a column is hidden.
5327 * @param {Number} colIndex The column index
5328 * @param {Boolean} hidden True if the column is hidden
5330 setHidden : function(colIndex, hidden){
5331 this.config[colIndex].hidden = hidden;
5332 this.totalWidth = null;
5333 this.fireEvent("hiddenchange", this, colIndex, hidden);
5337 * Sets the editor for a column.
5338 * @param {Number} col The column index
5339 * @param {Object} editor The editor object
5341 setEditor : function(col, editor){
5342 this.config[col].editor = editor;
5346 Roo.grid.ColumnModel.defaultRenderer = function(value){
5347 if(typeof value == "string" && value.length < 1){
5353 // Alias for backwards compatibility
5354 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5357 * Ext JS Library 1.1.1
5358 * Copyright(c) 2006-2007, Ext JS, LLC.
5360 * Originally Released Under LGPL - original licence link has changed is not relivant.
5363 * <script type="text/javascript">
5367 * @class Roo.LoadMask
5368 * A simple utility class for generically masking elements while loading data. If the element being masked has
5369 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5370 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5371 * element's UpdateManager load indicator and will be destroyed after the initial load.
5373 * Create a new LoadMask
5374 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5375 * @param {Object} config The config object
5377 Roo.LoadMask = function(el, config){
5378 this.el = Roo.get(el);
5379 Roo.apply(this, config);
5381 this.store.on('beforeload', this.onBeforeLoad, this);
5382 this.store.on('load', this.onLoad, this);
5383 this.store.on('loadexception', this.onLoadException, this);
5384 this.removeMask = false;
5386 var um = this.el.getUpdateManager();
5387 um.showLoadIndicator = false; // disable the default indicator
5388 um.on('beforeupdate', this.onBeforeLoad, this);
5389 um.on('update', this.onLoad, this);
5390 um.on('failure', this.onLoad, this);
5391 this.removeMask = true;
5395 Roo.LoadMask.prototype = {
5397 * @cfg {Boolean} removeMask
5398 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5399 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5403 * The text to display in a centered loading message box (defaults to 'Loading...')
5407 * @cfg {String} msgCls
5408 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5410 msgCls : 'x-mask-loading',
5413 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5419 * Disables the mask to prevent it from being displayed
5421 disable : function(){
5422 this.disabled = true;
5426 * Enables the mask so that it can be displayed
5428 enable : function(){
5429 this.disabled = false;
5432 onLoadException : function()
5436 if (typeof(arguments[3]) != 'undefined') {
5437 Roo.MessageBox.alert("Error loading",arguments[3]);
5441 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5442 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5451 this.el.unmask(this.removeMask);
5456 this.el.unmask(this.removeMask);
5460 onBeforeLoad : function(){
5462 this.el.mask(this.msg, this.msgCls);
5467 destroy : function(){
5469 this.store.un('beforeload', this.onBeforeLoad, this);
5470 this.store.un('load', this.onLoad, this);
5471 this.store.un('loadexception', this.onLoadException, this);
5473 var um = this.el.getUpdateManager();
5474 um.un('beforeupdate', this.onBeforeLoad, this);
5475 um.un('update', this.onLoad, this);
5476 um.un('failure', this.onLoad, this);
5487 * @class Roo.bootstrap.Table
5488 * @extends Roo.bootstrap.Component
5489 * Bootstrap Table class
5490 * @cfg {String} cls table class
5491 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5492 * @cfg {String} bgcolor Specifies the background color for a table
5493 * @cfg {Number} border Specifies whether the table cells should have borders or not
5494 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5495 * @cfg {Number} cellspacing Specifies the space between cells
5496 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5497 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5498 * @cfg {String} sortable Specifies that the table should be sortable
5499 * @cfg {String} summary Specifies a summary of the content of a table
5500 * @cfg {Number} width Specifies the width of a table
5501 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5503 * @cfg {boolean} striped Should the rows be alternative striped
5504 * @cfg {boolean} bordered Add borders to the table
5505 * @cfg {boolean} hover Add hover highlighting
5506 * @cfg {boolean} condensed Format condensed
5507 * @cfg {boolean} responsive Format condensed
5508 * @cfg {Boolean} loadMask (true|false) default false
5509 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5510 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5511 * @cfg {Boolean} rowSelection (true|false) default false
5512 * @cfg {Boolean} cellSelection (true|false) default false
5513 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5517 * Create a new Table
5518 * @param {Object} config The config object
5521 Roo.bootstrap.Table = function(config){
5522 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5525 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5526 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5527 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5528 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5532 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5533 this.sm = this.selModel;
5534 this.sm.xmodule = this.xmodule || false;
5536 if (this.cm && typeof(this.cm.config) == 'undefined') {
5537 this.colModel = new Roo.grid.ColumnModel(this.cm);
5538 this.cm = this.colModel;
5539 this.cm.xmodule = this.xmodule || false;
5542 this.store= Roo.factory(this.store, Roo.data);
5543 this.ds = this.store;
5544 this.ds.xmodule = this.xmodule || false;
5547 if (this.footer && this.store) {
5548 this.footer.dataSource = this.ds;
5549 this.footer = Roo.factory(this.footer);
5556 * Fires when a cell is clicked
5557 * @param {Roo.bootstrap.Table} this
5558 * @param {Roo.Element} el
5559 * @param {Number} rowIndex
5560 * @param {Number} columnIndex
5561 * @param {Roo.EventObject} e
5565 * @event celldblclick
5566 * Fires when a cell is double clicked
5567 * @param {Roo.bootstrap.Table} this
5568 * @param {Roo.Element} el
5569 * @param {Number} rowIndex
5570 * @param {Number} columnIndex
5571 * @param {Roo.EventObject} e
5573 "celldblclick" : true,
5576 * Fires when a row is clicked
5577 * @param {Roo.bootstrap.Table} this
5578 * @param {Roo.Element} el
5579 * @param {Number} rowIndex
5580 * @param {Roo.EventObject} e
5584 * @event rowdblclick
5585 * Fires when a row is double clicked
5586 * @param {Roo.bootstrap.Table} this
5587 * @param {Roo.Element} el
5588 * @param {Number} rowIndex
5589 * @param {Roo.EventObject} e
5591 "rowdblclick" : true,
5594 * Fires when a mouseover occur
5595 * @param {Roo.bootstrap.Table} this
5596 * @param {Roo.Element} el
5597 * @param {Number} rowIndex
5598 * @param {Number} columnIndex
5599 * @param {Roo.EventObject} e
5604 * Fires when a mouseout occur
5605 * @param {Roo.bootstrap.Table} this
5606 * @param {Roo.Element} el
5607 * @param {Number} rowIndex
5608 * @param {Number} columnIndex
5609 * @param {Roo.EventObject} e
5614 * Fires when a row is rendered, so you can change add a style to it.
5615 * @param {Roo.bootstrap.Table} this
5616 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5620 * @event rowsrendered
5621 * Fires when all the rows have been rendered
5622 * @param {Roo.bootstrap.Table} this
5624 'rowsrendered' : true
5629 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5654 rowSelection : false,
5655 cellSelection : false,
5658 // Roo.Element - the tbody
5661 getAutoCreate : function(){
5662 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5671 cfg.cls += ' table-striped';
5675 cfg.cls += ' table-hover';
5677 if (this.bordered) {
5678 cfg.cls += ' table-bordered';
5680 if (this.condensed) {
5681 cfg.cls += ' table-condensed';
5683 if (this.responsive) {
5684 cfg.cls += ' table-responsive';
5688 cfg.cls+= ' ' +this.cls;
5691 // this lot should be simplifed...
5694 cfg.align=this.align;
5697 cfg.bgcolor=this.bgcolor;
5700 cfg.border=this.border;
5702 if (this.cellpadding) {
5703 cfg.cellpadding=this.cellpadding;
5705 if (this.cellspacing) {
5706 cfg.cellspacing=this.cellspacing;
5709 cfg.frame=this.frame;
5712 cfg.rules=this.rules;
5714 if (this.sortable) {
5715 cfg.sortable=this.sortable;
5718 cfg.summary=this.summary;
5721 cfg.width=this.width;
5724 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5727 if(this.store || this.cm){
5728 if(this.headerShow){
5729 cfg.cn.push(this.renderHeader());
5732 cfg.cn.push(this.renderBody());
5734 if(this.footerShow){
5735 cfg.cn.push(this.renderFooter());
5738 cfg.cls+= ' TableGrid';
5741 return { cn : [ cfg ] };
5744 initEvents : function()
5746 if(!this.store || !this.cm){
5750 //Roo.log('initEvents with ds!!!!');
5752 this.mainBody = this.el.select('tbody', true).first();
5757 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5758 e.on('click', _this.sort, _this);
5761 this.el.on("click", this.onClick, this);
5762 this.el.on("dblclick", this.onDblClick, this);
5764 // why is this done????? = it breaks dialogs??
5765 //this.parent().el.setStyle('position', 'relative');
5769 this.footer.parentId = this.id;
5770 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5773 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5775 this.store.on('load', this.onLoad, this);
5776 this.store.on('beforeload', this.onBeforeLoad, this);
5777 this.store.on('update', this.onUpdate, this);
5778 this.store.on('add', this.onAdd, this);
5782 onMouseover : function(e, el)
5784 var cell = Roo.get(el);
5790 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5791 cell = cell.findParent('td', false, true);
5794 var row = cell.findParent('tr', false, true);
5795 var cellIndex = cell.dom.cellIndex;
5796 var rowIndex = row.dom.rowIndex - 1; // start from 0
5798 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5802 onMouseout : function(e, el)
5804 var cell = Roo.get(el);
5810 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5811 cell = cell.findParent('td', false, true);
5814 var row = cell.findParent('tr', false, true);
5815 var cellIndex = cell.dom.cellIndex;
5816 var rowIndex = row.dom.rowIndex - 1; // start from 0
5818 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5822 onClick : function(e, el)
5824 var cell = Roo.get(el);
5826 if(!cell || (!this.cellSelection && !this.rowSelection)){
5830 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5831 cell = cell.findParent('td', false, true);
5834 if(!cell || typeof(cell) == 'undefined'){
5838 var row = cell.findParent('tr', false, true);
5840 if(!row || typeof(row) == 'undefined'){
5844 var cellIndex = cell.dom.cellIndex;
5845 var rowIndex = this.getRowIndex(row);
5847 // why??? - should these not be based on SelectionModel?
5848 if(this.cellSelection){
5849 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5852 if(this.rowSelection){
5853 this.fireEvent('rowclick', this, row, rowIndex, e);
5859 onDblClick : function(e,el)
5861 var cell = Roo.get(el);
5863 if(!cell || (!this.CellSelection && !this.RowSelection)){
5867 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5868 cell = cell.findParent('td', false, true);
5871 if(!cell || typeof(cell) == 'undefined'){
5875 var row = cell.findParent('tr', false, true);
5877 if(!row || typeof(row) == 'undefined'){
5881 var cellIndex = cell.dom.cellIndex;
5882 var rowIndex = this.getRowIndex(row);
5884 if(this.CellSelection){
5885 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5888 if(this.RowSelection){
5889 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5893 sort : function(e,el)
5895 var col = Roo.get(el);
5897 if(!col.hasClass('sortable')){
5901 var sort = col.attr('sort');
5904 if(col.hasClass('glyphicon-arrow-up')){
5908 this.store.sortInfo = {field : sort, direction : dir};
5911 Roo.log("calling footer first");
5912 this.footer.onClick('first');
5915 this.store.load({ params : { start : 0 } });
5919 renderHeader : function()
5928 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5930 var config = cm.config[i];
5935 html: cm.getColumnHeader(i)
5940 if(typeof(config.lgHeader) != 'undefined'){
5941 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5944 if(typeof(config.mdHeader) != 'undefined'){
5945 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5948 if(typeof(config.smHeader) != 'undefined'){
5949 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5952 if(typeof(config.xsHeader) != 'undefined'){
5953 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5960 if(typeof(config.tooltip) != 'undefined'){
5961 c.tooltip = config.tooltip;
5964 if(typeof(config.colspan) != 'undefined'){
5965 c.colspan = config.colspan;
5968 if(typeof(config.hidden) != 'undefined' && config.hidden){
5969 c.style += ' display:none;';
5972 if(typeof(config.dataIndex) != 'undefined'){
5973 c.sort = config.dataIndex;
5976 if(typeof(config.sortable) != 'undefined' && config.sortable){
5980 if(typeof(config.align) != 'undefined' && config.align.length){
5981 c.style += ' text-align:' + config.align + ';';
5984 if(typeof(config.width) != 'undefined'){
5985 c.style += ' width:' + config.width + 'px;';
5988 if(typeof(config.cls) != 'undefined'){
5989 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5992 ['xs','sm','md','lg'].map(function(size){
5994 if(typeof(config[size]) == 'undefined'){
5998 if (!config[size]) { // 0 = hidden
5999 cfg.cls += ' hidden-' + size;
6003 cfg.cls += ' col-' + size + '-' + config[size];
6013 renderBody : function()
6023 colspan : this.cm.getColumnCount()
6033 renderFooter : function()
6043 colspan : this.cm.getColumnCount()
6057 Roo.log('ds onload');
6062 var ds = this.store;
6064 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6065 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6067 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6068 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6071 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6072 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6076 var tbody = this.mainBody;
6078 if(ds.getCount() > 0){
6079 ds.data.each(function(d,rowIndex){
6080 var row = this.renderRow(cm, ds, rowIndex);
6082 tbody.createChild(row);
6086 if(row.cellObjects.length){
6087 Roo.each(row.cellObjects, function(r){
6088 _this.renderCellObject(r);
6095 Roo.each(this.el.select('tbody td', true).elements, function(e){
6096 e.on('mouseover', _this.onMouseover, _this);
6099 Roo.each(this.el.select('tbody td', true).elements, function(e){
6100 e.on('mouseout', _this.onMouseout, _this);
6102 this.fireEvent('rowsrendered', this);
6103 //if(this.loadMask){
6104 // this.maskEl.hide();
6109 onUpdate : function(ds,record)
6111 this.refreshRow(record);
6114 onRemove : function(ds, record, index, isUpdate){
6115 if(isUpdate !== true){
6116 this.fireEvent("beforerowremoved", this, index, record);
6118 var bt = this.mainBody.dom;
6120 var rows = this.el.select('tbody > tr', true).elements;
6122 if(typeof(rows[index]) != 'undefined'){
6123 bt.removeChild(rows[index].dom);
6126 // if(bt.rows[index]){
6127 // bt.removeChild(bt.rows[index]);
6130 if(isUpdate !== true){
6131 //this.stripeRows(index);
6132 //this.syncRowHeights(index, index);
6134 this.fireEvent("rowremoved", this, index, record);
6138 onAdd : function(ds, records, rowIndex)
6140 //Roo.log('on Add called');
6141 // - note this does not handle multiple adding very well..
6142 var bt = this.mainBody.dom;
6143 for (var i =0 ; i < records.length;i++) {
6144 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6145 //Roo.log(records[i]);
6146 //Roo.log(this.store.getAt(rowIndex+i));
6147 this.insertRow(this.store, rowIndex + i, false);
6154 refreshRow : function(record){
6155 var ds = this.store, index;
6156 if(typeof record == 'number'){
6158 record = ds.getAt(index);
6160 index = ds.indexOf(record);
6162 this.insertRow(ds, index, true);
6163 this.onRemove(ds, record, index+1, true);
6164 //this.syncRowHeights(index, index);
6166 this.fireEvent("rowupdated", this, index, record);
6169 insertRow : function(dm, rowIndex, isUpdate){
6172 this.fireEvent("beforerowsinserted", this, rowIndex);
6174 //var s = this.getScrollState();
6175 var row = this.renderRow(this.cm, this.store, rowIndex);
6176 // insert before rowIndex..
6177 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6181 if(row.cellObjects.length){
6182 Roo.each(row.cellObjects, function(r){
6183 _this.renderCellObject(r);
6188 this.fireEvent("rowsinserted", this, rowIndex);
6189 //this.syncRowHeights(firstRow, lastRow);
6190 //this.stripeRows(firstRow);
6197 getRowDom : function(rowIndex)
6199 var rows = this.el.select('tbody > tr', true).elements;
6201 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6204 // returns the object tree for a tr..
6207 renderRow : function(cm, ds, rowIndex)
6210 var d = ds.getAt(rowIndex);
6217 var cellObjects = [];
6219 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6220 var config = cm.config[i];
6222 var renderer = cm.getRenderer(i);
6226 if(typeof(renderer) !== 'undefined'){
6227 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6229 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6230 // and are rendered into the cells after the row is rendered - using the id for the element.
6232 if(typeof(value) === 'object'){
6242 rowIndex : rowIndex,
6247 this.fireEvent('rowclass', this, rowcfg);
6251 cls : rowcfg.rowClass,
6253 html: (typeof(value) === 'object') ? '' : value
6260 if(typeof(config.colspan) != 'undefined'){
6261 td.colspan = config.colspan;
6264 if(typeof(config.hidden) != 'undefined' && config.hidden){
6265 td.style += ' display:none;';
6268 if(typeof(config.align) != 'undefined' && config.align.length){
6269 td.style += ' text-align:' + config.align + ';';
6272 if(typeof(config.width) != 'undefined'){
6273 td.style += ' width:' + config.width + 'px;';
6276 if(typeof(config.cursor) != 'undefined'){
6277 td.style += ' cursor:' + config.cursor + ';';
6280 if(typeof(config.cls) != 'undefined'){
6281 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6284 ['xs','sm','md','lg'].map(function(size){
6286 if(typeof(config[size]) == 'undefined'){
6290 if (!config[size]) { // 0 = hidden
6291 td.cls += ' hidden-' + size;
6295 td.cls += ' col-' + size + '-' + config[size];
6303 row.cellObjects = cellObjects;
6311 onBeforeLoad : function()
6313 //Roo.log('ds onBeforeLoad');
6317 //if(this.loadMask){
6318 // this.maskEl.show();
6326 this.el.select('tbody', true).first().dom.innerHTML = '';
6329 * Show or hide a row.
6330 * @param {Number} rowIndex to show or hide
6331 * @param {Boolean} state hide
6333 setRowVisibility : function(rowIndex, state)
6335 var bt = this.mainBody.dom;
6337 var rows = this.el.select('tbody > tr', true).elements;
6339 if(typeof(rows[rowIndex]) == 'undefined'){
6342 rows[rowIndex].dom.style.display = state ? '' : 'none';
6346 getSelectionModel : function(){
6348 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6350 return this.selModel;
6353 * Render the Roo.bootstrap object from renderder
6355 renderCellObject : function(r)
6359 var t = r.cfg.render(r.container);
6362 Roo.each(r.cfg.cn, function(c){
6364 container: t.getChildContainer(),
6367 _this.renderCellObject(child);
6372 getRowIndex : function(row)
6376 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6399 * @class Roo.bootstrap.TableCell
6400 * @extends Roo.bootstrap.Component
6401 * Bootstrap TableCell class
6402 * @cfg {String} html cell contain text
6403 * @cfg {String} cls cell class
6404 * @cfg {String} tag cell tag (td|th) default td
6405 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6406 * @cfg {String} align Aligns the content in a cell
6407 * @cfg {String} axis Categorizes cells
6408 * @cfg {String} bgcolor Specifies the background color of a cell
6409 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6410 * @cfg {Number} colspan Specifies the number of columns a cell should span
6411 * @cfg {String} headers Specifies one or more header cells a cell is related to
6412 * @cfg {Number} height Sets the height of a cell
6413 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6414 * @cfg {Number} rowspan Sets the number of rows a cell should span
6415 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6416 * @cfg {String} valign Vertical aligns the content in a cell
6417 * @cfg {Number} width Specifies the width of a cell
6420 * Create a new TableCell
6421 * @param {Object} config The config object
6424 Roo.bootstrap.TableCell = function(config){
6425 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6428 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6448 getAutoCreate : function(){
6449 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6469 cfg.align=this.align
6475 cfg.bgcolor=this.bgcolor
6478 cfg.charoff=this.charoff
6481 cfg.colspan=this.colspan
6484 cfg.headers=this.headers
6487 cfg.height=this.height
6490 cfg.nowrap=this.nowrap
6493 cfg.rowspan=this.rowspan
6496 cfg.scope=this.scope
6499 cfg.valign=this.valign
6502 cfg.width=this.width
6521 * @class Roo.bootstrap.TableRow
6522 * @extends Roo.bootstrap.Component
6523 * Bootstrap TableRow class
6524 * @cfg {String} cls row class
6525 * @cfg {String} align Aligns the content in a table row
6526 * @cfg {String} bgcolor Specifies a background color for a table row
6527 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6528 * @cfg {String} valign Vertical aligns the content in a table row
6531 * Create a new TableRow
6532 * @param {Object} config The config object
6535 Roo.bootstrap.TableRow = function(config){
6536 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6539 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6547 getAutoCreate : function(){
6548 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6558 cfg.align = this.align;
6561 cfg.bgcolor = this.bgcolor;
6564 cfg.charoff = this.charoff;
6567 cfg.valign = this.valign;
6585 * @class Roo.bootstrap.TableBody
6586 * @extends Roo.bootstrap.Component
6587 * Bootstrap TableBody class
6588 * @cfg {String} cls element class
6589 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6590 * @cfg {String} align Aligns the content inside the element
6591 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6592 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6595 * Create a new TableBody
6596 * @param {Object} config The config object
6599 Roo.bootstrap.TableBody = function(config){
6600 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6603 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6611 getAutoCreate : function(){
6612 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6626 cfg.align = this.align;
6629 cfg.charoff = this.charoff;
6632 cfg.valign = this.valign;
6639 // initEvents : function()
6646 // this.store = Roo.factory(this.store, Roo.data);
6647 // this.store.on('load', this.onLoad, this);
6649 // this.store.load();
6653 // onLoad: function ()
6655 // this.fireEvent('load', this);
6665 * Ext JS Library 1.1.1
6666 * Copyright(c) 2006-2007, Ext JS, LLC.
6668 * Originally Released Under LGPL - original licence link has changed is not relivant.
6671 * <script type="text/javascript">
6674 // as we use this in bootstrap.
6675 Roo.namespace('Roo.form');
6677 * @class Roo.form.Action
6678 * Internal Class used to handle form actions
6680 * @param {Roo.form.BasicForm} el The form element or its id
6681 * @param {Object} config Configuration options
6686 // define the action interface
6687 Roo.form.Action = function(form, options){
6689 this.options = options || {};
6692 * Client Validation Failed
6695 Roo.form.Action.CLIENT_INVALID = 'client';
6697 * Server Validation Failed
6700 Roo.form.Action.SERVER_INVALID = 'server';
6702 * Connect to Server Failed
6705 Roo.form.Action.CONNECT_FAILURE = 'connect';
6707 * Reading Data from Server Failed
6710 Roo.form.Action.LOAD_FAILURE = 'load';
6712 Roo.form.Action.prototype = {
6714 failureType : undefined,
6715 response : undefined,
6719 run : function(options){
6724 success : function(response){
6729 handleResponse : function(response){
6733 // default connection failure
6734 failure : function(response){
6736 this.response = response;
6737 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6738 this.form.afterAction(this, false);
6741 processResponse : function(response){
6742 this.response = response;
6743 if(!response.responseText){
6746 this.result = this.handleResponse(response);
6750 // utility functions used internally
6751 getUrl : function(appendParams){
6752 var url = this.options.url || this.form.url || this.form.el.dom.action;
6754 var p = this.getParams();
6756 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6762 getMethod : function(){
6763 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6766 getParams : function(){
6767 var bp = this.form.baseParams;
6768 var p = this.options.params;
6770 if(typeof p == "object"){
6771 p = Roo.urlEncode(Roo.applyIf(p, bp));
6772 }else if(typeof p == 'string' && bp){
6773 p += '&' + Roo.urlEncode(bp);
6776 p = Roo.urlEncode(bp);
6781 createCallback : function(){
6783 success: this.success,
6784 failure: this.failure,
6786 timeout: (this.form.timeout*1000),
6787 upload: this.form.fileUpload ? this.success : undefined
6792 Roo.form.Action.Submit = function(form, options){
6793 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6796 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6799 haveProgress : false,
6800 uploadComplete : false,
6802 // uploadProgress indicator.
6803 uploadProgress : function()
6805 if (!this.form.progressUrl) {
6809 if (!this.haveProgress) {
6810 Roo.MessageBox.progress("Uploading", "Uploading");
6812 if (this.uploadComplete) {
6813 Roo.MessageBox.hide();
6817 this.haveProgress = true;
6819 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6821 var c = new Roo.data.Connection();
6823 url : this.form.progressUrl,
6828 success : function(req){
6829 //console.log(data);
6833 rdata = Roo.decode(req.responseText)
6835 Roo.log("Invalid data from server..");
6839 if (!rdata || !rdata.success) {
6841 Roo.MessageBox.alert(Roo.encode(rdata));
6844 var data = rdata.data;
6846 if (this.uploadComplete) {
6847 Roo.MessageBox.hide();
6852 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6853 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6856 this.uploadProgress.defer(2000,this);
6859 failure: function(data) {
6860 Roo.log('progress url failed ');
6871 // run get Values on the form, so it syncs any secondary forms.
6872 this.form.getValues();
6874 var o = this.options;
6875 var method = this.getMethod();
6876 var isPost = method == 'POST';
6877 if(o.clientValidation === false || this.form.isValid()){
6879 if (this.form.progressUrl) {
6880 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6881 (new Date() * 1) + '' + Math.random());
6886 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6887 form:this.form.el.dom,
6888 url:this.getUrl(!isPost),
6890 params:isPost ? this.getParams() : null,
6891 isUpload: this.form.fileUpload
6894 this.uploadProgress();
6896 }else if (o.clientValidation !== false){ // client validation failed
6897 this.failureType = Roo.form.Action.CLIENT_INVALID;
6898 this.form.afterAction(this, false);
6902 success : function(response)
6904 this.uploadComplete= true;
6905 if (this.haveProgress) {
6906 Roo.MessageBox.hide();
6910 var result = this.processResponse(response);
6911 if(result === true || result.success){
6912 this.form.afterAction(this, true);
6916 this.form.markInvalid(result.errors);
6917 this.failureType = Roo.form.Action.SERVER_INVALID;
6919 this.form.afterAction(this, false);
6921 failure : function(response)
6923 this.uploadComplete= true;
6924 if (this.haveProgress) {
6925 Roo.MessageBox.hide();
6928 this.response = response;
6929 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6930 this.form.afterAction(this, false);
6933 handleResponse : function(response){
6934 if(this.form.errorReader){
6935 var rs = this.form.errorReader.read(response);
6938 for(var i = 0, len = rs.records.length; i < len; i++) {
6939 var r = rs.records[i];
6943 if(errors.length < 1){
6947 success : rs.success,
6953 ret = Roo.decode(response.responseText);
6957 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6967 Roo.form.Action.Load = function(form, options){
6968 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6969 this.reader = this.form.reader;
6972 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6977 Roo.Ajax.request(Roo.apply(
6978 this.createCallback(), {
6979 method:this.getMethod(),
6980 url:this.getUrl(false),
6981 params:this.getParams()
6985 success : function(response){
6987 var result = this.processResponse(response);
6988 if(result === true || !result.success || !result.data){
6989 this.failureType = Roo.form.Action.LOAD_FAILURE;
6990 this.form.afterAction(this, false);
6993 this.form.clearInvalid();
6994 this.form.setValues(result.data);
6995 this.form.afterAction(this, true);
6998 handleResponse : function(response){
6999 if(this.form.reader){
7000 var rs = this.form.reader.read(response);
7001 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7003 success : rs.success,
7007 return Roo.decode(response.responseText);
7011 Roo.form.Action.ACTION_TYPES = {
7012 'load' : Roo.form.Action.Load,
7013 'submit' : Roo.form.Action.Submit
7022 * @class Roo.bootstrap.Form
7023 * @extends Roo.bootstrap.Component
7024 * Bootstrap Form class
7025 * @cfg {String} method GET | POST (default POST)
7026 * @cfg {String} labelAlign top | left (default top)
7027 * @cfg {String} align left | right - for navbars
7028 * @cfg {Boolean} loadMask load mask when submit (default true)
7033 * @param {Object} config The config object
7037 Roo.bootstrap.Form = function(config){
7038 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7041 * @event clientvalidation
7042 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7043 * @param {Form} this
7044 * @param {Boolean} valid true if the form has passed client-side validation
7046 clientvalidation: true,
7048 * @event beforeaction
7049 * Fires before any action is performed. Return false to cancel the action.
7050 * @param {Form} this
7051 * @param {Action} action The action to be performed
7055 * @event actionfailed
7056 * Fires when an action fails.
7057 * @param {Form} this
7058 * @param {Action} action The action that failed
7060 actionfailed : true,
7062 * @event actioncomplete
7063 * Fires when an action is completed.
7064 * @param {Form} this
7065 * @param {Action} action The action that completed
7067 actioncomplete : true
7072 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7075 * @cfg {String} method
7076 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7081 * The URL to use for form actions if one isn't supplied in the action options.
7084 * @cfg {Boolean} fileUpload
7085 * Set to true if this form is a file upload.
7089 * @cfg {Object} baseParams
7090 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7094 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7098 * @cfg {Sting} align (left|right) for navbar forms
7103 activeAction : null,
7106 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7107 * element by passing it or its id or mask the form itself by passing in true.
7110 waitMsgTarget : false,
7114 getAutoCreate : function(){
7118 method : this.method || 'POST',
7119 id : this.id || Roo.id(),
7122 if (this.parent().xtype.match(/^Nav/)) {
7123 cfg.cls = 'navbar-form navbar-' + this.align;
7127 if (this.labelAlign == 'left' ) {
7128 cfg.cls += ' form-horizontal';
7134 initEvents : function()
7136 this.el.on('submit', this.onSubmit, this);
7137 // this was added as random key presses on the form where triggering form submit.
7138 this.el.on('keypress', function(e) {
7139 if (e.getCharCode() != 13) {
7142 // we might need to allow it for textareas.. and some other items.
7143 // check e.getTarget().
7145 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7149 Roo.log("keypress blocked");
7157 onSubmit : function(e){
7162 * Returns true if client-side validation on the form is successful.
7165 isValid : function(){
7166 var items = this.getItems();
7168 items.each(function(f){
7177 * Returns true if any fields in this form have changed since their original load.
7180 isDirty : function(){
7182 var items = this.getItems();
7183 items.each(function(f){
7193 * Performs a predefined action (submit or load) or custom actions you define on this form.
7194 * @param {String} actionName The name of the action type
7195 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7196 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7197 * accept other config options):
7199 Property Type Description
7200 ---------------- --------------- ----------------------------------------------------------------------------------
7201 url String The url for the action (defaults to the form's url)
7202 method String The form method to use (defaults to the form's method, or POST if not defined)
7203 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7204 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7205 validate the form on the client (defaults to false)
7207 * @return {BasicForm} this
7209 doAction : function(action, options){
7210 if(typeof action == 'string'){
7211 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7213 if(this.fireEvent('beforeaction', this, action) !== false){
7214 this.beforeAction(action);
7215 action.run.defer(100, action);
7221 beforeAction : function(action){
7222 var o = action.options;
7225 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7227 // not really supported yet.. ??
7229 //if(this.waitMsgTarget === true){
7230 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7231 //}else if(this.waitMsgTarget){
7232 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7233 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7235 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7241 afterAction : function(action, success){
7242 this.activeAction = null;
7243 var o = action.options;
7245 //if(this.waitMsgTarget === true){
7247 //}else if(this.waitMsgTarget){
7248 // this.waitMsgTarget.unmask();
7250 // Roo.MessageBox.updateProgress(1);
7251 // Roo.MessageBox.hide();
7258 Roo.callback(o.success, o.scope, [this, action]);
7259 this.fireEvent('actioncomplete', this, action);
7263 // failure condition..
7264 // we have a scenario where updates need confirming.
7265 // eg. if a locking scenario exists..
7266 // we look for { errors : { needs_confirm : true }} in the response.
7268 (typeof(action.result) != 'undefined') &&
7269 (typeof(action.result.errors) != 'undefined') &&
7270 (typeof(action.result.errors.needs_confirm) != 'undefined')
7273 Roo.log("not supported yet");
7276 Roo.MessageBox.confirm(
7277 "Change requires confirmation",
7278 action.result.errorMsg,
7283 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7293 Roo.callback(o.failure, o.scope, [this, action]);
7294 // show an error message if no failed handler is set..
7295 if (!this.hasListener('actionfailed')) {
7296 Roo.log("need to add dialog support");
7298 Roo.MessageBox.alert("Error",
7299 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7300 action.result.errorMsg :
7301 "Saving Failed, please check your entries or try again"
7306 this.fireEvent('actionfailed', this, action);
7311 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7312 * @param {String} id The value to search for
7315 findField : function(id){
7316 var items = this.getItems();
7317 var field = items.get(id);
7319 items.each(function(f){
7320 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7327 return field || null;
7330 * Mark fields in this form invalid in bulk.
7331 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7332 * @return {BasicForm} this
7334 markInvalid : function(errors){
7335 if(errors instanceof Array){
7336 for(var i = 0, len = errors.length; i < len; i++){
7337 var fieldError = errors[i];
7338 var f = this.findField(fieldError.id);
7340 f.markInvalid(fieldError.msg);
7346 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7347 field.markInvalid(errors[id]);
7351 //Roo.each(this.childForms || [], function (f) {
7352 // f.markInvalid(errors);
7359 * Set values for fields in this form in bulk.
7360 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7361 * @return {BasicForm} this
7363 setValues : function(values){
7364 if(values instanceof Array){ // array of objects
7365 for(var i = 0, len = values.length; i < len; i++){
7367 var f = this.findField(v.id);
7369 f.setValue(v.value);
7370 if(this.trackResetOnLoad){
7371 f.originalValue = f.getValue();
7375 }else{ // object hash
7378 if(typeof values[id] != 'function' && (field = this.findField(id))){
7380 if (field.setFromData &&
7382 field.displayField &&
7383 // combos' with local stores can
7384 // be queried via setValue()
7385 // to set their value..
7386 (field.store && !field.store.isLocal)
7390 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7391 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7392 field.setFromData(sd);
7395 field.setValue(values[id]);
7399 if(this.trackResetOnLoad){
7400 field.originalValue = field.getValue();
7406 //Roo.each(this.childForms || [], function (f) {
7407 // f.setValues(values);
7414 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7415 * they are returned as an array.
7416 * @param {Boolean} asString
7419 getValues : function(asString){
7420 //if (this.childForms) {
7421 // copy values from the child forms
7422 // Roo.each(this.childForms, function (f) {
7423 // this.setValues(f.getValues());
7429 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7430 if(asString === true){
7433 return Roo.urlDecode(fs);
7437 * Returns the fields in this form as an object with key/value pairs.
7438 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7441 getFieldValues : function(with_hidden)
7443 var items = this.getItems();
7445 items.each(function(f){
7449 var v = f.getValue();
7450 if (f.inputType =='radio') {
7451 if (typeof(ret[f.getName()]) == 'undefined') {
7452 ret[f.getName()] = ''; // empty..
7455 if (!f.el.dom.checked) {
7463 // not sure if this supported any more..
7464 if ((typeof(v) == 'object') && f.getRawValue) {
7465 v = f.getRawValue() ; // dates..
7467 // combo boxes where name != hiddenName...
7468 if (f.name != f.getName()) {
7469 ret[f.name] = f.getRawValue();
7471 ret[f.getName()] = v;
7478 * Clears all invalid messages in this form.
7479 * @return {BasicForm} this
7481 clearInvalid : function(){
7482 var items = this.getItems();
7484 items.each(function(f){
7495 * @return {BasicForm} this
7498 var items = this.getItems();
7499 items.each(function(f){
7503 Roo.each(this.childForms || [], function (f) {
7510 getItems : function()
7512 var r=new Roo.util.MixedCollection(false, function(o){
7513 return o.id || (o.id = Roo.id());
7515 var iter = function(el) {
7522 Roo.each(el.items,function(e) {
7542 * Ext JS Library 1.1.1
7543 * Copyright(c) 2006-2007, Ext JS, LLC.
7545 * Originally Released Under LGPL - original licence link has changed is not relivant.
7548 * <script type="text/javascript">
7551 * @class Roo.form.VTypes
7552 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7555 Roo.form.VTypes = function(){
7556 // closure these in so they are only created once.
7557 var alpha = /^[a-zA-Z_]+$/;
7558 var alphanum = /^[a-zA-Z0-9_]+$/;
7559 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7560 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7562 // All these messages and functions are configurable
7565 * The function used to validate email addresses
7566 * @param {String} value The email address
7568 'email' : function(v){
7569 return email.test(v);
7572 * The error text to display when the email validation function returns false
7575 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7577 * The keystroke filter mask to be applied on email input
7580 'emailMask' : /[a-z0-9_\.\-@]/i,
7583 * The function used to validate URLs
7584 * @param {String} value The URL
7586 'url' : function(v){
7590 * The error text to display when the url validation function returns false
7593 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7596 * The function used to validate alpha values
7597 * @param {String} value The value
7599 'alpha' : function(v){
7600 return alpha.test(v);
7603 * The error text to display when the alpha validation function returns false
7606 'alphaText' : 'This field should only contain letters and _',
7608 * The keystroke filter mask to be applied on alpha input
7611 'alphaMask' : /[a-z_]/i,
7614 * The function used to validate alphanumeric values
7615 * @param {String} value The value
7617 'alphanum' : function(v){
7618 return alphanum.test(v);
7621 * The error text to display when the alphanumeric validation function returns false
7624 'alphanumText' : 'This field should only contain letters, numbers and _',
7626 * The keystroke filter mask to be applied on alphanumeric input
7629 'alphanumMask' : /[a-z0-9_]/i
7639 * @class Roo.bootstrap.Input
7640 * @extends Roo.bootstrap.Component
7641 * Bootstrap Input class
7642 * @cfg {Boolean} disabled is it disabled
7643 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7644 * @cfg {String} name name of the input
7645 * @cfg {string} fieldLabel - the label associated
7646 * @cfg {string} placeholder - placeholder to put in text.
7647 * @cfg {string} before - input group add on before
7648 * @cfg {string} after - input group add on after
7649 * @cfg {string} size - (lg|sm) or leave empty..
7650 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7651 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7652 * @cfg {Number} md colspan out of 12 for computer-sized screens
7653 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7654 * @cfg {string} value default value of the input
7655 * @cfg {Number} labelWidth set the width of label (0-12)
7656 * @cfg {String} labelAlign (top|left)
7657 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7658 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7660 * @cfg {String} align (left|center|right) Default left
7661 * @cfg {Boolean} forceFeedback (true|false) Default false
7667 * Create a new Input
7668 * @param {Object} config The config object
7671 Roo.bootstrap.Input = function(config){
7672 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7677 * Fires when this field receives input focus.
7678 * @param {Roo.form.Field} this
7683 * Fires when this field loses input focus.
7684 * @param {Roo.form.Field} this
7689 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7690 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7691 * @param {Roo.form.Field} this
7692 * @param {Roo.EventObject} e The event object
7697 * Fires just before the field blurs if the field value has changed.
7698 * @param {Roo.form.Field} this
7699 * @param {Mixed} newValue The new value
7700 * @param {Mixed} oldValue The original value
7705 * Fires after the field has been marked as invalid.
7706 * @param {Roo.form.Field} this
7707 * @param {String} msg The validation message
7712 * Fires after the field has been validated with no errors.
7713 * @param {Roo.form.Field} this
7718 * Fires after the key up
7719 * @param {Roo.form.Field} this
7720 * @param {Roo.EventObject} e The event Object
7726 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7728 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7729 automatic validation (defaults to "keyup").
7731 validationEvent : "keyup",
7733 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7735 validateOnBlur : true,
7737 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7739 validationDelay : 250,
7741 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7743 focusClass : "x-form-focus", // not needed???
7747 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7749 invalidClass : "has-warning",
7752 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7754 validClass : "has-success",
7757 * @cfg {Boolean} hasFeedback (true|false) default true
7762 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7764 invalidFeedbackClass : "glyphicon-warning-sign",
7767 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7769 validFeedbackClass : "glyphicon-ok",
7772 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7774 selectOnFocus : false,
7777 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7781 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7786 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7788 disableKeyFilter : false,
7791 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7795 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7799 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7801 blankText : "This field is required",
7804 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7808 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7810 maxLength : Number.MAX_VALUE,
7812 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7814 minLengthText : "The minimum length for this field is {0}",
7816 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7818 maxLengthText : "The maximum length for this field is {0}",
7822 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7823 * If available, this function will be called only after the basic validators all return true, and will be passed the
7824 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7828 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7829 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7830 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7834 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7838 autocomplete: false,
7857 formatedValue : false,
7858 forceFeedback : false,
7860 parentLabelAlign : function()
7863 while (parent.parent()) {
7864 parent = parent.parent();
7865 if (typeof(parent.labelAlign) !='undefined') {
7866 return parent.labelAlign;
7873 getAutoCreate : function(){
7875 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7881 if(this.inputType != 'hidden'){
7882 cfg.cls = 'form-group' //input-group
7888 type : this.inputType,
7890 cls : 'form-control',
7891 placeholder : this.placeholder || '',
7892 autocomplete : this.autocomplete || 'new-password'
7897 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7900 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7901 input.maxLength = this.maxLength;
7904 if (this.disabled) {
7905 input.disabled=true;
7908 if (this.readOnly) {
7909 input.readonly=true;
7913 input.name = this.name;
7916 input.cls += ' input-' + this.size;
7919 ['xs','sm','md','lg'].map(function(size){
7920 if (settings[size]) {
7921 cfg.cls += ' col-' + size + '-' + settings[size];
7925 var inputblock = input;
7929 cls: 'glyphicon form-control-feedback'
7932 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7935 cls : 'has-feedback',
7943 if (this.before || this.after) {
7946 cls : 'input-group',
7950 if (this.before && typeof(this.before) == 'string') {
7952 inputblock.cn.push({
7954 cls : 'roo-input-before input-group-addon',
7958 if (this.before && typeof(this.before) == 'object') {
7959 this.before = Roo.factory(this.before);
7960 Roo.log(this.before);
7961 inputblock.cn.push({
7963 cls : 'roo-input-before input-group-' +
7964 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7968 inputblock.cn.push(input);
7970 if (this.after && typeof(this.after) == 'string') {
7971 inputblock.cn.push({
7973 cls : 'roo-input-after input-group-addon',
7977 if (this.after && typeof(this.after) == 'object') {
7978 this.after = Roo.factory(this.after);
7979 Roo.log(this.after);
7980 inputblock.cn.push({
7982 cls : 'roo-input-after input-group-' +
7983 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7987 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7988 inputblock.cls += ' has-feedback';
7989 inputblock.cn.push(feedback);
7993 if (align ==='left' && this.fieldLabel.length) {
7994 Roo.log("left and has label");
8000 cls : 'control-label col-sm-' + this.labelWidth,
8001 html : this.fieldLabel
8005 cls : "col-sm-" + (12 - this.labelWidth),
8012 } else if ( this.fieldLabel.length) {
8018 //cls : 'input-group-addon',
8019 html : this.fieldLabel
8029 Roo.log(" no label && no align");
8038 Roo.log('input-parentType: ' + this.parentType);
8040 if (this.parentType === 'Navbar' && this.parent().bar) {
8041 cfg.cls += ' navbar-form';
8049 * return the real input element.
8051 inputEl: function ()
8053 return this.el.select('input.form-control',true).first();
8056 tooltipEl : function()
8058 return this.inputEl();
8061 setDisabled : function(v)
8063 var i = this.inputEl().dom;
8065 i.removeAttribute('disabled');
8069 i.setAttribute('disabled','true');
8071 initEvents : function()
8074 this.inputEl().on("keydown" , this.fireKey, this);
8075 this.inputEl().on("focus", this.onFocus, this);
8076 this.inputEl().on("blur", this.onBlur, this);
8078 this.inputEl().relayEvent('keyup', this);
8080 // reference to original value for reset
8081 this.originalValue = this.getValue();
8082 //Roo.form.TextField.superclass.initEvents.call(this);
8083 if(this.validationEvent == 'keyup'){
8084 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8085 this.inputEl().on('keyup', this.filterValidation, this);
8087 else if(this.validationEvent !== false){
8088 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8091 if(this.selectOnFocus){
8092 this.on("focus", this.preFocus, this);
8095 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8096 this.inputEl().on("keypress", this.filterKeys, this);
8099 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8100 this.el.on("click", this.autoSize, this);
8103 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8104 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8107 if (typeof(this.before) == 'object') {
8108 this.before.render(this.el.select('.roo-input-before',true).first());
8110 if (typeof(this.after) == 'object') {
8111 this.after.render(this.el.select('.roo-input-after',true).first());
8116 filterValidation : function(e){
8117 if(!e.isNavKeyPress()){
8118 this.validationTask.delay(this.validationDelay);
8122 * Validates the field value
8123 * @return {Boolean} True if the value is valid, else false
8125 validate : function(){
8126 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8127 if(this.disabled || this.validateValue(this.getRawValue())){
8138 * Validates a value according to the field's validation rules and marks the field as invalid
8139 * if the validation fails
8140 * @param {Mixed} value The value to validate
8141 * @return {Boolean} True if the value is valid, else false
8143 validateValue : function(value){
8144 if(value.length < 1) { // if it's blank
8145 if(this.allowBlank){
8151 if(value.length < this.minLength){
8154 if(value.length > this.maxLength){
8158 var vt = Roo.form.VTypes;
8159 if(!vt[this.vtype](value, this)){
8163 if(typeof this.validator == "function"){
8164 var msg = this.validator(value);
8170 if(this.regex && !this.regex.test(value)){
8180 fireKey : function(e){
8181 //Roo.log('field ' + e.getKey());
8182 if(e.isNavKeyPress()){
8183 this.fireEvent("specialkey", this, e);
8186 focus : function (selectText){
8188 this.inputEl().focus();
8189 if(selectText === true){
8190 this.inputEl().dom.select();
8196 onFocus : function(){
8197 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8198 // this.el.addClass(this.focusClass);
8201 this.hasFocus = true;
8202 this.startValue = this.getValue();
8203 this.fireEvent("focus", this);
8207 beforeBlur : Roo.emptyFn,
8211 onBlur : function(){
8213 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8214 //this.el.removeClass(this.focusClass);
8216 this.hasFocus = false;
8217 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8220 var v = this.getValue();
8221 if(String(v) !== String(this.startValue)){
8222 this.fireEvent('change', this, v, this.startValue);
8224 this.fireEvent("blur", this);
8228 * Resets the current field value to the originally loaded value and clears any validation messages
8231 this.setValue(this.originalValue);
8235 * Returns the name of the field
8236 * @return {Mixed} name The name field
8238 getName: function(){
8242 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8243 * @return {Mixed} value The field value
8245 getValue : function(){
8247 var v = this.inputEl().getValue();
8252 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8253 * @return {Mixed} value The field value
8255 getRawValue : function(){
8256 var v = this.inputEl().getValue();
8262 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8263 * @param {Mixed} value The value to set
8265 setRawValue : function(v){
8266 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8269 selectText : function(start, end){
8270 var v = this.getRawValue();
8272 start = start === undefined ? 0 : start;
8273 end = end === undefined ? v.length : end;
8274 var d = this.inputEl().dom;
8275 if(d.setSelectionRange){
8276 d.setSelectionRange(start, end);
8277 }else if(d.createTextRange){
8278 var range = d.createTextRange();
8279 range.moveStart("character", start);
8280 range.moveEnd("character", v.length-end);
8287 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8288 * @param {Mixed} value The value to set
8290 setValue : function(v){
8293 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8299 processValue : function(value){
8300 if(this.stripCharsRe){
8301 var newValue = value.replace(this.stripCharsRe, '');
8302 if(newValue !== value){
8303 this.setRawValue(newValue);
8310 preFocus : function(){
8312 if(this.selectOnFocus){
8313 this.inputEl().dom.select();
8316 filterKeys : function(e){
8318 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8321 var c = e.getCharCode(), cc = String.fromCharCode(c);
8322 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8325 if(!this.maskRe.test(cc)){
8330 * Clear any invalid styles/messages for this field
8332 clearInvalid : function(){
8334 if(!this.el || this.preventMark){ // not rendered
8337 this.el.removeClass(this.invalidClass);
8339 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8341 var feedback = this.el.select('.form-control-feedback', true).first();
8344 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8349 this.fireEvent('valid', this);
8353 * Mark this field as valid
8355 markValid : function()
8357 if(!this.el || this.preventMark){ // not rendered
8361 this.el.removeClass([this.invalidClass, this.validClass]);
8363 var feedback = this.el.select('.form-control-feedback', true).first();
8366 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8369 if(this.disabled || this.allowBlank){
8373 var formGroup = this.el.findParent('.form-group', false, true);
8377 var label = formGroup.select('label', true).first();
8378 var icon = formGroup.select('i.fa-star', true).first();
8385 this.el.addClass(this.validClass);
8387 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8389 var feedback = this.el.select('.form-control-feedback', true).first();
8392 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8393 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8398 this.fireEvent('valid', this);
8402 * Mark this field as invalid
8403 * @param {String} msg The validation message
8405 markInvalid : function(msg)
8407 if(!this.el || this.preventMark){ // not rendered
8411 this.el.removeClass([this.invalidClass, this.validClass]);
8413 var feedback = this.el.select('.form-control-feedback', true).first();
8416 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8419 if(this.disabled || this.allowBlank){
8423 var formGroup = this.el.findParent('.form-group', false, true);
8426 var label = formGroup.select('label', true).first();
8427 var icon = formGroup.select('i.fa-star', true).first();
8429 if(!this.getValue().length && label && !icon){
8430 this.el.findParent('.form-group', false, true).createChild({
8432 cls : 'text-danger fa fa-lg fa-star',
8433 tooltip : 'This field is required',
8434 style : 'margin-right:5px;'
8440 this.el.addClass(this.invalidClass);
8442 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8444 var feedback = this.el.select('.form-control-feedback', true).first();
8447 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8449 if(this.getValue().length || this.forceFeedback){
8450 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8457 this.fireEvent('invalid', this, msg);
8460 SafariOnKeyDown : function(event)
8462 // this is a workaround for a password hang bug on chrome/ webkit.
8464 var isSelectAll = false;
8466 if(this.inputEl().dom.selectionEnd > 0){
8467 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8469 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8470 event.preventDefault();
8475 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8477 event.preventDefault();
8478 // this is very hacky as keydown always get's upper case.
8480 var cc = String.fromCharCode(event.getCharCode());
8481 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8485 adjustWidth : function(tag, w){
8486 tag = tag.toLowerCase();
8487 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8488 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8492 if(tag == 'textarea'){
8495 }else if(Roo.isOpera){
8499 if(tag == 'textarea'){
8518 * @class Roo.bootstrap.TextArea
8519 * @extends Roo.bootstrap.Input
8520 * Bootstrap TextArea class
8521 * @cfg {Number} cols Specifies the visible width of a text area
8522 * @cfg {Number} rows Specifies the visible number of lines in a text area
8523 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8524 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8525 * @cfg {string} html text
8528 * Create a new TextArea
8529 * @param {Object} config The config object
8532 Roo.bootstrap.TextArea = function(config){
8533 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8537 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8547 getAutoCreate : function(){
8549 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8560 value : this.value || '',
8561 html: this.html || '',
8562 cls : 'form-control',
8563 placeholder : this.placeholder || ''
8567 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8568 input.maxLength = this.maxLength;
8572 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8576 input.cols = this.cols;
8579 if (this.readOnly) {
8580 input.readonly = true;
8584 input.name = this.name;
8588 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8592 ['xs','sm','md','lg'].map(function(size){
8593 if (settings[size]) {
8594 cfg.cls += ' col-' + size + '-' + settings[size];
8598 var inputblock = input;
8600 if(this.hasFeedback && !this.allowBlank){
8604 cls: 'glyphicon form-control-feedback'
8608 cls : 'has-feedback',
8617 if (this.before || this.after) {
8620 cls : 'input-group',
8624 inputblock.cn.push({
8626 cls : 'input-group-addon',
8631 inputblock.cn.push(input);
8633 if(this.hasFeedback && !this.allowBlank){
8634 inputblock.cls += ' has-feedback';
8635 inputblock.cn.push(feedback);
8639 inputblock.cn.push({
8641 cls : 'input-group-addon',
8648 if (align ==='left' && this.fieldLabel.length) {
8649 Roo.log("left and has label");
8655 cls : 'control-label col-sm-' + this.labelWidth,
8656 html : this.fieldLabel
8660 cls : "col-sm-" + (12 - this.labelWidth),
8667 } else if ( this.fieldLabel.length) {
8673 //cls : 'input-group-addon',
8674 html : this.fieldLabel
8684 Roo.log(" no label && no align");
8694 if (this.disabled) {
8695 input.disabled=true;
8702 * return the real textarea element.
8704 inputEl: function ()
8706 return this.el.select('textarea.form-control',true).first();
8710 * Clear any invalid styles/messages for this field
8712 clearInvalid : function()
8715 if(!this.el || this.preventMark){ // not rendered
8719 var label = this.el.select('label', true).first();
8720 var icon = this.el.select('i.fa-star', true).first();
8726 this.el.removeClass(this.invalidClass);
8728 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8730 var feedback = this.el.select('.form-control-feedback', true).first();
8733 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8738 this.fireEvent('valid', this);
8742 * Mark this field as valid
8744 markValid : function()
8746 if(!this.el || this.preventMark){ // not rendered
8750 this.el.removeClass([this.invalidClass, this.validClass]);
8752 var feedback = this.el.select('.form-control-feedback', true).first();
8755 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8758 if(this.disabled || this.allowBlank){
8762 var label = this.el.select('label', true).first();
8763 var icon = this.el.select('i.fa-star', true).first();
8769 this.el.addClass(this.validClass);
8771 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8773 var feedback = this.el.select('.form-control-feedback', true).first();
8776 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8777 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8782 this.fireEvent('valid', this);
8786 * Mark this field as invalid
8787 * @param {String} msg The validation message
8789 markInvalid : function(msg)
8791 if(!this.el || this.preventMark){ // not rendered
8795 this.el.removeClass([this.invalidClass, this.validClass]);
8797 var feedback = this.el.select('.form-control-feedback', true).first();
8800 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8803 if(this.disabled || this.allowBlank){
8807 var label = this.el.select('label', true).first();
8808 var icon = this.el.select('i.fa-star', true).first();
8810 if(!this.getValue().length && label && !icon){
8811 this.el.createChild({
8813 cls : 'text-danger fa fa-lg fa-star',
8814 tooltip : 'This field is required',
8815 style : 'margin-right:5px;'
8819 this.el.addClass(this.invalidClass);
8821 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8823 var feedback = this.el.select('.form-control-feedback', true).first();
8826 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8828 if(this.getValue().length || this.forceFeedback){
8829 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8836 this.fireEvent('invalid', this, msg);
8844 * trigger field - base class for combo..
8849 * @class Roo.bootstrap.TriggerField
8850 * @extends Roo.bootstrap.Input
8851 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8852 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8853 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8854 * for which you can provide a custom implementation. For example:
8856 var trigger = new Roo.bootstrap.TriggerField();
8857 trigger.onTriggerClick = myTriggerFn;
8858 trigger.applyTo('my-field');
8861 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8862 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8863 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8864 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8865 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8868 * Create a new TriggerField.
8869 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8870 * to the base TextField)
8872 Roo.bootstrap.TriggerField = function(config){
8873 this.mimicing = false;
8874 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8877 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8879 * @cfg {String} triggerClass A CSS class to apply to the trigger
8882 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8887 * @cfg {Boolean} removable (true|false) special filter default false
8891 /** @cfg {Boolean} grow @hide */
8892 /** @cfg {Number} growMin @hide */
8893 /** @cfg {Number} growMax @hide */
8899 autoSize: Roo.emptyFn,
8906 actionMode : 'wrap',
8911 getAutoCreate : function(){
8913 var align = this.labelAlign || this.parentLabelAlign();
8918 cls: 'form-group' //input-group
8925 type : this.inputType,
8926 cls : 'form-control',
8927 autocomplete: 'new-password',
8928 placeholder : this.placeholder || ''
8932 input.name = this.name;
8935 input.cls += ' input-' + this.size;
8938 if (this.disabled) {
8939 input.disabled=true;
8942 var inputblock = input;
8944 if(this.hasFeedback && !this.allowBlank){
8948 cls: 'glyphicon form-control-feedback'
8951 if(this.removable && !this.editable && !this.tickable){
8953 cls : 'has-feedback',
8959 cls : 'roo-combo-removable-btn close'
8966 cls : 'has-feedback',
8975 if(this.removable && !this.editable && !this.tickable){
8977 cls : 'roo-removable',
8983 cls : 'roo-combo-removable-btn close'
8990 if (this.before || this.after) {
8993 cls : 'input-group',
8997 inputblock.cn.push({
8999 cls : 'input-group-addon',
9004 inputblock.cn.push(input);
9006 if(this.hasFeedback && !this.allowBlank){
9007 inputblock.cls += ' has-feedback';
9008 inputblock.cn.push(feedback);
9012 inputblock.cn.push({
9014 cls : 'input-group-addon',
9027 cls: 'form-hidden-field'
9035 Roo.log('multiple');
9043 cls: 'form-hidden-field'
9047 cls: 'select2-choices',
9051 cls: 'select2-search-field',
9064 cls: 'select2-container input-group',
9069 // cls: 'typeahead typeahead-long dropdown-menu',
9070 // style: 'display:none'
9075 if(!this.multiple && this.showToggleBtn){
9081 if (this.caret != false) {
9084 cls: 'fa fa-' + this.caret
9091 cls : 'input-group-addon btn dropdown-toggle',
9096 cls: 'combobox-clear',
9110 combobox.cls += ' select2-container-multi';
9113 if (align ==='left' && this.fieldLabel.length) {
9115 Roo.log("left and has label");
9121 cls : 'control-label col-sm-' + this.labelWidth,
9122 html : this.fieldLabel
9126 cls : "col-sm-" + (12 - this.labelWidth),
9133 } else if ( this.fieldLabel.length) {
9139 //cls : 'input-group-addon',
9140 html : this.fieldLabel
9150 Roo.log(" no label && no align");
9157 ['xs','sm','md','lg'].map(function(size){
9158 if (settings[size]) {
9159 cfg.cls += ' col-' + size + '-' + settings[size];
9170 onResize : function(w, h){
9171 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9172 // if(typeof w == 'number'){
9173 // var x = w - this.trigger.getWidth();
9174 // this.inputEl().setWidth(this.adjustWidth('input', x));
9175 // this.trigger.setStyle('left', x+'px');
9180 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9183 getResizeEl : function(){
9184 return this.inputEl();
9188 getPositionEl : function(){
9189 return this.inputEl();
9193 alignErrorIcon : function(){
9194 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9198 initEvents : function(){
9202 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9203 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9204 if(!this.multiple && this.showToggleBtn){
9205 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9206 if(this.hideTrigger){
9207 this.trigger.setDisplayed(false);
9209 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9213 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9216 if(this.removable && !this.editable && !this.tickable){
9217 var close = this.closeTriggerEl();
9220 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9221 close.on('click', this.removeBtnClick, this, close);
9225 //this.trigger.addClassOnOver('x-form-trigger-over');
9226 //this.trigger.addClassOnClick('x-form-trigger-click');
9229 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9233 closeTriggerEl : function()
9235 var close = this.el.select('.roo-combo-removable-btn', true).first();
9236 return close ? close : false;
9239 removeBtnClick : function(e, h, el)
9243 if(this.fireEvent("remove", this) !== false){
9248 createList : function()
9250 this.list = Roo.get(document.body).createChild({
9252 cls: 'typeahead typeahead-long dropdown-menu',
9253 style: 'display:none'
9256 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9261 initTrigger : function(){
9266 onDestroy : function(){
9268 this.trigger.removeAllListeners();
9269 // this.trigger.remove();
9272 // this.wrap.remove();
9274 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9278 onFocus : function(){
9279 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9282 this.wrap.addClass('x-trigger-wrap-focus');
9283 this.mimicing = true;
9284 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9285 if(this.monitorTab){
9286 this.el.on("keydown", this.checkTab, this);
9293 checkTab : function(e){
9294 if(e.getKey() == e.TAB){
9300 onBlur : function(){
9305 mimicBlur : function(e, t){
9307 if(!this.wrap.contains(t) && this.validateBlur()){
9314 triggerBlur : function(){
9315 this.mimicing = false;
9316 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9317 if(this.monitorTab){
9318 this.el.un("keydown", this.checkTab, this);
9320 //this.wrap.removeClass('x-trigger-wrap-focus');
9321 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9325 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9326 validateBlur : function(e, t){
9331 onDisable : function(){
9332 this.inputEl().dom.disabled = true;
9333 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9335 // this.wrap.addClass('x-item-disabled');
9340 onEnable : function(){
9341 this.inputEl().dom.disabled = false;
9342 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9344 // this.el.removeClass('x-item-disabled');
9349 onShow : function(){
9350 var ae = this.getActionEl();
9353 ae.dom.style.display = '';
9354 ae.dom.style.visibility = 'visible';
9360 onHide : function(){
9361 var ae = this.getActionEl();
9362 ae.dom.style.display = 'none';
9366 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9367 * by an implementing function.
9369 * @param {EventObject} e
9371 onTriggerClick : Roo.emptyFn
9375 * Ext JS Library 1.1.1
9376 * Copyright(c) 2006-2007, Ext JS, LLC.
9378 * Originally Released Under LGPL - original licence link has changed is not relivant.
9381 * <script type="text/javascript">
9386 * @class Roo.data.SortTypes
9388 * Defines the default sorting (casting?) comparison functions used when sorting data.
9390 Roo.data.SortTypes = {
9392 * Default sort that does nothing
9393 * @param {Mixed} s The value being converted
9394 * @return {Mixed} The comparison value
9401 * The regular expression used to strip tags
9405 stripTagsRE : /<\/?[^>]+>/gi,
9408 * Strips all HTML tags to sort on text only
9409 * @param {Mixed} s The value being converted
9410 * @return {String} The comparison value
9412 asText : function(s){
9413 return String(s).replace(this.stripTagsRE, "");
9417 * Strips all HTML tags to sort on text only - Case insensitive
9418 * @param {Mixed} s The value being converted
9419 * @return {String} The comparison value
9421 asUCText : function(s){
9422 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9426 * Case insensitive string
9427 * @param {Mixed} s The value being converted
9428 * @return {String} The comparison value
9430 asUCString : function(s) {
9431 return String(s).toUpperCase();
9436 * @param {Mixed} s The value being converted
9437 * @return {Number} The comparison value
9439 asDate : function(s) {
9443 if(s instanceof Date){
9446 return Date.parse(String(s));
9451 * @param {Mixed} s The value being converted
9452 * @return {Float} The comparison value
9454 asFloat : function(s) {
9455 var val = parseFloat(String(s).replace(/,/g, ""));
9464 * @param {Mixed} s The value being converted
9465 * @return {Number} The comparison value
9467 asInt : function(s) {
9468 var val = parseInt(String(s).replace(/,/g, ""));
9476 * Ext JS Library 1.1.1
9477 * Copyright(c) 2006-2007, Ext JS, LLC.
9479 * Originally Released Under LGPL - original licence link has changed is not relivant.
9482 * <script type="text/javascript">
9486 * @class Roo.data.Record
9487 * Instances of this class encapsulate both record <em>definition</em> information, and record
9488 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9489 * to access Records cached in an {@link Roo.data.Store} object.<br>
9491 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9492 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9495 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9497 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9498 * {@link #create}. The parameters are the same.
9499 * @param {Array} data An associative Array of data values keyed by the field name.
9500 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9501 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9502 * not specified an integer id is generated.
9504 Roo.data.Record = function(data, id){
9505 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9510 * Generate a constructor for a specific record layout.
9511 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9512 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9513 * Each field definition object may contain the following properties: <ul>
9514 * <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,
9515 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9516 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9517 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9518 * is being used, then this is a string containing the javascript expression to reference the data relative to
9519 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9520 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9521 * this may be omitted.</p></li>
9522 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9523 * <ul><li>auto (Default, implies no conversion)</li>
9528 * <li>date</li></ul></p></li>
9529 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9530 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9531 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9532 * by the Reader into an object that will be stored in the Record. It is passed the
9533 * following parameters:<ul>
9534 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9536 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9538 * <br>usage:<br><pre><code>
9539 var TopicRecord = Roo.data.Record.create(
9540 {name: 'title', mapping: 'topic_title'},
9541 {name: 'author', mapping: 'username'},
9542 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9543 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9544 {name: 'lastPoster', mapping: 'user2'},
9545 {name: 'excerpt', mapping: 'post_text'}
9548 var myNewRecord = new TopicRecord({
9549 title: 'Do my job please',
9552 lastPost: new Date(),
9553 lastPoster: 'Animal',
9554 excerpt: 'No way dude!'
9556 myStore.add(myNewRecord);
9561 Roo.data.Record.create = function(o){
9563 f.superclass.constructor.apply(this, arguments);
9565 Roo.extend(f, Roo.data.Record);
9566 var p = f.prototype;
9567 p.fields = new Roo.util.MixedCollection(false, function(field){
9570 for(var i = 0, len = o.length; i < len; i++){
9571 p.fields.add(new Roo.data.Field(o[i]));
9573 f.getField = function(name){
9574 return p.fields.get(name);
9579 Roo.data.Record.AUTO_ID = 1000;
9580 Roo.data.Record.EDIT = 'edit';
9581 Roo.data.Record.REJECT = 'reject';
9582 Roo.data.Record.COMMIT = 'commit';
9584 Roo.data.Record.prototype = {
9586 * Readonly flag - true if this record has been modified.
9595 join : function(store){
9600 * Set the named field to the specified value.
9601 * @param {String} name The name of the field to set.
9602 * @param {Object} value The value to set the field to.
9604 set : function(name, value){
9605 if(this.data[name] == value){
9612 if(typeof this.modified[name] == 'undefined'){
9613 this.modified[name] = this.data[name];
9615 this.data[name] = value;
9616 if(!this.editing && this.store){
9617 this.store.afterEdit(this);
9622 * Get the value of the named field.
9623 * @param {String} name The name of the field to get the value of.
9624 * @return {Object} The value of the field.
9626 get : function(name){
9627 return this.data[name];
9631 beginEdit : function(){
9632 this.editing = true;
9637 cancelEdit : function(){
9638 this.editing = false;
9639 delete this.modified;
9643 endEdit : function(){
9644 this.editing = false;
9645 if(this.dirty && this.store){
9646 this.store.afterEdit(this);
9651 * Usually called by the {@link Roo.data.Store} which owns the Record.
9652 * Rejects all changes made to the Record since either creation, or the last commit operation.
9653 * Modified fields are reverted to their original values.
9655 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9656 * of reject operations.
9658 reject : function(){
9659 var m = this.modified;
9661 if(typeof m[n] != "function"){
9662 this.data[n] = m[n];
9666 delete this.modified;
9667 this.editing = false;
9669 this.store.afterReject(this);
9674 * Usually called by the {@link Roo.data.Store} which owns the Record.
9675 * Commits all changes made to the Record since either creation, or the last commit operation.
9677 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9678 * of commit operations.
9680 commit : function(){
9682 delete this.modified;
9683 this.editing = false;
9685 this.store.afterCommit(this);
9690 hasError : function(){
9691 return this.error != null;
9695 clearError : function(){
9700 * Creates a copy of this record.
9701 * @param {String} id (optional) A new record id if you don't want to use this record's id
9704 copy : function(newId) {
9705 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9709 * Ext JS Library 1.1.1
9710 * Copyright(c) 2006-2007, Ext JS, LLC.
9712 * Originally Released Under LGPL - original licence link has changed is not relivant.
9715 * <script type="text/javascript">
9721 * @class Roo.data.Store
9722 * @extends Roo.util.Observable
9723 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9724 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9726 * 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
9727 * has no knowledge of the format of the data returned by the Proxy.<br>
9729 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9730 * instances from the data object. These records are cached and made available through accessor functions.
9732 * Creates a new Store.
9733 * @param {Object} config A config object containing the objects needed for the Store to access data,
9734 * and read the data into Records.
9736 Roo.data.Store = function(config){
9737 this.data = new Roo.util.MixedCollection(false);
9738 this.data.getKey = function(o){
9741 this.baseParams = {};
9748 "multisort" : "_multisort"
9751 if(config && config.data){
9752 this.inlineData = config.data;
9756 Roo.apply(this, config);
9758 if(this.reader){ // reader passed
9759 this.reader = Roo.factory(this.reader, Roo.data);
9760 this.reader.xmodule = this.xmodule || false;
9761 if(!this.recordType){
9762 this.recordType = this.reader.recordType;
9764 if(this.reader.onMetaChange){
9765 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9769 if(this.recordType){
9770 this.fields = this.recordType.prototype.fields;
9776 * @event datachanged
9777 * Fires when the data cache has changed, and a widget which is using this Store
9778 * as a Record cache should refresh its view.
9779 * @param {Store} this
9784 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9785 * @param {Store} this
9786 * @param {Object} meta The JSON metadata
9791 * Fires when Records have been added to the Store
9792 * @param {Store} this
9793 * @param {Roo.data.Record[]} records The array of Records added
9794 * @param {Number} index The index at which the record(s) were added
9799 * Fires when a Record has been removed from the Store
9800 * @param {Store} this
9801 * @param {Roo.data.Record} record The Record that was removed
9802 * @param {Number} index The index at which the record was removed
9807 * Fires when a Record has been updated
9808 * @param {Store} this
9809 * @param {Roo.data.Record} record The Record that was updated
9810 * @param {String} operation The update operation being performed. Value may be one of:
9812 Roo.data.Record.EDIT
9813 Roo.data.Record.REJECT
9814 Roo.data.Record.COMMIT
9820 * Fires when the data cache has been cleared.
9821 * @param {Store} this
9826 * Fires before a request is made for a new data object. If the beforeload handler returns false
9827 * the load action will be canceled.
9828 * @param {Store} this
9829 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9833 * @event beforeloadadd
9834 * Fires after a new set of Records has been loaded.
9835 * @param {Store} this
9836 * @param {Roo.data.Record[]} records The Records that were loaded
9837 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9839 beforeloadadd : true,
9842 * Fires after a new set of Records has been loaded, before they are added to the store.
9843 * @param {Store} this
9844 * @param {Roo.data.Record[]} records The Records that were loaded
9845 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9846 * @params {Object} return from reader
9850 * @event loadexception
9851 * Fires if an exception occurs in the Proxy during loading.
9852 * Called with the signature of the Proxy's "loadexception" event.
9853 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9856 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9857 * @param {Object} load options
9858 * @param {Object} jsonData from your request (normally this contains the Exception)
9860 loadexception : true
9864 this.proxy = Roo.factory(this.proxy, Roo.data);
9865 this.proxy.xmodule = this.xmodule || false;
9866 this.relayEvents(this.proxy, ["loadexception"]);
9868 this.sortToggle = {};
9869 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9871 Roo.data.Store.superclass.constructor.call(this);
9873 if(this.inlineData){
9874 this.loadData(this.inlineData);
9875 delete this.inlineData;
9879 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9881 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9882 * without a remote query - used by combo/forms at present.
9886 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9889 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9892 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9893 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9896 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9897 * on any HTTP request
9900 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9903 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9907 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9908 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9913 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9914 * loaded or when a record is removed. (defaults to false).
9916 pruneModifiedRecords : false,
9922 * Add Records to the Store and fires the add event.
9923 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9925 add : function(records){
9926 records = [].concat(records);
9927 for(var i = 0, len = records.length; i < len; i++){
9928 records[i].join(this);
9930 var index = this.data.length;
9931 this.data.addAll(records);
9932 this.fireEvent("add", this, records, index);
9936 * Remove a Record from the Store and fires the remove event.
9937 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9939 remove : function(record){
9940 var index = this.data.indexOf(record);
9941 this.data.removeAt(index);
9942 if(this.pruneModifiedRecords){
9943 this.modified.remove(record);
9945 this.fireEvent("remove", this, record, index);
9949 * Remove all Records from the Store and fires the clear event.
9951 removeAll : function(){
9953 if(this.pruneModifiedRecords){
9956 this.fireEvent("clear", this);
9960 * Inserts Records to the Store at the given index and fires the add event.
9961 * @param {Number} index The start index at which to insert the passed Records.
9962 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9964 insert : function(index, records){
9965 records = [].concat(records);
9966 for(var i = 0, len = records.length; i < len; i++){
9967 this.data.insert(index, records[i]);
9968 records[i].join(this);
9970 this.fireEvent("add", this, records, index);
9974 * Get the index within the cache of the passed Record.
9975 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9976 * @return {Number} The index of the passed Record. Returns -1 if not found.
9978 indexOf : function(record){
9979 return this.data.indexOf(record);
9983 * Get the index within the cache of the Record with the passed id.
9984 * @param {String} id The id of the Record to find.
9985 * @return {Number} The index of the Record. Returns -1 if not found.
9987 indexOfId : function(id){
9988 return this.data.indexOfKey(id);
9992 * Get the Record with the specified id.
9993 * @param {String} id The id of the Record to find.
9994 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9996 getById : function(id){
9997 return this.data.key(id);
10001 * Get the Record at the specified index.
10002 * @param {Number} index The index of the Record to find.
10003 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10005 getAt : function(index){
10006 return this.data.itemAt(index);
10010 * Returns a range of Records between specified indices.
10011 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10012 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10013 * @return {Roo.data.Record[]} An array of Records
10015 getRange : function(start, end){
10016 return this.data.getRange(start, end);
10020 storeOptions : function(o){
10021 o = Roo.apply({}, o);
10024 this.lastOptions = o;
10028 * Loads the Record cache from the configured Proxy using the configured Reader.
10030 * If using remote paging, then the first load call must specify the <em>start</em>
10031 * and <em>limit</em> properties in the options.params property to establish the initial
10032 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10034 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10035 * and this call will return before the new data has been loaded. Perform any post-processing
10036 * in a callback function, or in a "load" event handler.</strong>
10038 * @param {Object} options An object containing properties which control loading options:<ul>
10039 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10040 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10041 * passed the following arguments:<ul>
10042 * <li>r : Roo.data.Record[]</li>
10043 * <li>options: Options object from the load call</li>
10044 * <li>success: Boolean success indicator</li></ul></li>
10045 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10046 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10049 load : function(options){
10050 options = options || {};
10051 if(this.fireEvent("beforeload", this, options) !== false){
10052 this.storeOptions(options);
10053 var p = Roo.apply(options.params || {}, this.baseParams);
10054 // if meta was not loaded from remote source.. try requesting it.
10055 if (!this.reader.metaFromRemote) {
10056 p._requestMeta = 1;
10058 if(this.sortInfo && this.remoteSort){
10059 var pn = this.paramNames;
10060 p[pn["sort"]] = this.sortInfo.field;
10061 p[pn["dir"]] = this.sortInfo.direction;
10063 if (this.multiSort) {
10064 var pn = this.paramNames;
10065 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10068 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10073 * Reloads the Record cache from the configured Proxy using the configured Reader and
10074 * the options from the last load operation performed.
10075 * @param {Object} options (optional) An object containing properties which may override the options
10076 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10077 * the most recently used options are reused).
10079 reload : function(options){
10080 this.load(Roo.applyIf(options||{}, this.lastOptions));
10084 // Called as a callback by the Reader during a load operation.
10085 loadRecords : function(o, options, success){
10086 if(!o || success === false){
10087 if(success !== false){
10088 this.fireEvent("load", this, [], options, o);
10090 if(options.callback){
10091 options.callback.call(options.scope || this, [], options, false);
10095 // if data returned failure - throw an exception.
10096 if (o.success === false) {
10097 // show a message if no listener is registered.
10098 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10099 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10101 // loadmask wil be hooked into this..
10102 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10105 var r = o.records, t = o.totalRecords || r.length;
10107 this.fireEvent("beforeloadadd", this, r, options, o);
10109 if(!options || options.add !== true){
10110 if(this.pruneModifiedRecords){
10111 this.modified = [];
10113 for(var i = 0, len = r.length; i < len; i++){
10117 this.data = this.snapshot;
10118 delete this.snapshot;
10121 this.data.addAll(r);
10122 this.totalLength = t;
10124 this.fireEvent("datachanged", this);
10126 this.totalLength = Math.max(t, this.data.length+r.length);
10129 this.fireEvent("load", this, r, options, o);
10130 if(options.callback){
10131 options.callback.call(options.scope || this, r, options, true);
10137 * Loads data from a passed data block. A Reader which understands the format of the data
10138 * must have been configured in the constructor.
10139 * @param {Object} data The data block from which to read the Records. The format of the data expected
10140 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10141 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10143 loadData : function(o, append){
10144 var r = this.reader.readRecords(o);
10145 this.loadRecords(r, {add: append}, true);
10149 * Gets the number of cached records.
10151 * <em>If using paging, this may not be the total size of the dataset. If the data object
10152 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10153 * the data set size</em>
10155 getCount : function(){
10156 return this.data.length || 0;
10160 * Gets the total number of records in the dataset as returned by the server.
10162 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10163 * the dataset size</em>
10165 getTotalCount : function(){
10166 return this.totalLength || 0;
10170 * Returns the sort state of the Store as an object with two properties:
10172 field {String} The name of the field by which the Records are sorted
10173 direction {String} The sort order, "ASC" or "DESC"
10176 getSortState : function(){
10177 return this.sortInfo;
10181 applySort : function(){
10182 if(this.sortInfo && !this.remoteSort){
10183 var s = this.sortInfo, f = s.field;
10184 var st = this.fields.get(f).sortType;
10185 var fn = function(r1, r2){
10186 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10187 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10189 this.data.sort(s.direction, fn);
10190 if(this.snapshot && this.snapshot != this.data){
10191 this.snapshot.sort(s.direction, fn);
10197 * Sets the default sort column and order to be used by the next load operation.
10198 * @param {String} fieldName The name of the field to sort by.
10199 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10201 setDefaultSort : function(field, dir){
10202 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10206 * Sort the Records.
10207 * If remote sorting is used, the sort is performed on the server, and the cache is
10208 * reloaded. If local sorting is used, the cache is sorted internally.
10209 * @param {String} fieldName The name of the field to sort by.
10210 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10212 sort : function(fieldName, dir){
10213 var f = this.fields.get(fieldName);
10215 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10217 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10218 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10223 this.sortToggle[f.name] = dir;
10224 this.sortInfo = {field: f.name, direction: dir};
10225 if(!this.remoteSort){
10227 this.fireEvent("datachanged", this);
10229 this.load(this.lastOptions);
10234 * Calls the specified function for each of the Records in the cache.
10235 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10236 * Returning <em>false</em> aborts and exits the iteration.
10237 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10239 each : function(fn, scope){
10240 this.data.each(fn, scope);
10244 * Gets all records modified since the last commit. Modified records are persisted across load operations
10245 * (e.g., during paging).
10246 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10248 getModifiedRecords : function(){
10249 return this.modified;
10253 createFilterFn : function(property, value, anyMatch){
10254 if(!value.exec){ // not a regex
10255 value = String(value);
10256 if(value.length == 0){
10259 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10261 return function(r){
10262 return value.test(r.data[property]);
10267 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10268 * @param {String} property A field on your records
10269 * @param {Number} start The record index to start at (defaults to 0)
10270 * @param {Number} end The last record index to include (defaults to length - 1)
10271 * @return {Number} The sum
10273 sum : function(property, start, end){
10274 var rs = this.data.items, v = 0;
10275 start = start || 0;
10276 end = (end || end === 0) ? end : rs.length-1;
10278 for(var i = start; i <= end; i++){
10279 v += (rs[i].data[property] || 0);
10285 * Filter the records by a specified property.
10286 * @param {String} field A field on your records
10287 * @param {String/RegExp} value Either a string that the field
10288 * should start with or a RegExp to test against the field
10289 * @param {Boolean} anyMatch True to match any part not just the beginning
10291 filter : function(property, value, anyMatch){
10292 var fn = this.createFilterFn(property, value, anyMatch);
10293 return fn ? this.filterBy(fn) : this.clearFilter();
10297 * Filter by a function. The specified function will be called with each
10298 * record in this data source. If the function returns true the record is included,
10299 * otherwise it is filtered.
10300 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10301 * @param {Object} scope (optional) The scope of the function (defaults to this)
10303 filterBy : function(fn, scope){
10304 this.snapshot = this.snapshot || this.data;
10305 this.data = this.queryBy(fn, scope||this);
10306 this.fireEvent("datachanged", this);
10310 * Query the records by a specified property.
10311 * @param {String} field A field on your records
10312 * @param {String/RegExp} value Either a string that the field
10313 * should start with or a RegExp to test against the field
10314 * @param {Boolean} anyMatch True to match any part not just the beginning
10315 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10317 query : function(property, value, anyMatch){
10318 var fn = this.createFilterFn(property, value, anyMatch);
10319 return fn ? this.queryBy(fn) : this.data.clone();
10323 * Query by a function. The specified function will be called with each
10324 * record in this data source. If the function returns true the record is included
10326 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10327 * @param {Object} scope (optional) The scope of the function (defaults to this)
10328 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10330 queryBy : function(fn, scope){
10331 var data = this.snapshot || this.data;
10332 return data.filterBy(fn, scope||this);
10336 * Collects unique values for a particular dataIndex from this store.
10337 * @param {String} dataIndex The property to collect
10338 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10339 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10340 * @return {Array} An array of the unique values
10342 collect : function(dataIndex, allowNull, bypassFilter){
10343 var d = (bypassFilter === true && this.snapshot) ?
10344 this.snapshot.items : this.data.items;
10345 var v, sv, r = [], l = {};
10346 for(var i = 0, len = d.length; i < len; i++){
10347 v = d[i].data[dataIndex];
10349 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10358 * Revert to a view of the Record cache with no filtering applied.
10359 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10361 clearFilter : function(suppressEvent){
10362 if(this.snapshot && this.snapshot != this.data){
10363 this.data = this.snapshot;
10364 delete this.snapshot;
10365 if(suppressEvent !== true){
10366 this.fireEvent("datachanged", this);
10372 afterEdit : function(record){
10373 if(this.modified.indexOf(record) == -1){
10374 this.modified.push(record);
10376 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10380 afterReject : function(record){
10381 this.modified.remove(record);
10382 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10386 afterCommit : function(record){
10387 this.modified.remove(record);
10388 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10392 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10393 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10395 commitChanges : function(){
10396 var m = this.modified.slice(0);
10397 this.modified = [];
10398 for(var i = 0, len = m.length; i < len; i++){
10404 * Cancel outstanding changes on all changed records.
10406 rejectChanges : function(){
10407 var m = this.modified.slice(0);
10408 this.modified = [];
10409 for(var i = 0, len = m.length; i < len; i++){
10414 onMetaChange : function(meta, rtype, o){
10415 this.recordType = rtype;
10416 this.fields = rtype.prototype.fields;
10417 delete this.snapshot;
10418 this.sortInfo = meta.sortInfo || this.sortInfo;
10419 this.modified = [];
10420 this.fireEvent('metachange', this, this.reader.meta);
10423 moveIndex : function(data, type)
10425 var index = this.indexOf(data);
10427 var newIndex = index + type;
10431 this.insert(newIndex, data);
10436 * Ext JS Library 1.1.1
10437 * Copyright(c) 2006-2007, Ext JS, LLC.
10439 * Originally Released Under LGPL - original licence link has changed is not relivant.
10442 * <script type="text/javascript">
10446 * @class Roo.data.SimpleStore
10447 * @extends Roo.data.Store
10448 * Small helper class to make creating Stores from Array data easier.
10449 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10450 * @cfg {Array} fields An array of field definition objects, or field name strings.
10451 * @cfg {Array} data The multi-dimensional array of data
10453 * @param {Object} config
10455 Roo.data.SimpleStore = function(config){
10456 Roo.data.SimpleStore.superclass.constructor.call(this, {
10458 reader: new Roo.data.ArrayReader({
10461 Roo.data.Record.create(config.fields)
10463 proxy : new Roo.data.MemoryProxy(config.data)
10467 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10469 * Ext JS Library 1.1.1
10470 * Copyright(c) 2006-2007, Ext JS, LLC.
10472 * Originally Released Under LGPL - original licence link has changed is not relivant.
10475 * <script type="text/javascript">
10480 * @extends Roo.data.Store
10481 * @class Roo.data.JsonStore
10482 * Small helper class to make creating Stores for JSON data easier. <br/>
10484 var store = new Roo.data.JsonStore({
10485 url: 'get-images.php',
10487 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10490 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10491 * JsonReader and HttpProxy (unless inline data is provided).</b>
10492 * @cfg {Array} fields An array of field definition objects, or field name strings.
10494 * @param {Object} config
10496 Roo.data.JsonStore = function(c){
10497 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10498 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10499 reader: new Roo.data.JsonReader(c, c.fields)
10502 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10504 * Ext JS Library 1.1.1
10505 * Copyright(c) 2006-2007, Ext JS, LLC.
10507 * Originally Released Under LGPL - original licence link has changed is not relivant.
10510 * <script type="text/javascript">
10514 Roo.data.Field = function(config){
10515 if(typeof config == "string"){
10516 config = {name: config};
10518 Roo.apply(this, config);
10521 this.type = "auto";
10524 var st = Roo.data.SortTypes;
10525 // named sortTypes are supported, here we look them up
10526 if(typeof this.sortType == "string"){
10527 this.sortType = st[this.sortType];
10530 // set default sortType for strings and dates
10531 if(!this.sortType){
10534 this.sortType = st.asUCString;
10537 this.sortType = st.asDate;
10540 this.sortType = st.none;
10545 var stripRe = /[\$,%]/g;
10547 // prebuilt conversion function for this field, instead of
10548 // switching every time we're reading a value
10550 var cv, dateFormat = this.dateFormat;
10555 cv = function(v){ return v; };
10558 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10562 return v !== undefined && v !== null && v !== '' ?
10563 parseInt(String(v).replace(stripRe, ""), 10) : '';
10568 return v !== undefined && v !== null && v !== '' ?
10569 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10574 cv = function(v){ return v === true || v === "true" || v == 1; };
10581 if(v instanceof Date){
10585 if(dateFormat == "timestamp"){
10586 return new Date(v*1000);
10588 return Date.parseDate(v, dateFormat);
10590 var parsed = Date.parse(v);
10591 return parsed ? new Date(parsed) : null;
10600 Roo.data.Field.prototype = {
10608 * Ext JS Library 1.1.1
10609 * Copyright(c) 2006-2007, Ext JS, LLC.
10611 * Originally Released Under LGPL - original licence link has changed is not relivant.
10614 * <script type="text/javascript">
10617 // Base class for reading structured data from a data source. This class is intended to be
10618 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10621 * @class Roo.data.DataReader
10622 * Base class for reading structured data from a data source. This class is intended to be
10623 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10626 Roo.data.DataReader = function(meta, recordType){
10630 this.recordType = recordType instanceof Array ?
10631 Roo.data.Record.create(recordType) : recordType;
10634 Roo.data.DataReader.prototype = {
10636 * Create an empty record
10637 * @param {Object} data (optional) - overlay some values
10638 * @return {Roo.data.Record} record created.
10640 newRow : function(d) {
10642 this.recordType.prototype.fields.each(function(c) {
10644 case 'int' : da[c.name] = 0; break;
10645 case 'date' : da[c.name] = new Date(); break;
10646 case 'float' : da[c.name] = 0.0; break;
10647 case 'boolean' : da[c.name] = false; break;
10648 default : da[c.name] = ""; break;
10652 return new this.recordType(Roo.apply(da, d));
10657 * Ext JS Library 1.1.1
10658 * Copyright(c) 2006-2007, Ext JS, LLC.
10660 * Originally Released Under LGPL - original licence link has changed is not relivant.
10663 * <script type="text/javascript">
10667 * @class Roo.data.DataProxy
10668 * @extends Roo.data.Observable
10669 * This class is an abstract base class for implementations which provide retrieval of
10670 * unformatted data objects.<br>
10672 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10673 * (of the appropriate type which knows how to parse the data object) to provide a block of
10674 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10676 * Custom implementations must implement the load method as described in
10677 * {@link Roo.data.HttpProxy#load}.
10679 Roo.data.DataProxy = function(){
10682 * @event beforeload
10683 * Fires before a network request is made to retrieve a data object.
10684 * @param {Object} This DataProxy object.
10685 * @param {Object} params The params parameter to the load function.
10690 * Fires before the load method's callback is called.
10691 * @param {Object} This DataProxy object.
10692 * @param {Object} o The data object.
10693 * @param {Object} arg The callback argument object passed to the load function.
10697 * @event loadexception
10698 * Fires if an Exception occurs during data retrieval.
10699 * @param {Object} This DataProxy object.
10700 * @param {Object} o The data object.
10701 * @param {Object} arg The callback argument object passed to the load function.
10702 * @param {Object} e The Exception.
10704 loadexception : true
10706 Roo.data.DataProxy.superclass.constructor.call(this);
10709 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10712 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10716 * Ext JS Library 1.1.1
10717 * Copyright(c) 2006-2007, Ext JS, LLC.
10719 * Originally Released Under LGPL - original licence link has changed is not relivant.
10722 * <script type="text/javascript">
10725 * @class Roo.data.MemoryProxy
10726 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10727 * to the Reader when its load method is called.
10729 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10731 Roo.data.MemoryProxy = function(data){
10735 Roo.data.MemoryProxy.superclass.constructor.call(this);
10739 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10741 * Load data from the requested source (in this case an in-memory
10742 * data object passed to the constructor), read the data object into
10743 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10744 * process that block using the passed callback.
10745 * @param {Object} params This parameter is not used by the MemoryProxy class.
10746 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10747 * object into a block of Roo.data.Records.
10748 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10749 * The function must be passed <ul>
10750 * <li>The Record block object</li>
10751 * <li>The "arg" argument from the load function</li>
10752 * <li>A boolean success indicator</li>
10754 * @param {Object} scope The scope in which to call the callback
10755 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10757 load : function(params, reader, callback, scope, arg){
10758 params = params || {};
10761 result = reader.readRecords(this.data);
10763 this.fireEvent("loadexception", this, arg, null, e);
10764 callback.call(scope, null, arg, false);
10767 callback.call(scope, result, arg, true);
10771 update : function(params, records){
10776 * Ext JS Library 1.1.1
10777 * Copyright(c) 2006-2007, Ext JS, LLC.
10779 * Originally Released Under LGPL - original licence link has changed is not relivant.
10782 * <script type="text/javascript">
10785 * @class Roo.data.HttpProxy
10786 * @extends Roo.data.DataProxy
10787 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10788 * configured to reference a certain URL.<br><br>
10790 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10791 * from which the running page was served.<br><br>
10793 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10795 * Be aware that to enable the browser to parse an XML document, the server must set
10796 * the Content-Type header in the HTTP response to "text/xml".
10798 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10799 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10800 * will be used to make the request.
10802 Roo.data.HttpProxy = function(conn){
10803 Roo.data.HttpProxy.superclass.constructor.call(this);
10804 // is conn a conn config or a real conn?
10806 this.useAjax = !conn || !conn.events;
10810 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10811 // thse are take from connection...
10814 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10817 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10818 * extra parameters to each request made by this object. (defaults to undefined)
10821 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10822 * to each request made by this object. (defaults to undefined)
10825 * @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)
10828 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10831 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10837 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10841 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10842 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10843 * a finer-grained basis than the DataProxy events.
10845 getConnection : function(){
10846 return this.useAjax ? Roo.Ajax : this.conn;
10850 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10851 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10852 * process that block using the passed callback.
10853 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10854 * for the request to the remote server.
10855 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10856 * object into a block of Roo.data.Records.
10857 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10858 * The function must be passed <ul>
10859 * <li>The Record block object</li>
10860 * <li>The "arg" argument from the load function</li>
10861 * <li>A boolean success indicator</li>
10863 * @param {Object} scope The scope in which to call the callback
10864 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10866 load : function(params, reader, callback, scope, arg){
10867 if(this.fireEvent("beforeload", this, params) !== false){
10869 params : params || {},
10871 callback : callback,
10876 callback : this.loadResponse,
10880 Roo.applyIf(o, this.conn);
10881 if(this.activeRequest){
10882 Roo.Ajax.abort(this.activeRequest);
10884 this.activeRequest = Roo.Ajax.request(o);
10886 this.conn.request(o);
10889 callback.call(scope||this, null, arg, false);
10894 loadResponse : function(o, success, response){
10895 delete this.activeRequest;
10897 this.fireEvent("loadexception", this, o, response);
10898 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10903 result = o.reader.read(response);
10905 this.fireEvent("loadexception", this, o, response, e);
10906 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10910 this.fireEvent("load", this, o, o.request.arg);
10911 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10915 update : function(dataSet){
10920 updateResponse : function(dataSet){
10925 * Ext JS Library 1.1.1
10926 * Copyright(c) 2006-2007, Ext JS, LLC.
10928 * Originally Released Under LGPL - original licence link has changed is not relivant.
10931 * <script type="text/javascript">
10935 * @class Roo.data.ScriptTagProxy
10936 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10937 * other than the originating domain of the running page.<br><br>
10939 * <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
10940 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10942 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10943 * source code that is used as the source inside a <script> tag.<br><br>
10945 * In order for the browser to process the returned data, the server must wrap the data object
10946 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10947 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10948 * depending on whether the callback name was passed:
10951 boolean scriptTag = false;
10952 String cb = request.getParameter("callback");
10955 response.setContentType("text/javascript");
10957 response.setContentType("application/x-json");
10959 Writer out = response.getWriter();
10961 out.write(cb + "(");
10963 out.print(dataBlock.toJsonString());
10970 * @param {Object} config A configuration object.
10972 Roo.data.ScriptTagProxy = function(config){
10973 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10974 Roo.apply(this, config);
10975 this.head = document.getElementsByTagName("head")[0];
10978 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10980 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10982 * @cfg {String} url The URL from which to request the data object.
10985 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10989 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10990 * the server the name of the callback function set up by the load call to process the returned data object.
10991 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10992 * javascript output which calls this named function passing the data object as its only parameter.
10994 callbackParam : "callback",
10996 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10997 * name to the request.
11002 * Load data from the configured URL, read the data object into
11003 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11004 * process that block using the passed callback.
11005 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11006 * for the request to the remote server.
11007 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11008 * object into a block of Roo.data.Records.
11009 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11010 * The function must be passed <ul>
11011 * <li>The Record block object</li>
11012 * <li>The "arg" argument from the load function</li>
11013 * <li>A boolean success indicator</li>
11015 * @param {Object} scope The scope in which to call the callback
11016 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11018 load : function(params, reader, callback, scope, arg){
11019 if(this.fireEvent("beforeload", this, params) !== false){
11021 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11023 var url = this.url;
11024 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11026 url += "&_dc=" + (new Date().getTime());
11028 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11031 cb : "stcCallback"+transId,
11032 scriptId : "stcScript"+transId,
11036 callback : callback,
11042 window[trans.cb] = function(o){
11043 conn.handleResponse(o, trans);
11046 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11048 if(this.autoAbort !== false){
11052 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11054 var script = document.createElement("script");
11055 script.setAttribute("src", url);
11056 script.setAttribute("type", "text/javascript");
11057 script.setAttribute("id", trans.scriptId);
11058 this.head.appendChild(script);
11060 this.trans = trans;
11062 callback.call(scope||this, null, arg, false);
11067 isLoading : function(){
11068 return this.trans ? true : false;
11072 * Abort the current server request.
11074 abort : function(){
11075 if(this.isLoading()){
11076 this.destroyTrans(this.trans);
11081 destroyTrans : function(trans, isLoaded){
11082 this.head.removeChild(document.getElementById(trans.scriptId));
11083 clearTimeout(trans.timeoutId);
11085 window[trans.cb] = undefined;
11087 delete window[trans.cb];
11090 // if hasn't been loaded, wait for load to remove it to prevent script error
11091 window[trans.cb] = function(){
11092 window[trans.cb] = undefined;
11094 delete window[trans.cb];
11101 handleResponse : function(o, trans){
11102 this.trans = false;
11103 this.destroyTrans(trans, true);
11106 result = trans.reader.readRecords(o);
11108 this.fireEvent("loadexception", this, o, trans.arg, e);
11109 trans.callback.call(trans.scope||window, null, trans.arg, false);
11112 this.fireEvent("load", this, o, trans.arg);
11113 trans.callback.call(trans.scope||window, result, trans.arg, true);
11117 handleFailure : function(trans){
11118 this.trans = false;
11119 this.destroyTrans(trans, false);
11120 this.fireEvent("loadexception", this, null, trans.arg);
11121 trans.callback.call(trans.scope||window, null, trans.arg, false);
11125 * Ext JS Library 1.1.1
11126 * Copyright(c) 2006-2007, Ext JS, LLC.
11128 * Originally Released Under LGPL - original licence link has changed is not relivant.
11131 * <script type="text/javascript">
11135 * @class Roo.data.JsonReader
11136 * @extends Roo.data.DataReader
11137 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11138 * based on mappings in a provided Roo.data.Record constructor.
11140 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11141 * in the reply previously.
11146 var RecordDef = Roo.data.Record.create([
11147 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11148 {name: 'occupation'} // This field will use "occupation" as the mapping.
11150 var myReader = new Roo.data.JsonReader({
11151 totalProperty: "results", // The property which contains the total dataset size (optional)
11152 root: "rows", // The property which contains an Array of row objects
11153 id: "id" // The property within each row object that provides an ID for the record (optional)
11157 * This would consume a JSON file like this:
11159 { 'results': 2, 'rows': [
11160 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11161 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11164 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11165 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11166 * paged from the remote server.
11167 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11168 * @cfg {String} root name of the property which contains the Array of row objects.
11169 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11170 * @cfg {Array} fields Array of field definition objects
11172 * Create a new JsonReader
11173 * @param {Object} meta Metadata configuration options
11174 * @param {Object} recordType Either an Array of field definition objects,
11175 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11177 Roo.data.JsonReader = function(meta, recordType){
11180 // set some defaults:
11181 Roo.applyIf(meta, {
11182 totalProperty: 'total',
11183 successProperty : 'success',
11188 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11193 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11194 * Used by Store query builder to append _requestMeta to params.
11197 metaFromRemote : false,
11199 * This method is only used by a DataProxy which has retrieved data from a remote server.
11200 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11201 * @return {Object} data A data block which is used by an Roo.data.Store object as
11202 * a cache of Roo.data.Records.
11204 read : function(response){
11205 var json = response.responseText;
11207 var o = /* eval:var:o */ eval("("+json+")");
11209 throw {message: "JsonReader.read: Json object not found"};
11215 this.metaFromRemote = true;
11216 this.meta = o.metaData;
11217 this.recordType = Roo.data.Record.create(o.metaData.fields);
11218 this.onMetaChange(this.meta, this.recordType, o);
11220 return this.readRecords(o);
11223 // private function a store will implement
11224 onMetaChange : function(meta, recordType, o){
11231 simpleAccess: function(obj, subsc) {
11238 getJsonAccessor: function(){
11240 return function(expr) {
11242 return(re.test(expr))
11243 ? new Function("obj", "return obj." + expr)
11248 return Roo.emptyFn;
11253 * Create a data block containing Roo.data.Records from an XML document.
11254 * @param {Object} o An object which contains an Array of row objects in the property specified
11255 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11256 * which contains the total size of the dataset.
11257 * @return {Object} data A data block which is used by an Roo.data.Store object as
11258 * a cache of Roo.data.Records.
11260 readRecords : function(o){
11262 * After any data loads, the raw JSON data is available for further custom processing.
11266 var s = this.meta, Record = this.recordType,
11267 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11269 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11271 if(s.totalProperty) {
11272 this.getTotal = this.getJsonAccessor(s.totalProperty);
11274 if(s.successProperty) {
11275 this.getSuccess = this.getJsonAccessor(s.successProperty);
11277 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11279 var g = this.getJsonAccessor(s.id);
11280 this.getId = function(rec) {
11282 return (r === undefined || r === "") ? null : r;
11285 this.getId = function(){return null;};
11288 for(var jj = 0; jj < fl; jj++){
11290 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11291 this.ef[jj] = this.getJsonAccessor(map);
11295 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11296 if(s.totalProperty){
11297 var vt = parseInt(this.getTotal(o), 10);
11302 if(s.successProperty){
11303 var vs = this.getSuccess(o);
11304 if(vs === false || vs === 'false'){
11309 for(var i = 0; i < c; i++){
11312 var id = this.getId(n);
11313 for(var j = 0; j < fl; j++){
11315 var v = this.ef[j](n);
11317 Roo.log('missing convert for ' + f.name);
11321 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11323 var record = new Record(values, id);
11325 records[i] = record;
11331 totalRecords : totalRecords
11336 * Ext JS Library 1.1.1
11337 * Copyright(c) 2006-2007, Ext JS, LLC.
11339 * Originally Released Under LGPL - original licence link has changed is not relivant.
11342 * <script type="text/javascript">
11346 * @class Roo.data.ArrayReader
11347 * @extends Roo.data.DataReader
11348 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11349 * Each element of that Array represents a row of data fields. The
11350 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11351 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11355 var RecordDef = Roo.data.Record.create([
11356 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11357 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11359 var myReader = new Roo.data.ArrayReader({
11360 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11364 * This would consume an Array like this:
11366 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11368 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11370 * Create a new JsonReader
11371 * @param {Object} meta Metadata configuration options.
11372 * @param {Object} recordType Either an Array of field definition objects
11373 * as specified to {@link Roo.data.Record#create},
11374 * or an {@link Roo.data.Record} object
11375 * created using {@link Roo.data.Record#create}.
11377 Roo.data.ArrayReader = function(meta, recordType){
11378 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11381 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11383 * Create a data block containing Roo.data.Records from an XML document.
11384 * @param {Object} o An Array of row objects which represents the dataset.
11385 * @return {Object} data A data block which is used by an Roo.data.Store object as
11386 * a cache of Roo.data.Records.
11388 readRecords : function(o){
11389 var sid = this.meta ? this.meta.id : null;
11390 var recordType = this.recordType, fields = recordType.prototype.fields;
11393 for(var i = 0; i < root.length; i++){
11396 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11397 for(var j = 0, jlen = fields.length; j < jlen; j++){
11398 var f = fields.items[j];
11399 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11400 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11402 values[f.name] = v;
11404 var record = new recordType(values, id);
11406 records[records.length] = record;
11410 totalRecords : records.length
11419 * @class Roo.bootstrap.ComboBox
11420 * @extends Roo.bootstrap.TriggerField
11421 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11422 * @cfg {Boolean} append (true|false) default false
11423 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11424 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11425 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11426 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11427 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11428 * @cfg {Boolean} animate default true
11429 * @cfg {Boolean} emptyResultText only for touch device
11430 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11432 * Create a new ComboBox.
11433 * @param {Object} config Configuration options
11435 Roo.bootstrap.ComboBox = function(config){
11436 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11440 * Fires when the dropdown list is expanded
11441 * @param {Roo.bootstrap.ComboBox} combo This combo box
11446 * Fires when the dropdown list is collapsed
11447 * @param {Roo.bootstrap.ComboBox} combo This combo box
11451 * @event beforeselect
11452 * Fires before a list item is selected. Return false to cancel the selection.
11453 * @param {Roo.bootstrap.ComboBox} combo This combo box
11454 * @param {Roo.data.Record} record The data record returned from the underlying store
11455 * @param {Number} index The index of the selected item in the dropdown list
11457 'beforeselect' : true,
11460 * Fires when a list item is selected
11461 * @param {Roo.bootstrap.ComboBox} combo This combo box
11462 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11463 * @param {Number} index The index of the selected item in the dropdown list
11467 * @event beforequery
11468 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11469 * The event object passed has these properties:
11470 * @param {Roo.bootstrap.ComboBox} combo This combo box
11471 * @param {String} query The query
11472 * @param {Boolean} forceAll true to force "all" query
11473 * @param {Boolean} cancel true to cancel the query
11474 * @param {Object} e The query event object
11476 'beforequery': true,
11479 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11480 * @param {Roo.bootstrap.ComboBox} combo This combo box
11485 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11486 * @param {Roo.bootstrap.ComboBox} combo This combo box
11487 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11492 * Fires when the remove value from the combobox array
11493 * @param {Roo.bootstrap.ComboBox} combo This combo box
11497 * @event specialfilter
11498 * Fires when specialfilter
11499 * @param {Roo.bootstrap.ComboBox} combo This combo box
11501 'specialfilter' : true,
11504 * Fires when tick the element
11505 * @param {Roo.bootstrap.ComboBox} combo This combo box
11509 * @event touchviewdisplay
11510 * Fires when touch view require special display (default is using displayField)
11511 * @param {Roo.bootstrap.ComboBox} combo This combo box
11512 * @param {Object} cfg set html .
11514 'touchviewdisplay' : true
11519 this.tickItems = [];
11521 this.selectedIndex = -1;
11522 if(this.mode == 'local'){
11523 if(config.queryDelay === undefined){
11524 this.queryDelay = 10;
11526 if(config.minChars === undefined){
11532 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11535 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11536 * rendering into an Roo.Editor, defaults to false)
11539 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11540 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11543 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11546 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11547 * the dropdown list (defaults to undefined, with no header element)
11551 * @cfg {String/Roo.Template} tpl The template to use to render the output
11555 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11557 listWidth: undefined,
11559 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11560 * mode = 'remote' or 'text' if mode = 'local')
11562 displayField: undefined,
11565 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11566 * mode = 'remote' or 'value' if mode = 'local').
11567 * Note: use of a valueField requires the user make a selection
11568 * in order for a value to be mapped.
11570 valueField: undefined,
11574 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11575 * field's data value (defaults to the underlying DOM element's name)
11577 hiddenName: undefined,
11579 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11583 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11585 selectedClass: 'active',
11588 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11592 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11593 * anchor positions (defaults to 'tl-bl')
11595 listAlign: 'tl-bl?',
11597 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11601 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11602 * query specified by the allQuery config option (defaults to 'query')
11604 triggerAction: 'query',
11606 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11607 * (defaults to 4, does not apply if editable = false)
11611 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11612 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11616 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11617 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11621 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11622 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11626 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11627 * when editable = true (defaults to false)
11629 selectOnFocus:false,
11631 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11633 queryParam: 'query',
11635 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11636 * when mode = 'remote' (defaults to 'Loading...')
11638 loadingText: 'Loading...',
11640 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11644 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11648 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11649 * traditional select (defaults to true)
11653 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11657 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11661 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11662 * listWidth has a higher value)
11666 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11667 * allow the user to set arbitrary text into the field (defaults to false)
11669 forceSelection:false,
11671 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11672 * if typeAhead = true (defaults to 250)
11674 typeAheadDelay : 250,
11676 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11677 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11679 valueNotFoundText : undefined,
11681 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11683 blockFocus : false,
11686 * @cfg {Boolean} disableClear Disable showing of clear button.
11688 disableClear : false,
11690 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11692 alwaysQuery : false,
11695 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11700 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11702 invalidClass : "has-warning",
11705 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11707 validClass : "has-success",
11710 * @cfg {Boolean} specialFilter (true|false) special filter default false
11712 specialFilter : false,
11715 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11717 mobileTouchView : true,
11729 btnPosition : 'right',
11730 triggerList : true,
11731 showToggleBtn : true,
11733 emptyResultText: 'Empty',
11734 triggerText : 'Select',
11736 // element that contains real text value.. (when hidden is used..)
11738 getAutoCreate : function()
11746 if(Roo.isTouch && this.mobileTouchView){
11747 cfg = this.getAutoCreateTouchView();
11754 if(!this.tickable){
11755 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11760 * ComboBox with tickable selections
11763 var align = this.labelAlign || this.parentLabelAlign();
11766 cls : 'form-group roo-combobox-tickable' //input-group
11771 cls : 'tickable-buttons',
11776 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11777 html : this.triggerText
11783 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11790 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11797 buttons.cn.unshift({
11799 cls: 'select2-search-field-input'
11805 Roo.each(buttons.cn, function(c){
11807 c.cls += ' btn-' + _this.size;
11810 if (_this.disabled) {
11821 cls: 'form-hidden-field'
11825 cls: 'select2-choices',
11829 cls: 'select2-search-field',
11841 cls: 'select2-container input-group select2-container-multi',
11846 // cls: 'typeahead typeahead-long dropdown-menu',
11847 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11852 if(this.hasFeedback && !this.allowBlank){
11856 cls: 'glyphicon form-control-feedback'
11859 combobox.cn.push(feedback);
11862 if (align ==='left' && this.fieldLabel.length) {
11864 Roo.log("left and has label");
11870 cls : 'control-label col-sm-' + this.labelWidth,
11871 html : this.fieldLabel
11875 cls : "col-sm-" + (12 - this.labelWidth),
11882 } else if ( this.fieldLabel.length) {
11888 //cls : 'input-group-addon',
11889 html : this.fieldLabel
11899 Roo.log(" no label && no align");
11906 ['xs','sm','md','lg'].map(function(size){
11907 if (settings[size]) {
11908 cfg.cls += ' col-' + size + '-' + settings[size];
11916 _initEventsCalled : false,
11919 initEvents: function()
11922 if (this._initEventsCalled) { // as we call render... prevent looping...
11925 this._initEventsCalled = true;
11928 throw "can not find store for combo";
11931 this.store = Roo.factory(this.store, Roo.data);
11933 // if we are building from html. then this element is so complex, that we can not really
11934 // use the rendered HTML.
11935 // so we have to trash and replace the previous code.
11936 if (Roo.XComponent.build_from_html) {
11938 // remove this element....
11939 var e = this.el.dom, k=0;
11940 while (e ) { e = e.previousSibling; ++k;}
11945 this.rendered = false;
11947 this.render(this.parent().getChildContainer(true), k);
11958 if(Roo.isTouch && this.mobileTouchView){
11959 this.initTouchView();
11964 this.initTickableEvents();
11968 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11970 if(this.hiddenName){
11972 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11974 this.hiddenField.dom.value =
11975 this.hiddenValue !== undefined ? this.hiddenValue :
11976 this.value !== undefined ? this.value : '';
11978 // prevent input submission
11979 this.el.dom.removeAttribute('name');
11980 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11985 // this.el.dom.setAttribute('autocomplete', 'off');
11988 var cls = 'x-combo-list';
11990 //this.list = new Roo.Layer({
11991 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11997 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11998 _this.list.setWidth(lw);
12001 this.list.on('mouseover', this.onViewOver, this);
12002 this.list.on('mousemove', this.onViewMove, this);
12004 this.list.on('scroll', this.onViewScroll, this);
12007 this.list.swallowEvent('mousewheel');
12008 this.assetHeight = 0;
12011 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12012 this.assetHeight += this.header.getHeight();
12015 this.innerList = this.list.createChild({cls:cls+'-inner'});
12016 this.innerList.on('mouseover', this.onViewOver, this);
12017 this.innerList.on('mousemove', this.onViewMove, this);
12018 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12020 if(this.allowBlank && !this.pageSize && !this.disableClear){
12021 this.footer = this.list.createChild({cls:cls+'-ft'});
12022 this.pageTb = new Roo.Toolbar(this.footer);
12026 this.footer = this.list.createChild({cls:cls+'-ft'});
12027 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12028 {pageSize: this.pageSize});
12032 if (this.pageTb && this.allowBlank && !this.disableClear) {
12034 this.pageTb.add(new Roo.Toolbar.Fill(), {
12035 cls: 'x-btn-icon x-btn-clear',
12037 handler: function()
12040 _this.clearValue();
12041 _this.onSelect(false, -1);
12046 this.assetHeight += this.footer.getHeight();
12051 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12054 this.view = new Roo.View(this.list, this.tpl, {
12055 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12057 //this.view.wrapEl.setDisplayed(false);
12058 this.view.on('click', this.onViewClick, this);
12062 this.store.on('beforeload', this.onBeforeLoad, this);
12063 this.store.on('load', this.onLoad, this);
12064 this.store.on('loadexception', this.onLoadException, this);
12066 if(this.resizable){
12067 this.resizer = new Roo.Resizable(this.list, {
12068 pinned:true, handles:'se'
12070 this.resizer.on('resize', function(r, w, h){
12071 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12072 this.listWidth = w;
12073 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12074 this.restrictHeight();
12076 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12079 if(!this.editable){
12080 this.editable = true;
12081 this.setEditable(false);
12086 if (typeof(this.events.add.listeners) != 'undefined') {
12088 this.addicon = this.wrap.createChild(
12089 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12091 this.addicon.on('click', function(e) {
12092 this.fireEvent('add', this);
12095 if (typeof(this.events.edit.listeners) != 'undefined') {
12097 this.editicon = this.wrap.createChild(
12098 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12099 if (this.addicon) {
12100 this.editicon.setStyle('margin-left', '40px');
12102 this.editicon.on('click', function(e) {
12104 // we fire even if inothing is selected..
12105 this.fireEvent('edit', this, this.lastData );
12111 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12112 "up" : function(e){
12113 this.inKeyMode = true;
12117 "down" : function(e){
12118 if(!this.isExpanded()){
12119 this.onTriggerClick();
12121 this.inKeyMode = true;
12126 "enter" : function(e){
12127 // this.onViewClick();
12131 if(this.fireEvent("specialkey", this, e)){
12132 this.onViewClick(false);
12138 "esc" : function(e){
12142 "tab" : function(e){
12145 if(this.fireEvent("specialkey", this, e)){
12146 this.onViewClick(false);
12154 doRelay : function(foo, bar, hname){
12155 if(hname == 'down' || this.scope.isExpanded()){
12156 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12165 this.queryDelay = Math.max(this.queryDelay || 10,
12166 this.mode == 'local' ? 10 : 250);
12169 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12171 if(this.typeAhead){
12172 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12174 if(this.editable !== false){
12175 this.inputEl().on("keyup", this.onKeyUp, this);
12177 if(this.forceSelection){
12178 this.inputEl().on('blur', this.doForce, this);
12182 this.choices = this.el.select('ul.select2-choices', true).first();
12183 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12187 initTickableEvents: function()
12191 if(this.hiddenName){
12193 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12195 this.hiddenField.dom.value =
12196 this.hiddenValue !== undefined ? this.hiddenValue :
12197 this.value !== undefined ? this.value : '';
12199 // prevent input submission
12200 this.el.dom.removeAttribute('name');
12201 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12206 // this.list = this.el.select('ul.dropdown-menu',true).first();
12208 this.choices = this.el.select('ul.select2-choices', true).first();
12209 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12210 if(this.triggerList){
12211 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12214 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12215 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12217 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12218 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12220 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12221 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12223 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12224 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12225 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12228 this.cancelBtn.hide();
12233 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12234 _this.list.setWidth(lw);
12237 this.list.on('mouseover', this.onViewOver, this);
12238 this.list.on('mousemove', this.onViewMove, this);
12240 this.list.on('scroll', this.onViewScroll, this);
12243 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>';
12246 this.view = new Roo.View(this.list, this.tpl, {
12247 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12250 //this.view.wrapEl.setDisplayed(false);
12251 this.view.on('click', this.onViewClick, this);
12255 this.store.on('beforeload', this.onBeforeLoad, this);
12256 this.store.on('load', this.onLoad, this);
12257 this.store.on('loadexception', this.onLoadException, this);
12260 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12261 "up" : function(e){
12262 this.inKeyMode = true;
12266 "down" : function(e){
12267 this.inKeyMode = true;
12271 "enter" : function(e){
12272 if(this.fireEvent("specialkey", this, e)){
12273 this.onViewClick(false);
12279 "esc" : function(e){
12280 this.onTickableFooterButtonClick(e, false, false);
12283 "tab" : function(e){
12284 this.fireEvent("specialkey", this, e);
12286 this.onTickableFooterButtonClick(e, false, false);
12293 doRelay : function(e, fn, key){
12294 if(this.scope.isExpanded()){
12295 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12304 this.queryDelay = Math.max(this.queryDelay || 10,
12305 this.mode == 'local' ? 10 : 250);
12308 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12310 if(this.typeAhead){
12311 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12314 if(this.editable !== false){
12315 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12320 onDestroy : function(){
12322 this.view.setStore(null);
12323 this.view.el.removeAllListeners();
12324 this.view.el.remove();
12325 this.view.purgeListeners();
12328 this.list.dom.innerHTML = '';
12332 this.store.un('beforeload', this.onBeforeLoad, this);
12333 this.store.un('load', this.onLoad, this);
12334 this.store.un('loadexception', this.onLoadException, this);
12336 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12340 fireKey : function(e){
12341 if(e.isNavKeyPress() && !this.list.isVisible()){
12342 this.fireEvent("specialkey", this, e);
12347 onResize: function(w, h){
12348 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12350 // if(typeof w != 'number'){
12351 // // we do not handle it!?!?
12354 // var tw = this.trigger.getWidth();
12355 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12356 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12358 // this.inputEl().setWidth( this.adjustWidth('input', x));
12360 // //this.trigger.setStyle('left', x+'px');
12362 // if(this.list && this.listWidth === undefined){
12363 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12364 // this.list.setWidth(lw);
12365 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12373 * Allow or prevent the user from directly editing the field text. If false is passed,
12374 * the user will only be able to select from the items defined in the dropdown list. This method
12375 * is the runtime equivalent of setting the 'editable' config option at config time.
12376 * @param {Boolean} value True to allow the user to directly edit the field text
12378 setEditable : function(value){
12379 if(value == this.editable){
12382 this.editable = value;
12384 this.inputEl().dom.setAttribute('readOnly', true);
12385 this.inputEl().on('mousedown', this.onTriggerClick, this);
12386 this.inputEl().addClass('x-combo-noedit');
12388 this.inputEl().dom.setAttribute('readOnly', false);
12389 this.inputEl().un('mousedown', this.onTriggerClick, this);
12390 this.inputEl().removeClass('x-combo-noedit');
12396 onBeforeLoad : function(combo,opts){
12397 if(!this.hasFocus){
12401 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12403 this.restrictHeight();
12404 this.selectedIndex = -1;
12408 onLoad : function(){
12410 this.hasQuery = false;
12412 if(!this.hasFocus){
12416 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12417 this.loading.hide();
12420 if(this.store.getCount() > 0){
12422 this.restrictHeight();
12423 if(this.lastQuery == this.allQuery){
12424 if(this.editable && !this.tickable){
12425 this.inputEl().dom.select();
12429 !this.selectByValue(this.value, true) &&
12432 !this.store.lastOptions ||
12433 typeof(this.store.lastOptions.add) == 'undefined' ||
12434 this.store.lastOptions.add != true
12437 this.select(0, true);
12440 if(this.autoFocus){
12443 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12444 this.taTask.delay(this.typeAheadDelay);
12448 this.onEmptyResults();
12454 onLoadException : function()
12456 this.hasQuery = false;
12458 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12459 this.loading.hide();
12462 if(this.tickable && this.editable){
12467 // only causes errors at present
12468 //Roo.log(this.store.reader.jsonData);
12469 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12471 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12477 onTypeAhead : function(){
12478 if(this.store.getCount() > 0){
12479 var r = this.store.getAt(0);
12480 var newValue = r.data[this.displayField];
12481 var len = newValue.length;
12482 var selStart = this.getRawValue().length;
12484 if(selStart != len){
12485 this.setRawValue(newValue);
12486 this.selectText(selStart, newValue.length);
12492 onSelect : function(record, index){
12494 if(this.fireEvent('beforeselect', this, record, index) !== false){
12496 this.setFromData(index > -1 ? record.data : false);
12499 this.fireEvent('select', this, record, index);
12504 * Returns the currently selected field value or empty string if no value is set.
12505 * @return {String} value The selected value
12507 getValue : function(){
12510 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12513 if(this.valueField){
12514 return typeof this.value != 'undefined' ? this.value : '';
12516 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12521 * Clears any text/value currently set in the field
12523 clearValue : function(){
12524 if(this.hiddenField){
12525 this.hiddenField.dom.value = '';
12528 this.setRawValue('');
12529 this.lastSelectionText = '';
12530 this.lastData = false;
12532 var close = this.closeTriggerEl();
12541 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12542 * will be displayed in the field. If the value does not match the data value of an existing item,
12543 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12544 * Otherwise the field will be blank (although the value will still be set).
12545 * @param {String} value The value to match
12547 setValue : function(v){
12554 if(this.valueField){
12555 var r = this.findRecord(this.valueField, v);
12557 text = r.data[this.displayField];
12558 }else if(this.valueNotFoundText !== undefined){
12559 text = this.valueNotFoundText;
12562 this.lastSelectionText = text;
12563 if(this.hiddenField){
12564 this.hiddenField.dom.value = v;
12566 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12569 var close = this.closeTriggerEl();
12572 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12576 * @property {Object} the last set data for the element
12581 * Sets the value of the field based on a object which is related to the record format for the store.
12582 * @param {Object} value the value to set as. or false on reset?
12584 setFromData : function(o){
12591 var dv = ''; // display value
12592 var vv = ''; // value value..
12594 if (this.displayField) {
12595 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12597 // this is an error condition!!!
12598 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12601 if(this.valueField){
12602 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12605 var close = this.closeTriggerEl();
12608 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12611 if(this.hiddenField){
12612 this.hiddenField.dom.value = vv;
12614 this.lastSelectionText = dv;
12615 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12619 // no hidden field.. - we store the value in 'value', but still display
12620 // display field!!!!
12621 this.lastSelectionText = dv;
12622 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12629 reset : function(){
12630 // overridden so that last data is reset..
12637 this.setValue(this.originalValue);
12638 this.clearInvalid();
12639 this.lastData = false;
12641 this.view.clearSelections();
12645 findRecord : function(prop, value){
12647 if(this.store.getCount() > 0){
12648 this.store.each(function(r){
12649 if(r.data[prop] == value){
12659 getName: function()
12661 // returns hidden if it's set..
12662 if (!this.rendered) {return ''};
12663 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12667 onViewMove : function(e, t){
12668 this.inKeyMode = false;
12672 onViewOver : function(e, t){
12673 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12676 var item = this.view.findItemFromChild(t);
12679 var index = this.view.indexOf(item);
12680 this.select(index, false);
12685 onViewClick : function(view, doFocus, el, e)
12687 var index = this.view.getSelectedIndexes()[0];
12689 var r = this.store.getAt(index);
12693 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12700 Roo.each(this.tickItems, function(v,k){
12702 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12704 _this.tickItems.splice(k, 1);
12706 if(typeof(e) == 'undefined' && view == false){
12707 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12719 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12720 this.tickItems.push(r.data);
12723 if(typeof(e) == 'undefined' && view == false){
12724 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12731 this.onSelect(r, index);
12733 if(doFocus !== false && !this.blockFocus){
12734 this.inputEl().focus();
12739 restrictHeight : function(){
12740 //this.innerList.dom.style.height = '';
12741 //var inner = this.innerList.dom;
12742 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12743 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12744 //this.list.beginUpdate();
12745 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12746 this.list.alignTo(this.inputEl(), this.listAlign);
12747 this.list.alignTo(this.inputEl(), this.listAlign);
12748 //this.list.endUpdate();
12752 onEmptyResults : function(){
12754 if(this.tickable && this.editable){
12755 this.restrictHeight();
12763 * Returns true if the dropdown list is expanded, else false.
12765 isExpanded : function(){
12766 return this.list.isVisible();
12770 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12771 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12772 * @param {String} value The data value of the item to select
12773 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12774 * selected item if it is not currently in view (defaults to true)
12775 * @return {Boolean} True if the value matched an item in the list, else false
12777 selectByValue : function(v, scrollIntoView){
12778 if(v !== undefined && v !== null){
12779 var r = this.findRecord(this.valueField || this.displayField, v);
12781 this.select(this.store.indexOf(r), scrollIntoView);
12789 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12790 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12791 * @param {Number} index The zero-based index of the list item to select
12792 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12793 * selected item if it is not currently in view (defaults to true)
12795 select : function(index, scrollIntoView){
12796 this.selectedIndex = index;
12797 this.view.select(index);
12798 if(scrollIntoView !== false){
12799 var el = this.view.getNode(index);
12801 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12804 this.list.scrollChildIntoView(el, false);
12810 selectNext : function(){
12811 var ct = this.store.getCount();
12813 if(this.selectedIndex == -1){
12815 }else if(this.selectedIndex < ct-1){
12816 this.select(this.selectedIndex+1);
12822 selectPrev : function(){
12823 var ct = this.store.getCount();
12825 if(this.selectedIndex == -1){
12827 }else if(this.selectedIndex != 0){
12828 this.select(this.selectedIndex-1);
12834 onKeyUp : function(e){
12835 if(this.editable !== false && !e.isSpecialKey()){
12836 this.lastKey = e.getKey();
12837 this.dqTask.delay(this.queryDelay);
12842 validateBlur : function(){
12843 return !this.list || !this.list.isVisible();
12847 initQuery : function(){
12849 var v = this.getRawValue();
12851 if(this.tickable && this.editable){
12852 v = this.tickableInputEl().getValue();
12859 doForce : function(){
12860 if(this.inputEl().dom.value.length > 0){
12861 this.inputEl().dom.value =
12862 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12868 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12869 * query allowing the query action to be canceled if needed.
12870 * @param {String} query The SQL query to execute
12871 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12872 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12873 * saved in the current store (defaults to false)
12875 doQuery : function(q, forceAll){
12877 if(q === undefined || q === null){
12882 forceAll: forceAll,
12886 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12891 forceAll = qe.forceAll;
12892 if(forceAll === true || (q.length >= this.minChars)){
12894 this.hasQuery = true;
12896 if(this.lastQuery != q || this.alwaysQuery){
12897 this.lastQuery = q;
12898 if(this.mode == 'local'){
12899 this.selectedIndex = -1;
12901 this.store.clearFilter();
12904 if(this.specialFilter){
12905 this.fireEvent('specialfilter', this);
12910 this.store.filter(this.displayField, q);
12913 this.store.fireEvent("datachanged", this.store);
12920 this.store.baseParams[this.queryParam] = q;
12922 var options = {params : this.getParams(q)};
12925 options.add = true;
12926 options.params.start = this.page * this.pageSize;
12929 this.store.load(options);
12932 * this code will make the page width larger, at the beginning, the list not align correctly,
12933 * we should expand the list on onLoad
12934 * so command out it
12939 this.selectedIndex = -1;
12944 this.loadNext = false;
12948 getParams : function(q){
12950 //p[this.queryParam] = q;
12954 p.limit = this.pageSize;
12960 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12962 collapse : function(){
12963 if(!this.isExpanded()){
12970 this.hasFocus = false;
12972 this.cancelBtn.hide();
12973 this.trigger.show();
12976 this.tickableInputEl().dom.value = '';
12977 this.tickableInputEl().blur();
12982 Roo.get(document).un('mousedown', this.collapseIf, this);
12983 Roo.get(document).un('mousewheel', this.collapseIf, this);
12984 if (!this.editable) {
12985 Roo.get(document).un('keydown', this.listKeyPress, this);
12987 this.fireEvent('collapse', this);
12991 collapseIf : function(e){
12992 var in_combo = e.within(this.el);
12993 var in_list = e.within(this.list);
12994 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12996 if (in_combo || in_list || is_list) {
12997 //e.stopPropagation();
13002 this.onTickableFooterButtonClick(e, false, false);
13010 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13012 expand : function(){
13014 if(this.isExpanded() || !this.hasFocus){
13018 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13019 this.list.setWidth(lw);
13026 this.restrictHeight();
13030 this.tickItems = Roo.apply([], this.item);
13033 this.cancelBtn.show();
13034 this.trigger.hide();
13037 this.tickableInputEl().focus();
13042 Roo.get(document).on('mousedown', this.collapseIf, this);
13043 Roo.get(document).on('mousewheel', this.collapseIf, this);
13044 if (!this.editable) {
13045 Roo.get(document).on('keydown', this.listKeyPress, this);
13048 this.fireEvent('expand', this);
13052 // Implements the default empty TriggerField.onTriggerClick function
13053 onTriggerClick : function(e)
13055 Roo.log('trigger click');
13057 if(this.disabled || !this.triggerList){
13062 this.loadNext = false;
13064 if(this.isExpanded()){
13066 if (!this.blockFocus) {
13067 this.inputEl().focus();
13071 this.hasFocus = true;
13072 if(this.triggerAction == 'all') {
13073 this.doQuery(this.allQuery, true);
13075 this.doQuery(this.getRawValue());
13077 if (!this.blockFocus) {
13078 this.inputEl().focus();
13083 onTickableTriggerClick : function(e)
13090 this.loadNext = false;
13091 this.hasFocus = true;
13093 if(this.triggerAction == 'all') {
13094 this.doQuery(this.allQuery, true);
13096 this.doQuery(this.getRawValue());
13100 onSearchFieldClick : function(e)
13102 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13103 this.onTickableFooterButtonClick(e, false, false);
13107 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13112 this.loadNext = false;
13113 this.hasFocus = true;
13115 if(this.triggerAction == 'all') {
13116 this.doQuery(this.allQuery, true);
13118 this.doQuery(this.getRawValue());
13122 listKeyPress : function(e)
13124 //Roo.log('listkeypress');
13125 // scroll to first matching element based on key pres..
13126 if (e.isSpecialKey()) {
13129 var k = String.fromCharCode(e.getKey()).toUpperCase();
13132 var csel = this.view.getSelectedNodes();
13133 var cselitem = false;
13135 var ix = this.view.indexOf(csel[0]);
13136 cselitem = this.store.getAt(ix);
13137 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13143 this.store.each(function(v) {
13145 // start at existing selection.
13146 if (cselitem.id == v.id) {
13152 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13153 match = this.store.indexOf(v);
13159 if (match === false) {
13160 return true; // no more action?
13163 this.view.select(match);
13164 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13165 sn.scrollIntoView(sn.dom.parentNode, false);
13168 onViewScroll : function(e, t){
13170 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){
13174 this.hasQuery = true;
13176 this.loading = this.list.select('.loading', true).first();
13178 if(this.loading === null){
13179 this.list.createChild({
13181 cls: 'loading select2-more-results select2-active',
13182 html: 'Loading more results...'
13185 this.loading = this.list.select('.loading', true).first();
13187 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13189 this.loading.hide();
13192 this.loading.show();
13197 this.loadNext = true;
13199 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13204 addItem : function(o)
13206 var dv = ''; // display value
13208 if (this.displayField) {
13209 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13211 // this is an error condition!!!
13212 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13219 var choice = this.choices.createChild({
13221 cls: 'select2-search-choice',
13230 cls: 'select2-search-choice-close',
13235 }, this.searchField);
13237 var close = choice.select('a.select2-search-choice-close', true).first();
13239 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13247 this.inputEl().dom.value = '';
13252 onRemoveItem : function(e, _self, o)
13254 e.preventDefault();
13256 this.lastItem = Roo.apply([], this.item);
13258 var index = this.item.indexOf(o.data) * 1;
13261 Roo.log('not this item?!');
13265 this.item.splice(index, 1);
13270 this.fireEvent('remove', this, e);
13276 syncValue : function()
13278 if(!this.item.length){
13285 Roo.each(this.item, function(i){
13286 if(_this.valueField){
13287 value.push(i[_this.valueField]);
13294 this.value = value.join(',');
13296 if(this.hiddenField){
13297 this.hiddenField.dom.value = this.value;
13300 this.store.fireEvent("datachanged", this.store);
13303 clearItem : function()
13305 if(!this.multiple){
13311 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13319 if(this.tickable && !Roo.isTouch){
13320 this.view.refresh();
13324 inputEl: function ()
13326 if(Roo.isTouch && this.mobileTouchView){
13327 return this.el.select('input.form-control',true).first();
13331 return this.searchField;
13334 return this.el.select('input.form-control',true).first();
13338 onTickableFooterButtonClick : function(e, btn, el)
13340 e.preventDefault();
13342 this.lastItem = Roo.apply([], this.item);
13344 if(btn && btn.name == 'cancel'){
13345 this.tickItems = Roo.apply([], this.item);
13354 Roo.each(this.tickItems, function(o){
13362 validate : function()
13364 var v = this.getRawValue();
13367 v = this.getValue();
13370 if(this.disabled || this.allowBlank || v.length){
13375 this.markInvalid();
13379 tickableInputEl : function()
13381 if(!this.tickable || !this.editable){
13382 return this.inputEl();
13385 return this.inputEl().select('.select2-search-field-input', true).first();
13389 getAutoCreateTouchView : function()
13394 cls: 'form-group' //input-group
13400 type : this.inputType,
13401 cls : 'form-control x-combo-noedit',
13402 autocomplete: 'new-password',
13403 placeholder : this.placeholder || '',
13408 input.name = this.name;
13412 input.cls += ' input-' + this.size;
13415 if (this.disabled) {
13416 input.disabled = true;
13427 inputblock.cls += ' input-group';
13429 inputblock.cn.unshift({
13431 cls : 'input-group-addon',
13436 if(this.removable && !this.multiple){
13437 inputblock.cls += ' roo-removable';
13439 inputblock.cn.push({
13442 cls : 'roo-combo-removable-btn close'
13446 if(this.hasFeedback && !this.allowBlank){
13448 inputblock.cls += ' has-feedback';
13450 inputblock.cn.push({
13452 cls: 'glyphicon form-control-feedback'
13459 inputblock.cls += (this.before) ? '' : ' input-group';
13461 inputblock.cn.push({
13463 cls : 'input-group-addon',
13474 cls: 'form-hidden-field'
13488 cls: 'form-hidden-field'
13492 cls: 'select2-choices',
13496 cls: 'select2-search-field',
13509 cls: 'select2-container input-group',
13516 combobox.cls += ' select2-container-multi';
13519 var align = this.labelAlign || this.parentLabelAlign();
13523 if(this.fieldLabel.length){
13525 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13526 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13531 cls : 'control-label ' + lw,
13532 html : this.fieldLabel
13544 var settings = this;
13546 ['xs','sm','md','lg'].map(function(size){
13547 if (settings[size]) {
13548 cfg.cls += ' col-' + size + '-' + settings[size];
13555 initTouchView : function()
13557 this.renderTouchView();
13559 this.touchViewEl.on('scroll', function(){
13560 this.el.dom.scrollTop = 0;
13563 this.inputEl().on("click", this.showTouchView, this);
13564 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13565 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13567 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13569 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13570 this.store.on('load', this.onTouchViewLoad, this);
13571 this.store.on('loadexception', this.onTouchViewLoadException, this);
13573 if(this.hiddenName){
13575 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13577 this.hiddenField.dom.value =
13578 this.hiddenValue !== undefined ? this.hiddenValue :
13579 this.value !== undefined ? this.value : '';
13581 this.el.dom.removeAttribute('name');
13582 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13586 this.choices = this.el.select('ul.select2-choices', true).first();
13587 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13590 if(this.removable && !this.multiple){
13591 var close = this.closeTriggerEl();
13593 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13594 close.on('click', this.removeBtnClick, this, close);
13603 renderTouchView : function()
13605 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13606 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13608 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13609 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13611 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13612 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13613 this.touchViewBodyEl.setStyle('overflow', 'auto');
13615 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13616 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13618 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13619 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13623 showTouchView : function()
13625 this.touchViewHeaderEl.hide();
13627 if(this.fieldLabel.length){
13628 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13629 this.touchViewHeaderEl.show();
13632 this.touchViewEl.show();
13634 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13635 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13637 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13639 if(this.fieldLabel.length){
13640 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13643 this.touchViewBodyEl.setHeight(bodyHeight);
13647 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13649 this.touchViewEl.addClass('in');
13652 this.doTouchViewQuery();
13656 hideTouchView : function()
13658 this.touchViewEl.removeClass('in');
13662 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13664 this.touchViewEl.setStyle('display', 'none');
13669 setTouchViewValue : function()
13676 Roo.each(this.tickItems, function(o){
13681 this.hideTouchView();
13684 doTouchViewQuery : function()
13693 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13697 if(!this.alwaysQuery || this.mode == 'local'){
13698 this.onTouchViewLoad();
13705 onTouchViewBeforeLoad : function(combo,opts)
13711 onTouchViewLoad : function()
13713 if(this.store.getCount() < 1){
13714 this.onTouchViewEmptyResults();
13718 this.clearTouchView();
13720 var rawValue = this.getRawValue();
13722 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13724 this.tickItems = [];
13726 this.store.data.each(function(d, rowIndex){
13727 var row = this.touchViewListGroup.createChild(template);
13729 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13732 html : d.data[this.displayField]
13735 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13736 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13740 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13741 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13744 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13745 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13746 this.tickItems.push(d.data);
13749 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13753 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13755 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13757 if(this.fieldLabel.length){
13758 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13761 var listHeight = this.touchViewListGroup.getHeight();
13765 if(firstChecked && listHeight > bodyHeight){
13766 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13771 onTouchViewLoadException : function()
13773 this.hideTouchView();
13776 onTouchViewEmptyResults : function()
13778 this.clearTouchView();
13780 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13782 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13786 clearTouchView : function()
13788 this.touchViewListGroup.dom.innerHTML = '';
13791 onTouchViewClick : function(e, el, o)
13793 e.preventDefault();
13796 var rowIndex = o.rowIndex;
13798 var r = this.store.getAt(rowIndex);
13800 if(!this.multiple){
13801 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13802 c.dom.removeAttribute('checked');
13805 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13807 this.setFromData(r.data);
13809 var close = this.closeTriggerEl();
13815 this.hideTouchView();
13817 this.fireEvent('select', this, r, rowIndex);
13822 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13823 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13824 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13828 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13829 this.addItem(r.data);
13830 this.tickItems.push(r.data);
13836 * @cfg {Boolean} grow
13840 * @cfg {Number} growMin
13844 * @cfg {Number} growMax
13853 Roo.apply(Roo.bootstrap.ComboBox, {
13857 cls: 'modal-header',
13879 cls: 'list-group-item',
13883 cls: 'roo-combobox-list-group-item-value'
13887 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13901 listItemCheckbox : {
13903 cls: 'list-group-item',
13907 cls: 'roo-combobox-list-group-item-value'
13911 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13927 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13932 cls: 'modal-footer',
13940 cls: 'col-xs-6 text-left',
13943 cls: 'btn btn-danger roo-touch-view-cancel',
13949 cls: 'col-xs-6 text-right',
13952 cls: 'btn btn-success roo-touch-view-ok',
13963 Roo.apply(Roo.bootstrap.ComboBox, {
13965 touchViewTemplate : {
13967 cls: 'modal fade roo-combobox-touch-view',
13971 cls: 'modal-dialog',
13975 cls: 'modal-content',
13977 Roo.bootstrap.ComboBox.header,
13978 Roo.bootstrap.ComboBox.body,
13979 Roo.bootstrap.ComboBox.footer
13988 * Ext JS Library 1.1.1
13989 * Copyright(c) 2006-2007, Ext JS, LLC.
13991 * Originally Released Under LGPL - original licence link has changed is not relivant.
13994 * <script type="text/javascript">
13999 * @extends Roo.util.Observable
14000 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14001 * This class also supports single and multi selection modes. <br>
14002 * Create a data model bound view:
14004 var store = new Roo.data.Store(...);
14006 var view = new Roo.View({
14008 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14010 singleSelect: true,
14011 selectedClass: "ydataview-selected",
14015 // listen for node click?
14016 view.on("click", function(vw, index, node, e){
14017 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14021 dataModel.load("foobar.xml");
14023 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14025 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14026 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14028 * Note: old style constructor is still suported (container, template, config)
14031 * Create a new View
14032 * @param {Object} config The config object
14035 Roo.View = function(config, depreciated_tpl, depreciated_config){
14037 this.parent = false;
14039 if (typeof(depreciated_tpl) == 'undefined') {
14040 // new way.. - universal constructor.
14041 Roo.apply(this, config);
14042 this.el = Roo.get(this.el);
14045 this.el = Roo.get(config);
14046 this.tpl = depreciated_tpl;
14047 Roo.apply(this, depreciated_config);
14049 this.wrapEl = this.el.wrap().wrap();
14050 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14053 if(typeof(this.tpl) == "string"){
14054 this.tpl = new Roo.Template(this.tpl);
14056 // support xtype ctors..
14057 this.tpl = new Roo.factory(this.tpl, Roo);
14061 this.tpl.compile();
14066 * @event beforeclick
14067 * Fires before a click is processed. Returns false to cancel the default action.
14068 * @param {Roo.View} this
14069 * @param {Number} index The index of the target node
14070 * @param {HTMLElement} node The target node
14071 * @param {Roo.EventObject} e The raw event object
14073 "beforeclick" : true,
14076 * Fires when a template node is clicked.
14077 * @param {Roo.View} this
14078 * @param {Number} index The index of the target node
14079 * @param {HTMLElement} node The target node
14080 * @param {Roo.EventObject} e The raw event object
14085 * Fires when a template node is double clicked.
14086 * @param {Roo.View} this
14087 * @param {Number} index The index of the target node
14088 * @param {HTMLElement} node The target node
14089 * @param {Roo.EventObject} e The raw event object
14093 * @event contextmenu
14094 * Fires when a template node is right clicked.
14095 * @param {Roo.View} this
14096 * @param {Number} index The index of the target node
14097 * @param {HTMLElement} node The target node
14098 * @param {Roo.EventObject} e The raw event object
14100 "contextmenu" : true,
14102 * @event selectionchange
14103 * Fires when the selected nodes change.
14104 * @param {Roo.View} this
14105 * @param {Array} selections Array of the selected nodes
14107 "selectionchange" : true,
14110 * @event beforeselect
14111 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14112 * @param {Roo.View} this
14113 * @param {HTMLElement} node The node to be selected
14114 * @param {Array} selections Array of currently selected nodes
14116 "beforeselect" : true,
14118 * @event preparedata
14119 * Fires on every row to render, to allow you to change the data.
14120 * @param {Roo.View} this
14121 * @param {Object} data to be rendered (change this)
14123 "preparedata" : true
14131 "click": this.onClick,
14132 "dblclick": this.onDblClick,
14133 "contextmenu": this.onContextMenu,
14137 this.selections = [];
14139 this.cmp = new Roo.CompositeElementLite([]);
14141 this.store = Roo.factory(this.store, Roo.data);
14142 this.setStore(this.store, true);
14145 if ( this.footer && this.footer.xtype) {
14147 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14149 this.footer.dataSource = this.store;
14150 this.footer.container = fctr;
14151 this.footer = Roo.factory(this.footer, Roo);
14152 fctr.insertFirst(this.el);
14154 // this is a bit insane - as the paging toolbar seems to detach the el..
14155 // dom.parentNode.parentNode.parentNode
14156 // they get detached?
14160 Roo.View.superclass.constructor.call(this);
14165 Roo.extend(Roo.View, Roo.util.Observable, {
14168 * @cfg {Roo.data.Store} store Data store to load data from.
14173 * @cfg {String|Roo.Element} el The container element.
14178 * @cfg {String|Roo.Template} tpl The template used by this View
14182 * @cfg {String} dataName the named area of the template to use as the data area
14183 * Works with domtemplates roo-name="name"
14187 * @cfg {String} selectedClass The css class to add to selected nodes
14189 selectedClass : "x-view-selected",
14191 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14196 * @cfg {String} text to display on mask (default Loading)
14200 * @cfg {Boolean} multiSelect Allow multiple selection
14202 multiSelect : false,
14204 * @cfg {Boolean} singleSelect Allow single selection
14206 singleSelect: false,
14209 * @cfg {Boolean} toggleSelect - selecting
14211 toggleSelect : false,
14214 * @cfg {Boolean} tickable - selecting
14219 * Returns the element this view is bound to.
14220 * @return {Roo.Element}
14222 getEl : function(){
14223 return this.wrapEl;
14229 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14231 refresh : function(){
14232 //Roo.log('refresh');
14235 // if we are using something like 'domtemplate', then
14236 // the what gets used is:
14237 // t.applySubtemplate(NAME, data, wrapping data..)
14238 // the outer template then get' applied with
14239 // the store 'extra data'
14240 // and the body get's added to the
14241 // roo-name="data" node?
14242 // <span class='roo-tpl-{name}'></span> ?????
14246 this.clearSelections();
14247 this.el.update("");
14249 var records = this.store.getRange();
14250 if(records.length < 1) {
14252 // is this valid?? = should it render a template??
14254 this.el.update(this.emptyText);
14258 if (this.dataName) {
14259 this.el.update(t.apply(this.store.meta)); //????
14260 el = this.el.child('.roo-tpl-' + this.dataName);
14263 for(var i = 0, len = records.length; i < len; i++){
14264 var data = this.prepareData(records[i].data, i, records[i]);
14265 this.fireEvent("preparedata", this, data, i, records[i]);
14267 var d = Roo.apply({}, data);
14270 Roo.apply(d, {'roo-id' : Roo.id()});
14274 Roo.each(this.parent.item, function(item){
14275 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14278 Roo.apply(d, {'roo-data-checked' : 'checked'});
14282 html[html.length] = Roo.util.Format.trim(
14284 t.applySubtemplate(this.dataName, d, this.store.meta) :
14291 el.update(html.join(""));
14292 this.nodes = el.dom.childNodes;
14293 this.updateIndexes(0);
14298 * Function to override to reformat the data that is sent to
14299 * the template for each node.
14300 * DEPRICATED - use the preparedata event handler.
14301 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14302 * a JSON object for an UpdateManager bound view).
14304 prepareData : function(data, index, record)
14306 this.fireEvent("preparedata", this, data, index, record);
14310 onUpdate : function(ds, record){
14311 // Roo.log('on update');
14312 this.clearSelections();
14313 var index = this.store.indexOf(record);
14314 var n = this.nodes[index];
14315 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14316 n.parentNode.removeChild(n);
14317 this.updateIndexes(index, index);
14323 onAdd : function(ds, records, index)
14325 //Roo.log(['on Add', ds, records, index] );
14326 this.clearSelections();
14327 if(this.nodes.length == 0){
14331 var n = this.nodes[index];
14332 for(var i = 0, len = records.length; i < len; i++){
14333 var d = this.prepareData(records[i].data, i, records[i]);
14335 this.tpl.insertBefore(n, d);
14338 this.tpl.append(this.el, d);
14341 this.updateIndexes(index);
14344 onRemove : function(ds, record, index){
14345 // Roo.log('onRemove');
14346 this.clearSelections();
14347 var el = this.dataName ?
14348 this.el.child('.roo-tpl-' + this.dataName) :
14351 el.dom.removeChild(this.nodes[index]);
14352 this.updateIndexes(index);
14356 * Refresh an individual node.
14357 * @param {Number} index
14359 refreshNode : function(index){
14360 this.onUpdate(this.store, this.store.getAt(index));
14363 updateIndexes : function(startIndex, endIndex){
14364 var ns = this.nodes;
14365 startIndex = startIndex || 0;
14366 endIndex = endIndex || ns.length - 1;
14367 for(var i = startIndex; i <= endIndex; i++){
14368 ns[i].nodeIndex = i;
14373 * Changes the data store this view uses and refresh the view.
14374 * @param {Store} store
14376 setStore : function(store, initial){
14377 if(!initial && this.store){
14378 this.store.un("datachanged", this.refresh);
14379 this.store.un("add", this.onAdd);
14380 this.store.un("remove", this.onRemove);
14381 this.store.un("update", this.onUpdate);
14382 this.store.un("clear", this.refresh);
14383 this.store.un("beforeload", this.onBeforeLoad);
14384 this.store.un("load", this.onLoad);
14385 this.store.un("loadexception", this.onLoad);
14389 store.on("datachanged", this.refresh, this);
14390 store.on("add", this.onAdd, this);
14391 store.on("remove", this.onRemove, this);
14392 store.on("update", this.onUpdate, this);
14393 store.on("clear", this.refresh, this);
14394 store.on("beforeload", this.onBeforeLoad, this);
14395 store.on("load", this.onLoad, this);
14396 store.on("loadexception", this.onLoad, this);
14404 * onbeforeLoad - masks the loading area.
14407 onBeforeLoad : function(store,opts)
14409 //Roo.log('onBeforeLoad');
14411 this.el.update("");
14413 this.el.mask(this.mask ? this.mask : "Loading" );
14415 onLoad : function ()
14422 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14423 * @param {HTMLElement} node
14424 * @return {HTMLElement} The template node
14426 findItemFromChild : function(node){
14427 var el = this.dataName ?
14428 this.el.child('.roo-tpl-' + this.dataName,true) :
14431 if(!node || node.parentNode == el){
14434 var p = node.parentNode;
14435 while(p && p != el){
14436 if(p.parentNode == el){
14445 onClick : function(e){
14446 var item = this.findItemFromChild(e.getTarget());
14448 var index = this.indexOf(item);
14449 if(this.onItemClick(item, index, e) !== false){
14450 this.fireEvent("click", this, index, item, e);
14453 this.clearSelections();
14458 onContextMenu : function(e){
14459 var item = this.findItemFromChild(e.getTarget());
14461 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14466 onDblClick : function(e){
14467 var item = this.findItemFromChild(e.getTarget());
14469 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14473 onItemClick : function(item, index, e)
14475 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14478 if (this.toggleSelect) {
14479 var m = this.isSelected(item) ? 'unselect' : 'select';
14482 _t[m](item, true, false);
14485 if(this.multiSelect || this.singleSelect){
14486 if(this.multiSelect && e.shiftKey && this.lastSelection){
14487 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14489 this.select(item, this.multiSelect && e.ctrlKey);
14490 this.lastSelection = item;
14493 if(!this.tickable){
14494 e.preventDefault();
14502 * Get the number of selected nodes.
14505 getSelectionCount : function(){
14506 return this.selections.length;
14510 * Get the currently selected nodes.
14511 * @return {Array} An array of HTMLElements
14513 getSelectedNodes : function(){
14514 return this.selections;
14518 * Get the indexes of the selected nodes.
14521 getSelectedIndexes : function(){
14522 var indexes = [], s = this.selections;
14523 for(var i = 0, len = s.length; i < len; i++){
14524 indexes.push(s[i].nodeIndex);
14530 * Clear all selections
14531 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14533 clearSelections : function(suppressEvent){
14534 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14535 this.cmp.elements = this.selections;
14536 this.cmp.removeClass(this.selectedClass);
14537 this.selections = [];
14538 if(!suppressEvent){
14539 this.fireEvent("selectionchange", this, this.selections);
14545 * Returns true if the passed node is selected
14546 * @param {HTMLElement/Number} node The node or node index
14547 * @return {Boolean}
14549 isSelected : function(node){
14550 var s = this.selections;
14554 node = this.getNode(node);
14555 return s.indexOf(node) !== -1;
14560 * @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
14561 * @param {Boolean} keepExisting (optional) true to keep existing selections
14562 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14564 select : function(nodeInfo, keepExisting, suppressEvent){
14565 if(nodeInfo instanceof Array){
14567 this.clearSelections(true);
14569 for(var i = 0, len = nodeInfo.length; i < len; i++){
14570 this.select(nodeInfo[i], true, true);
14574 var node = this.getNode(nodeInfo);
14575 if(!node || this.isSelected(node)){
14576 return; // already selected.
14579 this.clearSelections(true);
14582 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14583 Roo.fly(node).addClass(this.selectedClass);
14584 this.selections.push(node);
14585 if(!suppressEvent){
14586 this.fireEvent("selectionchange", this, this.selections);
14594 * @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
14595 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14596 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14598 unselect : function(nodeInfo, keepExisting, suppressEvent)
14600 if(nodeInfo instanceof Array){
14601 Roo.each(this.selections, function(s) {
14602 this.unselect(s, nodeInfo);
14606 var node = this.getNode(nodeInfo);
14607 if(!node || !this.isSelected(node)){
14608 //Roo.log("not selected");
14609 return; // not selected.
14613 Roo.each(this.selections, function(s) {
14615 Roo.fly(node).removeClass(this.selectedClass);
14622 this.selections= ns;
14623 this.fireEvent("selectionchange", this, this.selections);
14627 * Gets a template node.
14628 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14629 * @return {HTMLElement} The node or null if it wasn't found
14631 getNode : function(nodeInfo){
14632 if(typeof nodeInfo == "string"){
14633 return document.getElementById(nodeInfo);
14634 }else if(typeof nodeInfo == "number"){
14635 return this.nodes[nodeInfo];
14641 * Gets a range template nodes.
14642 * @param {Number} startIndex
14643 * @param {Number} endIndex
14644 * @return {Array} An array of nodes
14646 getNodes : function(start, end){
14647 var ns = this.nodes;
14648 start = start || 0;
14649 end = typeof end == "undefined" ? ns.length - 1 : end;
14652 for(var i = start; i <= end; i++){
14656 for(var i = start; i >= end; i--){
14664 * Finds the index of the passed node
14665 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14666 * @return {Number} The index of the node or -1
14668 indexOf : function(node){
14669 node = this.getNode(node);
14670 if(typeof node.nodeIndex == "number"){
14671 return node.nodeIndex;
14673 var ns = this.nodes;
14674 for(var i = 0, len = ns.length; i < len; i++){
14685 * based on jquery fullcalendar
14689 Roo.bootstrap = Roo.bootstrap || {};
14691 * @class Roo.bootstrap.Calendar
14692 * @extends Roo.bootstrap.Component
14693 * Bootstrap Calendar class
14694 * @cfg {Boolean} loadMask (true|false) default false
14695 * @cfg {Object} header generate the user specific header of the calendar, default false
14698 * Create a new Container
14699 * @param {Object} config The config object
14704 Roo.bootstrap.Calendar = function(config){
14705 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14709 * Fires when a date is selected
14710 * @param {DatePicker} this
14711 * @param {Date} date The selected date
14715 * @event monthchange
14716 * Fires when the displayed month changes
14717 * @param {DatePicker} this
14718 * @param {Date} date The selected month
14720 'monthchange': true,
14722 * @event evententer
14723 * Fires when mouse over an event
14724 * @param {Calendar} this
14725 * @param {event} Event
14727 'evententer': true,
14729 * @event eventleave
14730 * Fires when the mouse leaves an
14731 * @param {Calendar} this
14734 'eventleave': true,
14736 * @event eventclick
14737 * Fires when the mouse click an
14738 * @param {Calendar} this
14747 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14750 * @cfg {Number} startDay
14751 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14759 getAutoCreate : function(){
14762 var fc_button = function(name, corner, style, content ) {
14763 return Roo.apply({},{
14765 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14767 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14770 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14781 style : 'width:100%',
14788 cls : 'fc-header-left',
14790 fc_button('prev', 'left', 'arrow', '‹' ),
14791 fc_button('next', 'right', 'arrow', '›' ),
14792 { tag: 'span', cls: 'fc-header-space' },
14793 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14801 cls : 'fc-header-center',
14805 cls: 'fc-header-title',
14808 html : 'month / year'
14816 cls : 'fc-header-right',
14818 /* fc_button('month', 'left', '', 'month' ),
14819 fc_button('week', '', '', 'week' ),
14820 fc_button('day', 'right', '', 'day' )
14832 header = this.header;
14835 var cal_heads = function() {
14837 // fixme - handle this.
14839 for (var i =0; i < Date.dayNames.length; i++) {
14840 var d = Date.dayNames[i];
14843 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14844 html : d.substring(0,3)
14848 ret[0].cls += ' fc-first';
14849 ret[6].cls += ' fc-last';
14852 var cal_cell = function(n) {
14855 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14860 cls: 'fc-day-number',
14864 cls: 'fc-day-content',
14868 style: 'position: relative;' // height: 17px;
14880 var cal_rows = function() {
14883 for (var r = 0; r < 6; r++) {
14890 for (var i =0; i < Date.dayNames.length; i++) {
14891 var d = Date.dayNames[i];
14892 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14895 row.cn[0].cls+=' fc-first';
14896 row.cn[0].cn[0].style = 'min-height:90px';
14897 row.cn[6].cls+=' fc-last';
14901 ret[0].cls += ' fc-first';
14902 ret[4].cls += ' fc-prev-last';
14903 ret[5].cls += ' fc-last';
14910 cls: 'fc-border-separate',
14911 style : 'width:100%',
14919 cls : 'fc-first fc-last',
14937 cls : 'fc-content',
14938 style : "position: relative;",
14941 cls : 'fc-view fc-view-month fc-grid',
14942 style : 'position: relative',
14943 unselectable : 'on',
14946 cls : 'fc-event-container',
14947 style : 'position:absolute;z-index:8;top:0;left:0;'
14965 initEvents : function()
14968 throw "can not find store for calendar";
14974 style: "text-align:center",
14978 style: "background-color:white;width:50%;margin:250 auto",
14982 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14993 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14995 var size = this.el.select('.fc-content', true).first().getSize();
14996 this.maskEl.setSize(size.width, size.height);
14997 this.maskEl.enableDisplayMode("block");
14998 if(!this.loadMask){
14999 this.maskEl.hide();
15002 this.store = Roo.factory(this.store, Roo.data);
15003 this.store.on('load', this.onLoad, this);
15004 this.store.on('beforeload', this.onBeforeLoad, this);
15008 this.cells = this.el.select('.fc-day',true);
15009 //Roo.log(this.cells);
15010 this.textNodes = this.el.query('.fc-day-number');
15011 this.cells.addClassOnOver('fc-state-hover');
15013 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15014 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15015 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15016 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15018 this.on('monthchange', this.onMonthChange, this);
15020 this.update(new Date().clearTime());
15023 resize : function() {
15024 var sz = this.el.getSize();
15026 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15027 this.el.select('.fc-day-content div',true).setHeight(34);
15032 showPrevMonth : function(e){
15033 this.update(this.activeDate.add("mo", -1));
15035 showToday : function(e){
15036 this.update(new Date().clearTime());
15039 showNextMonth : function(e){
15040 this.update(this.activeDate.add("mo", 1));
15044 showPrevYear : function(){
15045 this.update(this.activeDate.add("y", -1));
15049 showNextYear : function(){
15050 this.update(this.activeDate.add("y", 1));
15055 update : function(date)
15057 var vd = this.activeDate;
15058 this.activeDate = date;
15059 // if(vd && this.el){
15060 // var t = date.getTime();
15061 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15062 // Roo.log('using add remove');
15064 // this.fireEvent('monthchange', this, date);
15066 // this.cells.removeClass("fc-state-highlight");
15067 // this.cells.each(function(c){
15068 // if(c.dateValue == t){
15069 // c.addClass("fc-state-highlight");
15070 // setTimeout(function(){
15071 // try{c.dom.firstChild.focus();}catch(e){}
15081 var days = date.getDaysInMonth();
15083 var firstOfMonth = date.getFirstDateOfMonth();
15084 var startingPos = firstOfMonth.getDay()-this.startDay;
15086 if(startingPos < this.startDay){
15090 var pm = date.add(Date.MONTH, -1);
15091 var prevStart = pm.getDaysInMonth()-startingPos;
15093 this.cells = this.el.select('.fc-day',true);
15094 this.textNodes = this.el.query('.fc-day-number');
15095 this.cells.addClassOnOver('fc-state-hover');
15097 var cells = this.cells.elements;
15098 var textEls = this.textNodes;
15100 Roo.each(cells, function(cell){
15101 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15104 days += startingPos;
15106 // convert everything to numbers so it's fast
15107 var day = 86400000;
15108 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15111 //Roo.log(prevStart);
15113 var today = new Date().clearTime().getTime();
15114 var sel = date.clearTime().getTime();
15115 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15116 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15117 var ddMatch = this.disabledDatesRE;
15118 var ddText = this.disabledDatesText;
15119 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15120 var ddaysText = this.disabledDaysText;
15121 var format = this.format;
15123 var setCellClass = function(cal, cell){
15127 //Roo.log('set Cell Class');
15129 var t = d.getTime();
15133 cell.dateValue = t;
15135 cell.className += " fc-today";
15136 cell.className += " fc-state-highlight";
15137 cell.title = cal.todayText;
15140 // disable highlight in other month..
15141 //cell.className += " fc-state-highlight";
15146 cell.className = " fc-state-disabled";
15147 cell.title = cal.minText;
15151 cell.className = " fc-state-disabled";
15152 cell.title = cal.maxText;
15156 if(ddays.indexOf(d.getDay()) != -1){
15157 cell.title = ddaysText;
15158 cell.className = " fc-state-disabled";
15161 if(ddMatch && format){
15162 var fvalue = d.dateFormat(format);
15163 if(ddMatch.test(fvalue)){
15164 cell.title = ddText.replace("%0", fvalue);
15165 cell.className = " fc-state-disabled";
15169 if (!cell.initialClassName) {
15170 cell.initialClassName = cell.dom.className;
15173 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15178 for(; i < startingPos; i++) {
15179 textEls[i].innerHTML = (++prevStart);
15180 d.setDate(d.getDate()+1);
15182 cells[i].className = "fc-past fc-other-month";
15183 setCellClass(this, cells[i]);
15188 for(; i < days; i++){
15189 intDay = i - startingPos + 1;
15190 textEls[i].innerHTML = (intDay);
15191 d.setDate(d.getDate()+1);
15193 cells[i].className = ''; // "x-date-active";
15194 setCellClass(this, cells[i]);
15198 for(; i < 42; i++) {
15199 textEls[i].innerHTML = (++extraDays);
15200 d.setDate(d.getDate()+1);
15202 cells[i].className = "fc-future fc-other-month";
15203 setCellClass(this, cells[i]);
15206 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15208 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15210 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15211 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15213 if(totalRows != 6){
15214 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15215 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15218 this.fireEvent('monthchange', this, date);
15222 if(!this.internalRender){
15223 var main = this.el.dom.firstChild;
15224 var w = main.offsetWidth;
15225 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15226 Roo.fly(main).setWidth(w);
15227 this.internalRender = true;
15228 // opera does not respect the auto grow header center column
15229 // then, after it gets a width opera refuses to recalculate
15230 // without a second pass
15231 if(Roo.isOpera && !this.secondPass){
15232 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15233 this.secondPass = true;
15234 this.update.defer(10, this, [date]);
15241 findCell : function(dt) {
15242 dt = dt.clearTime().getTime();
15244 this.cells.each(function(c){
15245 //Roo.log("check " +c.dateValue + '?=' + dt);
15246 if(c.dateValue == dt){
15256 findCells : function(ev) {
15257 var s = ev.start.clone().clearTime().getTime();
15259 var e= ev.end.clone().clearTime().getTime();
15262 this.cells.each(function(c){
15263 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15265 if(c.dateValue > e){
15268 if(c.dateValue < s){
15277 // findBestRow: function(cells)
15281 // for (var i =0 ; i < cells.length;i++) {
15282 // ret = Math.max(cells[i].rows || 0,ret);
15289 addItem : function(ev)
15291 // look for vertical location slot in
15292 var cells = this.findCells(ev);
15294 // ev.row = this.findBestRow(cells);
15296 // work out the location.
15300 for(var i =0; i < cells.length; i++) {
15302 cells[i].row = cells[0].row;
15305 cells[i].row = cells[i].row + 1;
15315 if (crow.start.getY() == cells[i].getY()) {
15317 crow.end = cells[i];
15334 cells[0].events.push(ev);
15336 this.calevents.push(ev);
15339 clearEvents: function() {
15341 if(!this.calevents){
15345 Roo.each(this.cells.elements, function(c){
15351 Roo.each(this.calevents, function(e) {
15352 Roo.each(e.els, function(el) {
15353 el.un('mouseenter' ,this.onEventEnter, this);
15354 el.un('mouseleave' ,this.onEventLeave, this);
15359 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15365 renderEvents: function()
15369 this.cells.each(function(c) {
15378 if(c.row != c.events.length){
15379 r = 4 - (4 - (c.row - c.events.length));
15382 c.events = ev.slice(0, r);
15383 c.more = ev.slice(r);
15385 if(c.more.length && c.more.length == 1){
15386 c.events.push(c.more.pop());
15389 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15393 this.cells.each(function(c) {
15395 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15398 for (var e = 0; e < c.events.length; e++){
15399 var ev = c.events[e];
15400 var rows = ev.rows;
15402 for(var i = 0; i < rows.length; i++) {
15404 // how many rows should it span..
15407 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15408 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15410 unselectable : "on",
15413 cls: 'fc-event-inner',
15417 // cls: 'fc-event-time',
15418 // html : cells.length > 1 ? '' : ev.time
15422 cls: 'fc-event-title',
15423 html : String.format('{0}', ev.title)
15430 cls: 'ui-resizable-handle ui-resizable-e',
15431 html : '  '
15438 cfg.cls += ' fc-event-start';
15440 if ((i+1) == rows.length) {
15441 cfg.cls += ' fc-event-end';
15444 var ctr = _this.el.select('.fc-event-container',true).first();
15445 var cg = ctr.createChild(cfg);
15447 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15448 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15450 var r = (c.more.length) ? 1 : 0;
15451 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15452 cg.setWidth(ebox.right - sbox.x -2);
15454 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15455 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15456 cg.on('click', _this.onEventClick, _this, ev);
15467 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15468 style : 'position: absolute',
15469 unselectable : "on",
15472 cls: 'fc-event-inner',
15476 cls: 'fc-event-title',
15484 cls: 'ui-resizable-handle ui-resizable-e',
15485 html : '  '
15491 var ctr = _this.el.select('.fc-event-container',true).first();
15492 var cg = ctr.createChild(cfg);
15494 var sbox = c.select('.fc-day-content',true).first().getBox();
15495 var ebox = c.select('.fc-day-content',true).first().getBox();
15497 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15498 cg.setWidth(ebox.right - sbox.x -2);
15500 cg.on('click', _this.onMoreEventClick, _this, c.more);
15510 onEventEnter: function (e, el,event,d) {
15511 this.fireEvent('evententer', this, el, event);
15514 onEventLeave: function (e, el,event,d) {
15515 this.fireEvent('eventleave', this, el, event);
15518 onEventClick: function (e, el,event,d) {
15519 this.fireEvent('eventclick', this, el, event);
15522 onMonthChange: function () {
15526 onMoreEventClick: function(e, el, more)
15530 this.calpopover.placement = 'right';
15531 this.calpopover.setTitle('More');
15533 this.calpopover.setContent('');
15535 var ctr = this.calpopover.el.select('.popover-content', true).first();
15537 Roo.each(more, function(m){
15539 cls : 'fc-event-hori fc-event-draggable',
15542 var cg = ctr.createChild(cfg);
15544 cg.on('click', _this.onEventClick, _this, m);
15547 this.calpopover.show(el);
15552 onLoad: function ()
15554 this.calevents = [];
15557 if(this.store.getCount() > 0){
15558 this.store.data.each(function(d){
15561 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15562 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15563 time : d.data.start_time,
15564 title : d.data.title,
15565 description : d.data.description,
15566 venue : d.data.venue
15571 this.renderEvents();
15573 if(this.calevents.length && this.loadMask){
15574 this.maskEl.hide();
15578 onBeforeLoad: function()
15580 this.clearEvents();
15582 this.maskEl.show();
15596 * @class Roo.bootstrap.Popover
15597 * @extends Roo.bootstrap.Component
15598 * Bootstrap Popover class
15599 * @cfg {String} html contents of the popover (or false to use children..)
15600 * @cfg {String} title of popover (or false to hide)
15601 * @cfg {String} placement how it is placed
15602 * @cfg {String} trigger click || hover (or false to trigger manually)
15603 * @cfg {String} over what (parent or false to trigger manually.)
15604 * @cfg {Number} delay - delay before showing
15607 * Create a new Popover
15608 * @param {Object} config The config object
15611 Roo.bootstrap.Popover = function(config){
15612 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15615 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15617 title: 'Fill in a title',
15620 placement : 'right',
15621 trigger : 'hover', // hover
15627 can_build_overlaid : false,
15629 getChildContainer : function()
15631 return this.el.select('.popover-content',true).first();
15634 getAutoCreate : function(){
15635 Roo.log('make popover?');
15637 cls : 'popover roo-dynamic',
15638 style: 'display:block',
15644 cls : 'popover-inner',
15648 cls: 'popover-title',
15652 cls : 'popover-content',
15663 setTitle: function(str)
15666 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15668 setContent: function(str)
15671 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15673 // as it get's added to the bottom of the page.
15674 onRender : function(ct, position)
15676 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15678 var cfg = Roo.apply({}, this.getAutoCreate());
15682 cfg.cls += ' ' + this.cls;
15685 cfg.style = this.style;
15687 //Roo.log("adding to ");
15688 this.el = Roo.get(document.body).createChild(cfg, position);
15694 initEvents : function()
15696 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15697 this.el.enableDisplayMode('block');
15699 if (this.over === false) {
15702 if (this.triggers === false) {
15705 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15706 var triggers = this.trigger ? this.trigger.split(' ') : [];
15707 Roo.each(triggers, function(trigger) {
15709 if (trigger == 'click') {
15710 on_el.on('click', this.toggle, this);
15711 } else if (trigger != 'manual') {
15712 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15713 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15715 on_el.on(eventIn ,this.enter, this);
15716 on_el.on(eventOut, this.leave, this);
15727 toggle : function () {
15728 this.hoverState == 'in' ? this.leave() : this.enter();
15731 enter : function () {
15734 clearTimeout(this.timeout);
15736 this.hoverState = 'in';
15738 if (!this.delay || !this.delay.show) {
15743 this.timeout = setTimeout(function () {
15744 if (_t.hoverState == 'in') {
15747 }, this.delay.show)
15749 leave : function() {
15750 clearTimeout(this.timeout);
15752 this.hoverState = 'out';
15754 if (!this.delay || !this.delay.hide) {
15759 this.timeout = setTimeout(function () {
15760 if (_t.hoverState == 'out') {
15763 }, this.delay.hide)
15766 show : function (on_el)
15769 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15772 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15773 if (this.html !== false) {
15774 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15776 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15777 if (!this.title.length) {
15778 this.el.select('.popover-title',true).hide();
15781 var placement = typeof this.placement == 'function' ?
15782 this.placement.call(this, this.el, on_el) :
15785 var autoToken = /\s?auto?\s?/i;
15786 var autoPlace = autoToken.test(placement);
15788 placement = placement.replace(autoToken, '') || 'top';
15792 //this.el.setXY([0,0]);
15794 this.el.dom.style.display='block';
15795 this.el.addClass(placement);
15797 //this.el.appendTo(on_el);
15799 var p = this.getPosition();
15800 var box = this.el.getBox();
15805 var align = Roo.bootstrap.Popover.alignment[placement];
15806 this.el.alignTo(on_el, align[0],align[1]);
15807 //var arrow = this.el.select('.arrow',true).first();
15808 //arrow.set(align[2],
15810 this.el.addClass('in');
15813 if (this.el.hasClass('fade')) {
15820 this.el.setXY([0,0]);
15821 this.el.removeClass('in');
15823 this.hoverState = null;
15829 Roo.bootstrap.Popover.alignment = {
15830 'left' : ['r-l', [-10,0], 'right'],
15831 'right' : ['l-r', [10,0], 'left'],
15832 'bottom' : ['t-b', [0,10], 'top'],
15833 'top' : [ 'b-t', [0,-10], 'bottom']
15844 * @class Roo.bootstrap.Progress
15845 * @extends Roo.bootstrap.Component
15846 * Bootstrap Progress class
15847 * @cfg {Boolean} striped striped of the progress bar
15848 * @cfg {Boolean} active animated of the progress bar
15852 * Create a new Progress
15853 * @param {Object} config The config object
15856 Roo.bootstrap.Progress = function(config){
15857 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15860 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15865 getAutoCreate : function(){
15873 cfg.cls += ' progress-striped';
15877 cfg.cls += ' active';
15896 * @class Roo.bootstrap.ProgressBar
15897 * @extends Roo.bootstrap.Component
15898 * Bootstrap ProgressBar class
15899 * @cfg {Number} aria_valuenow aria-value now
15900 * @cfg {Number} aria_valuemin aria-value min
15901 * @cfg {Number} aria_valuemax aria-value max
15902 * @cfg {String} label label for the progress bar
15903 * @cfg {String} panel (success | info | warning | danger )
15904 * @cfg {String} role role of the progress bar
15905 * @cfg {String} sr_only text
15909 * Create a new ProgressBar
15910 * @param {Object} config The config object
15913 Roo.bootstrap.ProgressBar = function(config){
15914 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15917 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15921 aria_valuemax : 100,
15927 getAutoCreate : function()
15932 cls: 'progress-bar',
15933 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15945 cfg.role = this.role;
15948 if(this.aria_valuenow){
15949 cfg['aria-valuenow'] = this.aria_valuenow;
15952 if(this.aria_valuemin){
15953 cfg['aria-valuemin'] = this.aria_valuemin;
15956 if(this.aria_valuemax){
15957 cfg['aria-valuemax'] = this.aria_valuemax;
15960 if(this.label && !this.sr_only){
15961 cfg.html = this.label;
15965 cfg.cls += ' progress-bar-' + this.panel;
15971 update : function(aria_valuenow)
15973 this.aria_valuenow = aria_valuenow;
15975 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15990 * @class Roo.bootstrap.TabGroup
15991 * @extends Roo.bootstrap.Column
15992 * Bootstrap Column class
15993 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15994 * @cfg {Boolean} carousel true to make the group behave like a carousel
15995 * @cfg {Boolean} bullets show bullets for the panels
15996 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15997 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15998 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16001 * Create a new TabGroup
16002 * @param {Object} config The config object
16005 Roo.bootstrap.TabGroup = function(config){
16006 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16008 this.navId = Roo.id();
16011 Roo.bootstrap.TabGroup.register(this);
16015 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16018 transition : false,
16023 slideOnTouch : false,
16025 getAutoCreate : function()
16027 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16029 cfg.cls += ' tab-content';
16031 Roo.log('get auto create...............');
16033 if (this.carousel) {
16034 cfg.cls += ' carousel slide';
16037 cls : 'carousel-inner'
16040 if(this.bullets && !Roo.isTouch){
16043 cls : 'carousel-bullets',
16047 if(this.bullets_cls){
16048 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16051 for (var i = 0; i < this.bullets; i++){
16053 cls : 'bullet bullet-' + i
16061 cfg.cn[0].cn = bullets;
16068 initEvents: function()
16070 Roo.log('-------- init events on tab group ---------');
16076 if(Roo.isTouch && this.slideOnTouch){
16077 this.el.on("touchstart", this.onTouchStart, this);
16080 if(this.autoslide){
16083 this.slideFn = window.setInterval(function() {
16084 _this.showPanelNext();
16090 onTouchStart : function(e, el, o)
16092 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16096 this.showPanelNext();
16099 getChildContainer : function()
16101 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16105 * register a Navigation item
16106 * @param {Roo.bootstrap.NavItem} the navitem to add
16108 register : function(item)
16110 this.tabs.push( item);
16111 item.navId = this.navId; // not really needed..
16116 getActivePanel : function()
16119 Roo.each(this.tabs, function(t) {
16129 getPanelByName : function(n)
16132 Roo.each(this.tabs, function(t) {
16133 if (t.tabId == n) {
16141 indexOfPanel : function(p)
16144 Roo.each(this.tabs, function(t,i) {
16145 if (t.tabId == p.tabId) {
16154 * show a specific panel
16155 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16156 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16158 showPanel : function (pan)
16160 if(this.transition){
16161 Roo.log("waiting for the transitionend");
16165 if (typeof(pan) == 'number') {
16166 pan = this.tabs[pan];
16168 if (typeof(pan) == 'string') {
16169 pan = this.getPanelByName(pan);
16171 if (pan.tabId == this.getActivePanel().tabId) {
16174 var cur = this.getActivePanel();
16176 if (false === cur.fireEvent('beforedeactivate')) {
16180 if(this.bullets > 0 && !Roo.isTouch){
16181 this.setActiveBullet(this.indexOfPanel(pan));
16184 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16186 this.transition = true;
16187 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16188 var lr = dir == 'next' ? 'left' : 'right';
16189 pan.el.addClass(dir); // or prev
16190 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16191 cur.el.addClass(lr); // or right
16192 pan.el.addClass(lr);
16195 cur.el.on('transitionend', function() {
16196 Roo.log("trans end?");
16198 pan.el.removeClass([lr,dir]);
16199 pan.setActive(true);
16201 cur.el.removeClass([lr]);
16202 cur.setActive(false);
16204 _this.transition = false;
16206 }, this, { single: true } );
16211 cur.setActive(false);
16212 pan.setActive(true);
16217 showPanelNext : function()
16219 var i = this.indexOfPanel(this.getActivePanel());
16221 if (i >= this.tabs.length - 1 && !this.autoslide) {
16225 if (i >= this.tabs.length - 1 && this.autoslide) {
16229 this.showPanel(this.tabs[i+1]);
16232 showPanelPrev : function()
16234 var i = this.indexOfPanel(this.getActivePanel());
16236 if (i < 1 && !this.autoslide) {
16240 if (i < 1 && this.autoslide) {
16241 i = this.tabs.length;
16244 this.showPanel(this.tabs[i-1]);
16248 addBullet: function()
16250 if(!this.bullets || Roo.isTouch){
16253 var ctr = this.el.select('.carousel-bullets',true).first();
16254 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16255 var bullet = ctr.createChild({
16256 cls : 'bullet bullet-' + i
16257 },ctr.dom.lastChild);
16262 bullet.on('click', (function(e, el, o, ii, t){
16264 e.preventDefault();
16266 this.showPanel(ii);
16268 if(this.autoslide && this.slideFn){
16269 clearInterval(this.slideFn);
16270 this.slideFn = window.setInterval(function() {
16271 _this.showPanelNext();
16275 }).createDelegate(this, [i, bullet], true));
16280 setActiveBullet : function(i)
16286 Roo.each(this.el.select('.bullet', true).elements, function(el){
16287 el.removeClass('selected');
16290 var bullet = this.el.select('.bullet-' + i, true).first();
16296 bullet.addClass('selected');
16307 Roo.apply(Roo.bootstrap.TabGroup, {
16311 * register a Navigation Group
16312 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16314 register : function(navgrp)
16316 this.groups[navgrp.navId] = navgrp;
16320 * fetch a Navigation Group based on the navigation ID
16321 * if one does not exist , it will get created.
16322 * @param {string} the navgroup to add
16323 * @returns {Roo.bootstrap.NavGroup} the navgroup
16325 get: function(navId) {
16326 if (typeof(this.groups[navId]) == 'undefined') {
16327 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16329 return this.groups[navId] ;
16344 * @class Roo.bootstrap.TabPanel
16345 * @extends Roo.bootstrap.Component
16346 * Bootstrap TabPanel class
16347 * @cfg {Boolean} active panel active
16348 * @cfg {String} html panel content
16349 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16350 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16354 * Create a new TabPanel
16355 * @param {Object} config The config object
16358 Roo.bootstrap.TabPanel = function(config){
16359 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16363 * Fires when the active status changes
16364 * @param {Roo.bootstrap.TabPanel} this
16365 * @param {Boolean} state the new state
16370 * @event beforedeactivate
16371 * Fires before a tab is de-activated - can be used to do validation on a form.
16372 * @param {Roo.bootstrap.TabPanel} this
16373 * @return {Boolean} false if there is an error
16376 'beforedeactivate': true
16379 this.tabId = this.tabId || Roo.id();
16383 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16390 getAutoCreate : function(){
16393 // item is needed for carousel - not sure if it has any effect otherwise
16394 cls: 'tab-pane item',
16395 html: this.html || ''
16399 cfg.cls += ' active';
16403 cfg.tabId = this.tabId;
16410 initEvents: function()
16412 Roo.log('-------- init events on tab panel ---------');
16414 var p = this.parent();
16415 this.navId = this.navId || p.navId;
16417 if (typeof(this.navId) != 'undefined') {
16418 // not really needed.. but just in case.. parent should be a NavGroup.
16419 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16420 Roo.log(['register', tg, this]);
16423 var i = tg.tabs.length - 1;
16425 if(this.active && tg.bullets > 0 && i < tg.bullets){
16426 tg.setActiveBullet(i);
16433 onRender : function(ct, position)
16435 // Roo.log("Call onRender: " + this.xtype);
16437 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16445 setActive: function(state)
16447 Roo.log("panel - set active " + this.tabId + "=" + state);
16449 this.active = state;
16451 this.el.removeClass('active');
16453 } else if (!this.el.hasClass('active')) {
16454 this.el.addClass('active');
16457 this.fireEvent('changed', this, state);
16474 * @class Roo.bootstrap.DateField
16475 * @extends Roo.bootstrap.Input
16476 * Bootstrap DateField class
16477 * @cfg {Number} weekStart default 0
16478 * @cfg {String} viewMode default empty, (months|years)
16479 * @cfg {String} minViewMode default empty, (months|years)
16480 * @cfg {Number} startDate default -Infinity
16481 * @cfg {Number} endDate default Infinity
16482 * @cfg {Boolean} todayHighlight default false
16483 * @cfg {Boolean} todayBtn default false
16484 * @cfg {Boolean} calendarWeeks default false
16485 * @cfg {Object} daysOfWeekDisabled default empty
16486 * @cfg {Boolean} singleMode default false (true | false)
16488 * @cfg {Boolean} keyboardNavigation default true
16489 * @cfg {String} language default en
16492 * Create a new DateField
16493 * @param {Object} config The config object
16496 Roo.bootstrap.DateField = function(config){
16497 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16501 * Fires when this field show.
16502 * @param {Roo.bootstrap.DateField} this
16503 * @param {Mixed} date The date value
16508 * Fires when this field hide.
16509 * @param {Roo.bootstrap.DateField} this
16510 * @param {Mixed} date The date value
16515 * Fires when select a date.
16516 * @param {Roo.bootstrap.DateField} this
16517 * @param {Mixed} date The date value
16521 * @event beforeselect
16522 * Fires when before select a date.
16523 * @param {Roo.bootstrap.DateField} this
16524 * @param {Mixed} date The date value
16526 beforeselect : true
16530 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16533 * @cfg {String} format
16534 * The default date format string which can be overriden for localization support. The format must be
16535 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16539 * @cfg {String} altFormats
16540 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16541 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16543 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16551 todayHighlight : false,
16557 keyboardNavigation: true,
16559 calendarWeeks: false,
16561 startDate: -Infinity,
16565 daysOfWeekDisabled: [],
16569 singleMode : false,
16571 UTCDate: function()
16573 return new Date(Date.UTC.apply(Date, arguments));
16576 UTCToday: function()
16578 var today = new Date();
16579 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16582 getDate: function() {
16583 var d = this.getUTCDate();
16584 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16587 getUTCDate: function() {
16591 setDate: function(d) {
16592 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16595 setUTCDate: function(d) {
16597 this.setValue(this.formatDate(this.date));
16600 onRender: function(ct, position)
16603 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16605 this.language = this.language || 'en';
16606 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16607 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16609 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16610 this.format = this.format || 'm/d/y';
16611 this.isInline = false;
16612 this.isInput = true;
16613 this.component = this.el.select('.add-on', true).first() || false;
16614 this.component = (this.component && this.component.length === 0) ? false : this.component;
16615 this.hasInput = this.component && this.inputEL().length;
16617 if (typeof(this.minViewMode === 'string')) {
16618 switch (this.minViewMode) {
16620 this.minViewMode = 1;
16623 this.minViewMode = 2;
16626 this.minViewMode = 0;
16631 if (typeof(this.viewMode === 'string')) {
16632 switch (this.viewMode) {
16645 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16647 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16649 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16651 this.picker().on('mousedown', this.onMousedown, this);
16652 this.picker().on('click', this.onClick, this);
16654 this.picker().addClass('datepicker-dropdown');
16656 this.startViewMode = this.viewMode;
16658 if(this.singleMode){
16659 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16660 v.setVisibilityMode(Roo.Element.DISPLAY);
16664 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16665 v.setStyle('width', '189px');
16669 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16670 if(!this.calendarWeeks){
16675 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16676 v.attr('colspan', function(i, val){
16677 return parseInt(val) + 1;
16682 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16684 this.setStartDate(this.startDate);
16685 this.setEndDate(this.endDate);
16687 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16694 if(this.isInline) {
16699 picker : function()
16701 return this.pickerEl;
16702 // return this.el.select('.datepicker', true).first();
16705 fillDow: function()
16707 var dowCnt = this.weekStart;
16716 if(this.calendarWeeks){
16724 while (dowCnt < this.weekStart + 7) {
16728 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16732 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16735 fillMonths: function()
16738 var months = this.picker().select('>.datepicker-months td', true).first();
16740 months.dom.innerHTML = '';
16746 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16749 months.createChild(month);
16756 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;
16758 if (this.date < this.startDate) {
16759 this.viewDate = new Date(this.startDate);
16760 } else if (this.date > this.endDate) {
16761 this.viewDate = new Date(this.endDate);
16763 this.viewDate = new Date(this.date);
16771 var d = new Date(this.viewDate),
16772 year = d.getUTCFullYear(),
16773 month = d.getUTCMonth(),
16774 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16775 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16776 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16777 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16778 currentDate = this.date && this.date.valueOf(),
16779 today = this.UTCToday();
16781 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16783 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16785 // this.picker.select('>tfoot th.today').
16786 // .text(dates[this.language].today)
16787 // .toggle(this.todayBtn !== false);
16789 this.updateNavArrows();
16792 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16794 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16796 prevMonth.setUTCDate(day);
16798 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16800 var nextMonth = new Date(prevMonth);
16802 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16804 nextMonth = nextMonth.valueOf();
16806 var fillMonths = false;
16808 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16810 while(prevMonth.valueOf() < nextMonth) {
16813 if (prevMonth.getUTCDay() === this.weekStart) {
16815 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16823 if(this.calendarWeeks){
16824 // ISO 8601: First week contains first thursday.
16825 // ISO also states week starts on Monday, but we can be more abstract here.
16827 // Start of current week: based on weekstart/current date
16828 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16829 // Thursday of this week
16830 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16831 // First Thursday of year, year from thursday
16832 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16833 // Calendar week: ms between thursdays, div ms per day, div 7 days
16834 calWeek = (th - yth) / 864e5 / 7 + 1;
16836 fillMonths.cn.push({
16844 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16846 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16849 if (this.todayHighlight &&
16850 prevMonth.getUTCFullYear() == today.getFullYear() &&
16851 prevMonth.getUTCMonth() == today.getMonth() &&
16852 prevMonth.getUTCDate() == today.getDate()) {
16853 clsName += ' today';
16856 if (currentDate && prevMonth.valueOf() === currentDate) {
16857 clsName += ' active';
16860 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16861 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16862 clsName += ' disabled';
16865 fillMonths.cn.push({
16867 cls: 'day ' + clsName,
16868 html: prevMonth.getDate()
16871 prevMonth.setDate(prevMonth.getDate()+1);
16874 var currentYear = this.date && this.date.getUTCFullYear();
16875 var currentMonth = this.date && this.date.getUTCMonth();
16877 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16879 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16880 v.removeClass('active');
16882 if(currentYear === year && k === currentMonth){
16883 v.addClass('active');
16886 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16887 v.addClass('disabled');
16893 year = parseInt(year/10, 10) * 10;
16895 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16897 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16900 for (var i = -1; i < 11; i++) {
16901 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16903 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16911 showMode: function(dir)
16914 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16917 Roo.each(this.picker().select('>div',true).elements, function(v){
16918 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16921 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16926 if(this.isInline) {
16930 this.picker().removeClass(['bottom', 'top']);
16932 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16934 * place to the top of element!
16938 this.picker().addClass('top');
16939 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16944 this.picker().addClass('bottom');
16946 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16949 parseDate : function(value)
16951 if(!value || value instanceof Date){
16954 var v = Date.parseDate(value, this.format);
16955 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16956 v = Date.parseDate(value, 'Y-m-d');
16958 if(!v && this.altFormats){
16959 if(!this.altFormatsArray){
16960 this.altFormatsArray = this.altFormats.split("|");
16962 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16963 v = Date.parseDate(value, this.altFormatsArray[i]);
16969 formatDate : function(date, fmt)
16971 return (!date || !(date instanceof Date)) ?
16972 date : date.dateFormat(fmt || this.format);
16975 onFocus : function()
16977 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16981 onBlur : function()
16983 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16985 var d = this.inputEl().getValue();
16994 this.picker().show();
16998 this.fireEvent('show', this, this.date);
17003 if(this.isInline) {
17006 this.picker().hide();
17007 this.viewMode = this.startViewMode;
17010 this.fireEvent('hide', this, this.date);
17014 onMousedown: function(e)
17016 e.stopPropagation();
17017 e.preventDefault();
17022 Roo.bootstrap.DateField.superclass.keyup.call(this);
17026 setValue: function(v)
17028 if(this.fireEvent('beforeselect', this, v) !== false){
17029 var d = new Date(this.parseDate(v) ).clearTime();
17031 if(isNaN(d.getTime())){
17032 this.date = this.viewDate = '';
17033 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17037 v = this.formatDate(d);
17039 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17041 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17045 this.fireEvent('select', this, this.date);
17049 getValue: function()
17051 return this.formatDate(this.date);
17054 fireKey: function(e)
17056 if (!this.picker().isVisible()){
17057 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17063 var dateChanged = false,
17065 newDate, newViewDate;
17070 e.preventDefault();
17074 if (!this.keyboardNavigation) {
17077 dir = e.keyCode == 37 ? -1 : 1;
17080 newDate = this.moveYear(this.date, dir);
17081 newViewDate = this.moveYear(this.viewDate, dir);
17082 } else if (e.shiftKey){
17083 newDate = this.moveMonth(this.date, dir);
17084 newViewDate = this.moveMonth(this.viewDate, dir);
17086 newDate = new Date(this.date);
17087 newDate.setUTCDate(this.date.getUTCDate() + dir);
17088 newViewDate = new Date(this.viewDate);
17089 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17091 if (this.dateWithinRange(newDate)){
17092 this.date = newDate;
17093 this.viewDate = newViewDate;
17094 this.setValue(this.formatDate(this.date));
17096 e.preventDefault();
17097 dateChanged = true;
17102 if (!this.keyboardNavigation) {
17105 dir = e.keyCode == 38 ? -1 : 1;
17107 newDate = this.moveYear(this.date, dir);
17108 newViewDate = this.moveYear(this.viewDate, dir);
17109 } else if (e.shiftKey){
17110 newDate = this.moveMonth(this.date, dir);
17111 newViewDate = this.moveMonth(this.viewDate, dir);
17113 newDate = new Date(this.date);
17114 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17115 newViewDate = new Date(this.viewDate);
17116 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17118 if (this.dateWithinRange(newDate)){
17119 this.date = newDate;
17120 this.viewDate = newViewDate;
17121 this.setValue(this.formatDate(this.date));
17123 e.preventDefault();
17124 dateChanged = true;
17128 this.setValue(this.formatDate(this.date));
17130 e.preventDefault();
17133 this.setValue(this.formatDate(this.date));
17147 onClick: function(e)
17149 e.stopPropagation();
17150 e.preventDefault();
17152 var target = e.getTarget();
17154 if(target.nodeName.toLowerCase() === 'i'){
17155 target = Roo.get(target).dom.parentNode;
17158 var nodeName = target.nodeName;
17159 var className = target.className;
17160 var html = target.innerHTML;
17161 //Roo.log(nodeName);
17163 switch(nodeName.toLowerCase()) {
17165 switch(className) {
17171 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17172 switch(this.viewMode){
17174 this.viewDate = this.moveMonth(this.viewDate, dir);
17178 this.viewDate = this.moveYear(this.viewDate, dir);
17184 var date = new Date();
17185 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17187 this.setValue(this.formatDate(this.date));
17194 if (className.indexOf('disabled') < 0) {
17195 this.viewDate.setUTCDate(1);
17196 if (className.indexOf('month') > -1) {
17197 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17199 var year = parseInt(html, 10) || 0;
17200 this.viewDate.setUTCFullYear(year);
17204 if(this.singleMode){
17205 this.setValue(this.formatDate(this.viewDate));
17216 //Roo.log(className);
17217 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17218 var day = parseInt(html, 10) || 1;
17219 var year = this.viewDate.getUTCFullYear(),
17220 month = this.viewDate.getUTCMonth();
17222 if (className.indexOf('old') > -1) {
17229 } else if (className.indexOf('new') > -1) {
17237 //Roo.log([year,month,day]);
17238 this.date = this.UTCDate(year, month, day,0,0,0,0);
17239 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17241 //Roo.log(this.formatDate(this.date));
17242 this.setValue(this.formatDate(this.date));
17249 setStartDate: function(startDate)
17251 this.startDate = startDate || -Infinity;
17252 if (this.startDate !== -Infinity) {
17253 this.startDate = this.parseDate(this.startDate);
17256 this.updateNavArrows();
17259 setEndDate: function(endDate)
17261 this.endDate = endDate || Infinity;
17262 if (this.endDate !== Infinity) {
17263 this.endDate = this.parseDate(this.endDate);
17266 this.updateNavArrows();
17269 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17271 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17272 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17273 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17275 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17276 return parseInt(d, 10);
17279 this.updateNavArrows();
17282 updateNavArrows: function()
17284 if(this.singleMode){
17288 var d = new Date(this.viewDate),
17289 year = d.getUTCFullYear(),
17290 month = d.getUTCMonth();
17292 Roo.each(this.picker().select('.prev', true).elements, function(v){
17294 switch (this.viewMode) {
17297 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17303 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17310 Roo.each(this.picker().select('.next', true).elements, function(v){
17312 switch (this.viewMode) {
17315 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17321 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17329 moveMonth: function(date, dir)
17334 var new_date = new Date(date.valueOf()),
17335 day = new_date.getUTCDate(),
17336 month = new_date.getUTCMonth(),
17337 mag = Math.abs(dir),
17339 dir = dir > 0 ? 1 : -1;
17342 // If going back one month, make sure month is not current month
17343 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17345 return new_date.getUTCMonth() == month;
17347 // If going forward one month, make sure month is as expected
17348 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17350 return new_date.getUTCMonth() != new_month;
17352 new_month = month + dir;
17353 new_date.setUTCMonth(new_month);
17354 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17355 if (new_month < 0 || new_month > 11) {
17356 new_month = (new_month + 12) % 12;
17359 // For magnitudes >1, move one month at a time...
17360 for (var i=0; i<mag; i++) {
17361 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17362 new_date = this.moveMonth(new_date, dir);
17364 // ...then reset the day, keeping it in the new month
17365 new_month = new_date.getUTCMonth();
17366 new_date.setUTCDate(day);
17368 return new_month != new_date.getUTCMonth();
17371 // Common date-resetting loop -- if date is beyond end of month, make it
17374 new_date.setUTCDate(--day);
17375 new_date.setUTCMonth(new_month);
17380 moveYear: function(date, dir)
17382 return this.moveMonth(date, dir*12);
17385 dateWithinRange: function(date)
17387 return date >= this.startDate && date <= this.endDate;
17393 this.picker().remove();
17398 Roo.apply(Roo.bootstrap.DateField, {
17409 html: '<i class="fa fa-arrow-left"/>'
17419 html: '<i class="fa fa-arrow-right"/>'
17461 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17462 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17463 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17464 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17465 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17478 navFnc: 'FullYear',
17483 navFnc: 'FullYear',
17488 Roo.apply(Roo.bootstrap.DateField, {
17492 cls: 'datepicker dropdown-menu roo-dynamic',
17496 cls: 'datepicker-days',
17500 cls: 'table-condensed',
17502 Roo.bootstrap.DateField.head,
17506 Roo.bootstrap.DateField.footer
17513 cls: 'datepicker-months',
17517 cls: 'table-condensed',
17519 Roo.bootstrap.DateField.head,
17520 Roo.bootstrap.DateField.content,
17521 Roo.bootstrap.DateField.footer
17528 cls: 'datepicker-years',
17532 cls: 'table-condensed',
17534 Roo.bootstrap.DateField.head,
17535 Roo.bootstrap.DateField.content,
17536 Roo.bootstrap.DateField.footer
17555 * @class Roo.bootstrap.TimeField
17556 * @extends Roo.bootstrap.Input
17557 * Bootstrap DateField class
17561 * Create a new TimeField
17562 * @param {Object} config The config object
17565 Roo.bootstrap.TimeField = function(config){
17566 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17570 * Fires when this field show.
17571 * @param {Roo.bootstrap.DateField} thisthis
17572 * @param {Mixed} date The date value
17577 * Fires when this field hide.
17578 * @param {Roo.bootstrap.DateField} this
17579 * @param {Mixed} date The date value
17584 * Fires when select a date.
17585 * @param {Roo.bootstrap.DateField} this
17586 * @param {Mixed} date The date value
17592 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17595 * @cfg {String} format
17596 * The default time format string which can be overriden for localization support. The format must be
17597 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17601 onRender: function(ct, position)
17604 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17606 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17608 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17610 this.pop = this.picker().select('>.datepicker-time',true).first();
17611 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17613 this.picker().on('mousedown', this.onMousedown, this);
17614 this.picker().on('click', this.onClick, this);
17616 this.picker().addClass('datepicker-dropdown');
17621 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17622 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17623 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17624 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17625 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17626 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17630 fireKey: function(e){
17631 if (!this.picker().isVisible()){
17632 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17638 e.preventDefault();
17646 this.onTogglePeriod();
17649 this.onIncrementMinutes();
17652 this.onDecrementMinutes();
17661 onClick: function(e) {
17662 e.stopPropagation();
17663 e.preventDefault();
17666 picker : function()
17668 return this.el.select('.datepicker', true).first();
17671 fillTime: function()
17673 var time = this.pop.select('tbody', true).first();
17675 time.dom.innerHTML = '';
17690 cls: 'hours-up glyphicon glyphicon-chevron-up'
17710 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17731 cls: 'timepicker-hour',
17746 cls: 'timepicker-minute',
17761 cls: 'btn btn-primary period',
17783 cls: 'hours-down glyphicon glyphicon-chevron-down'
17803 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17821 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17828 var hours = this.time.getHours();
17829 var minutes = this.time.getMinutes();
17842 hours = hours - 12;
17846 hours = '0' + hours;
17850 minutes = '0' + minutes;
17853 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17854 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17855 this.pop.select('button', true).first().dom.innerHTML = period;
17861 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17863 var cls = ['bottom'];
17865 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17872 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17877 this.picker().addClass(cls.join('-'));
17881 Roo.each(cls, function(c){
17883 _this.picker().setTop(_this.inputEl().getHeight());
17887 _this.picker().setTop(0 - _this.picker().getHeight());
17892 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17896 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17903 onFocus : function()
17905 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17909 onBlur : function()
17911 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17917 this.picker().show();
17922 this.fireEvent('show', this, this.date);
17927 this.picker().hide();
17930 this.fireEvent('hide', this, this.date);
17933 setTime : function()
17936 this.setValue(this.time.format(this.format));
17938 this.fireEvent('select', this, this.date);
17943 onMousedown: function(e){
17944 e.stopPropagation();
17945 e.preventDefault();
17948 onIncrementHours: function()
17950 Roo.log('onIncrementHours');
17951 this.time = this.time.add(Date.HOUR, 1);
17956 onDecrementHours: function()
17958 Roo.log('onDecrementHours');
17959 this.time = this.time.add(Date.HOUR, -1);
17963 onIncrementMinutes: function()
17965 Roo.log('onIncrementMinutes');
17966 this.time = this.time.add(Date.MINUTE, 1);
17970 onDecrementMinutes: function()
17972 Roo.log('onDecrementMinutes');
17973 this.time = this.time.add(Date.MINUTE, -1);
17977 onTogglePeriod: function()
17979 Roo.log('onTogglePeriod');
17980 this.time = this.time.add(Date.HOUR, 12);
17987 Roo.apply(Roo.bootstrap.TimeField, {
18017 cls: 'btn btn-info ok',
18029 Roo.apply(Roo.bootstrap.TimeField, {
18033 cls: 'datepicker dropdown-menu',
18037 cls: 'datepicker-time',
18041 cls: 'table-condensed',
18043 Roo.bootstrap.TimeField.content,
18044 Roo.bootstrap.TimeField.footer
18063 * @class Roo.bootstrap.MonthField
18064 * @extends Roo.bootstrap.Input
18065 * Bootstrap MonthField class
18067 * @cfg {String} language default en
18070 * Create a new MonthField
18071 * @param {Object} config The config object
18074 Roo.bootstrap.MonthField = function(config){
18075 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18080 * Fires when this field show.
18081 * @param {Roo.bootstrap.MonthField} this
18082 * @param {Mixed} date The date value
18087 * Fires when this field hide.
18088 * @param {Roo.bootstrap.MonthField} this
18089 * @param {Mixed} date The date value
18094 * Fires when select a date.
18095 * @param {Roo.bootstrap.MonthField} this
18096 * @param {String} oldvalue The old value
18097 * @param {String} newvalue The new value
18103 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18105 onRender: function(ct, position)
18108 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18110 this.language = this.language || 'en';
18111 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18112 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18114 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18115 this.isInline = false;
18116 this.isInput = true;
18117 this.component = this.el.select('.add-on', true).first() || false;
18118 this.component = (this.component && this.component.length === 0) ? false : this.component;
18119 this.hasInput = this.component && this.inputEL().length;
18121 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18123 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18125 this.picker().on('mousedown', this.onMousedown, this);
18126 this.picker().on('click', this.onClick, this);
18128 this.picker().addClass('datepicker-dropdown');
18130 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18131 v.setStyle('width', '189px');
18138 if(this.isInline) {
18144 setValue: function(v, suppressEvent)
18146 var o = this.getValue();
18148 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18152 if(suppressEvent !== true){
18153 this.fireEvent('select', this, o, v);
18158 getValue: function()
18163 onClick: function(e)
18165 e.stopPropagation();
18166 e.preventDefault();
18168 var target = e.getTarget();
18170 if(target.nodeName.toLowerCase() === 'i'){
18171 target = Roo.get(target).dom.parentNode;
18174 var nodeName = target.nodeName;
18175 var className = target.className;
18176 var html = target.innerHTML;
18178 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18182 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18184 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18190 picker : function()
18192 return this.pickerEl;
18195 fillMonths: function()
18198 var months = this.picker().select('>.datepicker-months td', true).first();
18200 months.dom.innerHTML = '';
18206 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18209 months.createChild(month);
18218 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18219 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18222 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18223 e.removeClass('active');
18225 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18226 e.addClass('active');
18233 if(this.isInline) {
18237 this.picker().removeClass(['bottom', 'top']);
18239 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18241 * place to the top of element!
18245 this.picker().addClass('top');
18246 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18251 this.picker().addClass('bottom');
18253 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18256 onFocus : function()
18258 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18262 onBlur : function()
18264 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18266 var d = this.inputEl().getValue();
18275 this.picker().show();
18276 this.picker().select('>.datepicker-months', true).first().show();
18280 this.fireEvent('show', this, this.date);
18285 if(this.isInline) {
18288 this.picker().hide();
18289 this.fireEvent('hide', this, this.date);
18293 onMousedown: function(e)
18295 e.stopPropagation();
18296 e.preventDefault();
18301 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18305 fireKey: function(e)
18307 if (!this.picker().isVisible()){
18308 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18319 e.preventDefault();
18323 dir = e.keyCode == 37 ? -1 : 1;
18325 this.vIndex = this.vIndex + dir;
18327 if(this.vIndex < 0){
18331 if(this.vIndex > 11){
18335 if(isNaN(this.vIndex)){
18339 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18345 dir = e.keyCode == 38 ? -1 : 1;
18347 this.vIndex = this.vIndex + dir * 4;
18349 if(this.vIndex < 0){
18353 if(this.vIndex > 11){
18357 if(isNaN(this.vIndex)){
18361 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18366 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18367 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18371 e.preventDefault();
18374 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18375 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18391 this.picker().remove();
18396 Roo.apply(Roo.bootstrap.MonthField, {
18415 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18416 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18421 Roo.apply(Roo.bootstrap.MonthField, {
18425 cls: 'datepicker dropdown-menu roo-dynamic',
18429 cls: 'datepicker-months',
18433 cls: 'table-condensed',
18435 Roo.bootstrap.DateField.content
18455 * @class Roo.bootstrap.CheckBox
18456 * @extends Roo.bootstrap.Input
18457 * Bootstrap CheckBox class
18459 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18460 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18461 * @cfg {String} boxLabel The text that appears beside the checkbox
18462 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18463 * @cfg {Boolean} checked initnal the element
18464 * @cfg {Boolean} inline inline the element (default false)
18465 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18468 * Create a new CheckBox
18469 * @param {Object} config The config object
18472 Roo.bootstrap.CheckBox = function(config){
18473 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18478 * Fires when the element is checked or unchecked.
18479 * @param {Roo.bootstrap.CheckBox} this This input
18480 * @param {Boolean} checked The new checked value
18487 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18489 inputType: 'checkbox',
18497 getAutoCreate : function()
18499 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18505 cfg.cls = 'form-group ' + this.inputType; //input-group
18508 cfg.cls += ' ' + this.inputType + '-inline';
18514 type : this.inputType,
18515 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18516 cls : 'roo-' + this.inputType, //'form-box',
18517 placeholder : this.placeholder || ''
18521 if (this.weight) { // Validity check?
18522 cfg.cls += " " + this.inputType + "-" + this.weight;
18525 if (this.disabled) {
18526 input.disabled=true;
18530 input.checked = this.checked;
18534 input.name = this.name;
18538 input.cls += ' input-' + this.size;
18543 ['xs','sm','md','lg'].map(function(size){
18544 if (settings[size]) {
18545 cfg.cls += ' col-' + size + '-' + settings[size];
18549 var inputblock = input;
18551 if (this.before || this.after) {
18554 cls : 'input-group',
18559 inputblock.cn.push({
18561 cls : 'input-group-addon',
18566 inputblock.cn.push(input);
18569 inputblock.cn.push({
18571 cls : 'input-group-addon',
18578 if (align ==='left' && this.fieldLabel.length) {
18579 Roo.log("left and has label");
18585 cls : 'control-label col-md-' + this.labelWidth,
18586 html : this.fieldLabel
18590 cls : "col-md-" + (12 - this.labelWidth),
18597 } else if ( this.fieldLabel.length) {
18602 tag: this.boxLabel ? 'span' : 'label',
18604 cls: 'control-label box-input-label',
18605 //cls : 'input-group-addon',
18606 html : this.fieldLabel
18616 Roo.log(" no label && no align");
18617 cfg.cn = [ inputblock ] ;
18622 var boxLabelCfg = {
18624 //'for': id, // box label is handled by onclick - so no for...
18626 html: this.boxLabel
18630 boxLabelCfg.tooltip = this.tooltip;
18633 cfg.cn.push(boxLabelCfg);
18643 * return the real input element.
18645 inputEl: function ()
18647 return this.el.select('input.roo-' + this.inputType,true).first();
18650 labelEl: function()
18652 return this.el.select('label.control-label',true).first();
18654 /* depricated... */
18658 return this.labelEl();
18661 initEvents : function()
18663 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18665 this.inputEl().on('click', this.onClick, this);
18667 if (this.boxLabel) {
18668 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18671 this.startValue = this.getValue();
18674 Roo.bootstrap.CheckBox.register(this);
18678 onClick : function()
18680 this.setChecked(!this.checked);
18683 setChecked : function(state,suppressEvent)
18685 this.startValue = this.getValue();
18687 if(this.inputType == 'radio'){
18689 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18690 e.dom.checked = false;
18693 this.inputEl().dom.checked = true;
18695 this.inputEl().dom.value = this.inputValue;
18697 if(suppressEvent !== true){
18698 this.fireEvent('check', this, true);
18706 this.checked = state;
18708 this.inputEl().dom.checked = state;
18710 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18712 if(suppressEvent !== true){
18713 this.fireEvent('check', this, state);
18719 getValue : function()
18721 if(this.inputType == 'radio'){
18722 return this.getGroupValue();
18725 return this.inputEl().getValue();
18729 getGroupValue : function()
18731 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18735 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18738 setValue : function(v,suppressEvent)
18740 if(this.inputType == 'radio'){
18741 this.setGroupValue(v, suppressEvent);
18745 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18750 setGroupValue : function(v, suppressEvent)
18752 this.startValue = this.getValue();
18754 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18755 e.dom.checked = false;
18757 if(e.dom.value == v){
18758 e.dom.checked = true;
18762 if(suppressEvent !== true){
18763 this.fireEvent('check', this, true);
18771 validate : function()
18775 (this.inputType == 'radio' && this.validateRadio()) ||
18776 (this.inputType == 'checkbox' && this.validateCheckbox())
18782 this.markInvalid();
18786 validateRadio : function()
18790 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18791 if(!e.dom.checked){
18803 validateCheckbox : function()
18806 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18809 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18817 for(var i in group){
18822 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18829 * Mark this field as valid
18831 markValid : function()
18833 if(this.allowBlank){
18839 this.fireEvent('valid', this);
18841 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18844 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18851 if(this.inputType == 'radio'){
18852 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18853 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18854 e.findParent('.form-group', false, true).addClass(_this.validClass);
18861 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18862 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18866 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18872 for(var i in group){
18873 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18874 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18879 * Mark this field as invalid
18880 * @param {String} msg The validation message
18882 markInvalid : function(msg)
18884 if(this.allowBlank){
18890 this.fireEvent('invalid', this, msg);
18892 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18895 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18899 label.markInvalid();
18902 if(this.inputType == 'radio'){
18903 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18904 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18905 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18912 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18913 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18917 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18923 for(var i in group){
18924 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18925 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18932 Roo.apply(Roo.bootstrap.CheckBox, {
18937 * register a CheckBox Group
18938 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18940 register : function(checkbox)
18942 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18943 this.groups[checkbox.groupId] = {};
18946 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18950 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18954 * fetch a CheckBox Group based on the group ID
18955 * @param {string} the group ID
18956 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18958 get: function(groupId) {
18959 if (typeof(this.groups[groupId]) == 'undefined') {
18963 return this.groups[groupId] ;
18975 *<div class="radio">
18977 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18978 Option one is this and that—be sure to include why it's great
18985 *<label class="radio-inline">fieldLabel</label>
18986 *<label class="radio-inline">
18987 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18995 * @class Roo.bootstrap.Radio
18996 * @extends Roo.bootstrap.CheckBox
18997 * Bootstrap Radio class
19000 * Create a new Radio
19001 * @param {Object} config The config object
19004 Roo.bootstrap.Radio = function(config){
19005 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19009 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19011 inputType: 'radio',
19015 getAutoCreate : function()
19017 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19018 align = align || 'left'; // default...
19025 tag : this.inline ? 'span' : 'div',
19030 var inline = this.inline ? ' radio-inline' : '';
19034 // does not need for, as we wrap the input with it..
19036 cls : 'control-label box-label' + inline,
19039 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19043 //cls : 'control-label' + inline,
19044 html : this.fieldLabel,
19045 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19054 type : this.inputType,
19055 //value : (!this.checked) ? this.valueOff : this.inputValue,
19056 value : this.inputValue,
19058 placeholder : this.placeholder || '' // ?? needed????
19061 if (this.weight) { // Validity check?
19062 input.cls += " radio-" + this.weight;
19064 if (this.disabled) {
19065 input.disabled=true;
19069 input.checked = this.checked;
19073 input.name = this.name;
19077 input.cls += ' input-' + this.size;
19080 //?? can span's inline have a width??
19083 ['xs','sm','md','lg'].map(function(size){
19084 if (settings[size]) {
19085 cfg.cls += ' col-' + size + '-' + settings[size];
19089 var inputblock = input;
19091 if (this.before || this.after) {
19094 cls : 'input-group',
19099 inputblock.cn.push({
19101 cls : 'input-group-addon',
19105 inputblock.cn.push(input);
19107 inputblock.cn.push({
19109 cls : 'input-group-addon',
19117 if (this.fieldLabel && this.fieldLabel.length) {
19118 cfg.cn.push(fieldLabel);
19121 // normal bootstrap puts the input inside the label.
19122 // however with our styled version - it has to go after the input.
19124 //lbl.cn.push(inputblock);
19128 cls: 'radio' + inline,
19135 cfg.cn.push( lblwrap);
19140 html: this.boxLabel
19149 initEvents : function()
19151 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19153 this.inputEl().on('click', this.onClick, this);
19154 if (this.boxLabel) {
19155 //Roo.log('find label');
19156 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19161 inputEl: function ()
19163 return this.el.select('input.roo-radio',true).first();
19165 onClick : function()
19168 this.setChecked(true);
19171 setChecked : function(state,suppressEvent)
19174 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19175 v.dom.checked = false;
19178 Roo.log(this.inputEl().dom);
19179 this.checked = state;
19180 this.inputEl().dom.checked = state;
19182 if(suppressEvent !== true){
19183 this.fireEvent('check', this, state);
19186 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19190 getGroupValue : function()
19193 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19194 if(v.dom.checked == true){
19195 value = v.dom.value;
19203 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19204 * @return {Mixed} value The field value
19206 getValue : function(){
19207 return this.getGroupValue();
19213 //<script type="text/javascript">
19216 * Based Ext JS Library 1.1.1
19217 * Copyright(c) 2006-2007, Ext JS, LLC.
19223 * @class Roo.HtmlEditorCore
19224 * @extends Roo.Component
19225 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19227 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19230 Roo.HtmlEditorCore = function(config){
19233 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19238 * @event initialize
19239 * Fires when the editor is fully initialized (including the iframe)
19240 * @param {Roo.HtmlEditorCore} this
19245 * Fires when the editor is first receives the focus. Any insertion must wait
19246 * until after this event.
19247 * @param {Roo.HtmlEditorCore} this
19251 * @event beforesync
19252 * Fires before the textarea is updated with content from the editor iframe. Return false
19253 * to cancel the sync.
19254 * @param {Roo.HtmlEditorCore} this
19255 * @param {String} html
19259 * @event beforepush
19260 * Fires before the iframe editor is updated with content from the textarea. Return false
19261 * to cancel the push.
19262 * @param {Roo.HtmlEditorCore} this
19263 * @param {String} html
19268 * Fires when the textarea is updated with content from the editor iframe.
19269 * @param {Roo.HtmlEditorCore} this
19270 * @param {String} html
19275 * Fires when the iframe editor is updated with content from the textarea.
19276 * @param {Roo.HtmlEditorCore} this
19277 * @param {String} html
19282 * @event editorevent
19283 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19284 * @param {Roo.HtmlEditorCore} this
19290 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19292 // defaults : white / black...
19293 this.applyBlacklists();
19300 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19304 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19310 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19315 * @cfg {Number} height (in pixels)
19319 * @cfg {Number} width (in pixels)
19324 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19327 stylesheets: false,
19332 // private properties
19333 validationEvent : false,
19335 initialized : false,
19337 sourceEditMode : false,
19338 onFocus : Roo.emptyFn,
19340 hideMode:'offsets',
19344 // blacklist + whitelisted elements..
19351 * Protected method that will not generally be called directly. It
19352 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19353 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19355 getDocMarkup : function(){
19359 // inherit styels from page...??
19360 if (this.stylesheets === false) {
19362 Roo.get(document.head).select('style').each(function(node) {
19363 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19366 Roo.get(document.head).select('link').each(function(node) {
19367 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19370 } else if (!this.stylesheets.length) {
19372 st = '<style type="text/css">' +
19373 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19379 st += '<style type="text/css">' +
19380 'IMG { cursor: pointer } ' +
19384 return '<html><head>' + st +
19385 //<style type="text/css">' +
19386 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19388 ' </head><body class="roo-htmleditor-body"></body></html>';
19392 onRender : function(ct, position)
19395 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19396 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19399 this.el.dom.style.border = '0 none';
19400 this.el.dom.setAttribute('tabIndex', -1);
19401 this.el.addClass('x-hidden hide');
19405 if(Roo.isIE){ // fix IE 1px bogus margin
19406 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19410 this.frameId = Roo.id();
19414 var iframe = this.owner.wrap.createChild({
19416 cls: 'form-control', // bootstrap..
19418 name: this.frameId,
19419 frameBorder : 'no',
19420 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19425 this.iframe = iframe.dom;
19427 this.assignDocWin();
19429 this.doc.designMode = 'on';
19432 this.doc.write(this.getDocMarkup());
19436 var task = { // must defer to wait for browser to be ready
19438 //console.log("run task?" + this.doc.readyState);
19439 this.assignDocWin();
19440 if(this.doc.body || this.doc.readyState == 'complete'){
19442 this.doc.designMode="on";
19446 Roo.TaskMgr.stop(task);
19447 this.initEditor.defer(10, this);
19454 Roo.TaskMgr.start(task);
19459 onResize : function(w, h)
19461 Roo.log('resize: ' +w + ',' + h );
19462 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19466 if(typeof w == 'number'){
19468 this.iframe.style.width = w + 'px';
19470 if(typeof h == 'number'){
19472 this.iframe.style.height = h + 'px';
19474 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19481 * Toggles the editor between standard and source edit mode.
19482 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19484 toggleSourceEdit : function(sourceEditMode){
19486 this.sourceEditMode = sourceEditMode === true;
19488 if(this.sourceEditMode){
19490 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19493 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19494 //this.iframe.className = '';
19497 //this.setSize(this.owner.wrap.getSize());
19498 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19505 * Protected method that will not generally be called directly. If you need/want
19506 * custom HTML cleanup, this is the method you should override.
19507 * @param {String} html The HTML to be cleaned
19508 * return {String} The cleaned HTML
19510 cleanHtml : function(html){
19511 html = String(html);
19512 if(html.length > 5){
19513 if(Roo.isSafari){ // strip safari nonsense
19514 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19517 if(html == ' '){
19524 * HTML Editor -> Textarea
19525 * Protected method that will not generally be called directly. Syncs the contents
19526 * of the editor iframe with the textarea.
19528 syncValue : function(){
19529 if(this.initialized){
19530 var bd = (this.doc.body || this.doc.documentElement);
19531 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19532 var html = bd.innerHTML;
19534 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19535 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19537 html = '<div style="'+m[0]+'">' + html + '</div>';
19540 html = this.cleanHtml(html);
19541 // fix up the special chars.. normaly like back quotes in word...
19542 // however we do not want to do this with chinese..
19543 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19544 var cc = b.charCodeAt();
19546 (cc >= 0x4E00 && cc < 0xA000 ) ||
19547 (cc >= 0x3400 && cc < 0x4E00 ) ||
19548 (cc >= 0xf900 && cc < 0xfb00 )
19554 if(this.owner.fireEvent('beforesync', this, html) !== false){
19555 this.el.dom.value = html;
19556 this.owner.fireEvent('sync', this, html);
19562 * Protected method that will not generally be called directly. Pushes the value of the textarea
19563 * into the iframe editor.
19565 pushValue : function(){
19566 if(this.initialized){
19567 var v = this.el.dom.value.trim();
19569 // if(v.length < 1){
19573 if(this.owner.fireEvent('beforepush', this, v) !== false){
19574 var d = (this.doc.body || this.doc.documentElement);
19576 this.cleanUpPaste();
19577 this.el.dom.value = d.innerHTML;
19578 this.owner.fireEvent('push', this, v);
19584 deferFocus : function(){
19585 this.focus.defer(10, this);
19589 focus : function(){
19590 if(this.win && !this.sourceEditMode){
19597 assignDocWin: function()
19599 var iframe = this.iframe;
19602 this.doc = iframe.contentWindow.document;
19603 this.win = iframe.contentWindow;
19605 // if (!Roo.get(this.frameId)) {
19608 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19609 // this.win = Roo.get(this.frameId).dom.contentWindow;
19611 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19615 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19616 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19621 initEditor : function(){
19622 //console.log("INIT EDITOR");
19623 this.assignDocWin();
19627 this.doc.designMode="on";
19629 this.doc.write(this.getDocMarkup());
19632 var dbody = (this.doc.body || this.doc.documentElement);
19633 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19634 // this copies styles from the containing element into thsi one..
19635 // not sure why we need all of this..
19636 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19638 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19639 //ss['background-attachment'] = 'fixed'; // w3c
19640 dbody.bgProperties = 'fixed'; // ie
19641 //Roo.DomHelper.applyStyles(dbody, ss);
19642 Roo.EventManager.on(this.doc, {
19643 //'mousedown': this.onEditorEvent,
19644 'mouseup': this.onEditorEvent,
19645 'dblclick': this.onEditorEvent,
19646 'click': this.onEditorEvent,
19647 'keyup': this.onEditorEvent,
19652 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19654 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19655 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19657 this.initialized = true;
19659 this.owner.fireEvent('initialize', this);
19664 onDestroy : function(){
19670 //for (var i =0; i < this.toolbars.length;i++) {
19671 // // fixme - ask toolbars for heights?
19672 // this.toolbars[i].onDestroy();
19675 //this.wrap.dom.innerHTML = '';
19676 //this.wrap.remove();
19681 onFirstFocus : function(){
19683 this.assignDocWin();
19686 this.activated = true;
19689 if(Roo.isGecko){ // prevent silly gecko errors
19691 var s = this.win.getSelection();
19692 if(!s.focusNode || s.focusNode.nodeType != 3){
19693 var r = s.getRangeAt(0);
19694 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19699 this.execCmd('useCSS', true);
19700 this.execCmd('styleWithCSS', false);
19703 this.owner.fireEvent('activate', this);
19707 adjustFont: function(btn){
19708 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19709 //if(Roo.isSafari){ // safari
19712 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19713 if(Roo.isSafari){ // safari
19714 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19715 v = (v < 10) ? 10 : v;
19716 v = (v > 48) ? 48 : v;
19717 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19722 v = Math.max(1, v+adjust);
19724 this.execCmd('FontSize', v );
19727 onEditorEvent : function(e)
19729 this.owner.fireEvent('editorevent', this, e);
19730 // this.updateToolbar();
19731 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19734 insertTag : function(tg)
19736 // could be a bit smarter... -> wrap the current selected tRoo..
19737 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19739 range = this.createRange(this.getSelection());
19740 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19741 wrappingNode.appendChild(range.extractContents());
19742 range.insertNode(wrappingNode);
19749 this.execCmd("formatblock", tg);
19753 insertText : function(txt)
19757 var range = this.createRange();
19758 range.deleteContents();
19759 //alert(Sender.getAttribute('label'));
19761 range.insertNode(this.doc.createTextNode(txt));
19767 * Executes a Midas editor command on the editor document and performs necessary focus and
19768 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19769 * @param {String} cmd The Midas command
19770 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19772 relayCmd : function(cmd, value){
19774 this.execCmd(cmd, value);
19775 this.owner.fireEvent('editorevent', this);
19776 //this.updateToolbar();
19777 this.owner.deferFocus();
19781 * Executes a Midas editor command directly on the editor document.
19782 * For visual commands, you should use {@link #relayCmd} instead.
19783 * <b>This should only be called after the editor is initialized.</b>
19784 * @param {String} cmd The Midas command
19785 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19787 execCmd : function(cmd, value){
19788 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19795 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19797 * @param {String} text | dom node..
19799 insertAtCursor : function(text)
19804 if(!this.activated){
19810 var r = this.doc.selection.createRange();
19821 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19825 // from jquery ui (MIT licenced)
19827 var win = this.win;
19829 if (win.getSelection && win.getSelection().getRangeAt) {
19830 range = win.getSelection().getRangeAt(0);
19831 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19832 range.insertNode(node);
19833 } else if (win.document.selection && win.document.selection.createRange) {
19834 // no firefox support
19835 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19836 win.document.selection.createRange().pasteHTML(txt);
19838 // no firefox support
19839 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19840 this.execCmd('InsertHTML', txt);
19849 mozKeyPress : function(e){
19851 var c = e.getCharCode(), cmd;
19854 c = String.fromCharCode(c).toLowerCase();
19868 this.cleanUpPaste.defer(100, this);
19876 e.preventDefault();
19884 fixKeys : function(){ // load time branching for fastest keydown performance
19886 return function(e){
19887 var k = e.getKey(), r;
19890 r = this.doc.selection.createRange();
19893 r.pasteHTML('    ');
19900 r = this.doc.selection.createRange();
19902 var target = r.parentElement();
19903 if(!target || target.tagName.toLowerCase() != 'li'){
19905 r.pasteHTML('<br />');
19911 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19912 this.cleanUpPaste.defer(100, this);
19918 }else if(Roo.isOpera){
19919 return function(e){
19920 var k = e.getKey();
19924 this.execCmd('InsertHTML','    ');
19927 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19928 this.cleanUpPaste.defer(100, this);
19933 }else if(Roo.isSafari){
19934 return function(e){
19935 var k = e.getKey();
19939 this.execCmd('InsertText','\t');
19943 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19944 this.cleanUpPaste.defer(100, this);
19952 getAllAncestors: function()
19954 var p = this.getSelectedNode();
19957 a.push(p); // push blank onto stack..
19958 p = this.getParentElement();
19962 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19966 a.push(this.doc.body);
19970 lastSelNode : false,
19973 getSelection : function()
19975 this.assignDocWin();
19976 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19979 getSelectedNode: function()
19981 // this may only work on Gecko!!!
19983 // should we cache this!!!!
19988 var range = this.createRange(this.getSelection()).cloneRange();
19991 var parent = range.parentElement();
19993 var testRange = range.duplicate();
19994 testRange.moveToElementText(parent);
19995 if (testRange.inRange(range)) {
19998 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20001 parent = parent.parentElement;
20006 // is ancestor a text element.
20007 var ac = range.commonAncestorContainer;
20008 if (ac.nodeType == 3) {
20009 ac = ac.parentNode;
20012 var ar = ac.childNodes;
20015 var other_nodes = [];
20016 var has_other_nodes = false;
20017 for (var i=0;i<ar.length;i++) {
20018 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20021 // fullly contained node.
20023 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20028 // probably selected..
20029 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20030 other_nodes.push(ar[i]);
20034 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20039 has_other_nodes = true;
20041 if (!nodes.length && other_nodes.length) {
20042 nodes= other_nodes;
20044 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20050 createRange: function(sel)
20052 // this has strange effects when using with
20053 // top toolbar - not sure if it's a great idea.
20054 //this.editor.contentWindow.focus();
20055 if (typeof sel != "undefined") {
20057 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20059 return this.doc.createRange();
20062 return this.doc.createRange();
20065 getParentElement: function()
20068 this.assignDocWin();
20069 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20071 var range = this.createRange(sel);
20074 var p = range.commonAncestorContainer;
20075 while (p.nodeType == 3) { // text node
20086 * Range intersection.. the hard stuff...
20090 * [ -- selected range --- ]
20094 * if end is before start or hits it. fail.
20095 * if start is after end or hits it fail.
20097 * if either hits (but other is outside. - then it's not
20103 // @see http://www.thismuchiknow.co.uk/?p=64.
20104 rangeIntersectsNode : function(range, node)
20106 var nodeRange = node.ownerDocument.createRange();
20108 nodeRange.selectNode(node);
20110 nodeRange.selectNodeContents(node);
20113 var rangeStartRange = range.cloneRange();
20114 rangeStartRange.collapse(true);
20116 var rangeEndRange = range.cloneRange();
20117 rangeEndRange.collapse(false);
20119 var nodeStartRange = nodeRange.cloneRange();
20120 nodeStartRange.collapse(true);
20122 var nodeEndRange = nodeRange.cloneRange();
20123 nodeEndRange.collapse(false);
20125 return rangeStartRange.compareBoundaryPoints(
20126 Range.START_TO_START, nodeEndRange) == -1 &&
20127 rangeEndRange.compareBoundaryPoints(
20128 Range.START_TO_START, nodeStartRange) == 1;
20132 rangeCompareNode : function(range, node)
20134 var nodeRange = node.ownerDocument.createRange();
20136 nodeRange.selectNode(node);
20138 nodeRange.selectNodeContents(node);
20142 range.collapse(true);
20144 nodeRange.collapse(true);
20146 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20147 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20149 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20151 var nodeIsBefore = ss == 1;
20152 var nodeIsAfter = ee == -1;
20154 if (nodeIsBefore && nodeIsAfter) {
20157 if (!nodeIsBefore && nodeIsAfter) {
20158 return 1; //right trailed.
20161 if (nodeIsBefore && !nodeIsAfter) {
20162 return 2; // left trailed.
20168 // private? - in a new class?
20169 cleanUpPaste : function()
20171 // cleans up the whole document..
20172 Roo.log('cleanuppaste');
20174 this.cleanUpChildren(this.doc.body);
20175 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20176 if (clean != this.doc.body.innerHTML) {
20177 this.doc.body.innerHTML = clean;
20182 cleanWordChars : function(input) {// change the chars to hex code
20183 var he = Roo.HtmlEditorCore;
20185 var output = input;
20186 Roo.each(he.swapCodes, function(sw) {
20187 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20189 output = output.replace(swapper, sw[1]);
20196 cleanUpChildren : function (n)
20198 if (!n.childNodes.length) {
20201 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20202 this.cleanUpChild(n.childNodes[i]);
20209 cleanUpChild : function (node)
20212 //console.log(node);
20213 if (node.nodeName == "#text") {
20214 // clean up silly Windows -- stuff?
20217 if (node.nodeName == "#comment") {
20218 node.parentNode.removeChild(node);
20219 // clean up silly Windows -- stuff?
20222 var lcname = node.tagName.toLowerCase();
20223 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20224 // whitelist of tags..
20226 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20228 node.parentNode.removeChild(node);
20233 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20235 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20236 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20238 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20239 // remove_keep_children = true;
20242 if (remove_keep_children) {
20243 this.cleanUpChildren(node);
20244 // inserts everything just before this node...
20245 while (node.childNodes.length) {
20246 var cn = node.childNodes[0];
20247 node.removeChild(cn);
20248 node.parentNode.insertBefore(cn, node);
20250 node.parentNode.removeChild(node);
20254 if (!node.attributes || !node.attributes.length) {
20255 this.cleanUpChildren(node);
20259 function cleanAttr(n,v)
20262 if (v.match(/^\./) || v.match(/^\//)) {
20265 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20268 if (v.match(/^#/)) {
20271 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20272 node.removeAttribute(n);
20276 var cwhite = this.cwhite;
20277 var cblack = this.cblack;
20279 function cleanStyle(n,v)
20281 if (v.match(/expression/)) { //XSS?? should we even bother..
20282 node.removeAttribute(n);
20286 var parts = v.split(/;/);
20289 Roo.each(parts, function(p) {
20290 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20294 var l = p.split(':').shift().replace(/\s+/g,'');
20295 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20297 if ( cwhite.length && cblack.indexOf(l) > -1) {
20298 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20299 //node.removeAttribute(n);
20303 // only allow 'c whitelisted system attributes'
20304 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20305 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20306 //node.removeAttribute(n);
20316 if (clean.length) {
20317 node.setAttribute(n, clean.join(';'));
20319 node.removeAttribute(n);
20325 for (var i = node.attributes.length-1; i > -1 ; i--) {
20326 var a = node.attributes[i];
20329 if (a.name.toLowerCase().substr(0,2)=='on') {
20330 node.removeAttribute(a.name);
20333 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20334 node.removeAttribute(a.name);
20337 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20338 cleanAttr(a.name,a.value); // fixme..
20341 if (a.name == 'style') {
20342 cleanStyle(a.name,a.value);
20345 /// clean up MS crap..
20346 // tecnically this should be a list of valid class'es..
20349 if (a.name == 'class') {
20350 if (a.value.match(/^Mso/)) {
20351 node.className = '';
20354 if (a.value.match(/body/)) {
20355 node.className = '';
20366 this.cleanUpChildren(node);
20372 * Clean up MS wordisms...
20374 cleanWord : function(node)
20379 this.cleanWord(this.doc.body);
20382 if (node.nodeName == "#text") {
20383 // clean up silly Windows -- stuff?
20386 if (node.nodeName == "#comment") {
20387 node.parentNode.removeChild(node);
20388 // clean up silly Windows -- stuff?
20392 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20393 node.parentNode.removeChild(node);
20397 // remove - but keep children..
20398 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20399 while (node.childNodes.length) {
20400 var cn = node.childNodes[0];
20401 node.removeChild(cn);
20402 node.parentNode.insertBefore(cn, node);
20404 node.parentNode.removeChild(node);
20405 this.iterateChildren(node, this.cleanWord);
20409 if (node.className.length) {
20411 var cn = node.className.split(/\W+/);
20413 Roo.each(cn, function(cls) {
20414 if (cls.match(/Mso[a-zA-Z]+/)) {
20419 node.className = cna.length ? cna.join(' ') : '';
20421 node.removeAttribute("class");
20425 if (node.hasAttribute("lang")) {
20426 node.removeAttribute("lang");
20429 if (node.hasAttribute("style")) {
20431 var styles = node.getAttribute("style").split(";");
20433 Roo.each(styles, function(s) {
20434 if (!s.match(/:/)) {
20437 var kv = s.split(":");
20438 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20441 // what ever is left... we allow.
20444 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20445 if (!nstyle.length) {
20446 node.removeAttribute('style');
20449 this.iterateChildren(node, this.cleanWord);
20455 * iterateChildren of a Node, calling fn each time, using this as the scole..
20456 * @param {DomNode} node node to iterate children of.
20457 * @param {Function} fn method of this class to call on each item.
20459 iterateChildren : function(node, fn)
20461 if (!node.childNodes.length) {
20464 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20465 fn.call(this, node.childNodes[i])
20471 * cleanTableWidths.
20473 * Quite often pasting from word etc.. results in tables with column and widths.
20474 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20477 cleanTableWidths : function(node)
20482 this.cleanTableWidths(this.doc.body);
20487 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20490 Roo.log(node.tagName);
20491 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20492 this.iterateChildren(node, this.cleanTableWidths);
20495 if (node.hasAttribute('width')) {
20496 node.removeAttribute('width');
20500 if (node.hasAttribute("style")) {
20503 var styles = node.getAttribute("style").split(";");
20505 Roo.each(styles, function(s) {
20506 if (!s.match(/:/)) {
20509 var kv = s.split(":");
20510 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20513 // what ever is left... we allow.
20516 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20517 if (!nstyle.length) {
20518 node.removeAttribute('style');
20522 this.iterateChildren(node, this.cleanTableWidths);
20530 domToHTML : function(currentElement, depth, nopadtext) {
20532 depth = depth || 0;
20533 nopadtext = nopadtext || false;
20535 if (!currentElement) {
20536 return this.domToHTML(this.doc.body);
20539 //Roo.log(currentElement);
20541 var allText = false;
20542 var nodeName = currentElement.nodeName;
20543 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20545 if (nodeName == '#text') {
20547 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20552 if (nodeName != 'BODY') {
20555 // Prints the node tagName, such as <A>, <IMG>, etc
20558 for(i = 0; i < currentElement.attributes.length;i++) {
20560 var aname = currentElement.attributes.item(i).name;
20561 if (!currentElement.attributes.item(i).value.length) {
20564 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20567 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20576 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20579 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20584 // Traverse the tree
20586 var currentElementChild = currentElement.childNodes.item(i);
20587 var allText = true;
20588 var innerHTML = '';
20590 while (currentElementChild) {
20591 // Formatting code (indent the tree so it looks nice on the screen)
20592 var nopad = nopadtext;
20593 if (lastnode == 'SPAN') {
20597 if (currentElementChild.nodeName == '#text') {
20598 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20599 toadd = nopadtext ? toadd : toadd.trim();
20600 if (!nopad && toadd.length > 80) {
20601 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20603 innerHTML += toadd;
20606 currentElementChild = currentElement.childNodes.item(i);
20612 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20614 // Recursively traverse the tree structure of the child node
20615 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20616 lastnode = currentElementChild.nodeName;
20618 currentElementChild=currentElement.childNodes.item(i);
20624 // The remaining code is mostly for formatting the tree
20625 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20630 ret+= "</"+tagName+">";
20636 applyBlacklists : function()
20638 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20639 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20643 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20644 if (b.indexOf(tag) > -1) {
20647 this.white.push(tag);
20651 Roo.each(w, function(tag) {
20652 if (b.indexOf(tag) > -1) {
20655 if (this.white.indexOf(tag) > -1) {
20658 this.white.push(tag);
20663 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20664 if (w.indexOf(tag) > -1) {
20667 this.black.push(tag);
20671 Roo.each(b, function(tag) {
20672 if (w.indexOf(tag) > -1) {
20675 if (this.black.indexOf(tag) > -1) {
20678 this.black.push(tag);
20683 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20684 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20688 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20689 if (b.indexOf(tag) > -1) {
20692 this.cwhite.push(tag);
20696 Roo.each(w, function(tag) {
20697 if (b.indexOf(tag) > -1) {
20700 if (this.cwhite.indexOf(tag) > -1) {
20703 this.cwhite.push(tag);
20708 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20709 if (w.indexOf(tag) > -1) {
20712 this.cblack.push(tag);
20716 Roo.each(b, function(tag) {
20717 if (w.indexOf(tag) > -1) {
20720 if (this.cblack.indexOf(tag) > -1) {
20723 this.cblack.push(tag);
20728 setStylesheets : function(stylesheets)
20730 if(typeof(stylesheets) == 'string'){
20731 Roo.get(this.iframe.contentDocument.head).createChild({
20733 rel : 'stylesheet',
20742 Roo.each(stylesheets, function(s) {
20747 Roo.get(_this.iframe.contentDocument.head).createChild({
20749 rel : 'stylesheet',
20758 removeStylesheets : function()
20762 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20767 // hide stuff that is not compatible
20781 * @event specialkey
20785 * @cfg {String} fieldClass @hide
20788 * @cfg {String} focusClass @hide
20791 * @cfg {String} autoCreate @hide
20794 * @cfg {String} inputType @hide
20797 * @cfg {String} invalidClass @hide
20800 * @cfg {String} invalidText @hide
20803 * @cfg {String} msgFx @hide
20806 * @cfg {String} validateOnBlur @hide
20810 Roo.HtmlEditorCore.white = [
20811 'area', 'br', 'img', 'input', 'hr', 'wbr',
20813 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20814 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20815 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20816 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20817 'table', 'ul', 'xmp',
20819 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20822 'dir', 'menu', 'ol', 'ul', 'dl',
20828 Roo.HtmlEditorCore.black = [
20829 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20831 'base', 'basefont', 'bgsound', 'blink', 'body',
20832 'frame', 'frameset', 'head', 'html', 'ilayer',
20833 'iframe', 'layer', 'link', 'meta', 'object',
20834 'script', 'style' ,'title', 'xml' // clean later..
20836 Roo.HtmlEditorCore.clean = [
20837 'script', 'style', 'title', 'xml'
20839 Roo.HtmlEditorCore.remove = [
20844 Roo.HtmlEditorCore.ablack = [
20848 Roo.HtmlEditorCore.aclean = [
20849 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20853 Roo.HtmlEditorCore.pwhite= [
20854 'http', 'https', 'mailto'
20857 // white listed style attributes.
20858 Roo.HtmlEditorCore.cwhite= [
20859 // 'text-align', /// default is to allow most things..
20865 // black listed style attributes.
20866 Roo.HtmlEditorCore.cblack= [
20867 // 'font-size' -- this can be set by the project
20871 Roo.HtmlEditorCore.swapCodes =[
20890 * @class Roo.bootstrap.HtmlEditor
20891 * @extends Roo.bootstrap.TextArea
20892 * Bootstrap HtmlEditor class
20895 * Create a new HtmlEditor
20896 * @param {Object} config The config object
20899 Roo.bootstrap.HtmlEditor = function(config){
20900 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20901 if (!this.toolbars) {
20902 this.toolbars = [];
20904 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20907 * @event initialize
20908 * Fires when the editor is fully initialized (including the iframe)
20909 * @param {HtmlEditor} this
20914 * Fires when the editor is first receives the focus. Any insertion must wait
20915 * until after this event.
20916 * @param {HtmlEditor} this
20920 * @event beforesync
20921 * Fires before the textarea is updated with content from the editor iframe. Return false
20922 * to cancel the sync.
20923 * @param {HtmlEditor} this
20924 * @param {String} html
20928 * @event beforepush
20929 * Fires before the iframe editor is updated with content from the textarea. Return false
20930 * to cancel the push.
20931 * @param {HtmlEditor} this
20932 * @param {String} html
20937 * Fires when the textarea is updated with content from the editor iframe.
20938 * @param {HtmlEditor} this
20939 * @param {String} html
20944 * Fires when the iframe editor is updated with content from the textarea.
20945 * @param {HtmlEditor} this
20946 * @param {String} html
20950 * @event editmodechange
20951 * Fires when the editor switches edit modes
20952 * @param {HtmlEditor} this
20953 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20955 editmodechange: true,
20957 * @event editorevent
20958 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20959 * @param {HtmlEditor} this
20963 * @event firstfocus
20964 * Fires when on first focus - needed by toolbars..
20965 * @param {HtmlEditor} this
20970 * Auto save the htmlEditor value as a file into Events
20971 * @param {HtmlEditor} this
20975 * @event savedpreview
20976 * preview the saved version of htmlEditor
20977 * @param {HtmlEditor} this
20984 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20988 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20993 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20998 * @cfg {Number} height (in pixels)
21002 * @cfg {Number} width (in pixels)
21007 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21010 stylesheets: false,
21015 // private properties
21016 validationEvent : false,
21018 initialized : false,
21021 onFocus : Roo.emptyFn,
21023 hideMode:'offsets',
21026 tbContainer : false,
21028 toolbarContainer :function() {
21029 return this.wrap.select('.x-html-editor-tb',true).first();
21033 * Protected method that will not generally be called directly. It
21034 * is called when the editor creates its toolbar. Override this method if you need to
21035 * add custom toolbar buttons.
21036 * @param {HtmlEditor} editor
21038 createToolbar : function(){
21040 Roo.log("create toolbars");
21042 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21043 this.toolbars[0].render(this.toolbarContainer());
21047 // if (!editor.toolbars || !editor.toolbars.length) {
21048 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21051 // for (var i =0 ; i < editor.toolbars.length;i++) {
21052 // editor.toolbars[i] = Roo.factory(
21053 // typeof(editor.toolbars[i]) == 'string' ?
21054 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21055 // Roo.bootstrap.HtmlEditor);
21056 // editor.toolbars[i].init(editor);
21062 onRender : function(ct, position)
21064 // Roo.log("Call onRender: " + this.xtype);
21066 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21068 this.wrap = this.inputEl().wrap({
21069 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21072 this.editorcore.onRender(ct, position);
21074 if (this.resizable) {
21075 this.resizeEl = new Roo.Resizable(this.wrap, {
21079 minHeight : this.height,
21080 height: this.height,
21081 handles : this.resizable,
21084 resize : function(r, w, h) {
21085 _t.onResize(w,h); // -something
21091 this.createToolbar(this);
21094 if(!this.width && this.resizable){
21095 this.setSize(this.wrap.getSize());
21097 if (this.resizeEl) {
21098 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21099 // should trigger onReize..
21105 onResize : function(w, h)
21107 Roo.log('resize: ' +w + ',' + h );
21108 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21112 if(this.inputEl() ){
21113 if(typeof w == 'number'){
21114 var aw = w - this.wrap.getFrameWidth('lr');
21115 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21118 if(typeof h == 'number'){
21119 var tbh = -11; // fixme it needs to tool bar size!
21120 for (var i =0; i < this.toolbars.length;i++) {
21121 // fixme - ask toolbars for heights?
21122 tbh += this.toolbars[i].el.getHeight();
21123 //if (this.toolbars[i].footer) {
21124 // tbh += this.toolbars[i].footer.el.getHeight();
21132 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21133 ah -= 5; // knock a few pixes off for look..
21134 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21138 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21139 this.editorcore.onResize(ew,eh);
21144 * Toggles the editor between standard and source edit mode.
21145 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21147 toggleSourceEdit : function(sourceEditMode)
21149 this.editorcore.toggleSourceEdit(sourceEditMode);
21151 if(this.editorcore.sourceEditMode){
21152 Roo.log('editor - showing textarea');
21155 // Roo.log(this.syncValue());
21157 this.inputEl().removeClass(['hide', 'x-hidden']);
21158 this.inputEl().dom.removeAttribute('tabIndex');
21159 this.inputEl().focus();
21161 Roo.log('editor - hiding textarea');
21163 // Roo.log(this.pushValue());
21166 this.inputEl().addClass(['hide', 'x-hidden']);
21167 this.inputEl().dom.setAttribute('tabIndex', -1);
21168 //this.deferFocus();
21171 if(this.resizable){
21172 this.setSize(this.wrap.getSize());
21175 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21178 // private (for BoxComponent)
21179 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21181 // private (for BoxComponent)
21182 getResizeEl : function(){
21186 // private (for BoxComponent)
21187 getPositionEl : function(){
21192 initEvents : function(){
21193 this.originalValue = this.getValue();
21197 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21200 // markInvalid : Roo.emptyFn,
21202 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21205 // clearInvalid : Roo.emptyFn,
21207 setValue : function(v){
21208 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21209 this.editorcore.pushValue();
21214 deferFocus : function(){
21215 this.focus.defer(10, this);
21219 focus : function(){
21220 this.editorcore.focus();
21226 onDestroy : function(){
21232 for (var i =0; i < this.toolbars.length;i++) {
21233 // fixme - ask toolbars for heights?
21234 this.toolbars[i].onDestroy();
21237 this.wrap.dom.innerHTML = '';
21238 this.wrap.remove();
21243 onFirstFocus : function(){
21244 //Roo.log("onFirstFocus");
21245 this.editorcore.onFirstFocus();
21246 for (var i =0; i < this.toolbars.length;i++) {
21247 this.toolbars[i].onFirstFocus();
21253 syncValue : function()
21255 this.editorcore.syncValue();
21258 pushValue : function()
21260 this.editorcore.pushValue();
21264 // hide stuff that is not compatible
21278 * @event specialkey
21282 * @cfg {String} fieldClass @hide
21285 * @cfg {String} focusClass @hide
21288 * @cfg {String} autoCreate @hide
21291 * @cfg {String} inputType @hide
21294 * @cfg {String} invalidClass @hide
21297 * @cfg {String} invalidText @hide
21300 * @cfg {String} msgFx @hide
21303 * @cfg {String} validateOnBlur @hide
21312 Roo.namespace('Roo.bootstrap.htmleditor');
21314 * @class Roo.bootstrap.HtmlEditorToolbar1
21319 new Roo.bootstrap.HtmlEditor({
21322 new Roo.bootstrap.HtmlEditorToolbar1({
21323 disable : { fonts: 1 , format: 1, ..., ... , ...],
21329 * @cfg {Object} disable List of elements to disable..
21330 * @cfg {Array} btns List of additional buttons.
21334 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21337 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21340 Roo.apply(this, config);
21342 // default disabled, based on 'good practice'..
21343 this.disable = this.disable || {};
21344 Roo.applyIf(this.disable, {
21347 specialElements : true
21349 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21351 this.editor = config.editor;
21352 this.editorcore = config.editor.editorcore;
21354 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21356 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21357 // dont call parent... till later.
21359 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21364 editorcore : false,
21369 "h1","h2","h3","h4","h5","h6",
21371 "abbr", "acronym", "address", "cite", "samp", "var",
21375 onRender : function(ct, position)
21377 // Roo.log("Call onRender: " + this.xtype);
21379 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21381 this.el.dom.style.marginBottom = '0';
21383 var editorcore = this.editorcore;
21384 var editor= this.editor;
21387 var btn = function(id,cmd , toggle, handler){
21389 var event = toggle ? 'toggle' : 'click';
21394 xns: Roo.bootstrap,
21397 enableToggle:toggle !== false,
21399 pressed : toggle ? false : null,
21402 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21403 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21412 xns: Roo.bootstrap,
21413 glyphicon : 'font',
21417 xns: Roo.bootstrap,
21421 Roo.each(this.formats, function(f) {
21422 style.menu.items.push({
21424 xns: Roo.bootstrap,
21425 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21430 editorcore.insertTag(this.tagname);
21437 children.push(style);
21440 btn('bold',false,true);
21441 btn('italic',false,true);
21442 btn('align-left', 'justifyleft',true);
21443 btn('align-center', 'justifycenter',true);
21444 btn('align-right' , 'justifyright',true);
21445 btn('link', false, false, function(btn) {
21446 //Roo.log("create link?");
21447 var url = prompt(this.createLinkText, this.defaultLinkValue);
21448 if(url && url != 'http:/'+'/'){
21449 this.editorcore.relayCmd('createlink', url);
21452 btn('list','insertunorderedlist',true);
21453 btn('pencil', false,true, function(btn){
21456 this.toggleSourceEdit(btn.pressed);
21462 xns: Roo.bootstrap,
21467 xns: Roo.bootstrap,
21472 cog.menu.items.push({
21474 xns: Roo.bootstrap,
21475 html : Clean styles,
21480 editorcore.insertTag(this.tagname);
21489 this.xtype = 'NavSimplebar';
21491 for(var i=0;i< children.length;i++) {
21493 this.buttons.add(this.addxtypeChild(children[i]));
21497 editor.on('editorevent', this.updateToolbar, this);
21499 onBtnClick : function(id)
21501 this.editorcore.relayCmd(id);
21502 this.editorcore.focus();
21506 * Protected method that will not generally be called directly. It triggers
21507 * a toolbar update by reading the markup state of the current selection in the editor.
21509 updateToolbar: function(){
21511 if(!this.editorcore.activated){
21512 this.editor.onFirstFocus(); // is this neeed?
21516 var btns = this.buttons;
21517 var doc = this.editorcore.doc;
21518 btns.get('bold').setActive(doc.queryCommandState('bold'));
21519 btns.get('italic').setActive(doc.queryCommandState('italic'));
21520 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21522 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21523 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21524 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21526 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21527 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21530 var ans = this.editorcore.getAllAncestors();
21531 if (this.formatCombo) {
21534 var store = this.formatCombo.store;
21535 this.formatCombo.setValue("");
21536 for (var i =0; i < ans.length;i++) {
21537 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21539 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21547 // hides menus... - so this cant be on a menu...
21548 Roo.bootstrap.MenuMgr.hideAll();
21550 Roo.bootstrap.MenuMgr.hideAll();
21551 //this.editorsyncValue();
21553 onFirstFocus: function() {
21554 this.buttons.each(function(item){
21558 toggleSourceEdit : function(sourceEditMode){
21561 if(sourceEditMode){
21562 Roo.log("disabling buttons");
21563 this.buttons.each( function(item){
21564 if(item.cmd != 'pencil'){
21570 Roo.log("enabling buttons");
21571 if(this.editorcore.initialized){
21572 this.buttons.each( function(item){
21578 Roo.log("calling toggole on editor");
21579 // tell the editor that it's been pressed..
21580 this.editor.toggleSourceEdit(sourceEditMode);
21590 * @class Roo.bootstrap.Table.AbstractSelectionModel
21591 * @extends Roo.util.Observable
21592 * Abstract base class for grid SelectionModels. It provides the interface that should be
21593 * implemented by descendant classes. This class should not be directly instantiated.
21596 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21597 this.locked = false;
21598 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21602 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21603 /** @ignore Called by the grid automatically. Do not call directly. */
21604 init : function(grid){
21610 * Locks the selections.
21613 this.locked = true;
21617 * Unlocks the selections.
21619 unlock : function(){
21620 this.locked = false;
21624 * Returns true if the selections are locked.
21625 * @return {Boolean}
21627 isLocked : function(){
21628 return this.locked;
21632 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21633 * @class Roo.bootstrap.Table.RowSelectionModel
21634 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21635 * It supports multiple selections and keyboard selection/navigation.
21637 * @param {Object} config
21640 Roo.bootstrap.Table.RowSelectionModel = function(config){
21641 Roo.apply(this, config);
21642 this.selections = new Roo.util.MixedCollection(false, function(o){
21647 this.lastActive = false;
21651 * @event selectionchange
21652 * Fires when the selection changes
21653 * @param {SelectionModel} this
21655 "selectionchange" : true,
21657 * @event afterselectionchange
21658 * Fires after the selection changes (eg. by key press or clicking)
21659 * @param {SelectionModel} this
21661 "afterselectionchange" : true,
21663 * @event beforerowselect
21664 * Fires when a row is selected being selected, return false to cancel.
21665 * @param {SelectionModel} this
21666 * @param {Number} rowIndex The selected index
21667 * @param {Boolean} keepExisting False if other selections will be cleared
21669 "beforerowselect" : true,
21672 * Fires when a row is selected.
21673 * @param {SelectionModel} this
21674 * @param {Number} rowIndex The selected index
21675 * @param {Roo.data.Record} r The record
21677 "rowselect" : true,
21679 * @event rowdeselect
21680 * Fires when a row is deselected.
21681 * @param {SelectionModel} this
21682 * @param {Number} rowIndex The selected index
21684 "rowdeselect" : true
21686 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21687 this.locked = false;
21690 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21692 * @cfg {Boolean} singleSelect
21693 * True to allow selection of only one row at a time (defaults to false)
21695 singleSelect : false,
21698 initEvents : function(){
21700 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21701 this.grid.on("mousedown", this.handleMouseDown, this);
21702 }else{ // allow click to work like normal
21703 this.grid.on("rowclick", this.handleDragableRowClick, this);
21706 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21707 "up" : function(e){
21709 this.selectPrevious(e.shiftKey);
21710 }else if(this.last !== false && this.lastActive !== false){
21711 var last = this.last;
21712 this.selectRange(this.last, this.lastActive-1);
21713 this.grid.getView().focusRow(this.lastActive);
21714 if(last !== false){
21718 this.selectFirstRow();
21720 this.fireEvent("afterselectionchange", this);
21722 "down" : function(e){
21724 this.selectNext(e.shiftKey);
21725 }else if(this.last !== false && this.lastActive !== false){
21726 var last = this.last;
21727 this.selectRange(this.last, this.lastActive+1);
21728 this.grid.getView().focusRow(this.lastActive);
21729 if(last !== false){
21733 this.selectFirstRow();
21735 this.fireEvent("afterselectionchange", this);
21740 var view = this.grid.view;
21741 view.on("refresh", this.onRefresh, this);
21742 view.on("rowupdated", this.onRowUpdated, this);
21743 view.on("rowremoved", this.onRemove, this);
21747 onRefresh : function(){
21748 var ds = this.grid.dataSource, i, v = this.grid.view;
21749 var s = this.selections;
21750 s.each(function(r){
21751 if((i = ds.indexOfId(r.id)) != -1){
21760 onRemove : function(v, index, r){
21761 this.selections.remove(r);
21765 onRowUpdated : function(v, index, r){
21766 if(this.isSelected(r)){
21767 v.onRowSelect(index);
21773 * @param {Array} records The records to select
21774 * @param {Boolean} keepExisting (optional) True to keep existing selections
21776 selectRecords : function(records, keepExisting){
21778 this.clearSelections();
21780 var ds = this.grid.dataSource;
21781 for(var i = 0, len = records.length; i < len; i++){
21782 this.selectRow(ds.indexOf(records[i]), true);
21787 * Gets the number of selected rows.
21790 getCount : function(){
21791 return this.selections.length;
21795 * Selects the first row in the grid.
21797 selectFirstRow : function(){
21802 * Select the last row.
21803 * @param {Boolean} keepExisting (optional) True to keep existing selections
21805 selectLastRow : function(keepExisting){
21806 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21810 * Selects the row immediately following the last selected row.
21811 * @param {Boolean} keepExisting (optional) True to keep existing selections
21813 selectNext : function(keepExisting){
21814 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21815 this.selectRow(this.last+1, keepExisting);
21816 this.grid.getView().focusRow(this.last);
21821 * Selects the row that precedes the last selected row.
21822 * @param {Boolean} keepExisting (optional) True to keep existing selections
21824 selectPrevious : function(keepExisting){
21826 this.selectRow(this.last-1, keepExisting);
21827 this.grid.getView().focusRow(this.last);
21832 * Returns the selected records
21833 * @return {Array} Array of selected records
21835 getSelections : function(){
21836 return [].concat(this.selections.items);
21840 * Returns the first selected record.
21843 getSelected : function(){
21844 return this.selections.itemAt(0);
21849 * Clears all selections.
21851 clearSelections : function(fast){
21856 var ds = this.grid.dataSource;
21857 var s = this.selections;
21858 s.each(function(r){
21859 this.deselectRow(ds.indexOfId(r.id));
21863 this.selections.clear();
21870 * Selects all rows.
21872 selectAll : function(){
21876 this.selections.clear();
21877 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21878 this.selectRow(i, true);
21883 * Returns True if there is a selection.
21884 * @return {Boolean}
21886 hasSelection : function(){
21887 return this.selections.length > 0;
21891 * Returns True if the specified row is selected.
21892 * @param {Number/Record} record The record or index of the record to check
21893 * @return {Boolean}
21895 isSelected : function(index){
21896 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21897 return (r && this.selections.key(r.id) ? true : false);
21901 * Returns True if the specified record id is selected.
21902 * @param {String} id The id of record to check
21903 * @return {Boolean}
21905 isIdSelected : function(id){
21906 return (this.selections.key(id) ? true : false);
21910 handleMouseDown : function(e, t){
21911 var view = this.grid.getView(), rowIndex;
21912 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21915 if(e.shiftKey && this.last !== false){
21916 var last = this.last;
21917 this.selectRange(last, rowIndex, e.ctrlKey);
21918 this.last = last; // reset the last
21919 view.focusRow(rowIndex);
21921 var isSelected = this.isSelected(rowIndex);
21922 if(e.button !== 0 && isSelected){
21923 view.focusRow(rowIndex);
21924 }else if(e.ctrlKey && isSelected){
21925 this.deselectRow(rowIndex);
21926 }else if(!isSelected){
21927 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21928 view.focusRow(rowIndex);
21931 this.fireEvent("afterselectionchange", this);
21934 handleDragableRowClick : function(grid, rowIndex, e)
21936 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21937 this.selectRow(rowIndex, false);
21938 grid.view.focusRow(rowIndex);
21939 this.fireEvent("afterselectionchange", this);
21944 * Selects multiple rows.
21945 * @param {Array} rows Array of the indexes of the row to select
21946 * @param {Boolean} keepExisting (optional) True to keep existing selections
21948 selectRows : function(rows, keepExisting){
21950 this.clearSelections();
21952 for(var i = 0, len = rows.length; i < len; i++){
21953 this.selectRow(rows[i], true);
21958 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21959 * @param {Number} startRow The index of the first row in the range
21960 * @param {Number} endRow The index of the last row in the range
21961 * @param {Boolean} keepExisting (optional) True to retain existing selections
21963 selectRange : function(startRow, endRow, keepExisting){
21968 this.clearSelections();
21970 if(startRow <= endRow){
21971 for(var i = startRow; i <= endRow; i++){
21972 this.selectRow(i, true);
21975 for(var i = startRow; i >= endRow; i--){
21976 this.selectRow(i, true);
21982 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21983 * @param {Number} startRow The index of the first row in the range
21984 * @param {Number} endRow The index of the last row in the range
21986 deselectRange : function(startRow, endRow, preventViewNotify){
21990 for(var i = startRow; i <= endRow; i++){
21991 this.deselectRow(i, preventViewNotify);
21997 * @param {Number} row The index of the row to select
21998 * @param {Boolean} keepExisting (optional) True to keep existing selections
22000 selectRow : function(index, keepExisting, preventViewNotify){
22001 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22004 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22005 if(!keepExisting || this.singleSelect){
22006 this.clearSelections();
22008 var r = this.grid.dataSource.getAt(index);
22009 this.selections.add(r);
22010 this.last = this.lastActive = index;
22011 if(!preventViewNotify){
22012 this.grid.getView().onRowSelect(index);
22014 this.fireEvent("rowselect", this, index, r);
22015 this.fireEvent("selectionchange", this);
22021 * @param {Number} row The index of the row to deselect
22023 deselectRow : function(index, preventViewNotify){
22027 if(this.last == index){
22030 if(this.lastActive == index){
22031 this.lastActive = false;
22033 var r = this.grid.dataSource.getAt(index);
22034 this.selections.remove(r);
22035 if(!preventViewNotify){
22036 this.grid.getView().onRowDeselect(index);
22038 this.fireEvent("rowdeselect", this, index);
22039 this.fireEvent("selectionchange", this);
22043 restoreLast : function(){
22045 this.last = this._last;
22050 acceptsNav : function(row, col, cm){
22051 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22055 onEditorKey : function(field, e){
22056 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22061 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22063 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22065 }else if(k == e.ENTER && !e.ctrlKey){
22069 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22071 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22073 }else if(k == e.ESC){
22077 g.startEditing(newCell[0], newCell[1]);
22082 * Ext JS Library 1.1.1
22083 * Copyright(c) 2006-2007, Ext JS, LLC.
22085 * Originally Released Under LGPL - original licence link has changed is not relivant.
22088 * <script type="text/javascript">
22092 * @class Roo.bootstrap.PagingToolbar
22093 * @extends Roo.bootstrap.NavSimplebar
22094 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22096 * Create a new PagingToolbar
22097 * @param {Object} config The config object
22098 * @param {Roo.data.Store} store
22100 Roo.bootstrap.PagingToolbar = function(config)
22102 // old args format still supported... - xtype is prefered..
22103 // created from xtype...
22105 this.ds = config.dataSource;
22107 if (config.store && !this.ds) {
22108 this.store= Roo.factory(config.store, Roo.data);
22109 this.ds = this.store;
22110 this.ds.xmodule = this.xmodule || false;
22113 this.toolbarItems = [];
22114 if (config.items) {
22115 this.toolbarItems = config.items;
22118 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22123 this.bind(this.ds);
22126 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22130 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22132 * @cfg {Roo.data.Store} dataSource
22133 * The underlying data store providing the paged data
22136 * @cfg {String/HTMLElement/Element} container
22137 * container The id or element that will contain the toolbar
22140 * @cfg {Boolean} displayInfo
22141 * True to display the displayMsg (defaults to false)
22144 * @cfg {Number} pageSize
22145 * The number of records to display per page (defaults to 20)
22149 * @cfg {String} displayMsg
22150 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22152 displayMsg : 'Displaying {0} - {1} of {2}',
22154 * @cfg {String} emptyMsg
22155 * The message to display when no records are found (defaults to "No data to display")
22157 emptyMsg : 'No data to display',
22159 * Customizable piece of the default paging text (defaults to "Page")
22162 beforePageText : "Page",
22164 * Customizable piece of the default paging text (defaults to "of %0")
22167 afterPageText : "of {0}",
22169 * Customizable piece of the default paging text (defaults to "First Page")
22172 firstText : "First Page",
22174 * Customizable piece of the default paging text (defaults to "Previous Page")
22177 prevText : "Previous Page",
22179 * Customizable piece of the default paging text (defaults to "Next Page")
22182 nextText : "Next Page",
22184 * Customizable piece of the default paging text (defaults to "Last Page")
22187 lastText : "Last Page",
22189 * Customizable piece of the default paging text (defaults to "Refresh")
22192 refreshText : "Refresh",
22196 onRender : function(ct, position)
22198 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22199 this.navgroup.parentId = this.id;
22200 this.navgroup.onRender(this.el, null);
22201 // add the buttons to the navgroup
22203 if(this.displayInfo){
22204 Roo.log(this.el.select('ul.navbar-nav',true).first());
22205 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22206 this.displayEl = this.el.select('.x-paging-info', true).first();
22207 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22208 // this.displayEl = navel.el.select('span',true).first();
22214 Roo.each(_this.buttons, function(e){ // this might need to use render????
22215 Roo.factory(e).onRender(_this.el, null);
22219 Roo.each(_this.toolbarItems, function(e) {
22220 _this.navgroup.addItem(e);
22224 this.first = this.navgroup.addItem({
22225 tooltip: this.firstText,
22227 icon : 'fa fa-backward',
22229 preventDefault: true,
22230 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22233 this.prev = this.navgroup.addItem({
22234 tooltip: this.prevText,
22236 icon : 'fa fa-step-backward',
22238 preventDefault: true,
22239 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22241 //this.addSeparator();
22244 var field = this.navgroup.addItem( {
22246 cls : 'x-paging-position',
22248 html : this.beforePageText +
22249 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22250 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22253 this.field = field.el.select('input', true).first();
22254 this.field.on("keydown", this.onPagingKeydown, this);
22255 this.field.on("focus", function(){this.dom.select();});
22258 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22259 //this.field.setHeight(18);
22260 //this.addSeparator();
22261 this.next = this.navgroup.addItem({
22262 tooltip: this.nextText,
22264 html : ' <i class="fa fa-step-forward">',
22266 preventDefault: true,
22267 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22269 this.last = this.navgroup.addItem({
22270 tooltip: this.lastText,
22271 icon : 'fa fa-forward',
22274 preventDefault: true,
22275 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22277 //this.addSeparator();
22278 this.loading = this.navgroup.addItem({
22279 tooltip: this.refreshText,
22280 icon: 'fa fa-refresh',
22281 preventDefault: true,
22282 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22288 updateInfo : function(){
22289 if(this.displayEl){
22290 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22291 var msg = count == 0 ?
22295 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22297 this.displayEl.update(msg);
22302 onLoad : function(ds, r, o){
22303 this.cursor = o.params ? o.params.start : 0;
22304 var d = this.getPageData(),
22308 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22309 this.field.dom.value = ap;
22310 this.first.setDisabled(ap == 1);
22311 this.prev.setDisabled(ap == 1);
22312 this.next.setDisabled(ap == ps);
22313 this.last.setDisabled(ap == ps);
22314 this.loading.enable();
22319 getPageData : function(){
22320 var total = this.ds.getTotalCount();
22323 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22324 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22329 onLoadError : function(){
22330 this.loading.enable();
22334 onPagingKeydown : function(e){
22335 var k = e.getKey();
22336 var d = this.getPageData();
22338 var v = this.field.dom.value, pageNum;
22339 if(!v || isNaN(pageNum = parseInt(v, 10))){
22340 this.field.dom.value = d.activePage;
22343 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22344 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22347 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))
22349 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22350 this.field.dom.value = pageNum;
22351 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22354 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22356 var v = this.field.dom.value, pageNum;
22357 var increment = (e.shiftKey) ? 10 : 1;
22358 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22361 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22362 this.field.dom.value = d.activePage;
22365 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22367 this.field.dom.value = parseInt(v, 10) + increment;
22368 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22369 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22376 beforeLoad : function(){
22378 this.loading.disable();
22383 onClick : function(which){
22392 ds.load({params:{start: 0, limit: this.pageSize}});
22395 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22398 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22401 var total = ds.getTotalCount();
22402 var extra = total % this.pageSize;
22403 var lastStart = extra ? (total - extra) : total-this.pageSize;
22404 ds.load({params:{start: lastStart, limit: this.pageSize}});
22407 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22413 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22414 * @param {Roo.data.Store} store The data store to unbind
22416 unbind : function(ds){
22417 ds.un("beforeload", this.beforeLoad, this);
22418 ds.un("load", this.onLoad, this);
22419 ds.un("loadexception", this.onLoadError, this);
22420 ds.un("remove", this.updateInfo, this);
22421 ds.un("add", this.updateInfo, this);
22422 this.ds = undefined;
22426 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22427 * @param {Roo.data.Store} store The data store to bind
22429 bind : function(ds){
22430 ds.on("beforeload", this.beforeLoad, this);
22431 ds.on("load", this.onLoad, this);
22432 ds.on("loadexception", this.onLoadError, this);
22433 ds.on("remove", this.updateInfo, this);
22434 ds.on("add", this.updateInfo, this);
22445 * @class Roo.bootstrap.MessageBar
22446 * @extends Roo.bootstrap.Component
22447 * Bootstrap MessageBar class
22448 * @cfg {String} html contents of the MessageBar
22449 * @cfg {String} weight (info | success | warning | danger) default info
22450 * @cfg {String} beforeClass insert the bar before the given class
22451 * @cfg {Boolean} closable (true | false) default false
22452 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22455 * Create a new Element
22456 * @param {Object} config The config object
22459 Roo.bootstrap.MessageBar = function(config){
22460 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22463 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22469 beforeClass: 'bootstrap-sticky-wrap',
22471 getAutoCreate : function(){
22475 cls: 'alert alert-dismissable alert-' + this.weight,
22480 html: this.html || ''
22486 cfg.cls += ' alert-messages-fixed';
22500 onRender : function(ct, position)
22502 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22505 var cfg = Roo.apply({}, this.getAutoCreate());
22509 cfg.cls += ' ' + this.cls;
22512 cfg.style = this.style;
22514 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22516 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22519 this.el.select('>button.close').on('click', this.hide, this);
22525 if (!this.rendered) {
22531 this.fireEvent('show', this);
22537 if (!this.rendered) {
22543 this.fireEvent('hide', this);
22546 update : function()
22548 // var e = this.el.dom.firstChild;
22550 // if(this.closable){
22551 // e = e.nextSibling;
22554 // e.data = this.html || '';
22556 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22572 * @class Roo.bootstrap.Graph
22573 * @extends Roo.bootstrap.Component
22574 * Bootstrap Graph class
22578 @cfg {String} graphtype bar | vbar | pie
22579 @cfg {number} g_x coodinator | centre x (pie)
22580 @cfg {number} g_y coodinator | centre y (pie)
22581 @cfg {number} g_r radius (pie)
22582 @cfg {number} g_height height of the chart (respected by all elements in the set)
22583 @cfg {number} g_width width of the chart (respected by all elements in the set)
22584 @cfg {Object} title The title of the chart
22587 -opts (object) options for the chart
22589 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22590 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22592 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.
22593 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22595 o stretch (boolean)
22597 -opts (object) options for the pie
22600 o startAngle (number)
22601 o endAngle (number)
22605 * Create a new Input
22606 * @param {Object} config The config object
22609 Roo.bootstrap.Graph = function(config){
22610 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22616 * The img click event for the img.
22617 * @param {Roo.EventObject} e
22623 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22634 //g_colors: this.colors,
22641 getAutoCreate : function(){
22652 onRender : function(ct,position){
22653 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22654 this.raphael = Raphael(this.el.dom);
22656 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22657 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22658 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22659 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22661 r.text(160, 10, "Single Series Chart").attr(txtattr);
22662 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22663 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22664 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22666 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22667 r.barchart(330, 10, 300, 220, data1);
22668 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22669 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22672 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22673 // r.barchart(30, 30, 560, 250, xdata, {
22674 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22675 // axis : "0 0 1 1",
22676 // axisxlabels : xdata
22677 // //yvalues : cols,
22680 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22682 // this.load(null,xdata,{
22683 // axis : "0 0 1 1",
22684 // axisxlabels : xdata
22689 load : function(graphtype,xdata,opts){
22690 this.raphael.clear();
22692 graphtype = this.graphtype;
22697 var r = this.raphael,
22698 fin = function () {
22699 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22701 fout = function () {
22702 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22704 pfin = function() {
22705 this.sector.stop();
22706 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22709 this.label[0].stop();
22710 this.label[0].attr({ r: 7.5 });
22711 this.label[1].attr({ "font-weight": 800 });
22714 pfout = function() {
22715 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22718 this.label[0].animate({ r: 5 }, 500, "bounce");
22719 this.label[1].attr({ "font-weight": 400 });
22725 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22728 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22731 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22732 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22734 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22741 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22746 setTitle: function(o)
22751 initEvents: function() {
22754 this.el.on('click', this.onClick, this);
22758 onClick : function(e)
22760 Roo.log('img onclick');
22761 this.fireEvent('click', this, e);
22773 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22776 * @class Roo.bootstrap.dash.NumberBox
22777 * @extends Roo.bootstrap.Component
22778 * Bootstrap NumberBox class
22779 * @cfg {String} headline Box headline
22780 * @cfg {String} content Box content
22781 * @cfg {String} icon Box icon
22782 * @cfg {String} footer Footer text
22783 * @cfg {String} fhref Footer href
22786 * Create a new NumberBox
22787 * @param {Object} config The config object
22791 Roo.bootstrap.dash.NumberBox = function(config){
22792 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22796 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22805 getAutoCreate : function(){
22809 cls : 'small-box ',
22817 cls : 'roo-headline',
22818 html : this.headline
22822 cls : 'roo-content',
22823 html : this.content
22837 cls : 'ion ' + this.icon
22846 cls : 'small-box-footer',
22847 href : this.fhref || '#',
22851 cfg.cn.push(footer);
22858 onRender : function(ct,position){
22859 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22866 setHeadline: function (value)
22868 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22871 setFooter: function (value, href)
22873 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22876 this.el.select('a.small-box-footer',true).first().attr('href', href);
22881 setContent: function (value)
22883 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22886 initEvents: function()
22900 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22903 * @class Roo.bootstrap.dash.TabBox
22904 * @extends Roo.bootstrap.Component
22905 * Bootstrap TabBox class
22906 * @cfg {String} title Title of the TabBox
22907 * @cfg {String} icon Icon of the TabBox
22908 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22909 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22912 * Create a new TabBox
22913 * @param {Object} config The config object
22917 Roo.bootstrap.dash.TabBox = function(config){
22918 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22923 * When a pane is added
22924 * @param {Roo.bootstrap.dash.TabPane} pane
22928 * @event activatepane
22929 * When a pane is activated
22930 * @param {Roo.bootstrap.dash.TabPane} pane
22932 "activatepane" : true
22940 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22945 tabScrollable : false,
22947 getChildContainer : function()
22949 return this.el.select('.tab-content', true).first();
22952 getAutoCreate : function(){
22956 cls: 'pull-left header',
22964 cls: 'fa ' + this.icon
22970 cls: 'nav nav-tabs pull-right',
22976 if(this.tabScrollable){
22983 cls: 'nav nav-tabs pull-right',
22994 cls: 'nav-tabs-custom',
22999 cls: 'tab-content no-padding',
23007 initEvents : function()
23009 //Roo.log('add add pane handler');
23010 this.on('addpane', this.onAddPane, this);
23013 * Updates the box title
23014 * @param {String} html to set the title to.
23016 setTitle : function(value)
23018 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23020 onAddPane : function(pane)
23022 this.panes.push(pane);
23023 //Roo.log('addpane');
23025 // tabs are rendere left to right..
23026 if(!this.showtabs){
23030 var ctr = this.el.select('.nav-tabs', true).first();
23033 var existing = ctr.select('.nav-tab',true);
23034 var qty = existing.getCount();;
23037 var tab = ctr.createChild({
23039 cls : 'nav-tab' + (qty ? '' : ' active'),
23047 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23050 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23052 pane.el.addClass('active');
23057 onTabClick : function(ev,un,ob,pane)
23059 //Roo.log('tab - prev default');
23060 ev.preventDefault();
23063 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23064 pane.tab.addClass('active');
23065 //Roo.log(pane.title);
23066 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23067 // technically we should have a deactivate event.. but maybe add later.
23068 // and it should not de-activate the selected tab...
23069 this.fireEvent('activatepane', pane);
23070 pane.el.addClass('active');
23071 pane.fireEvent('activate');
23076 getActivePane : function()
23079 Roo.each(this.panes, function(p) {
23080 if(p.el.hasClass('active')){
23101 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23103 * @class Roo.bootstrap.TabPane
23104 * @extends Roo.bootstrap.Component
23105 * Bootstrap TabPane class
23106 * @cfg {Boolean} active (false | true) Default false
23107 * @cfg {String} title title of panel
23111 * Create a new TabPane
23112 * @param {Object} config The config object
23115 Roo.bootstrap.dash.TabPane = function(config){
23116 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23122 * When a pane is activated
23123 * @param {Roo.bootstrap.dash.TabPane} pane
23130 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23135 // the tabBox that this is attached to.
23138 getAutoCreate : function()
23146 cfg.cls += ' active';
23151 initEvents : function()
23153 //Roo.log('trigger add pane handler');
23154 this.parent().fireEvent('addpane', this)
23158 * Updates the tab title
23159 * @param {String} html to set the title to.
23161 setTitle: function(str)
23167 this.tab.select('a', true).first().dom.innerHTML = str;
23184 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23187 * @class Roo.bootstrap.menu.Menu
23188 * @extends Roo.bootstrap.Component
23189 * Bootstrap Menu class - container for Menu
23190 * @cfg {String} html Text of the menu
23191 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23192 * @cfg {String} icon Font awesome icon
23193 * @cfg {String} pos Menu align to (top | bottom) default bottom
23197 * Create a new Menu
23198 * @param {Object} config The config object
23202 Roo.bootstrap.menu.Menu = function(config){
23203 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23207 * @event beforeshow
23208 * Fires before this menu is displayed
23209 * @param {Roo.bootstrap.menu.Menu} this
23213 * @event beforehide
23214 * Fires before this menu is hidden
23215 * @param {Roo.bootstrap.menu.Menu} this
23220 * Fires after this menu is displayed
23221 * @param {Roo.bootstrap.menu.Menu} this
23226 * Fires after this menu is hidden
23227 * @param {Roo.bootstrap.menu.Menu} this
23232 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23233 * @param {Roo.bootstrap.menu.Menu} this
23234 * @param {Roo.EventObject} e
23241 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23245 weight : 'default',
23250 getChildContainer : function() {
23251 if(this.isSubMenu){
23255 return this.el.select('ul.dropdown-menu', true).first();
23258 getAutoCreate : function()
23263 cls : 'roo-menu-text',
23271 cls : 'fa ' + this.icon
23282 cls : 'dropdown-button btn btn-' + this.weight,
23287 cls : 'dropdown-toggle btn btn-' + this.weight,
23297 cls : 'dropdown-menu'
23303 if(this.pos == 'top'){
23304 cfg.cls += ' dropup';
23307 if(this.isSubMenu){
23310 cls : 'dropdown-menu'
23317 onRender : function(ct, position)
23319 this.isSubMenu = ct.hasClass('dropdown-submenu');
23321 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23324 initEvents : function()
23326 if(this.isSubMenu){
23330 this.hidden = true;
23332 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23333 this.triggerEl.on('click', this.onTriggerPress, this);
23335 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23336 this.buttonEl.on('click', this.onClick, this);
23342 if(this.isSubMenu){
23346 return this.el.select('ul.dropdown-menu', true).first();
23349 onClick : function(e)
23351 this.fireEvent("click", this, e);
23354 onTriggerPress : function(e)
23356 if (this.isVisible()) {
23363 isVisible : function(){
23364 return !this.hidden;
23369 this.fireEvent("beforeshow", this);
23371 this.hidden = false;
23372 this.el.addClass('open');
23374 Roo.get(document).on("mouseup", this.onMouseUp, this);
23376 this.fireEvent("show", this);
23383 this.fireEvent("beforehide", this);
23385 this.hidden = true;
23386 this.el.removeClass('open');
23388 Roo.get(document).un("mouseup", this.onMouseUp);
23390 this.fireEvent("hide", this);
23393 onMouseUp : function()
23407 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23410 * @class Roo.bootstrap.menu.Item
23411 * @extends Roo.bootstrap.Component
23412 * Bootstrap MenuItem class
23413 * @cfg {Boolean} submenu (true | false) default false
23414 * @cfg {String} html text of the item
23415 * @cfg {String} href the link
23416 * @cfg {Boolean} disable (true | false) default false
23417 * @cfg {Boolean} preventDefault (true | false) default true
23418 * @cfg {String} icon Font awesome icon
23419 * @cfg {String} pos Submenu align to (left | right) default right
23423 * Create a new Item
23424 * @param {Object} config The config object
23428 Roo.bootstrap.menu.Item = function(config){
23429 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23433 * Fires when the mouse is hovering over this menu
23434 * @param {Roo.bootstrap.menu.Item} this
23435 * @param {Roo.EventObject} e
23440 * Fires when the mouse exits this menu
23441 * @param {Roo.bootstrap.menu.Item} this
23442 * @param {Roo.EventObject} e
23448 * The raw click event for the entire grid.
23449 * @param {Roo.EventObject} e
23455 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23460 preventDefault: true,
23465 getAutoCreate : function()
23470 cls : 'roo-menu-item-text',
23478 cls : 'fa ' + this.icon
23487 href : this.href || '#',
23494 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23498 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23500 if(this.pos == 'left'){
23501 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23508 initEvents : function()
23510 this.el.on('mouseover', this.onMouseOver, this);
23511 this.el.on('mouseout', this.onMouseOut, this);
23513 this.el.select('a', true).first().on('click', this.onClick, this);
23517 onClick : function(e)
23519 if(this.preventDefault){
23520 e.preventDefault();
23523 this.fireEvent("click", this, e);
23526 onMouseOver : function(e)
23528 if(this.submenu && this.pos == 'left'){
23529 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23532 this.fireEvent("mouseover", this, e);
23535 onMouseOut : function(e)
23537 this.fireEvent("mouseout", this, e);
23549 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23552 * @class Roo.bootstrap.menu.Separator
23553 * @extends Roo.bootstrap.Component
23554 * Bootstrap Separator class
23557 * Create a new Separator
23558 * @param {Object} config The config object
23562 Roo.bootstrap.menu.Separator = function(config){
23563 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23566 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23568 getAutoCreate : function(){
23589 * @class Roo.bootstrap.Tooltip
23590 * Bootstrap Tooltip class
23591 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23592 * to determine which dom element triggers the tooltip.
23594 * It needs to add support for additional attributes like tooltip-position
23597 * Create a new Toolti
23598 * @param {Object} config The config object
23601 Roo.bootstrap.Tooltip = function(config){
23602 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23605 Roo.apply(Roo.bootstrap.Tooltip, {
23607 * @function init initialize tooltip monitoring.
23611 currentTip : false,
23612 currentRegion : false,
23618 Roo.get(document).on('mouseover', this.enter ,this);
23619 Roo.get(document).on('mouseout', this.leave, this);
23622 this.currentTip = new Roo.bootstrap.Tooltip();
23625 enter : function(ev)
23627 var dom = ev.getTarget();
23629 //Roo.log(['enter',dom]);
23630 var el = Roo.fly(dom);
23631 if (this.currentEl) {
23633 //Roo.log(this.currentEl);
23634 //Roo.log(this.currentEl.contains(dom));
23635 if (this.currentEl == el) {
23638 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23644 if (this.currentTip.el) {
23645 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23650 // you can not look for children, as if el is the body.. then everythign is the child..
23651 if (!el.attr('tooltip')) { //
23652 if (!el.select("[tooltip]").elements.length) {
23655 // is the mouse over this child...?
23656 bindEl = el.select("[tooltip]").first();
23657 var xy = ev.getXY();
23658 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23659 //Roo.log("not in region.");
23662 //Roo.log("child element over..");
23665 this.currentEl = bindEl;
23666 this.currentTip.bind(bindEl);
23667 this.currentRegion = Roo.lib.Region.getRegion(dom);
23668 this.currentTip.enter();
23671 leave : function(ev)
23673 var dom = ev.getTarget();
23674 //Roo.log(['leave',dom]);
23675 if (!this.currentEl) {
23680 if (dom != this.currentEl.dom) {
23683 var xy = ev.getXY();
23684 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23687 // only activate leave if mouse cursor is outside... bounding box..
23692 if (this.currentTip) {
23693 this.currentTip.leave();
23695 //Roo.log('clear currentEl');
23696 this.currentEl = false;
23701 'left' : ['r-l', [-2,0], 'right'],
23702 'right' : ['l-r', [2,0], 'left'],
23703 'bottom' : ['t-b', [0,2], 'top'],
23704 'top' : [ 'b-t', [0,-2], 'bottom']
23710 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23715 delay : null, // can be { show : 300 , hide: 500}
23719 hoverState : null, //???
23721 placement : 'bottom',
23723 getAutoCreate : function(){
23730 cls : 'tooltip-arrow'
23733 cls : 'tooltip-inner'
23740 bind : function(el)
23746 enter : function () {
23748 if (this.timeout != null) {
23749 clearTimeout(this.timeout);
23752 this.hoverState = 'in';
23753 //Roo.log("enter - show");
23754 if (!this.delay || !this.delay.show) {
23759 this.timeout = setTimeout(function () {
23760 if (_t.hoverState == 'in') {
23763 }, this.delay.show);
23767 clearTimeout(this.timeout);
23769 this.hoverState = 'out';
23770 if (!this.delay || !this.delay.hide) {
23776 this.timeout = setTimeout(function () {
23777 //Roo.log("leave - timeout");
23779 if (_t.hoverState == 'out') {
23781 Roo.bootstrap.Tooltip.currentEl = false;
23789 this.render(document.body);
23792 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23794 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23796 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23798 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23800 var placement = typeof this.placement == 'function' ?
23801 this.placement.call(this, this.el, on_el) :
23804 var autoToken = /\s?auto?\s?/i;
23805 var autoPlace = autoToken.test(placement);
23807 placement = placement.replace(autoToken, '') || 'top';
23811 //this.el.setXY([0,0]);
23813 //this.el.dom.style.display='block';
23815 //this.el.appendTo(on_el);
23817 var p = this.getPosition();
23818 var box = this.el.getBox();
23824 var align = Roo.bootstrap.Tooltip.alignment[placement];
23826 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23828 if(placement == 'top' || placement == 'bottom'){
23830 placement = 'right';
23833 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23834 placement = 'left';
23838 align = Roo.bootstrap.Tooltip.alignment[placement];
23840 this.el.alignTo(this.bindEl, align[0],align[1]);
23841 //var arrow = this.el.select('.arrow',true).first();
23842 //arrow.set(align[2],
23844 this.el.addClass(placement);
23846 this.el.addClass('in fade');
23848 this.hoverState = null;
23850 if (this.el.hasClass('fade')) {
23861 //this.el.setXY([0,0]);
23862 this.el.removeClass('in');
23878 * @class Roo.bootstrap.LocationPicker
23879 * @extends Roo.bootstrap.Component
23880 * Bootstrap LocationPicker class
23881 * @cfg {Number} latitude Position when init default 0
23882 * @cfg {Number} longitude Position when init default 0
23883 * @cfg {Number} zoom default 15
23884 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23885 * @cfg {Boolean} mapTypeControl default false
23886 * @cfg {Boolean} disableDoubleClickZoom default false
23887 * @cfg {Boolean} scrollwheel default true
23888 * @cfg {Boolean} streetViewControl default false
23889 * @cfg {Number} radius default 0
23890 * @cfg {String} locationName
23891 * @cfg {Boolean} draggable default true
23892 * @cfg {Boolean} enableAutocomplete default false
23893 * @cfg {Boolean} enableReverseGeocode default true
23894 * @cfg {String} markerTitle
23897 * Create a new LocationPicker
23898 * @param {Object} config The config object
23902 Roo.bootstrap.LocationPicker = function(config){
23904 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23909 * Fires when the picker initialized.
23910 * @param {Roo.bootstrap.LocationPicker} this
23911 * @param {Google Location} location
23915 * @event positionchanged
23916 * Fires when the picker position changed.
23917 * @param {Roo.bootstrap.LocationPicker} this
23918 * @param {Google Location} location
23920 positionchanged : true,
23923 * Fires when the map resize.
23924 * @param {Roo.bootstrap.LocationPicker} this
23929 * Fires when the map show.
23930 * @param {Roo.bootstrap.LocationPicker} this
23935 * Fires when the map hide.
23936 * @param {Roo.bootstrap.LocationPicker} this
23941 * Fires when click the map.
23942 * @param {Roo.bootstrap.LocationPicker} this
23943 * @param {Map event} e
23947 * @event mapRightClick
23948 * Fires when right click the map.
23949 * @param {Roo.bootstrap.LocationPicker} this
23950 * @param {Map event} e
23952 mapRightClick : true,
23954 * @event markerClick
23955 * Fires when click the marker.
23956 * @param {Roo.bootstrap.LocationPicker} this
23957 * @param {Map event} e
23959 markerClick : true,
23961 * @event markerRightClick
23962 * Fires when right click the marker.
23963 * @param {Roo.bootstrap.LocationPicker} this
23964 * @param {Map event} e
23966 markerRightClick : true,
23968 * @event OverlayViewDraw
23969 * Fires when OverlayView Draw
23970 * @param {Roo.bootstrap.LocationPicker} this
23972 OverlayViewDraw : true,
23974 * @event OverlayViewOnAdd
23975 * Fires when OverlayView Draw
23976 * @param {Roo.bootstrap.LocationPicker} this
23978 OverlayViewOnAdd : true,
23980 * @event OverlayViewOnRemove
23981 * Fires when OverlayView Draw
23982 * @param {Roo.bootstrap.LocationPicker} this
23984 OverlayViewOnRemove : true,
23986 * @event OverlayViewShow
23987 * Fires when OverlayView Draw
23988 * @param {Roo.bootstrap.LocationPicker} this
23989 * @param {Pixel} cpx
23991 OverlayViewShow : true,
23993 * @event OverlayViewHide
23994 * Fires when OverlayView Draw
23995 * @param {Roo.bootstrap.LocationPicker} this
23997 OverlayViewHide : true,
23999 * @event loadexception
24000 * Fires when load google lib failed.
24001 * @param {Roo.bootstrap.LocationPicker} this
24003 loadexception : true
24008 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24010 gMapContext: false,
24016 mapTypeControl: false,
24017 disableDoubleClickZoom: false,
24019 streetViewControl: false,
24023 enableAutocomplete: false,
24024 enableReverseGeocode: true,
24027 getAutoCreate: function()
24032 cls: 'roo-location-picker'
24038 initEvents: function(ct, position)
24040 if(!this.el.getWidth() || this.isApplied()){
24044 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24049 initial: function()
24051 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24052 this.fireEvent('loadexception', this);
24056 if(!this.mapTypeId){
24057 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24060 this.gMapContext = this.GMapContext();
24062 this.initOverlayView();
24064 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24068 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24069 _this.setPosition(_this.gMapContext.marker.position);
24072 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24073 _this.fireEvent('mapClick', this, event);
24077 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24078 _this.fireEvent('mapRightClick', this, event);
24082 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24083 _this.fireEvent('markerClick', this, event);
24087 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24088 _this.fireEvent('markerRightClick', this, event);
24092 this.setPosition(this.gMapContext.location);
24094 this.fireEvent('initial', this, this.gMapContext.location);
24097 initOverlayView: function()
24101 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24105 _this.fireEvent('OverlayViewDraw', _this);
24110 _this.fireEvent('OverlayViewOnAdd', _this);
24113 onRemove: function()
24115 _this.fireEvent('OverlayViewOnRemove', _this);
24118 show: function(cpx)
24120 _this.fireEvent('OverlayViewShow', _this, cpx);
24125 _this.fireEvent('OverlayViewHide', _this);
24131 fromLatLngToContainerPixel: function(event)
24133 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24136 isApplied: function()
24138 return this.getGmapContext() == false ? false : true;
24141 getGmapContext: function()
24143 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24146 GMapContext: function()
24148 var position = new google.maps.LatLng(this.latitude, this.longitude);
24150 var _map = new google.maps.Map(this.el.dom, {
24153 mapTypeId: this.mapTypeId,
24154 mapTypeControl: this.mapTypeControl,
24155 disableDoubleClickZoom: this.disableDoubleClickZoom,
24156 scrollwheel: this.scrollwheel,
24157 streetViewControl: this.streetViewControl,
24158 locationName: this.locationName,
24159 draggable: this.draggable,
24160 enableAutocomplete: this.enableAutocomplete,
24161 enableReverseGeocode: this.enableReverseGeocode
24164 var _marker = new google.maps.Marker({
24165 position: position,
24167 title: this.markerTitle,
24168 draggable: this.draggable
24175 location: position,
24176 radius: this.radius,
24177 locationName: this.locationName,
24178 addressComponents: {
24179 formatted_address: null,
24180 addressLine1: null,
24181 addressLine2: null,
24183 streetNumber: null,
24187 stateOrProvince: null
24190 domContainer: this.el.dom,
24191 geodecoder: new google.maps.Geocoder()
24195 drawCircle: function(center, radius, options)
24197 if (this.gMapContext.circle != null) {
24198 this.gMapContext.circle.setMap(null);
24202 options = Roo.apply({}, options, {
24203 strokeColor: "#0000FF",
24204 strokeOpacity: .35,
24206 fillColor: "#0000FF",
24210 options.map = this.gMapContext.map;
24211 options.radius = radius;
24212 options.center = center;
24213 this.gMapContext.circle = new google.maps.Circle(options);
24214 return this.gMapContext.circle;
24220 setPosition: function(location)
24222 this.gMapContext.location = location;
24223 this.gMapContext.marker.setPosition(location);
24224 this.gMapContext.map.panTo(location);
24225 this.drawCircle(location, this.gMapContext.radius, {});
24229 if (this.gMapContext.settings.enableReverseGeocode) {
24230 this.gMapContext.geodecoder.geocode({
24231 latLng: this.gMapContext.location
24232 }, function(results, status) {
24234 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24235 _this.gMapContext.locationName = results[0].formatted_address;
24236 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24238 _this.fireEvent('positionchanged', this, location);
24245 this.fireEvent('positionchanged', this, location);
24250 google.maps.event.trigger(this.gMapContext.map, "resize");
24252 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24254 this.fireEvent('resize', this);
24257 setPositionByLatLng: function(latitude, longitude)
24259 this.setPosition(new google.maps.LatLng(latitude, longitude));
24262 getCurrentPosition: function()
24265 latitude: this.gMapContext.location.lat(),
24266 longitude: this.gMapContext.location.lng()
24270 getAddressName: function()
24272 return this.gMapContext.locationName;
24275 getAddressComponents: function()
24277 return this.gMapContext.addressComponents;
24280 address_component_from_google_geocode: function(address_components)
24284 for (var i = 0; i < address_components.length; i++) {
24285 var component = address_components[i];
24286 if (component.types.indexOf("postal_code") >= 0) {
24287 result.postalCode = component.short_name;
24288 } else if (component.types.indexOf("street_number") >= 0) {
24289 result.streetNumber = component.short_name;
24290 } else if (component.types.indexOf("route") >= 0) {
24291 result.streetName = component.short_name;
24292 } else if (component.types.indexOf("neighborhood") >= 0) {
24293 result.city = component.short_name;
24294 } else if (component.types.indexOf("locality") >= 0) {
24295 result.city = component.short_name;
24296 } else if (component.types.indexOf("sublocality") >= 0) {
24297 result.district = component.short_name;
24298 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24299 result.stateOrProvince = component.short_name;
24300 } else if (component.types.indexOf("country") >= 0) {
24301 result.country = component.short_name;
24305 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24306 result.addressLine2 = "";
24310 setZoomLevel: function(zoom)
24312 this.gMapContext.map.setZoom(zoom);
24325 this.fireEvent('show', this);
24336 this.fireEvent('hide', this);
24341 Roo.apply(Roo.bootstrap.LocationPicker, {
24343 OverlayView : function(map, options)
24345 options = options || {};
24359 * @class Roo.bootstrap.Alert
24360 * @extends Roo.bootstrap.Component
24361 * Bootstrap Alert class
24362 * @cfg {String} title The title of alert
24363 * @cfg {String} html The content of alert
24364 * @cfg {String} weight ( success | info | warning | danger )
24365 * @cfg {String} faicon font-awesomeicon
24368 * Create a new alert
24369 * @param {Object} config The config object
24373 Roo.bootstrap.Alert = function(config){
24374 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24378 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24385 getAutoCreate : function()
24394 cls : 'roo-alert-icon'
24399 cls : 'roo-alert-title',
24404 cls : 'roo-alert-text',
24411 cfg.cn[0].cls += ' fa ' + this.faicon;
24415 cfg.cls += ' alert-' + this.weight;
24421 initEvents: function()
24423 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24426 setTitle : function(str)
24428 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24431 setText : function(str)
24433 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24436 setWeight : function(weight)
24439 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24442 this.weight = weight;
24444 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24447 setIcon : function(icon)
24450 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24453 this.faicon = icon;
24455 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24476 * @class Roo.bootstrap.UploadCropbox
24477 * @extends Roo.bootstrap.Component
24478 * Bootstrap UploadCropbox class
24479 * @cfg {String} emptyText show when image has been loaded
24480 * @cfg {String} rotateNotify show when image too small to rotate
24481 * @cfg {Number} errorTimeout default 3000
24482 * @cfg {Number} minWidth default 300
24483 * @cfg {Number} minHeight default 300
24484 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24485 * @cfg {Boolean} isDocument (true|false) default false
24486 * @cfg {String} url action url
24487 * @cfg {String} paramName default 'imageUpload'
24488 * @cfg {String} method default POST
24489 * @cfg {Boolean} loadMask (true|false) default true
24490 * @cfg {Boolean} loadingText default 'Loading...'
24493 * Create a new UploadCropbox
24494 * @param {Object} config The config object
24497 Roo.bootstrap.UploadCropbox = function(config){
24498 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24502 * @event beforeselectfile
24503 * Fire before select file
24504 * @param {Roo.bootstrap.UploadCropbox} this
24506 "beforeselectfile" : true,
24509 * Fire after initEvent
24510 * @param {Roo.bootstrap.UploadCropbox} this
24515 * Fire after initEvent
24516 * @param {Roo.bootstrap.UploadCropbox} this
24517 * @param {String} data
24522 * Fire when preparing the file data
24523 * @param {Roo.bootstrap.UploadCropbox} this
24524 * @param {Object} file
24529 * Fire when get exception
24530 * @param {Roo.bootstrap.UploadCropbox} this
24531 * @param {XMLHttpRequest} xhr
24533 "exception" : true,
24535 * @event beforeloadcanvas
24536 * Fire before load the canvas
24537 * @param {Roo.bootstrap.UploadCropbox} this
24538 * @param {String} src
24540 "beforeloadcanvas" : true,
24543 * Fire when trash image
24544 * @param {Roo.bootstrap.UploadCropbox} this
24549 * Fire when download the image
24550 * @param {Roo.bootstrap.UploadCropbox} this
24554 * @event footerbuttonclick
24555 * Fire when footerbuttonclick
24556 * @param {Roo.bootstrap.UploadCropbox} this
24557 * @param {String} type
24559 "footerbuttonclick" : true,
24563 * @param {Roo.bootstrap.UploadCropbox} this
24568 * Fire when rotate the image
24569 * @param {Roo.bootstrap.UploadCropbox} this
24570 * @param {String} pos
24575 * Fire when inspect the file
24576 * @param {Roo.bootstrap.UploadCropbox} this
24577 * @param {Object} file
24582 * Fire when xhr upload the file
24583 * @param {Roo.bootstrap.UploadCropbox} this
24584 * @param {Object} data
24589 * Fire when arrange the file data
24590 * @param {Roo.bootstrap.UploadCropbox} this
24591 * @param {Object} formData
24596 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24599 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24601 emptyText : 'Click to upload image',
24602 rotateNotify : 'Image is too small to rotate',
24603 errorTimeout : 3000,
24617 cropType : 'image/jpeg',
24619 canvasLoaded : false,
24620 isDocument : false,
24622 paramName : 'imageUpload',
24624 loadingText : 'Loading...',
24627 getAutoCreate : function()
24631 cls : 'roo-upload-cropbox',
24635 cls : 'roo-upload-cropbox-selector',
24640 cls : 'roo-upload-cropbox-body',
24641 style : 'cursor:pointer',
24645 cls : 'roo-upload-cropbox-preview'
24649 cls : 'roo-upload-cropbox-thumb'
24653 cls : 'roo-upload-cropbox-empty-notify',
24654 html : this.emptyText
24658 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24659 html : this.rotateNotify
24665 cls : 'roo-upload-cropbox-footer',
24668 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24678 onRender : function(ct, position)
24680 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24682 if (this.buttons.length) {
24684 Roo.each(this.buttons, function(bb) {
24686 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24688 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24694 this.maskEl = this.el;
24698 initEvents : function()
24700 this.urlAPI = (window.createObjectURL && window) ||
24701 (window.URL && URL.revokeObjectURL && URL) ||
24702 (window.webkitURL && webkitURL);
24704 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24705 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24707 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24708 this.selectorEl.hide();
24710 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24711 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24713 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24714 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24715 this.thumbEl.hide();
24717 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24718 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24720 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24721 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24722 this.errorEl.hide();
24724 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24725 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24726 this.footerEl.hide();
24728 this.setThumbBoxSize();
24734 this.fireEvent('initial', this);
24741 window.addEventListener("resize", function() { _this.resize(); } );
24743 this.bodyEl.on('click', this.beforeSelectFile, this);
24746 this.bodyEl.on('touchstart', this.onTouchStart, this);
24747 this.bodyEl.on('touchmove', this.onTouchMove, this);
24748 this.bodyEl.on('touchend', this.onTouchEnd, this);
24752 this.bodyEl.on('mousedown', this.onMouseDown, this);
24753 this.bodyEl.on('mousemove', this.onMouseMove, this);
24754 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24755 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24756 Roo.get(document).on('mouseup', this.onMouseUp, this);
24759 this.selectorEl.on('change', this.onFileSelected, this);
24765 this.baseScale = 1;
24767 this.baseRotate = 1;
24768 this.dragable = false;
24769 this.pinching = false;
24772 this.cropData = false;
24773 this.notifyEl.dom.innerHTML = this.emptyText;
24775 this.selectorEl.dom.value = '';
24779 resize : function()
24781 if(this.fireEvent('resize', this) != false){
24782 this.setThumbBoxPosition();
24783 this.setCanvasPosition();
24787 onFooterButtonClick : function(e, el, o, type)
24790 case 'rotate-left' :
24791 this.onRotateLeft(e);
24793 case 'rotate-right' :
24794 this.onRotateRight(e);
24797 this.beforeSelectFile(e);
24812 this.fireEvent('footerbuttonclick', this, type);
24815 beforeSelectFile : function(e)
24817 e.preventDefault();
24819 if(this.fireEvent('beforeselectfile', this) != false){
24820 this.selectorEl.dom.click();
24824 onFileSelected : function(e)
24826 e.preventDefault();
24828 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24832 var file = this.selectorEl.dom.files[0];
24834 if(this.fireEvent('inspect', this, file) != false){
24835 this.prepare(file);
24840 trash : function(e)
24842 this.fireEvent('trash', this);
24845 download : function(e)
24847 this.fireEvent('download', this);
24850 loadCanvas : function(src)
24852 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24856 this.imageEl = document.createElement('img');
24860 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24862 this.imageEl.src = src;
24866 onLoadCanvas : function()
24868 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24869 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24871 this.bodyEl.un('click', this.beforeSelectFile, this);
24873 this.notifyEl.hide();
24874 this.thumbEl.show();
24875 this.footerEl.show();
24877 this.baseRotateLevel();
24879 if(this.isDocument){
24880 this.setThumbBoxSize();
24883 this.setThumbBoxPosition();
24885 this.baseScaleLevel();
24891 this.canvasLoaded = true;
24894 this.maskEl.unmask();
24899 setCanvasPosition : function()
24901 if(!this.canvasEl){
24905 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24906 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24908 this.previewEl.setLeft(pw);
24909 this.previewEl.setTop(ph);
24913 onMouseDown : function(e)
24917 this.dragable = true;
24918 this.pinching = false;
24920 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24921 this.dragable = false;
24925 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24926 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24930 onMouseMove : function(e)
24934 if(!this.canvasLoaded){
24938 if (!this.dragable){
24942 var minX = Math.ceil(this.thumbEl.getLeft(true));
24943 var minY = Math.ceil(this.thumbEl.getTop(true));
24945 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24946 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24948 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24949 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24951 x = x - this.mouseX;
24952 y = y - this.mouseY;
24954 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24955 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24957 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24958 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24960 this.previewEl.setLeft(bgX);
24961 this.previewEl.setTop(bgY);
24963 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24964 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24967 onMouseUp : function(e)
24971 this.dragable = false;
24974 onMouseWheel : function(e)
24978 this.startScale = this.scale;
24980 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24982 if(!this.zoomable()){
24983 this.scale = this.startScale;
24992 zoomable : function()
24994 var minScale = this.thumbEl.getWidth() / this.minWidth;
24996 if(this.minWidth < this.minHeight){
24997 minScale = this.thumbEl.getHeight() / this.minHeight;
25000 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25001 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25005 (this.rotate == 0 || this.rotate == 180) &&
25007 width > this.imageEl.OriginWidth ||
25008 height > this.imageEl.OriginHeight ||
25009 (width < this.minWidth && height < this.minHeight)
25017 (this.rotate == 90 || this.rotate == 270) &&
25019 width > this.imageEl.OriginWidth ||
25020 height > this.imageEl.OriginHeight ||
25021 (width < this.minHeight && height < this.minWidth)
25028 !this.isDocument &&
25029 (this.rotate == 0 || this.rotate == 180) &&
25031 width < this.minWidth ||
25032 width > this.imageEl.OriginWidth ||
25033 height < this.minHeight ||
25034 height > this.imageEl.OriginHeight
25041 !this.isDocument &&
25042 (this.rotate == 90 || this.rotate == 270) &&
25044 width < this.minHeight ||
25045 width > this.imageEl.OriginWidth ||
25046 height < this.minWidth ||
25047 height > this.imageEl.OriginHeight
25057 onRotateLeft : function(e)
25059 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25061 var minScale = this.thumbEl.getWidth() / this.minWidth;
25063 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25064 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25066 this.startScale = this.scale;
25068 while (this.getScaleLevel() < minScale){
25070 this.scale = this.scale + 1;
25072 if(!this.zoomable()){
25077 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25078 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25083 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25090 this.scale = this.startScale;
25092 this.onRotateFail();
25097 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25099 if(this.isDocument){
25100 this.setThumbBoxSize();
25101 this.setThumbBoxPosition();
25102 this.setCanvasPosition();
25107 this.fireEvent('rotate', this, 'left');
25111 onRotateRight : function(e)
25113 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25115 var minScale = this.thumbEl.getWidth() / this.minWidth;
25117 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25118 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25120 this.startScale = this.scale;
25122 while (this.getScaleLevel() < minScale){
25124 this.scale = this.scale + 1;
25126 if(!this.zoomable()){
25131 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25132 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25137 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25144 this.scale = this.startScale;
25146 this.onRotateFail();
25151 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25153 if(this.isDocument){
25154 this.setThumbBoxSize();
25155 this.setThumbBoxPosition();
25156 this.setCanvasPosition();
25161 this.fireEvent('rotate', this, 'right');
25164 onRotateFail : function()
25166 this.errorEl.show(true);
25170 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25175 this.previewEl.dom.innerHTML = '';
25177 var canvasEl = document.createElement("canvas");
25179 var contextEl = canvasEl.getContext("2d");
25181 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25182 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25183 var center = this.imageEl.OriginWidth / 2;
25185 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25186 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25187 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25188 center = this.imageEl.OriginHeight / 2;
25191 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25193 contextEl.translate(center, center);
25194 contextEl.rotate(this.rotate * Math.PI / 180);
25196 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25198 this.canvasEl = document.createElement("canvas");
25200 this.contextEl = this.canvasEl.getContext("2d");
25202 switch (this.rotate) {
25205 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25206 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25208 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25213 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25214 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25216 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25217 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);
25221 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25226 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25227 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25229 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25230 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);
25234 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);
25239 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25240 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25242 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25243 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25247 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);
25254 this.previewEl.appendChild(this.canvasEl);
25256 this.setCanvasPosition();
25261 if(!this.canvasLoaded){
25265 var imageCanvas = document.createElement("canvas");
25267 var imageContext = imageCanvas.getContext("2d");
25269 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25270 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25272 var center = imageCanvas.width / 2;
25274 imageContext.translate(center, center);
25276 imageContext.rotate(this.rotate * Math.PI / 180);
25278 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25280 var canvas = document.createElement("canvas");
25282 var context = canvas.getContext("2d");
25284 canvas.width = this.minWidth;
25285 canvas.height = this.minHeight;
25287 switch (this.rotate) {
25290 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25291 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25293 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25294 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25296 var targetWidth = this.minWidth - 2 * x;
25297 var targetHeight = this.minHeight - 2 * y;
25301 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25302 scale = targetWidth / width;
25305 if(x > 0 && y == 0){
25306 scale = targetHeight / height;
25309 if(x > 0 && y > 0){
25310 scale = targetWidth / width;
25312 if(width < height){
25313 scale = targetHeight / height;
25317 context.scale(scale, scale);
25319 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25320 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25322 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25323 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25325 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25330 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25331 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25333 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25334 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25336 var targetWidth = this.minWidth - 2 * x;
25337 var targetHeight = this.minHeight - 2 * y;
25341 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25342 scale = targetWidth / width;
25345 if(x > 0 && y == 0){
25346 scale = targetHeight / height;
25349 if(x > 0 && y > 0){
25350 scale = targetWidth / width;
25352 if(width < height){
25353 scale = targetHeight / height;
25357 context.scale(scale, scale);
25359 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25360 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25362 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25363 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25365 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25367 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25372 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25373 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25375 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25376 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25378 var targetWidth = this.minWidth - 2 * x;
25379 var targetHeight = this.minHeight - 2 * y;
25383 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25384 scale = targetWidth / width;
25387 if(x > 0 && y == 0){
25388 scale = targetHeight / height;
25391 if(x > 0 && y > 0){
25392 scale = targetWidth / width;
25394 if(width < height){
25395 scale = targetHeight / height;
25399 context.scale(scale, scale);
25401 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25402 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25404 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25405 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25407 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25408 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25410 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25415 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25416 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25418 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25419 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25421 var targetWidth = this.minWidth - 2 * x;
25422 var targetHeight = this.minHeight - 2 * y;
25426 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25427 scale = targetWidth / width;
25430 if(x > 0 && y == 0){
25431 scale = targetHeight / height;
25434 if(x > 0 && y > 0){
25435 scale = targetWidth / width;
25437 if(width < height){
25438 scale = targetHeight / height;
25442 context.scale(scale, scale);
25444 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25445 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25447 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25448 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25450 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25452 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25459 this.cropData = canvas.toDataURL(this.cropType);
25461 if(this.fireEvent('crop', this, this.cropData) !== false){
25462 this.process(this.file, this.cropData);
25469 setThumbBoxSize : function()
25473 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25474 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25475 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25477 this.minWidth = width;
25478 this.minHeight = height;
25480 if(this.rotate == 90 || this.rotate == 270){
25481 this.minWidth = height;
25482 this.minHeight = width;
25487 width = Math.ceil(this.minWidth * height / this.minHeight);
25489 if(this.minWidth > this.minHeight){
25491 height = Math.ceil(this.minHeight * width / this.minWidth);
25494 this.thumbEl.setStyle({
25495 width : width + 'px',
25496 height : height + 'px'
25503 setThumbBoxPosition : function()
25505 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25506 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25508 this.thumbEl.setLeft(x);
25509 this.thumbEl.setTop(y);
25513 baseRotateLevel : function()
25515 this.baseRotate = 1;
25518 typeof(this.exif) != 'undefined' &&
25519 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25520 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25522 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25525 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25529 baseScaleLevel : function()
25533 if(this.isDocument){
25535 if(this.baseRotate == 6 || this.baseRotate == 8){
25537 height = this.thumbEl.getHeight();
25538 this.baseScale = height / this.imageEl.OriginWidth;
25540 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25541 width = this.thumbEl.getWidth();
25542 this.baseScale = width / this.imageEl.OriginHeight;
25548 height = this.thumbEl.getHeight();
25549 this.baseScale = height / this.imageEl.OriginHeight;
25551 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25552 width = this.thumbEl.getWidth();
25553 this.baseScale = width / this.imageEl.OriginWidth;
25559 if(this.baseRotate == 6 || this.baseRotate == 8){
25561 width = this.thumbEl.getHeight();
25562 this.baseScale = width / this.imageEl.OriginHeight;
25564 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25565 height = this.thumbEl.getWidth();
25566 this.baseScale = height / this.imageEl.OriginHeight;
25569 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25570 height = this.thumbEl.getWidth();
25571 this.baseScale = height / this.imageEl.OriginHeight;
25573 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25574 width = this.thumbEl.getHeight();
25575 this.baseScale = width / this.imageEl.OriginWidth;
25582 width = this.thumbEl.getWidth();
25583 this.baseScale = width / this.imageEl.OriginWidth;
25585 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25586 height = this.thumbEl.getHeight();
25587 this.baseScale = height / this.imageEl.OriginHeight;
25590 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25592 height = this.thumbEl.getHeight();
25593 this.baseScale = height / this.imageEl.OriginHeight;
25595 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25596 width = this.thumbEl.getWidth();
25597 this.baseScale = width / this.imageEl.OriginWidth;
25605 getScaleLevel : function()
25607 return this.baseScale * Math.pow(1.1, this.scale);
25610 onTouchStart : function(e)
25612 if(!this.canvasLoaded){
25613 this.beforeSelectFile(e);
25617 var touches = e.browserEvent.touches;
25623 if(touches.length == 1){
25624 this.onMouseDown(e);
25628 if(touches.length != 2){
25634 for(var i = 0, finger; finger = touches[i]; i++){
25635 coords.push(finger.pageX, finger.pageY);
25638 var x = Math.pow(coords[0] - coords[2], 2);
25639 var y = Math.pow(coords[1] - coords[3], 2);
25641 this.startDistance = Math.sqrt(x + y);
25643 this.startScale = this.scale;
25645 this.pinching = true;
25646 this.dragable = false;
25650 onTouchMove : function(e)
25652 if(!this.pinching && !this.dragable){
25656 var touches = e.browserEvent.touches;
25663 this.onMouseMove(e);
25669 for(var i = 0, finger; finger = touches[i]; i++){
25670 coords.push(finger.pageX, finger.pageY);
25673 var x = Math.pow(coords[0] - coords[2], 2);
25674 var y = Math.pow(coords[1] - coords[3], 2);
25676 this.endDistance = Math.sqrt(x + y);
25678 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25680 if(!this.zoomable()){
25681 this.scale = this.startScale;
25689 onTouchEnd : function(e)
25691 this.pinching = false;
25692 this.dragable = false;
25696 process : function(file, crop)
25699 this.maskEl.mask(this.loadingText);
25702 this.xhr = new XMLHttpRequest();
25704 file.xhr = this.xhr;
25706 this.xhr.open(this.method, this.url, true);
25709 "Accept": "application/json",
25710 "Cache-Control": "no-cache",
25711 "X-Requested-With": "XMLHttpRequest"
25714 for (var headerName in headers) {
25715 var headerValue = headers[headerName];
25717 this.xhr.setRequestHeader(headerName, headerValue);
25723 this.xhr.onload = function()
25725 _this.xhrOnLoad(_this.xhr);
25728 this.xhr.onerror = function()
25730 _this.xhrOnError(_this.xhr);
25733 var formData = new FormData();
25735 formData.append('returnHTML', 'NO');
25738 formData.append('crop', crop);
25741 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25742 formData.append(this.paramName, file, file.name);
25745 if(typeof(file.filename) != 'undefined'){
25746 formData.append('filename', file.filename);
25749 if(typeof(file.mimetype) != 'undefined'){
25750 formData.append('mimetype', file.mimetype);
25753 if(this.fireEvent('arrange', this, formData) != false){
25754 this.xhr.send(formData);
25758 xhrOnLoad : function(xhr)
25761 this.maskEl.unmask();
25764 if (xhr.readyState !== 4) {
25765 this.fireEvent('exception', this, xhr);
25769 var response = Roo.decode(xhr.responseText);
25771 if(!response.success){
25772 this.fireEvent('exception', this, xhr);
25776 var response = Roo.decode(xhr.responseText);
25778 this.fireEvent('upload', this, response);
25782 xhrOnError : function()
25785 this.maskEl.unmask();
25788 Roo.log('xhr on error');
25790 var response = Roo.decode(xhr.responseText);
25796 prepare : function(file)
25799 this.maskEl.mask(this.loadingText);
25805 if(typeof(file) === 'string'){
25806 this.loadCanvas(file);
25810 if(!file || !this.urlAPI){
25815 this.cropType = file.type;
25819 if(this.fireEvent('prepare', this, this.file) != false){
25821 var reader = new FileReader();
25823 reader.onload = function (e) {
25824 if (e.target.error) {
25825 Roo.log(e.target.error);
25829 var buffer = e.target.result,
25830 dataView = new DataView(buffer),
25832 maxOffset = dataView.byteLength - 4,
25836 if (dataView.getUint16(0) === 0xffd8) {
25837 while (offset < maxOffset) {
25838 markerBytes = dataView.getUint16(offset);
25840 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25841 markerLength = dataView.getUint16(offset + 2) + 2;
25842 if (offset + markerLength > dataView.byteLength) {
25843 Roo.log('Invalid meta data: Invalid segment size.');
25847 if(markerBytes == 0xffe1){
25848 _this.parseExifData(
25855 offset += markerLength;
25865 var url = _this.urlAPI.createObjectURL(_this.file);
25867 _this.loadCanvas(url);
25872 reader.readAsArrayBuffer(this.file);
25878 parseExifData : function(dataView, offset, length)
25880 var tiffOffset = offset + 10,
25884 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25885 // No Exif data, might be XMP data instead
25889 // Check for the ASCII code for "Exif" (0x45786966):
25890 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25891 // No Exif data, might be XMP data instead
25894 if (tiffOffset + 8 > dataView.byteLength) {
25895 Roo.log('Invalid Exif data: Invalid segment size.');
25898 // Check for the two null bytes:
25899 if (dataView.getUint16(offset + 8) !== 0x0000) {
25900 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25903 // Check the byte alignment:
25904 switch (dataView.getUint16(tiffOffset)) {
25906 littleEndian = true;
25909 littleEndian = false;
25912 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25915 // Check for the TIFF tag marker (0x002A):
25916 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25917 Roo.log('Invalid Exif data: Missing TIFF marker.');
25920 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25921 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25923 this.parseExifTags(
25926 tiffOffset + dirOffset,
25931 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25936 if (dirOffset + 6 > dataView.byteLength) {
25937 Roo.log('Invalid Exif data: Invalid directory offset.');
25940 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25941 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25942 if (dirEndOffset + 4 > dataView.byteLength) {
25943 Roo.log('Invalid Exif data: Invalid directory size.');
25946 for (i = 0; i < tagsNumber; i += 1) {
25950 dirOffset + 2 + 12 * i, // tag offset
25954 // Return the offset to the next directory:
25955 return dataView.getUint32(dirEndOffset, littleEndian);
25958 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25960 var tag = dataView.getUint16(offset, littleEndian);
25962 this.exif[tag] = this.getExifValue(
25966 dataView.getUint16(offset + 2, littleEndian), // tag type
25967 dataView.getUint32(offset + 4, littleEndian), // tag length
25972 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25974 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25983 Roo.log('Invalid Exif data: Invalid tag type.');
25987 tagSize = tagType.size * length;
25988 // Determine if the value is contained in the dataOffset bytes,
25989 // or if the value at the dataOffset is a pointer to the actual data:
25990 dataOffset = tagSize > 4 ?
25991 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25992 if (dataOffset + tagSize > dataView.byteLength) {
25993 Roo.log('Invalid Exif data: Invalid data offset.');
25996 if (length === 1) {
25997 return tagType.getValue(dataView, dataOffset, littleEndian);
26000 for (i = 0; i < length; i += 1) {
26001 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26004 if (tagType.ascii) {
26006 // Concatenate the chars:
26007 for (i = 0; i < values.length; i += 1) {
26009 // Ignore the terminating NULL byte(s):
26010 if (c === '\u0000') {
26022 Roo.apply(Roo.bootstrap.UploadCropbox, {
26024 'Orientation': 0x0112
26028 1: 0, //'top-left',
26030 3: 180, //'bottom-right',
26031 // 4: 'bottom-left',
26033 6: 90, //'right-top',
26034 // 7: 'right-bottom',
26035 8: 270 //'left-bottom'
26039 // byte, 8-bit unsigned int:
26041 getValue: function (dataView, dataOffset) {
26042 return dataView.getUint8(dataOffset);
26046 // ascii, 8-bit byte:
26048 getValue: function (dataView, dataOffset) {
26049 return String.fromCharCode(dataView.getUint8(dataOffset));
26054 // short, 16 bit int:
26056 getValue: function (dataView, dataOffset, littleEndian) {
26057 return dataView.getUint16(dataOffset, littleEndian);
26061 // long, 32 bit int:
26063 getValue: function (dataView, dataOffset, littleEndian) {
26064 return dataView.getUint32(dataOffset, littleEndian);
26068 // rational = two long values, first is numerator, second is denominator:
26070 getValue: function (dataView, dataOffset, littleEndian) {
26071 return dataView.getUint32(dataOffset, littleEndian) /
26072 dataView.getUint32(dataOffset + 4, littleEndian);
26076 // slong, 32 bit signed int:
26078 getValue: function (dataView, dataOffset, littleEndian) {
26079 return dataView.getInt32(dataOffset, littleEndian);
26083 // srational, two slongs, first is numerator, second is denominator:
26085 getValue: function (dataView, dataOffset, littleEndian) {
26086 return dataView.getInt32(dataOffset, littleEndian) /
26087 dataView.getInt32(dataOffset + 4, littleEndian);
26097 cls : 'btn-group roo-upload-cropbox-rotate-left',
26098 action : 'rotate-left',
26102 cls : 'btn btn-default',
26103 html : '<i class="fa fa-undo"></i>'
26109 cls : 'btn-group roo-upload-cropbox-picture',
26110 action : 'picture',
26114 cls : 'btn btn-default',
26115 html : '<i class="fa fa-picture-o"></i>'
26121 cls : 'btn-group roo-upload-cropbox-rotate-right',
26122 action : 'rotate-right',
26126 cls : 'btn btn-default',
26127 html : '<i class="fa fa-repeat"></i>'
26135 cls : 'btn-group roo-upload-cropbox-rotate-left',
26136 action : 'rotate-left',
26140 cls : 'btn btn-default',
26141 html : '<i class="fa fa-undo"></i>'
26147 cls : 'btn-group roo-upload-cropbox-download',
26148 action : 'download',
26152 cls : 'btn btn-default',
26153 html : '<i class="fa fa-download"></i>'
26159 cls : 'btn-group roo-upload-cropbox-crop',
26164 cls : 'btn btn-default',
26165 html : '<i class="fa fa-crop"></i>'
26171 cls : 'btn-group roo-upload-cropbox-trash',
26176 cls : 'btn btn-default',
26177 html : '<i class="fa fa-trash"></i>'
26183 cls : 'btn-group roo-upload-cropbox-rotate-right',
26184 action : 'rotate-right',
26188 cls : 'btn btn-default',
26189 html : '<i class="fa fa-repeat"></i>'
26197 cls : 'btn-group roo-upload-cropbox-rotate-left',
26198 action : 'rotate-left',
26202 cls : 'btn btn-default',
26203 html : '<i class="fa fa-undo"></i>'
26209 cls : 'btn-group roo-upload-cropbox-rotate-right',
26210 action : 'rotate-right',
26214 cls : 'btn btn-default',
26215 html : '<i class="fa fa-repeat"></i>'
26228 * @class Roo.bootstrap.DocumentManager
26229 * @extends Roo.bootstrap.Component
26230 * Bootstrap DocumentManager class
26231 * @cfg {String} paramName default 'imageUpload'
26232 * @cfg {String} method default POST
26233 * @cfg {String} url action url
26234 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26235 * @cfg {Boolean} multiple multiple upload default true
26236 * @cfg {Number} thumbSize default 300
26237 * @cfg {String} fieldLabel
26238 * @cfg {Number} labelWidth default 4
26239 * @cfg {String} labelAlign (left|top) default left
26240 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26243 * Create a new DocumentManager
26244 * @param {Object} config The config object
26247 Roo.bootstrap.DocumentManager = function(config){
26248 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26253 * Fire when initial the DocumentManager
26254 * @param {Roo.bootstrap.DocumentManager} this
26259 * inspect selected file
26260 * @param {Roo.bootstrap.DocumentManager} this
26261 * @param {File} file
26266 * Fire when xhr load exception
26267 * @param {Roo.bootstrap.DocumentManager} this
26268 * @param {XMLHttpRequest} xhr
26270 "exception" : true,
26273 * prepare the form data
26274 * @param {Roo.bootstrap.DocumentManager} this
26275 * @param {Object} formData
26280 * Fire when remove the file
26281 * @param {Roo.bootstrap.DocumentManager} this
26282 * @param {Object} file
26287 * Fire after refresh the file
26288 * @param {Roo.bootstrap.DocumentManager} this
26293 * Fire after click the image
26294 * @param {Roo.bootstrap.DocumentManager} this
26295 * @param {Object} file
26300 * Fire when upload a image and editable set to true
26301 * @param {Roo.bootstrap.DocumentManager} this
26302 * @param {Object} file
26306 * @event beforeselectfile
26307 * Fire before select file
26308 * @param {Roo.bootstrap.DocumentManager} this
26310 "beforeselectfile" : true,
26313 * Fire before process file
26314 * @param {Roo.bootstrap.DocumentManager} this
26315 * @param {Object} file
26322 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26331 paramName : 'imageUpload',
26334 labelAlign : 'left',
26341 getAutoCreate : function()
26343 var managerWidget = {
26345 cls : 'roo-document-manager',
26349 cls : 'roo-document-manager-selector',
26354 cls : 'roo-document-manager-uploader',
26358 cls : 'roo-document-manager-upload-btn',
26359 html : '<i class="fa fa-plus"></i>'
26370 cls : 'column col-md-12',
26375 if(this.fieldLabel.length){
26380 cls : 'column col-md-12',
26381 html : this.fieldLabel
26385 cls : 'column col-md-12',
26390 if(this.labelAlign == 'left'){
26394 cls : 'column col-md-' + this.labelWidth,
26395 html : this.fieldLabel
26399 cls : 'column col-md-' + (12 - this.labelWidth),
26409 cls : 'row clearfix',
26417 initEvents : function()
26419 this.managerEl = this.el.select('.roo-document-manager', true).first();
26420 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26422 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26423 this.selectorEl.hide();
26426 this.selectorEl.attr('multiple', 'multiple');
26429 this.selectorEl.on('change', this.onFileSelected, this);
26431 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26432 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26434 this.uploader.on('click', this.onUploaderClick, this);
26436 this.renderProgressDialog();
26440 window.addEventListener("resize", function() { _this.refresh(); } );
26442 this.fireEvent('initial', this);
26445 renderProgressDialog : function()
26449 this.progressDialog = new Roo.bootstrap.Modal({
26450 cls : 'roo-document-manager-progress-dialog',
26451 allow_close : false,
26461 btnclick : function() {
26462 _this.uploadCancel();
26468 this.progressDialog.render(Roo.get(document.body));
26470 this.progress = new Roo.bootstrap.Progress({
26471 cls : 'roo-document-manager-progress',
26476 this.progress.render(this.progressDialog.getChildContainer());
26478 this.progressBar = new Roo.bootstrap.ProgressBar({
26479 cls : 'roo-document-manager-progress-bar',
26482 aria_valuemax : 12,
26486 this.progressBar.render(this.progress.getChildContainer());
26489 onUploaderClick : function(e)
26491 e.preventDefault();
26493 if(this.fireEvent('beforeselectfile', this) != false){
26494 this.selectorEl.dom.click();
26499 onFileSelected : function(e)
26501 e.preventDefault();
26503 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26507 Roo.each(this.selectorEl.dom.files, function(file){
26508 if(this.fireEvent('inspect', this, file) != false){
26509 this.files.push(file);
26519 this.selectorEl.dom.value = '';
26521 if(!this.files.length){
26525 if(this.boxes > 0 && this.files.length > this.boxes){
26526 this.files = this.files.slice(0, this.boxes);
26529 this.uploader.show();
26531 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26532 this.uploader.hide();
26541 Roo.each(this.files, function(file){
26543 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26544 var f = this.renderPreview(file);
26549 if(file.type.indexOf('image') != -1){
26550 this.delegates.push(
26552 _this.process(file);
26553 }).createDelegate(this)
26561 _this.process(file);
26562 }).createDelegate(this)
26567 this.files = files;
26569 this.delegates = this.delegates.concat(docs);
26571 if(!this.delegates.length){
26576 this.progressBar.aria_valuemax = this.delegates.length;
26583 arrange : function()
26585 if(!this.delegates.length){
26586 this.progressDialog.hide();
26591 var delegate = this.delegates.shift();
26593 this.progressDialog.show();
26595 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26597 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26602 refresh : function()
26604 this.uploader.show();
26606 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26607 this.uploader.hide();
26610 Roo.isTouch ? this.closable(false) : this.closable(true);
26612 this.fireEvent('refresh', this);
26615 onRemove : function(e, el, o)
26617 e.preventDefault();
26619 this.fireEvent('remove', this, o);
26623 remove : function(o)
26627 Roo.each(this.files, function(file){
26628 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26637 this.files = files;
26644 Roo.each(this.files, function(file){
26649 file.target.remove();
26658 onClick : function(e, el, o)
26660 e.preventDefault();
26662 this.fireEvent('click', this, o);
26666 closable : function(closable)
26668 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26670 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26682 xhrOnLoad : function(xhr)
26684 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26688 if (xhr.readyState !== 4) {
26690 this.fireEvent('exception', this, xhr);
26694 var response = Roo.decode(xhr.responseText);
26696 if(!response.success){
26698 this.fireEvent('exception', this, xhr);
26702 var file = this.renderPreview(response.data);
26704 this.files.push(file);
26710 xhrOnError : function()
26712 Roo.log('xhr on error');
26714 var response = Roo.decode(xhr.responseText);
26721 process : function(file)
26723 if(this.fireEvent('process', this, file) !== false){
26724 if(this.editable && file.type.indexOf('image') != -1){
26725 this.fireEvent('edit', this, file);
26729 this.uploadStart(file, false);
26736 uploadStart : function(file, crop)
26738 this.xhr = new XMLHttpRequest();
26740 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26745 file.xhr = this.xhr;
26747 this.managerEl.createChild({
26749 cls : 'roo-document-manager-loading',
26753 tooltip : file.name,
26754 cls : 'roo-document-manager-thumb',
26755 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26761 this.xhr.open(this.method, this.url, true);
26764 "Accept": "application/json",
26765 "Cache-Control": "no-cache",
26766 "X-Requested-With": "XMLHttpRequest"
26769 for (var headerName in headers) {
26770 var headerValue = headers[headerName];
26772 this.xhr.setRequestHeader(headerName, headerValue);
26778 this.xhr.onload = function()
26780 _this.xhrOnLoad(_this.xhr);
26783 this.xhr.onerror = function()
26785 _this.xhrOnError(_this.xhr);
26788 var formData = new FormData();
26790 formData.append('returnHTML', 'NO');
26793 formData.append('crop', crop);
26796 formData.append(this.paramName, file, file.name);
26798 if(this.fireEvent('prepare', this, formData) != false){
26799 this.xhr.send(formData);
26803 uploadCancel : function()
26810 this.delegates = [];
26812 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26819 renderPreview : function(file)
26821 if(typeof(file.target) != 'undefined' && file.target){
26825 var previewEl = this.managerEl.createChild({
26827 cls : 'roo-document-manager-preview',
26831 tooltip : file.filename,
26832 cls : 'roo-document-manager-thumb',
26833 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26838 html : '<i class="fa fa-times-circle"></i>'
26843 var close = previewEl.select('button.close', true).first();
26845 close.on('click', this.onRemove, this, file);
26847 file.target = previewEl;
26849 var image = previewEl.select('img', true).first();
26853 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26855 image.on('click', this.onClick, this, file);
26861 onPreviewLoad : function(file, image)
26863 if(typeof(file.target) == 'undefined' || !file.target){
26867 var width = image.dom.naturalWidth || image.dom.width;
26868 var height = image.dom.naturalHeight || image.dom.height;
26870 if(width > height){
26871 file.target.addClass('wide');
26875 file.target.addClass('tall');
26880 uploadFromSource : function(file, crop)
26882 this.xhr = new XMLHttpRequest();
26884 this.managerEl.createChild({
26886 cls : 'roo-document-manager-loading',
26890 tooltip : file.name,
26891 cls : 'roo-document-manager-thumb',
26892 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26898 this.xhr.open(this.method, this.url, true);
26901 "Accept": "application/json",
26902 "Cache-Control": "no-cache",
26903 "X-Requested-With": "XMLHttpRequest"
26906 for (var headerName in headers) {
26907 var headerValue = headers[headerName];
26909 this.xhr.setRequestHeader(headerName, headerValue);
26915 this.xhr.onload = function()
26917 _this.xhrOnLoad(_this.xhr);
26920 this.xhr.onerror = function()
26922 _this.xhrOnError(_this.xhr);
26925 var formData = new FormData();
26927 formData.append('returnHTML', 'NO');
26929 formData.append('crop', crop);
26931 if(typeof(file.filename) != 'undefined'){
26932 formData.append('filename', file.filename);
26935 if(typeof(file.mimetype) != 'undefined'){
26936 formData.append('mimetype', file.mimetype);
26939 if(this.fireEvent('prepare', this, formData) != false){
26940 this.xhr.send(formData);
26950 * @class Roo.bootstrap.DocumentViewer
26951 * @extends Roo.bootstrap.Component
26952 * Bootstrap DocumentViewer class
26955 * Create a new DocumentViewer
26956 * @param {Object} config The config object
26959 Roo.bootstrap.DocumentViewer = function(config){
26960 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26965 * Fire after initEvent
26966 * @param {Roo.bootstrap.DocumentViewer} this
26972 * @param {Roo.bootstrap.DocumentViewer} this
26977 * Fire after trash button
26978 * @param {Roo.bootstrap.DocumentViewer} this
26985 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26987 getAutoCreate : function()
26991 cls : 'roo-document-viewer',
26995 cls : 'roo-document-viewer-body',
26999 cls : 'roo-document-viewer-thumb',
27003 cls : 'roo-document-viewer-image'
27011 cls : 'roo-document-viewer-footer',
27014 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27022 cls : 'btn btn-default roo-document-viewer-trash',
27023 html : '<i class="fa fa-trash"></i>'
27036 initEvents : function()
27039 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27040 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27042 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27043 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27045 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27046 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27048 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27049 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27051 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27052 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27054 this.bodyEl.on('click', this.onClick, this);
27056 this.trashBtn.on('click', this.onTrash, this);
27060 initial : function()
27062 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27065 this.fireEvent('initial', this);
27069 onClick : function(e)
27071 e.preventDefault();
27073 this.fireEvent('click', this);
27076 onTrash : function(e)
27078 e.preventDefault();
27080 this.fireEvent('trash', this);
27092 * @class Roo.bootstrap.NavProgressBar
27093 * @extends Roo.bootstrap.Component
27094 * Bootstrap NavProgressBar class
27097 * Create a new nav progress bar
27098 * @param {Object} config The config object
27101 Roo.bootstrap.NavProgressBar = function(config){
27102 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27104 this.bullets = this.bullets || [];
27106 // Roo.bootstrap.NavProgressBar.register(this);
27110 * Fires when the active item changes
27111 * @param {Roo.bootstrap.NavProgressBar} this
27112 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27113 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27120 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27125 getAutoCreate : function()
27127 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27131 cls : 'roo-navigation-bar-group',
27135 cls : 'roo-navigation-top-bar'
27139 cls : 'roo-navigation-bullets-bar',
27143 cls : 'roo-navigation-bar'
27150 cls : 'roo-navigation-bottom-bar'
27160 initEvents: function()
27165 onRender : function(ct, position)
27167 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27169 if(this.bullets.length){
27170 Roo.each(this.bullets, function(b){
27179 addItem : function(cfg)
27181 var item = new Roo.bootstrap.NavProgressItem(cfg);
27183 item.parentId = this.id;
27184 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27187 var top = new Roo.bootstrap.Element({
27189 cls : 'roo-navigation-bar-text'
27192 var bottom = new Roo.bootstrap.Element({
27194 cls : 'roo-navigation-bar-text'
27197 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27198 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27200 var topText = new Roo.bootstrap.Element({
27202 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27205 var bottomText = new Roo.bootstrap.Element({
27207 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27210 topText.onRender(top.el, null);
27211 bottomText.onRender(bottom.el, null);
27214 item.bottomEl = bottom;
27217 this.barItems.push(item);
27222 getActive : function()
27224 var active = false;
27226 Roo.each(this.barItems, function(v){
27228 if (!v.isActive()) {
27240 setActiveItem : function(item)
27244 Roo.each(this.barItems, function(v){
27245 if (v.rid == item.rid) {
27249 if (v.isActive()) {
27250 v.setActive(false);
27255 item.setActive(true);
27257 this.fireEvent('changed', this, item, prev);
27260 getBarItem: function(rid)
27264 Roo.each(this.barItems, function(e) {
27265 if (e.rid != rid) {
27276 indexOfItem : function(item)
27280 Roo.each(this.barItems, function(v, i){
27282 if (v.rid != item.rid) {
27293 setActiveNext : function()
27295 var i = this.indexOfItem(this.getActive());
27297 if (i > this.barItems.length) {
27301 this.setActiveItem(this.barItems[i+1]);
27304 setActivePrev : function()
27306 var i = this.indexOfItem(this.getActive());
27312 this.setActiveItem(this.barItems[i-1]);
27315 format : function()
27317 if(!this.barItems.length){
27321 var width = 100 / this.barItems.length;
27323 Roo.each(this.barItems, function(i){
27324 i.el.setStyle('width', width + '%');
27325 i.topEl.el.setStyle('width', width + '%');
27326 i.bottomEl.el.setStyle('width', width + '%');
27335 * Nav Progress Item
27340 * @class Roo.bootstrap.NavProgressItem
27341 * @extends Roo.bootstrap.Component
27342 * Bootstrap NavProgressItem class
27343 * @cfg {String} rid the reference id
27344 * @cfg {Boolean} active (true|false) Is item active default false
27345 * @cfg {Boolean} disabled (true|false) Is item active default false
27346 * @cfg {String} html
27347 * @cfg {String} position (top|bottom) text position default bottom
27348 * @cfg {String} icon show icon instead of number
27351 * Create a new NavProgressItem
27352 * @param {Object} config The config object
27354 Roo.bootstrap.NavProgressItem = function(config){
27355 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27360 * The raw click event for the entire grid.
27361 * @param {Roo.bootstrap.NavProgressItem} this
27362 * @param {Roo.EventObject} e
27369 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27375 position : 'bottom',
27378 getAutoCreate : function()
27380 var iconCls = 'roo-navigation-bar-item-icon';
27382 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27386 cls: 'roo-navigation-bar-item',
27396 cfg.cls += ' active';
27399 cfg.cls += ' disabled';
27405 disable : function()
27407 this.setDisabled(true);
27410 enable : function()
27412 this.setDisabled(false);
27415 initEvents: function()
27417 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27419 this.iconEl.on('click', this.onClick, this);
27422 onClick : function(e)
27424 e.preventDefault();
27430 if(this.fireEvent('click', this, e) === false){
27434 this.parent().setActiveItem(this);
27437 isActive: function ()
27439 return this.active;
27442 setActive : function(state)
27444 if(this.active == state){
27448 this.active = state;
27451 this.el.addClass('active');
27455 this.el.removeClass('active');
27460 setDisabled : function(state)
27462 if(this.disabled == state){
27466 this.disabled = state;
27469 this.el.addClass('disabled');
27473 this.el.removeClass('disabled');
27476 tooltipEl : function()
27478 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27491 * @class Roo.bootstrap.FieldLabel
27492 * @extends Roo.bootstrap.Component
27493 * Bootstrap FieldLabel class
27494 * @cfg {String} html contents of the element
27495 * @cfg {String} tag tag of the element default label
27496 * @cfg {String} cls class of the element
27497 * @cfg {String} target label target
27498 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27499 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27500 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27501 * @cfg {String} iconTooltip default "This field is required"
27504 * Create a new FieldLabel
27505 * @param {Object} config The config object
27508 Roo.bootstrap.FieldLabel = function(config){
27509 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27514 * Fires after the field has been marked as invalid.
27515 * @param {Roo.form.FieldLabel} this
27516 * @param {String} msg The validation message
27521 * Fires after the field has been validated with no errors.
27522 * @param {Roo.form.FieldLabel} this
27528 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27535 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27536 validClass : 'text-success fa fa-lg fa-check',
27537 iconTooltip : 'This field is required',
27539 getAutoCreate : function(){
27543 cls : 'roo-bootstrap-field-label ' + this.cls,
27549 tooltip : this.iconTooltip
27561 initEvents: function()
27563 Roo.bootstrap.Element.superclass.initEvents.call(this);
27565 this.iconEl = this.el.select('i', true).first();
27567 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27569 Roo.bootstrap.FieldLabel.register(this);
27573 * Mark this field as valid
27575 markValid : function()
27577 this.iconEl.show();
27579 this.iconEl.removeClass(this.invalidClass);
27581 this.iconEl.addClass(this.validClass);
27583 this.fireEvent('valid', this);
27587 * Mark this field as invalid
27588 * @param {String} msg The validation message
27590 markInvalid : function(msg)
27592 this.iconEl.show();
27594 this.iconEl.removeClass(this.validClass);
27596 this.iconEl.addClass(this.invalidClass);
27598 this.fireEvent('invalid', this, msg);
27604 Roo.apply(Roo.bootstrap.FieldLabel, {
27609 * register a FieldLabel Group
27610 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27612 register : function(label)
27614 if(this.groups.hasOwnProperty(label.target)){
27618 this.groups[label.target] = label;
27622 * fetch a FieldLabel Group based on the target
27623 * @param {string} target
27624 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27626 get: function(target) {
27627 if (typeof(this.groups[target]) == 'undefined') {
27631 return this.groups[target] ;
27640 * page DateSplitField.
27646 * @class Roo.bootstrap.DateSplitField
27647 * @extends Roo.bootstrap.Component
27648 * Bootstrap DateSplitField class
27649 * @cfg {string} fieldLabel - the label associated
27650 * @cfg {Number} labelWidth set the width of label (0-12)
27651 * @cfg {String} labelAlign (top|left)
27652 * @cfg {Boolean} dayAllowBlank (true|false) default false
27653 * @cfg {Boolean} monthAllowBlank (true|false) default false
27654 * @cfg {Boolean} yearAllowBlank (true|false) default false
27655 * @cfg {string} dayPlaceholder
27656 * @cfg {string} monthPlaceholder
27657 * @cfg {string} yearPlaceholder
27658 * @cfg {string} dayFormat default 'd'
27659 * @cfg {string} monthFormat default 'm'
27660 * @cfg {string} yearFormat default 'Y'
27664 * Create a new DateSplitField
27665 * @param {Object} config The config object
27668 Roo.bootstrap.DateSplitField = function(config){
27669 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27675 * getting the data of years
27676 * @param {Roo.bootstrap.DateSplitField} this
27677 * @param {Object} years
27682 * getting the data of days
27683 * @param {Roo.bootstrap.DateSplitField} this
27684 * @param {Object} days
27689 * Fires after the field has been marked as invalid.
27690 * @param {Roo.form.Field} this
27691 * @param {String} msg The validation message
27696 * Fires after the field has been validated with no errors.
27697 * @param {Roo.form.Field} this
27703 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27706 labelAlign : 'top',
27708 dayAllowBlank : false,
27709 monthAllowBlank : false,
27710 yearAllowBlank : false,
27711 dayPlaceholder : '',
27712 monthPlaceholder : '',
27713 yearPlaceholder : '',
27717 isFormField : true,
27719 getAutoCreate : function()
27723 cls : 'row roo-date-split-field-group',
27728 cls : 'form-hidden-field roo-date-split-field-group-value',
27734 if(this.fieldLabel){
27737 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27741 html : this.fieldLabel
27747 Roo.each(['day', 'month', 'year'], function(t){
27750 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27757 inputEl: function ()
27759 return this.el.select('.roo-date-split-field-group-value', true).first();
27762 onRender : function(ct, position)
27766 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27768 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27770 this.dayField = new Roo.bootstrap.ComboBox({
27771 allowBlank : this.dayAllowBlank,
27772 alwaysQuery : true,
27773 displayField : 'value',
27776 forceSelection : true,
27778 placeholder : this.dayPlaceholder,
27779 selectOnFocus : true,
27780 tpl : '<div class="select2-result"><b>{value}</b></div>',
27781 triggerAction : 'all',
27783 valueField : 'value',
27784 store : new Roo.data.SimpleStore({
27785 data : (function() {
27787 _this.fireEvent('days', _this, days);
27790 fields : [ 'value' ]
27793 select : function (_self, record, index)
27795 _this.setValue(_this.getValue());
27800 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27802 this.monthField = new Roo.bootstrap.MonthField({
27803 after : '<i class=\"fa fa-calendar\"></i>',
27804 allowBlank : this.monthAllowBlank,
27805 placeholder : this.monthPlaceholder,
27808 render : function (_self)
27810 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27811 e.preventDefault();
27815 select : function (_self, oldvalue, newvalue)
27817 _this.setValue(_this.getValue());
27822 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27824 this.yearField = new Roo.bootstrap.ComboBox({
27825 allowBlank : this.yearAllowBlank,
27826 alwaysQuery : true,
27827 displayField : 'value',
27830 forceSelection : true,
27832 placeholder : this.yearPlaceholder,
27833 selectOnFocus : true,
27834 tpl : '<div class="select2-result"><b>{value}</b></div>',
27835 triggerAction : 'all',
27837 valueField : 'value',
27838 store : new Roo.data.SimpleStore({
27839 data : (function() {
27841 _this.fireEvent('years', _this, years);
27844 fields : [ 'value' ]
27847 select : function (_self, record, index)
27849 _this.setValue(_this.getValue());
27854 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27857 setValue : function(v, format)
27859 this.inputEl.dom.value = v;
27861 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27863 var d = Date.parseDate(v, f);
27870 this.setDay(d.format(this.dayFormat));
27871 this.setMonth(d.format(this.monthFormat));
27872 this.setYear(d.format(this.yearFormat));
27879 setDay : function(v)
27881 this.dayField.setValue(v);
27882 this.inputEl.dom.value = this.getValue();
27887 setMonth : function(v)
27889 this.monthField.setValue(v, true);
27890 this.inputEl.dom.value = this.getValue();
27895 setYear : function(v)
27897 this.yearField.setValue(v);
27898 this.inputEl.dom.value = this.getValue();
27903 getDay : function()
27905 return this.dayField.getValue();
27908 getMonth : function()
27910 return this.monthField.getValue();
27913 getYear : function()
27915 return this.yearField.getValue();
27918 getValue : function()
27920 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27922 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27932 this.inputEl.dom.value = '';
27937 validate : function()
27939 var d = this.dayField.validate();
27940 var m = this.monthField.validate();
27941 var y = this.yearField.validate();
27946 (!this.dayAllowBlank && !d) ||
27947 (!this.monthAllowBlank && !m) ||
27948 (!this.yearAllowBlank && !y)
27953 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27962 this.markInvalid();
27967 markValid : function()
27970 var label = this.el.select('label', true).first();
27971 var icon = this.el.select('i.fa-star', true).first();
27977 this.fireEvent('valid', this);
27981 * Mark this field as invalid
27982 * @param {String} msg The validation message
27984 markInvalid : function(msg)
27987 var label = this.el.select('label', true).first();
27988 var icon = this.el.select('i.fa-star', true).first();
27990 if(label && !icon){
27991 this.el.select('.roo-date-split-field-label', true).createChild({
27993 cls : 'text-danger fa fa-lg fa-star',
27994 tooltip : 'This field is required',
27995 style : 'margin-right:5px;'
27999 this.fireEvent('invalid', this, msg);
28002 clearInvalid : function()
28004 var label = this.el.select('label', true).first();
28005 var icon = this.el.select('i.fa-star', true).first();
28011 this.fireEvent('valid', this);
28014 getName: function()