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
11512 this.tickItems = [];
11514 this.selectedIndex = -1;
11515 if(this.mode == 'local'){
11516 if(config.queryDelay === undefined){
11517 this.queryDelay = 10;
11519 if(config.minChars === undefined){
11525 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11528 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11529 * rendering into an Roo.Editor, defaults to false)
11532 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11533 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11536 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11539 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11540 * the dropdown list (defaults to undefined, with no header element)
11544 * @cfg {String/Roo.Template} tpl The template to use to render the output
11548 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11550 listWidth: undefined,
11552 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11553 * mode = 'remote' or 'text' if mode = 'local')
11555 displayField: undefined,
11558 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11559 * mode = 'remote' or 'value' if mode = 'local').
11560 * Note: use of a valueField requires the user make a selection
11561 * in order for a value to be mapped.
11563 valueField: undefined,
11567 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11568 * field's data value (defaults to the underlying DOM element's name)
11570 hiddenName: undefined,
11572 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11576 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11578 selectedClass: 'active',
11581 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11585 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11586 * anchor positions (defaults to 'tl-bl')
11588 listAlign: 'tl-bl?',
11590 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11594 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11595 * query specified by the allQuery config option (defaults to 'query')
11597 triggerAction: 'query',
11599 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11600 * (defaults to 4, does not apply if editable = false)
11604 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11605 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11609 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11610 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11614 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11615 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11619 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11620 * when editable = true (defaults to false)
11622 selectOnFocus:false,
11624 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11626 queryParam: 'query',
11628 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11629 * when mode = 'remote' (defaults to 'Loading...')
11631 loadingText: 'Loading...',
11633 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11637 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11641 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11642 * traditional select (defaults to true)
11646 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11650 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11654 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11655 * listWidth has a higher value)
11659 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11660 * allow the user to set arbitrary text into the field (defaults to false)
11662 forceSelection:false,
11664 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11665 * if typeAhead = true (defaults to 250)
11667 typeAheadDelay : 250,
11669 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11670 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11672 valueNotFoundText : undefined,
11674 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11676 blockFocus : false,
11679 * @cfg {Boolean} disableClear Disable showing of clear button.
11681 disableClear : false,
11683 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11685 alwaysQuery : false,
11688 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11693 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11695 invalidClass : "has-warning",
11698 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11700 validClass : "has-success",
11703 * @cfg {Boolean} specialFilter (true|false) special filter default false
11705 specialFilter : false,
11708 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11710 mobileTouchView : true,
11722 btnPosition : 'right',
11723 triggerList : true,
11724 showToggleBtn : true,
11726 emptyResultText: 'Empty',
11727 triggerText : 'Select',
11729 // element that contains real text value.. (when hidden is used..)
11731 getAutoCreate : function()
11739 if(Roo.isTouch && this.mobileTouchView){
11740 cfg = this.getAutoCreateTouchView();
11747 if(!this.tickable){
11748 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11753 * ComboBox with tickable selections
11756 var align = this.labelAlign || this.parentLabelAlign();
11759 cls : 'form-group roo-combobox-tickable' //input-group
11764 cls : 'tickable-buttons',
11769 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11770 html : this.triggerText
11776 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11783 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11790 buttons.cn.unshift({
11792 cls: 'select2-search-field-input'
11798 Roo.each(buttons.cn, function(c){
11800 c.cls += ' btn-' + _this.size;
11803 if (_this.disabled) {
11814 cls: 'form-hidden-field'
11818 cls: 'select2-choices',
11822 cls: 'select2-search-field',
11834 cls: 'select2-container input-group select2-container-multi',
11839 // cls: 'typeahead typeahead-long dropdown-menu',
11840 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11845 if(this.hasFeedback && !this.allowBlank){
11849 cls: 'glyphicon form-control-feedback'
11852 combobox.cn.push(feedback);
11855 if (align ==='left' && this.fieldLabel.length) {
11857 Roo.log("left and has label");
11863 cls : 'control-label col-sm-' + this.labelWidth,
11864 html : this.fieldLabel
11868 cls : "col-sm-" + (12 - this.labelWidth),
11875 } else if ( this.fieldLabel.length) {
11881 //cls : 'input-group-addon',
11882 html : this.fieldLabel
11892 Roo.log(" no label && no align");
11899 ['xs','sm','md','lg'].map(function(size){
11900 if (settings[size]) {
11901 cfg.cls += ' col-' + size + '-' + settings[size];
11909 _initEventsCalled : false,
11912 initEvents: function()
11915 if (this._initEventsCalled) { // as we call render... prevent looping...
11918 this._initEventsCalled = true;
11921 throw "can not find store for combo";
11924 this.store = Roo.factory(this.store, Roo.data);
11926 // if we are building from html. then this element is so complex, that we can not really
11927 // use the rendered HTML.
11928 // so we have to trash and replace the previous code.
11929 if (Roo.XComponent.build_from_html) {
11931 // remove this element....
11932 var e = this.el.dom, k=0;
11933 while (e ) { e = e.previousSibling; ++k;}
11938 this.rendered = false;
11940 this.render(this.parent().getChildContainer(true), k);
11951 if(Roo.isTouch && this.mobileTouchView){
11952 this.initTouchView();
11957 this.initTickableEvents();
11961 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11963 if(this.hiddenName){
11965 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11967 this.hiddenField.dom.value =
11968 this.hiddenValue !== undefined ? this.hiddenValue :
11969 this.value !== undefined ? this.value : '';
11971 // prevent input submission
11972 this.el.dom.removeAttribute('name');
11973 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11978 // this.el.dom.setAttribute('autocomplete', 'off');
11981 var cls = 'x-combo-list';
11983 //this.list = new Roo.Layer({
11984 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11990 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11991 _this.list.setWidth(lw);
11994 this.list.on('mouseover', this.onViewOver, this);
11995 this.list.on('mousemove', this.onViewMove, this);
11997 this.list.on('scroll', this.onViewScroll, this);
12000 this.list.swallowEvent('mousewheel');
12001 this.assetHeight = 0;
12004 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12005 this.assetHeight += this.header.getHeight();
12008 this.innerList = this.list.createChild({cls:cls+'-inner'});
12009 this.innerList.on('mouseover', this.onViewOver, this);
12010 this.innerList.on('mousemove', this.onViewMove, this);
12011 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12013 if(this.allowBlank && !this.pageSize && !this.disableClear){
12014 this.footer = this.list.createChild({cls:cls+'-ft'});
12015 this.pageTb = new Roo.Toolbar(this.footer);
12019 this.footer = this.list.createChild({cls:cls+'-ft'});
12020 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12021 {pageSize: this.pageSize});
12025 if (this.pageTb && this.allowBlank && !this.disableClear) {
12027 this.pageTb.add(new Roo.Toolbar.Fill(), {
12028 cls: 'x-btn-icon x-btn-clear',
12030 handler: function()
12033 _this.clearValue();
12034 _this.onSelect(false, -1);
12039 this.assetHeight += this.footer.getHeight();
12044 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12047 this.view = new Roo.View(this.list, this.tpl, {
12048 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12050 //this.view.wrapEl.setDisplayed(false);
12051 this.view.on('click', this.onViewClick, this);
12055 this.store.on('beforeload', this.onBeforeLoad, this);
12056 this.store.on('load', this.onLoad, this);
12057 this.store.on('loadexception', this.onLoadException, this);
12059 if(this.resizable){
12060 this.resizer = new Roo.Resizable(this.list, {
12061 pinned:true, handles:'se'
12063 this.resizer.on('resize', function(r, w, h){
12064 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12065 this.listWidth = w;
12066 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12067 this.restrictHeight();
12069 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12072 if(!this.editable){
12073 this.editable = true;
12074 this.setEditable(false);
12079 if (typeof(this.events.add.listeners) != 'undefined') {
12081 this.addicon = this.wrap.createChild(
12082 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12084 this.addicon.on('click', function(e) {
12085 this.fireEvent('add', this);
12088 if (typeof(this.events.edit.listeners) != 'undefined') {
12090 this.editicon = this.wrap.createChild(
12091 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12092 if (this.addicon) {
12093 this.editicon.setStyle('margin-left', '40px');
12095 this.editicon.on('click', function(e) {
12097 // we fire even if inothing is selected..
12098 this.fireEvent('edit', this, this.lastData );
12104 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12105 "up" : function(e){
12106 this.inKeyMode = true;
12110 "down" : function(e){
12111 if(!this.isExpanded()){
12112 this.onTriggerClick();
12114 this.inKeyMode = true;
12119 "enter" : function(e){
12120 // this.onViewClick();
12124 if(this.fireEvent("specialkey", this, e)){
12125 this.onViewClick(false);
12131 "esc" : function(e){
12135 "tab" : function(e){
12138 if(this.fireEvent("specialkey", this, e)){
12139 this.onViewClick(false);
12147 doRelay : function(foo, bar, hname){
12148 if(hname == 'down' || this.scope.isExpanded()){
12149 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12158 this.queryDelay = Math.max(this.queryDelay || 10,
12159 this.mode == 'local' ? 10 : 250);
12162 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12164 if(this.typeAhead){
12165 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12167 if(this.editable !== false){
12168 this.inputEl().on("keyup", this.onKeyUp, this);
12170 if(this.forceSelection){
12171 this.inputEl().on('blur', this.doForce, this);
12175 this.choices = this.el.select('ul.select2-choices', true).first();
12176 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12180 initTickableEvents: function()
12184 if(this.hiddenName){
12186 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12188 this.hiddenField.dom.value =
12189 this.hiddenValue !== undefined ? this.hiddenValue :
12190 this.value !== undefined ? this.value : '';
12192 // prevent input submission
12193 this.el.dom.removeAttribute('name');
12194 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12199 // this.list = this.el.select('ul.dropdown-menu',true).first();
12201 this.choices = this.el.select('ul.select2-choices', true).first();
12202 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12203 if(this.triggerList){
12204 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12207 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12208 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12210 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12211 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12213 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12214 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12216 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12217 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12218 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12221 this.cancelBtn.hide();
12226 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12227 _this.list.setWidth(lw);
12230 this.list.on('mouseover', this.onViewOver, this);
12231 this.list.on('mousemove', this.onViewMove, this);
12233 this.list.on('scroll', this.onViewScroll, this);
12236 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>';
12239 this.view = new Roo.View(this.list, this.tpl, {
12240 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12243 //this.view.wrapEl.setDisplayed(false);
12244 this.view.on('click', this.onViewClick, this);
12248 this.store.on('beforeload', this.onBeforeLoad, this);
12249 this.store.on('load', this.onLoad, this);
12250 this.store.on('loadexception', this.onLoadException, this);
12253 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12254 "up" : function(e){
12255 this.inKeyMode = true;
12259 "down" : function(e){
12260 this.inKeyMode = true;
12264 "enter" : function(e){
12265 if(this.fireEvent("specialkey", this, e)){
12266 this.onViewClick(false);
12272 "esc" : function(e){
12273 this.onTickableFooterButtonClick(e, false, false);
12276 "tab" : function(e){
12277 this.fireEvent("specialkey", this, e);
12279 this.onTickableFooterButtonClick(e, false, false);
12286 doRelay : function(e, fn, key){
12287 if(this.scope.isExpanded()){
12288 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12297 this.queryDelay = Math.max(this.queryDelay || 10,
12298 this.mode == 'local' ? 10 : 250);
12301 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12303 if(this.typeAhead){
12304 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12307 if(this.editable !== false){
12308 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12313 onDestroy : function(){
12315 this.view.setStore(null);
12316 this.view.el.removeAllListeners();
12317 this.view.el.remove();
12318 this.view.purgeListeners();
12321 this.list.dom.innerHTML = '';
12325 this.store.un('beforeload', this.onBeforeLoad, this);
12326 this.store.un('load', this.onLoad, this);
12327 this.store.un('loadexception', this.onLoadException, this);
12329 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12333 fireKey : function(e){
12334 if(e.isNavKeyPress() && !this.list.isVisible()){
12335 this.fireEvent("specialkey", this, e);
12340 onResize: function(w, h){
12341 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12343 // if(typeof w != 'number'){
12344 // // we do not handle it!?!?
12347 // var tw = this.trigger.getWidth();
12348 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12349 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12351 // this.inputEl().setWidth( this.adjustWidth('input', x));
12353 // //this.trigger.setStyle('left', x+'px');
12355 // if(this.list && this.listWidth === undefined){
12356 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12357 // this.list.setWidth(lw);
12358 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12366 * Allow or prevent the user from directly editing the field text. If false is passed,
12367 * the user will only be able to select from the items defined in the dropdown list. This method
12368 * is the runtime equivalent of setting the 'editable' config option at config time.
12369 * @param {Boolean} value True to allow the user to directly edit the field text
12371 setEditable : function(value){
12372 if(value == this.editable){
12375 this.editable = value;
12377 this.inputEl().dom.setAttribute('readOnly', true);
12378 this.inputEl().on('mousedown', this.onTriggerClick, this);
12379 this.inputEl().addClass('x-combo-noedit');
12381 this.inputEl().dom.setAttribute('readOnly', false);
12382 this.inputEl().un('mousedown', this.onTriggerClick, this);
12383 this.inputEl().removeClass('x-combo-noedit');
12389 onBeforeLoad : function(combo,opts){
12390 if(!this.hasFocus){
12394 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12396 this.restrictHeight();
12397 this.selectedIndex = -1;
12401 onLoad : function(){
12403 this.hasQuery = false;
12405 if(!this.hasFocus){
12409 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12410 this.loading.hide();
12413 if(this.store.getCount() > 0){
12415 this.restrictHeight();
12416 if(this.lastQuery == this.allQuery){
12417 if(this.editable && !this.tickable){
12418 this.inputEl().dom.select();
12422 !this.selectByValue(this.value, true) &&
12425 !this.store.lastOptions ||
12426 typeof(this.store.lastOptions.add) == 'undefined' ||
12427 this.store.lastOptions.add != true
12430 this.select(0, true);
12433 if(this.autoFocus){
12436 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12437 this.taTask.delay(this.typeAheadDelay);
12441 this.onEmptyResults();
12447 onLoadException : function()
12449 this.hasQuery = false;
12451 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12452 this.loading.hide();
12455 if(this.tickable && this.editable){
12460 // only causes errors at present
12461 //Roo.log(this.store.reader.jsonData);
12462 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12464 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12470 onTypeAhead : function(){
12471 if(this.store.getCount() > 0){
12472 var r = this.store.getAt(0);
12473 var newValue = r.data[this.displayField];
12474 var len = newValue.length;
12475 var selStart = this.getRawValue().length;
12477 if(selStart != len){
12478 this.setRawValue(newValue);
12479 this.selectText(selStart, newValue.length);
12485 onSelect : function(record, index){
12487 if(this.fireEvent('beforeselect', this, record, index) !== false){
12489 this.setFromData(index > -1 ? record.data : false);
12492 this.fireEvent('select', this, record, index);
12497 * Returns the currently selected field value or empty string if no value is set.
12498 * @return {String} value The selected value
12500 getValue : function(){
12503 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12506 if(this.valueField){
12507 return typeof this.value != 'undefined' ? this.value : '';
12509 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12514 * Clears any text/value currently set in the field
12516 clearValue : function(){
12517 if(this.hiddenField){
12518 this.hiddenField.dom.value = '';
12521 this.setRawValue('');
12522 this.lastSelectionText = '';
12523 this.lastData = false;
12525 var close = this.closeTriggerEl();
12534 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12535 * will be displayed in the field. If the value does not match the data value of an existing item,
12536 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12537 * Otherwise the field will be blank (although the value will still be set).
12538 * @param {String} value The value to match
12540 setValue : function(v){
12547 if(this.valueField){
12548 var r = this.findRecord(this.valueField, v);
12550 text = r.data[this.displayField];
12551 }else if(this.valueNotFoundText !== undefined){
12552 text = this.valueNotFoundText;
12555 this.lastSelectionText = text;
12556 if(this.hiddenField){
12557 this.hiddenField.dom.value = v;
12559 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12562 var close = this.closeTriggerEl();
12565 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12569 * @property {Object} the last set data for the element
12574 * Sets the value of the field based on a object which is related to the record format for the store.
12575 * @param {Object} value the value to set as. or false on reset?
12577 setFromData : function(o){
12584 var dv = ''; // display value
12585 var vv = ''; // value value..
12587 if (this.displayField) {
12588 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12590 // this is an error condition!!!
12591 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12594 if(this.valueField){
12595 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12598 var close = this.closeTriggerEl();
12601 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12604 if(this.hiddenField){
12605 this.hiddenField.dom.value = vv;
12607 this.lastSelectionText = dv;
12608 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12612 // no hidden field.. - we store the value in 'value', but still display
12613 // display field!!!!
12614 this.lastSelectionText = dv;
12615 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12622 reset : function(){
12623 // overridden so that last data is reset..
12630 this.setValue(this.originalValue);
12631 this.clearInvalid();
12632 this.lastData = false;
12634 this.view.clearSelections();
12638 findRecord : function(prop, value){
12640 if(this.store.getCount() > 0){
12641 this.store.each(function(r){
12642 if(r.data[prop] == value){
12652 getName: function()
12654 // returns hidden if it's set..
12655 if (!this.rendered) {return ''};
12656 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12660 onViewMove : function(e, t){
12661 this.inKeyMode = false;
12665 onViewOver : function(e, t){
12666 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12669 var item = this.view.findItemFromChild(t);
12672 var index = this.view.indexOf(item);
12673 this.select(index, false);
12678 onViewClick : function(view, doFocus, el, e)
12680 var index = this.view.getSelectedIndexes()[0];
12682 var r = this.store.getAt(index);
12686 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12693 Roo.each(this.tickItems, function(v,k){
12695 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12697 _this.tickItems.splice(k, 1);
12699 if(typeof(e) == 'undefined' && view == false){
12700 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12712 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12713 this.tickItems.push(r.data);
12716 if(typeof(e) == 'undefined' && view == false){
12717 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12724 this.onSelect(r, index);
12726 if(doFocus !== false && !this.blockFocus){
12727 this.inputEl().focus();
12732 restrictHeight : function(){
12733 //this.innerList.dom.style.height = '';
12734 //var inner = this.innerList.dom;
12735 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12736 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12737 //this.list.beginUpdate();
12738 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12739 this.list.alignTo(this.inputEl(), this.listAlign);
12740 this.list.alignTo(this.inputEl(), this.listAlign);
12741 //this.list.endUpdate();
12745 onEmptyResults : function(){
12747 if(this.tickable && this.editable){
12748 this.restrictHeight();
12756 * Returns true if the dropdown list is expanded, else false.
12758 isExpanded : function(){
12759 return this.list.isVisible();
12763 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12764 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12765 * @param {String} value The data value of the item to select
12766 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12767 * selected item if it is not currently in view (defaults to true)
12768 * @return {Boolean} True if the value matched an item in the list, else false
12770 selectByValue : function(v, scrollIntoView){
12771 if(v !== undefined && v !== null){
12772 var r = this.findRecord(this.valueField || this.displayField, v);
12774 this.select(this.store.indexOf(r), scrollIntoView);
12782 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12783 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12784 * @param {Number} index The zero-based index of the list item to select
12785 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12786 * selected item if it is not currently in view (defaults to true)
12788 select : function(index, scrollIntoView){
12789 this.selectedIndex = index;
12790 this.view.select(index);
12791 if(scrollIntoView !== false){
12792 var el = this.view.getNode(index);
12794 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12797 this.list.scrollChildIntoView(el, false);
12803 selectNext : function(){
12804 var ct = this.store.getCount();
12806 if(this.selectedIndex == -1){
12808 }else if(this.selectedIndex < ct-1){
12809 this.select(this.selectedIndex+1);
12815 selectPrev : function(){
12816 var ct = this.store.getCount();
12818 if(this.selectedIndex == -1){
12820 }else if(this.selectedIndex != 0){
12821 this.select(this.selectedIndex-1);
12827 onKeyUp : function(e){
12828 if(this.editable !== false && !e.isSpecialKey()){
12829 this.lastKey = e.getKey();
12830 this.dqTask.delay(this.queryDelay);
12835 validateBlur : function(){
12836 return !this.list || !this.list.isVisible();
12840 initQuery : function(){
12842 var v = this.getRawValue();
12844 if(this.tickable && this.editable){
12845 v = this.tickableInputEl().getValue();
12852 doForce : function(){
12853 if(this.inputEl().dom.value.length > 0){
12854 this.inputEl().dom.value =
12855 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12861 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12862 * query allowing the query action to be canceled if needed.
12863 * @param {String} query The SQL query to execute
12864 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12865 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12866 * saved in the current store (defaults to false)
12868 doQuery : function(q, forceAll){
12870 if(q === undefined || q === null){
12875 forceAll: forceAll,
12879 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12884 forceAll = qe.forceAll;
12885 if(forceAll === true || (q.length >= this.minChars)){
12887 this.hasQuery = true;
12889 if(this.lastQuery != q || this.alwaysQuery){
12890 this.lastQuery = q;
12891 if(this.mode == 'local'){
12892 this.selectedIndex = -1;
12894 this.store.clearFilter();
12897 if(this.specialFilter){
12898 this.fireEvent('specialfilter', this);
12903 this.store.filter(this.displayField, q);
12906 this.store.fireEvent("datachanged", this.store);
12913 this.store.baseParams[this.queryParam] = q;
12915 var options = {params : this.getParams(q)};
12918 options.add = true;
12919 options.params.start = this.page * this.pageSize;
12922 this.store.load(options);
12925 * this code will make the page width larger, at the beginning, the list not align correctly,
12926 * we should expand the list on onLoad
12927 * so command out it
12932 this.selectedIndex = -1;
12937 this.loadNext = false;
12941 getParams : function(q){
12943 //p[this.queryParam] = q;
12947 p.limit = this.pageSize;
12953 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12955 collapse : function(){
12956 if(!this.isExpanded()){
12963 this.hasFocus = false;
12965 this.cancelBtn.hide();
12966 this.trigger.show();
12969 this.tickableInputEl().dom.value = '';
12970 this.tickableInputEl().blur();
12975 Roo.get(document).un('mousedown', this.collapseIf, this);
12976 Roo.get(document).un('mousewheel', this.collapseIf, this);
12977 if (!this.editable) {
12978 Roo.get(document).un('keydown', this.listKeyPress, this);
12980 this.fireEvent('collapse', this);
12984 collapseIf : function(e){
12985 var in_combo = e.within(this.el);
12986 var in_list = e.within(this.list);
12987 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12989 if (in_combo || in_list || is_list) {
12990 //e.stopPropagation();
12995 this.onTickableFooterButtonClick(e, false, false);
13003 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13005 expand : function(){
13007 if(this.isExpanded() || !this.hasFocus){
13011 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13012 this.list.setWidth(lw);
13019 this.restrictHeight();
13023 this.tickItems = Roo.apply([], this.item);
13026 this.cancelBtn.show();
13027 this.trigger.hide();
13030 this.tickableInputEl().focus();
13035 Roo.get(document).on('mousedown', this.collapseIf, this);
13036 Roo.get(document).on('mousewheel', this.collapseIf, this);
13037 if (!this.editable) {
13038 Roo.get(document).on('keydown', this.listKeyPress, this);
13041 this.fireEvent('expand', this);
13045 // Implements the default empty TriggerField.onTriggerClick function
13046 onTriggerClick : function(e)
13048 Roo.log('trigger click');
13050 if(this.disabled || !this.triggerList){
13055 this.loadNext = false;
13057 if(this.isExpanded()){
13059 if (!this.blockFocus) {
13060 this.inputEl().focus();
13064 this.hasFocus = true;
13065 if(this.triggerAction == 'all') {
13066 this.doQuery(this.allQuery, true);
13068 this.doQuery(this.getRawValue());
13070 if (!this.blockFocus) {
13071 this.inputEl().focus();
13076 onTickableTriggerClick : function(e)
13083 this.loadNext = false;
13084 this.hasFocus = true;
13086 if(this.triggerAction == 'all') {
13087 this.doQuery(this.allQuery, true);
13089 this.doQuery(this.getRawValue());
13093 onSearchFieldClick : function(e)
13095 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13096 this.onTickableFooterButtonClick(e, false, false);
13100 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13105 this.loadNext = false;
13106 this.hasFocus = true;
13108 if(this.triggerAction == 'all') {
13109 this.doQuery(this.allQuery, true);
13111 this.doQuery(this.getRawValue());
13115 listKeyPress : function(e)
13117 //Roo.log('listkeypress');
13118 // scroll to first matching element based on key pres..
13119 if (e.isSpecialKey()) {
13122 var k = String.fromCharCode(e.getKey()).toUpperCase();
13125 var csel = this.view.getSelectedNodes();
13126 var cselitem = false;
13128 var ix = this.view.indexOf(csel[0]);
13129 cselitem = this.store.getAt(ix);
13130 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13136 this.store.each(function(v) {
13138 // start at existing selection.
13139 if (cselitem.id == v.id) {
13145 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13146 match = this.store.indexOf(v);
13152 if (match === false) {
13153 return true; // no more action?
13156 this.view.select(match);
13157 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13158 sn.scrollIntoView(sn.dom.parentNode, false);
13161 onViewScroll : function(e, t){
13163 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){
13167 this.hasQuery = true;
13169 this.loading = this.list.select('.loading', true).first();
13171 if(this.loading === null){
13172 this.list.createChild({
13174 cls: 'loading select2-more-results select2-active',
13175 html: 'Loading more results...'
13178 this.loading = this.list.select('.loading', true).first();
13180 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13182 this.loading.hide();
13185 this.loading.show();
13190 this.loadNext = true;
13192 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13197 addItem : function(o)
13199 var dv = ''; // display value
13201 if (this.displayField) {
13202 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13204 // this is an error condition!!!
13205 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13212 var choice = this.choices.createChild({
13214 cls: 'select2-search-choice',
13223 cls: 'select2-search-choice-close',
13228 }, this.searchField);
13230 var close = choice.select('a.select2-search-choice-close', true).first();
13232 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13240 this.inputEl().dom.value = '';
13245 onRemoveItem : function(e, _self, o)
13247 e.preventDefault();
13249 this.lastItem = Roo.apply([], this.item);
13251 var index = this.item.indexOf(o.data) * 1;
13254 Roo.log('not this item?!');
13258 this.item.splice(index, 1);
13263 this.fireEvent('remove', this, e);
13269 syncValue : function()
13271 if(!this.item.length){
13278 Roo.each(this.item, function(i){
13279 if(_this.valueField){
13280 value.push(i[_this.valueField]);
13287 this.value = value.join(',');
13289 if(this.hiddenField){
13290 this.hiddenField.dom.value = this.value;
13293 this.store.fireEvent("datachanged", this.store);
13296 clearItem : function()
13298 if(!this.multiple){
13304 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13312 if(this.tickable && !Roo.isTouch){
13313 this.view.refresh();
13317 inputEl: function ()
13319 if(Roo.isTouch && this.mobileTouchView){
13320 return this.el.select('input.form-control',true).first();
13324 return this.searchField;
13327 return this.el.select('input.form-control',true).first();
13331 onTickableFooterButtonClick : function(e, btn, el)
13333 e.preventDefault();
13335 this.lastItem = Roo.apply([], this.item);
13337 if(btn && btn.name == 'cancel'){
13338 this.tickItems = Roo.apply([], this.item);
13347 Roo.each(this.tickItems, function(o){
13355 validate : function()
13357 var v = this.getRawValue();
13360 v = this.getValue();
13363 if(this.disabled || this.allowBlank || v.length){
13368 this.markInvalid();
13372 tickableInputEl : function()
13374 if(!this.tickable || !this.editable){
13375 return this.inputEl();
13378 return this.inputEl().select('.select2-search-field-input', true).first();
13382 getAutoCreateTouchView : function()
13387 cls: 'form-group' //input-group
13393 type : this.inputType,
13394 cls : 'form-control x-combo-noedit',
13395 autocomplete: 'new-password',
13396 placeholder : this.placeholder || '',
13401 input.name = this.name;
13405 input.cls += ' input-' + this.size;
13408 if (this.disabled) {
13409 input.disabled = true;
13420 inputblock.cls += ' input-group';
13422 inputblock.cn.unshift({
13424 cls : 'input-group-addon',
13429 if(this.removable && !this.multiple){
13430 inputblock.cls += ' roo-removable';
13432 inputblock.cn.push({
13435 cls : 'roo-combo-removable-btn close'
13439 if(this.hasFeedback && !this.allowBlank){
13441 inputblock.cls += ' has-feedback';
13443 inputblock.cn.push({
13445 cls: 'glyphicon form-control-feedback'
13452 inputblock.cls += (this.before) ? '' : ' input-group';
13454 inputblock.cn.push({
13456 cls : 'input-group-addon',
13467 cls: 'form-hidden-field'
13481 cls: 'form-hidden-field'
13485 cls: 'select2-choices',
13489 cls: 'select2-search-field',
13502 cls: 'select2-container input-group',
13509 combobox.cls += ' select2-container-multi';
13512 var align = this.labelAlign || this.parentLabelAlign();
13516 if(this.fieldLabel.length){
13518 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13519 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13524 cls : 'control-label ' + lw,
13525 html : this.fieldLabel
13537 var settings = this;
13539 ['xs','sm','md','lg'].map(function(size){
13540 if (settings[size]) {
13541 cfg.cls += ' col-' + size + '-' + settings[size];
13548 initTouchView : function()
13550 this.renderTouchView();
13552 this.touchViewEl.on('scroll', function(){
13553 this.el.dom.scrollTop = 0;
13556 this.inputEl().on("click", this.showTouchView, this);
13557 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13558 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13560 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13562 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13563 this.store.on('load', this.onTouchViewLoad, this);
13564 this.store.on('loadexception', this.onTouchViewLoadException, this);
13566 if(this.hiddenName){
13568 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13570 this.hiddenField.dom.value =
13571 this.hiddenValue !== undefined ? this.hiddenValue :
13572 this.value !== undefined ? this.value : '';
13574 this.el.dom.removeAttribute('name');
13575 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13579 this.choices = this.el.select('ul.select2-choices', true).first();
13580 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13583 if(this.removable && !this.multiple){
13584 var close = this.closeTriggerEl();
13586 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13587 close.on('click', this.removeBtnClick, this, close);
13596 renderTouchView : function()
13598 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13599 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13601 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13602 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13604 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13605 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13606 this.touchViewBodyEl.setStyle('overflow', 'auto');
13608 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13609 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13611 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13612 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13616 showTouchView : function()
13618 this.touchViewHeaderEl.hide();
13620 if(this.fieldLabel.length){
13621 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13622 this.touchViewHeaderEl.show();
13625 this.touchViewEl.show();
13627 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13628 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13630 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13632 if(this.fieldLabel.length){
13633 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13636 this.touchViewBodyEl.setHeight(bodyHeight);
13640 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13642 this.touchViewEl.addClass('in');
13645 this.doTouchViewQuery();
13649 hideTouchView : function()
13651 this.touchViewEl.removeClass('in');
13655 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13657 this.touchViewEl.setStyle('display', 'none');
13662 setTouchViewValue : function()
13669 Roo.each(this.tickItems, function(o){
13674 this.hideTouchView();
13677 doTouchViewQuery : function()
13686 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13690 if(!this.alwaysQuery || this.mode == 'local'){
13691 this.onTouchViewLoad();
13698 onTouchViewBeforeLoad : function(combo,opts)
13704 onTouchViewLoad : function()
13706 if(this.store.getCount() < 1){
13707 this.onTouchViewEmptyResults();
13711 this.clearTouchView();
13713 var rawValue = this.getRawValue();
13715 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13717 this.tickItems = [];
13719 this.store.data.each(function(d, rowIndex){
13720 var row = this.touchViewListGroup.createChild(template);
13722 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13723 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13726 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13727 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13730 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13731 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13732 this.tickItems.push(d.data);
13735 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13739 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13741 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13743 if(this.fieldLabel.length){
13744 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13747 var listHeight = this.touchViewListGroup.getHeight();
13751 if(firstChecked && listHeight > bodyHeight){
13752 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13757 onTouchViewLoadException : function()
13759 this.hideTouchView();
13762 onTouchViewEmptyResults : function()
13764 this.clearTouchView();
13766 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13768 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13772 clearTouchView : function()
13774 this.touchViewListGroup.dom.innerHTML = '';
13777 onTouchViewClick : function(e, el, o)
13779 e.preventDefault();
13782 var rowIndex = o.rowIndex;
13784 var r = this.store.getAt(rowIndex);
13786 if(!this.multiple){
13787 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13788 c.dom.removeAttribute('checked');
13791 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13793 this.setFromData(r.data);
13795 var close = this.closeTriggerEl();
13801 this.hideTouchView();
13803 this.fireEvent('select', this, r, rowIndex);
13808 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13809 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13810 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13814 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13815 this.addItem(r.data);
13816 this.tickItems.push(r.data);
13822 * @cfg {Boolean} grow
13826 * @cfg {Number} growMin
13830 * @cfg {Number} growMax
13839 Roo.apply(Roo.bootstrap.ComboBox, {
13843 cls: 'modal-header',
13865 cls: 'list-group-item',
13869 cls: 'roo-combobox-list-group-item-value'
13873 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13887 listItemCheckbox : {
13889 cls: 'list-group-item',
13893 cls: 'roo-combobox-list-group-item-value'
13897 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13913 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13918 cls: 'modal-footer',
13926 cls: 'col-xs-6 text-left',
13929 cls: 'btn btn-danger roo-touch-view-cancel',
13935 cls: 'col-xs-6 text-right',
13938 cls: 'btn btn-success roo-touch-view-ok',
13949 Roo.apply(Roo.bootstrap.ComboBox, {
13951 touchViewTemplate : {
13953 cls: 'modal fade roo-combobox-touch-view',
13957 cls: 'modal-dialog',
13961 cls: 'modal-content',
13963 Roo.bootstrap.ComboBox.header,
13964 Roo.bootstrap.ComboBox.body,
13965 Roo.bootstrap.ComboBox.footer
13974 * Ext JS Library 1.1.1
13975 * Copyright(c) 2006-2007, Ext JS, LLC.
13977 * Originally Released Under LGPL - original licence link has changed is not relivant.
13980 * <script type="text/javascript">
13985 * @extends Roo.util.Observable
13986 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13987 * This class also supports single and multi selection modes. <br>
13988 * Create a data model bound view:
13990 var store = new Roo.data.Store(...);
13992 var view = new Roo.View({
13994 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13996 singleSelect: true,
13997 selectedClass: "ydataview-selected",
14001 // listen for node click?
14002 view.on("click", function(vw, index, node, e){
14003 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14007 dataModel.load("foobar.xml");
14009 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14011 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14012 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14014 * Note: old style constructor is still suported (container, template, config)
14017 * Create a new View
14018 * @param {Object} config The config object
14021 Roo.View = function(config, depreciated_tpl, depreciated_config){
14023 this.parent = false;
14025 if (typeof(depreciated_tpl) == 'undefined') {
14026 // new way.. - universal constructor.
14027 Roo.apply(this, config);
14028 this.el = Roo.get(this.el);
14031 this.el = Roo.get(config);
14032 this.tpl = depreciated_tpl;
14033 Roo.apply(this, depreciated_config);
14035 this.wrapEl = this.el.wrap().wrap();
14036 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14039 if(typeof(this.tpl) == "string"){
14040 this.tpl = new Roo.Template(this.tpl);
14042 // support xtype ctors..
14043 this.tpl = new Roo.factory(this.tpl, Roo);
14047 this.tpl.compile();
14052 * @event beforeclick
14053 * Fires before a click is processed. Returns false to cancel the default action.
14054 * @param {Roo.View} this
14055 * @param {Number} index The index of the target node
14056 * @param {HTMLElement} node The target node
14057 * @param {Roo.EventObject} e The raw event object
14059 "beforeclick" : true,
14062 * Fires when a template node is clicked.
14063 * @param {Roo.View} this
14064 * @param {Number} index The index of the target node
14065 * @param {HTMLElement} node The target node
14066 * @param {Roo.EventObject} e The raw event object
14071 * Fires when a template node is double clicked.
14072 * @param {Roo.View} this
14073 * @param {Number} index The index of the target node
14074 * @param {HTMLElement} node The target node
14075 * @param {Roo.EventObject} e The raw event object
14079 * @event contextmenu
14080 * Fires when a template node is right clicked.
14081 * @param {Roo.View} this
14082 * @param {Number} index The index of the target node
14083 * @param {HTMLElement} node The target node
14084 * @param {Roo.EventObject} e The raw event object
14086 "contextmenu" : true,
14088 * @event selectionchange
14089 * Fires when the selected nodes change.
14090 * @param {Roo.View} this
14091 * @param {Array} selections Array of the selected nodes
14093 "selectionchange" : true,
14096 * @event beforeselect
14097 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14098 * @param {Roo.View} this
14099 * @param {HTMLElement} node The node to be selected
14100 * @param {Array} selections Array of currently selected nodes
14102 "beforeselect" : true,
14104 * @event preparedata
14105 * Fires on every row to render, to allow you to change the data.
14106 * @param {Roo.View} this
14107 * @param {Object} data to be rendered (change this)
14109 "preparedata" : true
14117 "click": this.onClick,
14118 "dblclick": this.onDblClick,
14119 "contextmenu": this.onContextMenu,
14123 this.selections = [];
14125 this.cmp = new Roo.CompositeElementLite([]);
14127 this.store = Roo.factory(this.store, Roo.data);
14128 this.setStore(this.store, true);
14131 if ( this.footer && this.footer.xtype) {
14133 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14135 this.footer.dataSource = this.store;
14136 this.footer.container = fctr;
14137 this.footer = Roo.factory(this.footer, Roo);
14138 fctr.insertFirst(this.el);
14140 // this is a bit insane - as the paging toolbar seems to detach the el..
14141 // dom.parentNode.parentNode.parentNode
14142 // they get detached?
14146 Roo.View.superclass.constructor.call(this);
14151 Roo.extend(Roo.View, Roo.util.Observable, {
14154 * @cfg {Roo.data.Store} store Data store to load data from.
14159 * @cfg {String|Roo.Element} el The container element.
14164 * @cfg {String|Roo.Template} tpl The template used by this View
14168 * @cfg {String} dataName the named area of the template to use as the data area
14169 * Works with domtemplates roo-name="name"
14173 * @cfg {String} selectedClass The css class to add to selected nodes
14175 selectedClass : "x-view-selected",
14177 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14182 * @cfg {String} text to display on mask (default Loading)
14186 * @cfg {Boolean} multiSelect Allow multiple selection
14188 multiSelect : false,
14190 * @cfg {Boolean} singleSelect Allow single selection
14192 singleSelect: false,
14195 * @cfg {Boolean} toggleSelect - selecting
14197 toggleSelect : false,
14200 * @cfg {Boolean} tickable - selecting
14205 * Returns the element this view is bound to.
14206 * @return {Roo.Element}
14208 getEl : function(){
14209 return this.wrapEl;
14215 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14217 refresh : function(){
14218 //Roo.log('refresh');
14221 // if we are using something like 'domtemplate', then
14222 // the what gets used is:
14223 // t.applySubtemplate(NAME, data, wrapping data..)
14224 // the outer template then get' applied with
14225 // the store 'extra data'
14226 // and the body get's added to the
14227 // roo-name="data" node?
14228 // <span class='roo-tpl-{name}'></span> ?????
14232 this.clearSelections();
14233 this.el.update("");
14235 var records = this.store.getRange();
14236 if(records.length < 1) {
14238 // is this valid?? = should it render a template??
14240 this.el.update(this.emptyText);
14244 if (this.dataName) {
14245 this.el.update(t.apply(this.store.meta)); //????
14246 el = this.el.child('.roo-tpl-' + this.dataName);
14249 for(var i = 0, len = records.length; i < len; i++){
14250 var data = this.prepareData(records[i].data, i, records[i]);
14251 this.fireEvent("preparedata", this, data, i, records[i]);
14253 var d = Roo.apply({}, data);
14256 Roo.apply(d, {'roo-id' : Roo.id()});
14260 Roo.each(this.parent.item, function(item){
14261 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14264 Roo.apply(d, {'roo-data-checked' : 'checked'});
14268 html[html.length] = Roo.util.Format.trim(
14270 t.applySubtemplate(this.dataName, d, this.store.meta) :
14277 el.update(html.join(""));
14278 this.nodes = el.dom.childNodes;
14279 this.updateIndexes(0);
14284 * Function to override to reformat the data that is sent to
14285 * the template for each node.
14286 * DEPRICATED - use the preparedata event handler.
14287 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14288 * a JSON object for an UpdateManager bound view).
14290 prepareData : function(data, index, record)
14292 this.fireEvent("preparedata", this, data, index, record);
14296 onUpdate : function(ds, record){
14297 // Roo.log('on update');
14298 this.clearSelections();
14299 var index = this.store.indexOf(record);
14300 var n = this.nodes[index];
14301 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14302 n.parentNode.removeChild(n);
14303 this.updateIndexes(index, index);
14309 onAdd : function(ds, records, index)
14311 //Roo.log(['on Add', ds, records, index] );
14312 this.clearSelections();
14313 if(this.nodes.length == 0){
14317 var n = this.nodes[index];
14318 for(var i = 0, len = records.length; i < len; i++){
14319 var d = this.prepareData(records[i].data, i, records[i]);
14321 this.tpl.insertBefore(n, d);
14324 this.tpl.append(this.el, d);
14327 this.updateIndexes(index);
14330 onRemove : function(ds, record, index){
14331 // Roo.log('onRemove');
14332 this.clearSelections();
14333 var el = this.dataName ?
14334 this.el.child('.roo-tpl-' + this.dataName) :
14337 el.dom.removeChild(this.nodes[index]);
14338 this.updateIndexes(index);
14342 * Refresh an individual node.
14343 * @param {Number} index
14345 refreshNode : function(index){
14346 this.onUpdate(this.store, this.store.getAt(index));
14349 updateIndexes : function(startIndex, endIndex){
14350 var ns = this.nodes;
14351 startIndex = startIndex || 0;
14352 endIndex = endIndex || ns.length - 1;
14353 for(var i = startIndex; i <= endIndex; i++){
14354 ns[i].nodeIndex = i;
14359 * Changes the data store this view uses and refresh the view.
14360 * @param {Store} store
14362 setStore : function(store, initial){
14363 if(!initial && this.store){
14364 this.store.un("datachanged", this.refresh);
14365 this.store.un("add", this.onAdd);
14366 this.store.un("remove", this.onRemove);
14367 this.store.un("update", this.onUpdate);
14368 this.store.un("clear", this.refresh);
14369 this.store.un("beforeload", this.onBeforeLoad);
14370 this.store.un("load", this.onLoad);
14371 this.store.un("loadexception", this.onLoad);
14375 store.on("datachanged", this.refresh, this);
14376 store.on("add", this.onAdd, this);
14377 store.on("remove", this.onRemove, this);
14378 store.on("update", this.onUpdate, this);
14379 store.on("clear", this.refresh, this);
14380 store.on("beforeload", this.onBeforeLoad, this);
14381 store.on("load", this.onLoad, this);
14382 store.on("loadexception", this.onLoad, this);
14390 * onbeforeLoad - masks the loading area.
14393 onBeforeLoad : function(store,opts)
14395 //Roo.log('onBeforeLoad');
14397 this.el.update("");
14399 this.el.mask(this.mask ? this.mask : "Loading" );
14401 onLoad : function ()
14408 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14409 * @param {HTMLElement} node
14410 * @return {HTMLElement} The template node
14412 findItemFromChild : function(node){
14413 var el = this.dataName ?
14414 this.el.child('.roo-tpl-' + this.dataName,true) :
14417 if(!node || node.parentNode == el){
14420 var p = node.parentNode;
14421 while(p && p != el){
14422 if(p.parentNode == el){
14431 onClick : function(e){
14432 var item = this.findItemFromChild(e.getTarget());
14434 var index = this.indexOf(item);
14435 if(this.onItemClick(item, index, e) !== false){
14436 this.fireEvent("click", this, index, item, e);
14439 this.clearSelections();
14444 onContextMenu : function(e){
14445 var item = this.findItemFromChild(e.getTarget());
14447 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14452 onDblClick : function(e){
14453 var item = this.findItemFromChild(e.getTarget());
14455 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14459 onItemClick : function(item, index, e)
14461 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14464 if (this.toggleSelect) {
14465 var m = this.isSelected(item) ? 'unselect' : 'select';
14468 _t[m](item, true, false);
14471 if(this.multiSelect || this.singleSelect){
14472 if(this.multiSelect && e.shiftKey && this.lastSelection){
14473 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14475 this.select(item, this.multiSelect && e.ctrlKey);
14476 this.lastSelection = item;
14479 if(!this.tickable){
14480 e.preventDefault();
14488 * Get the number of selected nodes.
14491 getSelectionCount : function(){
14492 return this.selections.length;
14496 * Get the currently selected nodes.
14497 * @return {Array} An array of HTMLElements
14499 getSelectedNodes : function(){
14500 return this.selections;
14504 * Get the indexes of the selected nodes.
14507 getSelectedIndexes : function(){
14508 var indexes = [], s = this.selections;
14509 for(var i = 0, len = s.length; i < len; i++){
14510 indexes.push(s[i].nodeIndex);
14516 * Clear all selections
14517 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14519 clearSelections : function(suppressEvent){
14520 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14521 this.cmp.elements = this.selections;
14522 this.cmp.removeClass(this.selectedClass);
14523 this.selections = [];
14524 if(!suppressEvent){
14525 this.fireEvent("selectionchange", this, this.selections);
14531 * Returns true if the passed node is selected
14532 * @param {HTMLElement/Number} node The node or node index
14533 * @return {Boolean}
14535 isSelected : function(node){
14536 var s = this.selections;
14540 node = this.getNode(node);
14541 return s.indexOf(node) !== -1;
14546 * @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
14547 * @param {Boolean} keepExisting (optional) true to keep existing selections
14548 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14550 select : function(nodeInfo, keepExisting, suppressEvent){
14551 if(nodeInfo instanceof Array){
14553 this.clearSelections(true);
14555 for(var i = 0, len = nodeInfo.length; i < len; i++){
14556 this.select(nodeInfo[i], true, true);
14560 var node = this.getNode(nodeInfo);
14561 if(!node || this.isSelected(node)){
14562 return; // already selected.
14565 this.clearSelections(true);
14568 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14569 Roo.fly(node).addClass(this.selectedClass);
14570 this.selections.push(node);
14571 if(!suppressEvent){
14572 this.fireEvent("selectionchange", this, this.selections);
14580 * @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
14581 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14582 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14584 unselect : function(nodeInfo, keepExisting, suppressEvent)
14586 if(nodeInfo instanceof Array){
14587 Roo.each(this.selections, function(s) {
14588 this.unselect(s, nodeInfo);
14592 var node = this.getNode(nodeInfo);
14593 if(!node || !this.isSelected(node)){
14594 //Roo.log("not selected");
14595 return; // not selected.
14599 Roo.each(this.selections, function(s) {
14601 Roo.fly(node).removeClass(this.selectedClass);
14608 this.selections= ns;
14609 this.fireEvent("selectionchange", this, this.selections);
14613 * Gets a template node.
14614 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14615 * @return {HTMLElement} The node or null if it wasn't found
14617 getNode : function(nodeInfo){
14618 if(typeof nodeInfo == "string"){
14619 return document.getElementById(nodeInfo);
14620 }else if(typeof nodeInfo == "number"){
14621 return this.nodes[nodeInfo];
14627 * Gets a range template nodes.
14628 * @param {Number} startIndex
14629 * @param {Number} endIndex
14630 * @return {Array} An array of nodes
14632 getNodes : function(start, end){
14633 var ns = this.nodes;
14634 start = start || 0;
14635 end = typeof end == "undefined" ? ns.length - 1 : end;
14638 for(var i = start; i <= end; i++){
14642 for(var i = start; i >= end; i--){
14650 * Finds the index of the passed node
14651 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14652 * @return {Number} The index of the node or -1
14654 indexOf : function(node){
14655 node = this.getNode(node);
14656 if(typeof node.nodeIndex == "number"){
14657 return node.nodeIndex;
14659 var ns = this.nodes;
14660 for(var i = 0, len = ns.length; i < len; i++){
14671 * based on jquery fullcalendar
14675 Roo.bootstrap = Roo.bootstrap || {};
14677 * @class Roo.bootstrap.Calendar
14678 * @extends Roo.bootstrap.Component
14679 * Bootstrap Calendar class
14680 * @cfg {Boolean} loadMask (true|false) default false
14681 * @cfg {Object} header generate the user specific header of the calendar, default false
14684 * Create a new Container
14685 * @param {Object} config The config object
14690 Roo.bootstrap.Calendar = function(config){
14691 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14695 * Fires when a date is selected
14696 * @param {DatePicker} this
14697 * @param {Date} date The selected date
14701 * @event monthchange
14702 * Fires when the displayed month changes
14703 * @param {DatePicker} this
14704 * @param {Date} date The selected month
14706 'monthchange': true,
14708 * @event evententer
14709 * Fires when mouse over an event
14710 * @param {Calendar} this
14711 * @param {event} Event
14713 'evententer': true,
14715 * @event eventleave
14716 * Fires when the mouse leaves an
14717 * @param {Calendar} this
14720 'eventleave': true,
14722 * @event eventclick
14723 * Fires when the mouse click an
14724 * @param {Calendar} this
14733 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14736 * @cfg {Number} startDay
14737 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14745 getAutoCreate : function(){
14748 var fc_button = function(name, corner, style, content ) {
14749 return Roo.apply({},{
14751 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14753 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14756 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14767 style : 'width:100%',
14774 cls : 'fc-header-left',
14776 fc_button('prev', 'left', 'arrow', '‹' ),
14777 fc_button('next', 'right', 'arrow', '›' ),
14778 { tag: 'span', cls: 'fc-header-space' },
14779 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14787 cls : 'fc-header-center',
14791 cls: 'fc-header-title',
14794 html : 'month / year'
14802 cls : 'fc-header-right',
14804 /* fc_button('month', 'left', '', 'month' ),
14805 fc_button('week', '', '', 'week' ),
14806 fc_button('day', 'right', '', 'day' )
14818 header = this.header;
14821 var cal_heads = function() {
14823 // fixme - handle this.
14825 for (var i =0; i < Date.dayNames.length; i++) {
14826 var d = Date.dayNames[i];
14829 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14830 html : d.substring(0,3)
14834 ret[0].cls += ' fc-first';
14835 ret[6].cls += ' fc-last';
14838 var cal_cell = function(n) {
14841 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14846 cls: 'fc-day-number',
14850 cls: 'fc-day-content',
14854 style: 'position: relative;' // height: 17px;
14866 var cal_rows = function() {
14869 for (var r = 0; r < 6; r++) {
14876 for (var i =0; i < Date.dayNames.length; i++) {
14877 var d = Date.dayNames[i];
14878 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14881 row.cn[0].cls+=' fc-first';
14882 row.cn[0].cn[0].style = 'min-height:90px';
14883 row.cn[6].cls+=' fc-last';
14887 ret[0].cls += ' fc-first';
14888 ret[4].cls += ' fc-prev-last';
14889 ret[5].cls += ' fc-last';
14896 cls: 'fc-border-separate',
14897 style : 'width:100%',
14905 cls : 'fc-first fc-last',
14923 cls : 'fc-content',
14924 style : "position: relative;",
14927 cls : 'fc-view fc-view-month fc-grid',
14928 style : 'position: relative',
14929 unselectable : 'on',
14932 cls : 'fc-event-container',
14933 style : 'position:absolute;z-index:8;top:0;left:0;'
14951 initEvents : function()
14954 throw "can not find store for calendar";
14960 style: "text-align:center",
14964 style: "background-color:white;width:50%;margin:250 auto",
14968 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14979 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14981 var size = this.el.select('.fc-content', true).first().getSize();
14982 this.maskEl.setSize(size.width, size.height);
14983 this.maskEl.enableDisplayMode("block");
14984 if(!this.loadMask){
14985 this.maskEl.hide();
14988 this.store = Roo.factory(this.store, Roo.data);
14989 this.store.on('load', this.onLoad, this);
14990 this.store.on('beforeload', this.onBeforeLoad, this);
14994 this.cells = this.el.select('.fc-day',true);
14995 //Roo.log(this.cells);
14996 this.textNodes = this.el.query('.fc-day-number');
14997 this.cells.addClassOnOver('fc-state-hover');
14999 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15000 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15001 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15002 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15004 this.on('monthchange', this.onMonthChange, this);
15006 this.update(new Date().clearTime());
15009 resize : function() {
15010 var sz = this.el.getSize();
15012 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15013 this.el.select('.fc-day-content div',true).setHeight(34);
15018 showPrevMonth : function(e){
15019 this.update(this.activeDate.add("mo", -1));
15021 showToday : function(e){
15022 this.update(new Date().clearTime());
15025 showNextMonth : function(e){
15026 this.update(this.activeDate.add("mo", 1));
15030 showPrevYear : function(){
15031 this.update(this.activeDate.add("y", -1));
15035 showNextYear : function(){
15036 this.update(this.activeDate.add("y", 1));
15041 update : function(date)
15043 var vd = this.activeDate;
15044 this.activeDate = date;
15045 // if(vd && this.el){
15046 // var t = date.getTime();
15047 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15048 // Roo.log('using add remove');
15050 // this.fireEvent('monthchange', this, date);
15052 // this.cells.removeClass("fc-state-highlight");
15053 // this.cells.each(function(c){
15054 // if(c.dateValue == t){
15055 // c.addClass("fc-state-highlight");
15056 // setTimeout(function(){
15057 // try{c.dom.firstChild.focus();}catch(e){}
15067 var days = date.getDaysInMonth();
15069 var firstOfMonth = date.getFirstDateOfMonth();
15070 var startingPos = firstOfMonth.getDay()-this.startDay;
15072 if(startingPos < this.startDay){
15076 var pm = date.add(Date.MONTH, -1);
15077 var prevStart = pm.getDaysInMonth()-startingPos;
15079 this.cells = this.el.select('.fc-day',true);
15080 this.textNodes = this.el.query('.fc-day-number');
15081 this.cells.addClassOnOver('fc-state-hover');
15083 var cells = this.cells.elements;
15084 var textEls = this.textNodes;
15086 Roo.each(cells, function(cell){
15087 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15090 days += startingPos;
15092 // convert everything to numbers so it's fast
15093 var day = 86400000;
15094 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15097 //Roo.log(prevStart);
15099 var today = new Date().clearTime().getTime();
15100 var sel = date.clearTime().getTime();
15101 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15102 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15103 var ddMatch = this.disabledDatesRE;
15104 var ddText = this.disabledDatesText;
15105 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15106 var ddaysText = this.disabledDaysText;
15107 var format = this.format;
15109 var setCellClass = function(cal, cell){
15113 //Roo.log('set Cell Class');
15115 var t = d.getTime();
15119 cell.dateValue = t;
15121 cell.className += " fc-today";
15122 cell.className += " fc-state-highlight";
15123 cell.title = cal.todayText;
15126 // disable highlight in other month..
15127 //cell.className += " fc-state-highlight";
15132 cell.className = " fc-state-disabled";
15133 cell.title = cal.minText;
15137 cell.className = " fc-state-disabled";
15138 cell.title = cal.maxText;
15142 if(ddays.indexOf(d.getDay()) != -1){
15143 cell.title = ddaysText;
15144 cell.className = " fc-state-disabled";
15147 if(ddMatch && format){
15148 var fvalue = d.dateFormat(format);
15149 if(ddMatch.test(fvalue)){
15150 cell.title = ddText.replace("%0", fvalue);
15151 cell.className = " fc-state-disabled";
15155 if (!cell.initialClassName) {
15156 cell.initialClassName = cell.dom.className;
15159 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15164 for(; i < startingPos; i++) {
15165 textEls[i].innerHTML = (++prevStart);
15166 d.setDate(d.getDate()+1);
15168 cells[i].className = "fc-past fc-other-month";
15169 setCellClass(this, cells[i]);
15174 for(; i < days; i++){
15175 intDay = i - startingPos + 1;
15176 textEls[i].innerHTML = (intDay);
15177 d.setDate(d.getDate()+1);
15179 cells[i].className = ''; // "x-date-active";
15180 setCellClass(this, cells[i]);
15184 for(; i < 42; i++) {
15185 textEls[i].innerHTML = (++extraDays);
15186 d.setDate(d.getDate()+1);
15188 cells[i].className = "fc-future fc-other-month";
15189 setCellClass(this, cells[i]);
15192 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15194 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15196 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15197 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15199 if(totalRows != 6){
15200 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15201 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15204 this.fireEvent('monthchange', this, date);
15208 if(!this.internalRender){
15209 var main = this.el.dom.firstChild;
15210 var w = main.offsetWidth;
15211 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15212 Roo.fly(main).setWidth(w);
15213 this.internalRender = true;
15214 // opera does not respect the auto grow header center column
15215 // then, after it gets a width opera refuses to recalculate
15216 // without a second pass
15217 if(Roo.isOpera && !this.secondPass){
15218 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15219 this.secondPass = true;
15220 this.update.defer(10, this, [date]);
15227 findCell : function(dt) {
15228 dt = dt.clearTime().getTime();
15230 this.cells.each(function(c){
15231 //Roo.log("check " +c.dateValue + '?=' + dt);
15232 if(c.dateValue == dt){
15242 findCells : function(ev) {
15243 var s = ev.start.clone().clearTime().getTime();
15245 var e= ev.end.clone().clearTime().getTime();
15248 this.cells.each(function(c){
15249 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15251 if(c.dateValue > e){
15254 if(c.dateValue < s){
15263 // findBestRow: function(cells)
15267 // for (var i =0 ; i < cells.length;i++) {
15268 // ret = Math.max(cells[i].rows || 0,ret);
15275 addItem : function(ev)
15277 // look for vertical location slot in
15278 var cells = this.findCells(ev);
15280 // ev.row = this.findBestRow(cells);
15282 // work out the location.
15286 for(var i =0; i < cells.length; i++) {
15288 cells[i].row = cells[0].row;
15291 cells[i].row = cells[i].row + 1;
15301 if (crow.start.getY() == cells[i].getY()) {
15303 crow.end = cells[i];
15320 cells[0].events.push(ev);
15322 this.calevents.push(ev);
15325 clearEvents: function() {
15327 if(!this.calevents){
15331 Roo.each(this.cells.elements, function(c){
15337 Roo.each(this.calevents, function(e) {
15338 Roo.each(e.els, function(el) {
15339 el.un('mouseenter' ,this.onEventEnter, this);
15340 el.un('mouseleave' ,this.onEventLeave, this);
15345 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15351 renderEvents: function()
15355 this.cells.each(function(c) {
15364 if(c.row != c.events.length){
15365 r = 4 - (4 - (c.row - c.events.length));
15368 c.events = ev.slice(0, r);
15369 c.more = ev.slice(r);
15371 if(c.more.length && c.more.length == 1){
15372 c.events.push(c.more.pop());
15375 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15379 this.cells.each(function(c) {
15381 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15384 for (var e = 0; e < c.events.length; e++){
15385 var ev = c.events[e];
15386 var rows = ev.rows;
15388 for(var i = 0; i < rows.length; i++) {
15390 // how many rows should it span..
15393 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15394 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15396 unselectable : "on",
15399 cls: 'fc-event-inner',
15403 // cls: 'fc-event-time',
15404 // html : cells.length > 1 ? '' : ev.time
15408 cls: 'fc-event-title',
15409 html : String.format('{0}', ev.title)
15416 cls: 'ui-resizable-handle ui-resizable-e',
15417 html : '  '
15424 cfg.cls += ' fc-event-start';
15426 if ((i+1) == rows.length) {
15427 cfg.cls += ' fc-event-end';
15430 var ctr = _this.el.select('.fc-event-container',true).first();
15431 var cg = ctr.createChild(cfg);
15433 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15434 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15436 var r = (c.more.length) ? 1 : 0;
15437 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15438 cg.setWidth(ebox.right - sbox.x -2);
15440 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15441 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15442 cg.on('click', _this.onEventClick, _this, ev);
15453 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15454 style : 'position: absolute',
15455 unselectable : "on",
15458 cls: 'fc-event-inner',
15462 cls: 'fc-event-title',
15470 cls: 'ui-resizable-handle ui-resizable-e',
15471 html : '  '
15477 var ctr = _this.el.select('.fc-event-container',true).first();
15478 var cg = ctr.createChild(cfg);
15480 var sbox = c.select('.fc-day-content',true).first().getBox();
15481 var ebox = c.select('.fc-day-content',true).first().getBox();
15483 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15484 cg.setWidth(ebox.right - sbox.x -2);
15486 cg.on('click', _this.onMoreEventClick, _this, c.more);
15496 onEventEnter: function (e, el,event,d) {
15497 this.fireEvent('evententer', this, el, event);
15500 onEventLeave: function (e, el,event,d) {
15501 this.fireEvent('eventleave', this, el, event);
15504 onEventClick: function (e, el,event,d) {
15505 this.fireEvent('eventclick', this, el, event);
15508 onMonthChange: function () {
15512 onMoreEventClick: function(e, el, more)
15516 this.calpopover.placement = 'right';
15517 this.calpopover.setTitle('More');
15519 this.calpopover.setContent('');
15521 var ctr = this.calpopover.el.select('.popover-content', true).first();
15523 Roo.each(more, function(m){
15525 cls : 'fc-event-hori fc-event-draggable',
15528 var cg = ctr.createChild(cfg);
15530 cg.on('click', _this.onEventClick, _this, m);
15533 this.calpopover.show(el);
15538 onLoad: function ()
15540 this.calevents = [];
15543 if(this.store.getCount() > 0){
15544 this.store.data.each(function(d){
15547 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15548 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15549 time : d.data.start_time,
15550 title : d.data.title,
15551 description : d.data.description,
15552 venue : d.data.venue
15557 this.renderEvents();
15559 if(this.calevents.length && this.loadMask){
15560 this.maskEl.hide();
15564 onBeforeLoad: function()
15566 this.clearEvents();
15568 this.maskEl.show();
15582 * @class Roo.bootstrap.Popover
15583 * @extends Roo.bootstrap.Component
15584 * Bootstrap Popover class
15585 * @cfg {String} html contents of the popover (or false to use children..)
15586 * @cfg {String} title of popover (or false to hide)
15587 * @cfg {String} placement how it is placed
15588 * @cfg {String} trigger click || hover (or false to trigger manually)
15589 * @cfg {String} over what (parent or false to trigger manually.)
15590 * @cfg {Number} delay - delay before showing
15593 * Create a new Popover
15594 * @param {Object} config The config object
15597 Roo.bootstrap.Popover = function(config){
15598 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15601 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15603 title: 'Fill in a title',
15606 placement : 'right',
15607 trigger : 'hover', // hover
15613 can_build_overlaid : false,
15615 getChildContainer : function()
15617 return this.el.select('.popover-content',true).first();
15620 getAutoCreate : function(){
15621 Roo.log('make popover?');
15623 cls : 'popover roo-dynamic',
15624 style: 'display:block',
15630 cls : 'popover-inner',
15634 cls: 'popover-title',
15638 cls : 'popover-content',
15649 setTitle: function(str)
15652 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15654 setContent: function(str)
15657 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15659 // as it get's added to the bottom of the page.
15660 onRender : function(ct, position)
15662 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15664 var cfg = Roo.apply({}, this.getAutoCreate());
15668 cfg.cls += ' ' + this.cls;
15671 cfg.style = this.style;
15673 //Roo.log("adding to ");
15674 this.el = Roo.get(document.body).createChild(cfg, position);
15680 initEvents : function()
15682 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15683 this.el.enableDisplayMode('block');
15685 if (this.over === false) {
15688 if (this.triggers === false) {
15691 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15692 var triggers = this.trigger ? this.trigger.split(' ') : [];
15693 Roo.each(triggers, function(trigger) {
15695 if (trigger == 'click') {
15696 on_el.on('click', this.toggle, this);
15697 } else if (trigger != 'manual') {
15698 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15699 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15701 on_el.on(eventIn ,this.enter, this);
15702 on_el.on(eventOut, this.leave, this);
15713 toggle : function () {
15714 this.hoverState == 'in' ? this.leave() : this.enter();
15717 enter : function () {
15720 clearTimeout(this.timeout);
15722 this.hoverState = 'in';
15724 if (!this.delay || !this.delay.show) {
15729 this.timeout = setTimeout(function () {
15730 if (_t.hoverState == 'in') {
15733 }, this.delay.show)
15735 leave : function() {
15736 clearTimeout(this.timeout);
15738 this.hoverState = 'out';
15740 if (!this.delay || !this.delay.hide) {
15745 this.timeout = setTimeout(function () {
15746 if (_t.hoverState == 'out') {
15749 }, this.delay.hide)
15752 show : function (on_el)
15755 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15758 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15759 if (this.html !== false) {
15760 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15762 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15763 if (!this.title.length) {
15764 this.el.select('.popover-title',true).hide();
15767 var placement = typeof this.placement == 'function' ?
15768 this.placement.call(this, this.el, on_el) :
15771 var autoToken = /\s?auto?\s?/i;
15772 var autoPlace = autoToken.test(placement);
15774 placement = placement.replace(autoToken, '') || 'top';
15778 //this.el.setXY([0,0]);
15780 this.el.dom.style.display='block';
15781 this.el.addClass(placement);
15783 //this.el.appendTo(on_el);
15785 var p = this.getPosition();
15786 var box = this.el.getBox();
15791 var align = Roo.bootstrap.Popover.alignment[placement];
15792 this.el.alignTo(on_el, align[0],align[1]);
15793 //var arrow = this.el.select('.arrow',true).first();
15794 //arrow.set(align[2],
15796 this.el.addClass('in');
15799 if (this.el.hasClass('fade')) {
15806 this.el.setXY([0,0]);
15807 this.el.removeClass('in');
15809 this.hoverState = null;
15815 Roo.bootstrap.Popover.alignment = {
15816 'left' : ['r-l', [-10,0], 'right'],
15817 'right' : ['l-r', [10,0], 'left'],
15818 'bottom' : ['t-b', [0,10], 'top'],
15819 'top' : [ 'b-t', [0,-10], 'bottom']
15830 * @class Roo.bootstrap.Progress
15831 * @extends Roo.bootstrap.Component
15832 * Bootstrap Progress class
15833 * @cfg {Boolean} striped striped of the progress bar
15834 * @cfg {Boolean} active animated of the progress bar
15838 * Create a new Progress
15839 * @param {Object} config The config object
15842 Roo.bootstrap.Progress = function(config){
15843 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15846 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15851 getAutoCreate : function(){
15859 cfg.cls += ' progress-striped';
15863 cfg.cls += ' active';
15882 * @class Roo.bootstrap.ProgressBar
15883 * @extends Roo.bootstrap.Component
15884 * Bootstrap ProgressBar class
15885 * @cfg {Number} aria_valuenow aria-value now
15886 * @cfg {Number} aria_valuemin aria-value min
15887 * @cfg {Number} aria_valuemax aria-value max
15888 * @cfg {String} label label for the progress bar
15889 * @cfg {String} panel (success | info | warning | danger )
15890 * @cfg {String} role role of the progress bar
15891 * @cfg {String} sr_only text
15895 * Create a new ProgressBar
15896 * @param {Object} config The config object
15899 Roo.bootstrap.ProgressBar = function(config){
15900 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15903 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15907 aria_valuemax : 100,
15913 getAutoCreate : function()
15918 cls: 'progress-bar',
15919 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15931 cfg.role = this.role;
15934 if(this.aria_valuenow){
15935 cfg['aria-valuenow'] = this.aria_valuenow;
15938 if(this.aria_valuemin){
15939 cfg['aria-valuemin'] = this.aria_valuemin;
15942 if(this.aria_valuemax){
15943 cfg['aria-valuemax'] = this.aria_valuemax;
15946 if(this.label && !this.sr_only){
15947 cfg.html = this.label;
15951 cfg.cls += ' progress-bar-' + this.panel;
15957 update : function(aria_valuenow)
15959 this.aria_valuenow = aria_valuenow;
15961 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15976 * @class Roo.bootstrap.TabGroup
15977 * @extends Roo.bootstrap.Column
15978 * Bootstrap Column class
15979 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15980 * @cfg {Boolean} carousel true to make the group behave like a carousel
15981 * @cfg {Boolean} bullets show bullets for the panels
15982 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15983 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15984 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15987 * Create a new TabGroup
15988 * @param {Object} config The config object
15991 Roo.bootstrap.TabGroup = function(config){
15992 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15994 this.navId = Roo.id();
15997 Roo.bootstrap.TabGroup.register(this);
16001 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16004 transition : false,
16009 slideOnTouch : false,
16011 getAutoCreate : function()
16013 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16015 cfg.cls += ' tab-content';
16017 Roo.log('get auto create...............');
16019 if (this.carousel) {
16020 cfg.cls += ' carousel slide';
16023 cls : 'carousel-inner'
16026 if(this.bullets && !Roo.isTouch){
16029 cls : 'carousel-bullets',
16033 if(this.bullets_cls){
16034 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16037 for (var i = 0; i < this.bullets; i++){
16039 cls : 'bullet bullet-' + i
16047 cfg.cn[0].cn = bullets;
16054 initEvents: function()
16056 Roo.log('-------- init events on tab group ---------');
16062 if(Roo.isTouch && this.slideOnTouch){
16063 this.el.on("touchstart", this.onTouchStart, this);
16066 if(this.autoslide){
16069 this.slideFn = window.setInterval(function() {
16070 _this.showPanelNext();
16076 onTouchStart : function(e, el, o)
16078 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16082 this.showPanelNext();
16085 getChildContainer : function()
16087 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16091 * register a Navigation item
16092 * @param {Roo.bootstrap.NavItem} the navitem to add
16094 register : function(item)
16096 this.tabs.push( item);
16097 item.navId = this.navId; // not really needed..
16102 getActivePanel : function()
16105 Roo.each(this.tabs, function(t) {
16115 getPanelByName : function(n)
16118 Roo.each(this.tabs, function(t) {
16119 if (t.tabId == n) {
16127 indexOfPanel : function(p)
16130 Roo.each(this.tabs, function(t,i) {
16131 if (t.tabId == p.tabId) {
16140 * show a specific panel
16141 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16142 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16144 showPanel : function (pan)
16146 if(this.transition){
16147 Roo.log("waiting for the transitionend");
16151 if (typeof(pan) == 'number') {
16152 pan = this.tabs[pan];
16154 if (typeof(pan) == 'string') {
16155 pan = this.getPanelByName(pan);
16157 if (pan.tabId == this.getActivePanel().tabId) {
16160 var cur = this.getActivePanel();
16162 if (false === cur.fireEvent('beforedeactivate')) {
16166 if(this.bullets > 0 && !Roo.isTouch){
16167 this.setActiveBullet(this.indexOfPanel(pan));
16170 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16172 this.transition = true;
16173 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16174 var lr = dir == 'next' ? 'left' : 'right';
16175 pan.el.addClass(dir); // or prev
16176 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16177 cur.el.addClass(lr); // or right
16178 pan.el.addClass(lr);
16181 cur.el.on('transitionend', function() {
16182 Roo.log("trans end?");
16184 pan.el.removeClass([lr,dir]);
16185 pan.setActive(true);
16187 cur.el.removeClass([lr]);
16188 cur.setActive(false);
16190 _this.transition = false;
16192 }, this, { single: true } );
16197 cur.setActive(false);
16198 pan.setActive(true);
16203 showPanelNext : function()
16205 var i = this.indexOfPanel(this.getActivePanel());
16207 if (i >= this.tabs.length - 1 && !this.autoslide) {
16211 if (i >= this.tabs.length - 1 && this.autoslide) {
16215 this.showPanel(this.tabs[i+1]);
16218 showPanelPrev : function()
16220 var i = this.indexOfPanel(this.getActivePanel());
16222 if (i < 1 && !this.autoslide) {
16226 if (i < 1 && this.autoslide) {
16227 i = this.tabs.length;
16230 this.showPanel(this.tabs[i-1]);
16234 addBullet: function()
16236 if(!this.bullets || Roo.isTouch){
16239 var ctr = this.el.select('.carousel-bullets',true).first();
16240 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16241 var bullet = ctr.createChild({
16242 cls : 'bullet bullet-' + i
16243 },ctr.dom.lastChild);
16248 bullet.on('click', (function(e, el, o, ii, t){
16250 e.preventDefault();
16252 this.showPanel(ii);
16254 if(this.autoslide && this.slideFn){
16255 clearInterval(this.slideFn);
16256 this.slideFn = window.setInterval(function() {
16257 _this.showPanelNext();
16261 }).createDelegate(this, [i, bullet], true));
16266 setActiveBullet : function(i)
16272 Roo.each(this.el.select('.bullet', true).elements, function(el){
16273 el.removeClass('selected');
16276 var bullet = this.el.select('.bullet-' + i, true).first();
16282 bullet.addClass('selected');
16293 Roo.apply(Roo.bootstrap.TabGroup, {
16297 * register a Navigation Group
16298 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16300 register : function(navgrp)
16302 this.groups[navgrp.navId] = navgrp;
16306 * fetch a Navigation Group based on the navigation ID
16307 * if one does not exist , it will get created.
16308 * @param {string} the navgroup to add
16309 * @returns {Roo.bootstrap.NavGroup} the navgroup
16311 get: function(navId) {
16312 if (typeof(this.groups[navId]) == 'undefined') {
16313 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16315 return this.groups[navId] ;
16330 * @class Roo.bootstrap.TabPanel
16331 * @extends Roo.bootstrap.Component
16332 * Bootstrap TabPanel class
16333 * @cfg {Boolean} active panel active
16334 * @cfg {String} html panel content
16335 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16336 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16340 * Create a new TabPanel
16341 * @param {Object} config The config object
16344 Roo.bootstrap.TabPanel = function(config){
16345 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16349 * Fires when the active status changes
16350 * @param {Roo.bootstrap.TabPanel} this
16351 * @param {Boolean} state the new state
16356 * @event beforedeactivate
16357 * Fires before a tab is de-activated - can be used to do validation on a form.
16358 * @param {Roo.bootstrap.TabPanel} this
16359 * @return {Boolean} false if there is an error
16362 'beforedeactivate': true
16365 this.tabId = this.tabId || Roo.id();
16369 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16376 getAutoCreate : function(){
16379 // item is needed for carousel - not sure if it has any effect otherwise
16380 cls: 'tab-pane item',
16381 html: this.html || ''
16385 cfg.cls += ' active';
16389 cfg.tabId = this.tabId;
16396 initEvents: function()
16398 Roo.log('-------- init events on tab panel ---------');
16400 var p = this.parent();
16401 this.navId = this.navId || p.navId;
16403 if (typeof(this.navId) != 'undefined') {
16404 // not really needed.. but just in case.. parent should be a NavGroup.
16405 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16406 Roo.log(['register', tg, this]);
16409 var i = tg.tabs.length - 1;
16411 if(this.active && tg.bullets > 0 && i < tg.bullets){
16412 tg.setActiveBullet(i);
16419 onRender : function(ct, position)
16421 // Roo.log("Call onRender: " + this.xtype);
16423 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16431 setActive: function(state)
16433 Roo.log("panel - set active " + this.tabId + "=" + state);
16435 this.active = state;
16437 this.el.removeClass('active');
16439 } else if (!this.el.hasClass('active')) {
16440 this.el.addClass('active');
16443 this.fireEvent('changed', this, state);
16460 * @class Roo.bootstrap.DateField
16461 * @extends Roo.bootstrap.Input
16462 * Bootstrap DateField class
16463 * @cfg {Number} weekStart default 0
16464 * @cfg {String} viewMode default empty, (months|years)
16465 * @cfg {String} minViewMode default empty, (months|years)
16466 * @cfg {Number} startDate default -Infinity
16467 * @cfg {Number} endDate default Infinity
16468 * @cfg {Boolean} todayHighlight default false
16469 * @cfg {Boolean} todayBtn default false
16470 * @cfg {Boolean} calendarWeeks default false
16471 * @cfg {Object} daysOfWeekDisabled default empty
16472 * @cfg {Boolean} singleMode default false (true | false)
16474 * @cfg {Boolean} keyboardNavigation default true
16475 * @cfg {String} language default en
16478 * Create a new DateField
16479 * @param {Object} config The config object
16482 Roo.bootstrap.DateField = function(config){
16483 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16487 * Fires when this field show.
16488 * @param {Roo.bootstrap.DateField} this
16489 * @param {Mixed} date The date value
16494 * Fires when this field hide.
16495 * @param {Roo.bootstrap.DateField} this
16496 * @param {Mixed} date The date value
16501 * Fires when select a date.
16502 * @param {Roo.bootstrap.DateField} this
16503 * @param {Mixed} date The date value
16507 * @event beforeselect
16508 * Fires when before select a date.
16509 * @param {Roo.bootstrap.DateField} this
16510 * @param {Mixed} date The date value
16512 beforeselect : true
16516 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16519 * @cfg {String} format
16520 * The default date format string which can be overriden for localization support. The format must be
16521 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16525 * @cfg {String} altFormats
16526 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16527 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16529 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16537 todayHighlight : false,
16543 keyboardNavigation: true,
16545 calendarWeeks: false,
16547 startDate: -Infinity,
16551 daysOfWeekDisabled: [],
16555 singleMode : false,
16557 UTCDate: function()
16559 return new Date(Date.UTC.apply(Date, arguments));
16562 UTCToday: function()
16564 var today = new Date();
16565 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16568 getDate: function() {
16569 var d = this.getUTCDate();
16570 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16573 getUTCDate: function() {
16577 setDate: function(d) {
16578 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16581 setUTCDate: function(d) {
16583 this.setValue(this.formatDate(this.date));
16586 onRender: function(ct, position)
16589 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16591 this.language = this.language || 'en';
16592 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16593 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16595 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16596 this.format = this.format || 'm/d/y';
16597 this.isInline = false;
16598 this.isInput = true;
16599 this.component = this.el.select('.add-on', true).first() || false;
16600 this.component = (this.component && this.component.length === 0) ? false : this.component;
16601 this.hasInput = this.component && this.inputEL().length;
16603 if (typeof(this.minViewMode === 'string')) {
16604 switch (this.minViewMode) {
16606 this.minViewMode = 1;
16609 this.minViewMode = 2;
16612 this.minViewMode = 0;
16617 if (typeof(this.viewMode === 'string')) {
16618 switch (this.viewMode) {
16631 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16633 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16635 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16637 this.picker().on('mousedown', this.onMousedown, this);
16638 this.picker().on('click', this.onClick, this);
16640 this.picker().addClass('datepicker-dropdown');
16642 this.startViewMode = this.viewMode;
16644 if(this.singleMode){
16645 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16646 v.setVisibilityMode(Roo.Element.DISPLAY);
16650 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16651 v.setStyle('width', '189px');
16655 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16656 if(!this.calendarWeeks){
16661 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16662 v.attr('colspan', function(i, val){
16663 return parseInt(val) + 1;
16668 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16670 this.setStartDate(this.startDate);
16671 this.setEndDate(this.endDate);
16673 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16680 if(this.isInline) {
16685 picker : function()
16687 return this.pickerEl;
16688 // return this.el.select('.datepicker', true).first();
16691 fillDow: function()
16693 var dowCnt = this.weekStart;
16702 if(this.calendarWeeks){
16710 while (dowCnt < this.weekStart + 7) {
16714 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16718 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16721 fillMonths: function()
16724 var months = this.picker().select('>.datepicker-months td', true).first();
16726 months.dom.innerHTML = '';
16732 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16735 months.createChild(month);
16742 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;
16744 if (this.date < this.startDate) {
16745 this.viewDate = new Date(this.startDate);
16746 } else if (this.date > this.endDate) {
16747 this.viewDate = new Date(this.endDate);
16749 this.viewDate = new Date(this.date);
16757 var d = new Date(this.viewDate),
16758 year = d.getUTCFullYear(),
16759 month = d.getUTCMonth(),
16760 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16761 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16762 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16763 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16764 currentDate = this.date && this.date.valueOf(),
16765 today = this.UTCToday();
16767 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16769 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16771 // this.picker.select('>tfoot th.today').
16772 // .text(dates[this.language].today)
16773 // .toggle(this.todayBtn !== false);
16775 this.updateNavArrows();
16778 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16780 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16782 prevMonth.setUTCDate(day);
16784 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16786 var nextMonth = new Date(prevMonth);
16788 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16790 nextMonth = nextMonth.valueOf();
16792 var fillMonths = false;
16794 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16796 while(prevMonth.valueOf() < nextMonth) {
16799 if (prevMonth.getUTCDay() === this.weekStart) {
16801 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16809 if(this.calendarWeeks){
16810 // ISO 8601: First week contains first thursday.
16811 // ISO also states week starts on Monday, but we can be more abstract here.
16813 // Start of current week: based on weekstart/current date
16814 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16815 // Thursday of this week
16816 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16817 // First Thursday of year, year from thursday
16818 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16819 // Calendar week: ms between thursdays, div ms per day, div 7 days
16820 calWeek = (th - yth) / 864e5 / 7 + 1;
16822 fillMonths.cn.push({
16830 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16832 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16835 if (this.todayHighlight &&
16836 prevMonth.getUTCFullYear() == today.getFullYear() &&
16837 prevMonth.getUTCMonth() == today.getMonth() &&
16838 prevMonth.getUTCDate() == today.getDate()) {
16839 clsName += ' today';
16842 if (currentDate && prevMonth.valueOf() === currentDate) {
16843 clsName += ' active';
16846 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16847 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16848 clsName += ' disabled';
16851 fillMonths.cn.push({
16853 cls: 'day ' + clsName,
16854 html: prevMonth.getDate()
16857 prevMonth.setDate(prevMonth.getDate()+1);
16860 var currentYear = this.date && this.date.getUTCFullYear();
16861 var currentMonth = this.date && this.date.getUTCMonth();
16863 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16865 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16866 v.removeClass('active');
16868 if(currentYear === year && k === currentMonth){
16869 v.addClass('active');
16872 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16873 v.addClass('disabled');
16879 year = parseInt(year/10, 10) * 10;
16881 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16883 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16886 for (var i = -1; i < 11; i++) {
16887 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16889 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16897 showMode: function(dir)
16900 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16903 Roo.each(this.picker().select('>div',true).elements, function(v){
16904 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16907 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16912 if(this.isInline) {
16916 this.picker().removeClass(['bottom', 'top']);
16918 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16920 * place to the top of element!
16924 this.picker().addClass('top');
16925 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16930 this.picker().addClass('bottom');
16932 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16935 parseDate : function(value)
16937 if(!value || value instanceof Date){
16940 var v = Date.parseDate(value, this.format);
16941 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16942 v = Date.parseDate(value, 'Y-m-d');
16944 if(!v && this.altFormats){
16945 if(!this.altFormatsArray){
16946 this.altFormatsArray = this.altFormats.split("|");
16948 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16949 v = Date.parseDate(value, this.altFormatsArray[i]);
16955 formatDate : function(date, fmt)
16957 return (!date || !(date instanceof Date)) ?
16958 date : date.dateFormat(fmt || this.format);
16961 onFocus : function()
16963 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16967 onBlur : function()
16969 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16971 var d = this.inputEl().getValue();
16980 this.picker().show();
16984 this.fireEvent('show', this, this.date);
16989 if(this.isInline) {
16992 this.picker().hide();
16993 this.viewMode = this.startViewMode;
16996 this.fireEvent('hide', this, this.date);
17000 onMousedown: function(e)
17002 e.stopPropagation();
17003 e.preventDefault();
17008 Roo.bootstrap.DateField.superclass.keyup.call(this);
17012 setValue: function(v)
17014 if(this.fireEvent('beforeselect', this, v) !== false){
17015 var d = new Date(this.parseDate(v) ).clearTime();
17017 if(isNaN(d.getTime())){
17018 this.date = this.viewDate = '';
17019 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17023 v = this.formatDate(d);
17025 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17027 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17031 this.fireEvent('select', this, this.date);
17035 getValue: function()
17037 return this.formatDate(this.date);
17040 fireKey: function(e)
17042 if (!this.picker().isVisible()){
17043 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17049 var dateChanged = false,
17051 newDate, newViewDate;
17056 e.preventDefault();
17060 if (!this.keyboardNavigation) {
17063 dir = e.keyCode == 37 ? -1 : 1;
17066 newDate = this.moveYear(this.date, dir);
17067 newViewDate = this.moveYear(this.viewDate, dir);
17068 } else if (e.shiftKey){
17069 newDate = this.moveMonth(this.date, dir);
17070 newViewDate = this.moveMonth(this.viewDate, dir);
17072 newDate = new Date(this.date);
17073 newDate.setUTCDate(this.date.getUTCDate() + dir);
17074 newViewDate = new Date(this.viewDate);
17075 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17077 if (this.dateWithinRange(newDate)){
17078 this.date = newDate;
17079 this.viewDate = newViewDate;
17080 this.setValue(this.formatDate(this.date));
17082 e.preventDefault();
17083 dateChanged = true;
17088 if (!this.keyboardNavigation) {
17091 dir = e.keyCode == 38 ? -1 : 1;
17093 newDate = this.moveYear(this.date, dir);
17094 newViewDate = this.moveYear(this.viewDate, dir);
17095 } else if (e.shiftKey){
17096 newDate = this.moveMonth(this.date, dir);
17097 newViewDate = this.moveMonth(this.viewDate, dir);
17099 newDate = new Date(this.date);
17100 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17101 newViewDate = new Date(this.viewDate);
17102 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17104 if (this.dateWithinRange(newDate)){
17105 this.date = newDate;
17106 this.viewDate = newViewDate;
17107 this.setValue(this.formatDate(this.date));
17109 e.preventDefault();
17110 dateChanged = true;
17114 this.setValue(this.formatDate(this.date));
17116 e.preventDefault();
17119 this.setValue(this.formatDate(this.date));
17133 onClick: function(e)
17135 e.stopPropagation();
17136 e.preventDefault();
17138 var target = e.getTarget();
17140 if(target.nodeName.toLowerCase() === 'i'){
17141 target = Roo.get(target).dom.parentNode;
17144 var nodeName = target.nodeName;
17145 var className = target.className;
17146 var html = target.innerHTML;
17147 //Roo.log(nodeName);
17149 switch(nodeName.toLowerCase()) {
17151 switch(className) {
17157 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17158 switch(this.viewMode){
17160 this.viewDate = this.moveMonth(this.viewDate, dir);
17164 this.viewDate = this.moveYear(this.viewDate, dir);
17170 var date = new Date();
17171 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17173 this.setValue(this.formatDate(this.date));
17180 if (className.indexOf('disabled') < 0) {
17181 this.viewDate.setUTCDate(1);
17182 if (className.indexOf('month') > -1) {
17183 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17185 var year = parseInt(html, 10) || 0;
17186 this.viewDate.setUTCFullYear(year);
17190 if(this.singleMode){
17191 this.setValue(this.formatDate(this.viewDate));
17202 //Roo.log(className);
17203 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17204 var day = parseInt(html, 10) || 1;
17205 var year = this.viewDate.getUTCFullYear(),
17206 month = this.viewDate.getUTCMonth();
17208 if (className.indexOf('old') > -1) {
17215 } else if (className.indexOf('new') > -1) {
17223 //Roo.log([year,month,day]);
17224 this.date = this.UTCDate(year, month, day,0,0,0,0);
17225 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17227 //Roo.log(this.formatDate(this.date));
17228 this.setValue(this.formatDate(this.date));
17235 setStartDate: function(startDate)
17237 this.startDate = startDate || -Infinity;
17238 if (this.startDate !== -Infinity) {
17239 this.startDate = this.parseDate(this.startDate);
17242 this.updateNavArrows();
17245 setEndDate: function(endDate)
17247 this.endDate = endDate || Infinity;
17248 if (this.endDate !== Infinity) {
17249 this.endDate = this.parseDate(this.endDate);
17252 this.updateNavArrows();
17255 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17257 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17258 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17259 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17261 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17262 return parseInt(d, 10);
17265 this.updateNavArrows();
17268 updateNavArrows: function()
17270 if(this.singleMode){
17274 var d = new Date(this.viewDate),
17275 year = d.getUTCFullYear(),
17276 month = d.getUTCMonth();
17278 Roo.each(this.picker().select('.prev', true).elements, function(v){
17280 switch (this.viewMode) {
17283 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17289 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17296 Roo.each(this.picker().select('.next', true).elements, function(v){
17298 switch (this.viewMode) {
17301 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17307 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17315 moveMonth: function(date, dir)
17320 var new_date = new Date(date.valueOf()),
17321 day = new_date.getUTCDate(),
17322 month = new_date.getUTCMonth(),
17323 mag = Math.abs(dir),
17325 dir = dir > 0 ? 1 : -1;
17328 // If going back one month, make sure month is not current month
17329 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17331 return new_date.getUTCMonth() == month;
17333 // If going forward one month, make sure month is as expected
17334 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17336 return new_date.getUTCMonth() != new_month;
17338 new_month = month + dir;
17339 new_date.setUTCMonth(new_month);
17340 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17341 if (new_month < 0 || new_month > 11) {
17342 new_month = (new_month + 12) % 12;
17345 // For magnitudes >1, move one month at a time...
17346 for (var i=0; i<mag; i++) {
17347 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17348 new_date = this.moveMonth(new_date, dir);
17350 // ...then reset the day, keeping it in the new month
17351 new_month = new_date.getUTCMonth();
17352 new_date.setUTCDate(day);
17354 return new_month != new_date.getUTCMonth();
17357 // Common date-resetting loop -- if date is beyond end of month, make it
17360 new_date.setUTCDate(--day);
17361 new_date.setUTCMonth(new_month);
17366 moveYear: function(date, dir)
17368 return this.moveMonth(date, dir*12);
17371 dateWithinRange: function(date)
17373 return date >= this.startDate && date <= this.endDate;
17379 this.picker().remove();
17384 Roo.apply(Roo.bootstrap.DateField, {
17395 html: '<i class="fa fa-arrow-left"/>'
17405 html: '<i class="fa fa-arrow-right"/>'
17447 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17448 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17449 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17450 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17451 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17464 navFnc: 'FullYear',
17469 navFnc: 'FullYear',
17474 Roo.apply(Roo.bootstrap.DateField, {
17478 cls: 'datepicker dropdown-menu roo-dynamic',
17482 cls: 'datepicker-days',
17486 cls: 'table-condensed',
17488 Roo.bootstrap.DateField.head,
17492 Roo.bootstrap.DateField.footer
17499 cls: 'datepicker-months',
17503 cls: 'table-condensed',
17505 Roo.bootstrap.DateField.head,
17506 Roo.bootstrap.DateField.content,
17507 Roo.bootstrap.DateField.footer
17514 cls: 'datepicker-years',
17518 cls: 'table-condensed',
17520 Roo.bootstrap.DateField.head,
17521 Roo.bootstrap.DateField.content,
17522 Roo.bootstrap.DateField.footer
17541 * @class Roo.bootstrap.TimeField
17542 * @extends Roo.bootstrap.Input
17543 * Bootstrap DateField class
17547 * Create a new TimeField
17548 * @param {Object} config The config object
17551 Roo.bootstrap.TimeField = function(config){
17552 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17556 * Fires when this field show.
17557 * @param {Roo.bootstrap.DateField} thisthis
17558 * @param {Mixed} date The date value
17563 * Fires when this field hide.
17564 * @param {Roo.bootstrap.DateField} this
17565 * @param {Mixed} date The date value
17570 * Fires when select a date.
17571 * @param {Roo.bootstrap.DateField} this
17572 * @param {Mixed} date The date value
17578 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17581 * @cfg {String} format
17582 * The default time format string which can be overriden for localization support. The format must be
17583 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17587 onRender: function(ct, position)
17590 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17592 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17594 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17596 this.pop = this.picker().select('>.datepicker-time',true).first();
17597 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17599 this.picker().on('mousedown', this.onMousedown, this);
17600 this.picker().on('click', this.onClick, this);
17602 this.picker().addClass('datepicker-dropdown');
17607 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17608 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17609 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17610 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17611 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17612 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17616 fireKey: function(e){
17617 if (!this.picker().isVisible()){
17618 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17624 e.preventDefault();
17632 this.onTogglePeriod();
17635 this.onIncrementMinutes();
17638 this.onDecrementMinutes();
17647 onClick: function(e) {
17648 e.stopPropagation();
17649 e.preventDefault();
17652 picker : function()
17654 return this.el.select('.datepicker', true).first();
17657 fillTime: function()
17659 var time = this.pop.select('tbody', true).first();
17661 time.dom.innerHTML = '';
17676 cls: 'hours-up glyphicon glyphicon-chevron-up'
17696 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17717 cls: 'timepicker-hour',
17732 cls: 'timepicker-minute',
17747 cls: 'btn btn-primary period',
17769 cls: 'hours-down glyphicon glyphicon-chevron-down'
17789 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17807 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17814 var hours = this.time.getHours();
17815 var minutes = this.time.getMinutes();
17828 hours = hours - 12;
17832 hours = '0' + hours;
17836 minutes = '0' + minutes;
17839 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17840 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17841 this.pop.select('button', true).first().dom.innerHTML = period;
17847 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17849 var cls = ['bottom'];
17851 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17858 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17863 this.picker().addClass(cls.join('-'));
17867 Roo.each(cls, function(c){
17869 _this.picker().setTop(_this.inputEl().getHeight());
17873 _this.picker().setTop(0 - _this.picker().getHeight());
17878 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17882 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17889 onFocus : function()
17891 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17895 onBlur : function()
17897 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17903 this.picker().show();
17908 this.fireEvent('show', this, this.date);
17913 this.picker().hide();
17916 this.fireEvent('hide', this, this.date);
17919 setTime : function()
17922 this.setValue(this.time.format(this.format));
17924 this.fireEvent('select', this, this.date);
17929 onMousedown: function(e){
17930 e.stopPropagation();
17931 e.preventDefault();
17934 onIncrementHours: function()
17936 Roo.log('onIncrementHours');
17937 this.time = this.time.add(Date.HOUR, 1);
17942 onDecrementHours: function()
17944 Roo.log('onDecrementHours');
17945 this.time = this.time.add(Date.HOUR, -1);
17949 onIncrementMinutes: function()
17951 Roo.log('onIncrementMinutes');
17952 this.time = this.time.add(Date.MINUTE, 1);
17956 onDecrementMinutes: function()
17958 Roo.log('onDecrementMinutes');
17959 this.time = this.time.add(Date.MINUTE, -1);
17963 onTogglePeriod: function()
17965 Roo.log('onTogglePeriod');
17966 this.time = this.time.add(Date.HOUR, 12);
17973 Roo.apply(Roo.bootstrap.TimeField, {
18003 cls: 'btn btn-info ok',
18015 Roo.apply(Roo.bootstrap.TimeField, {
18019 cls: 'datepicker dropdown-menu',
18023 cls: 'datepicker-time',
18027 cls: 'table-condensed',
18029 Roo.bootstrap.TimeField.content,
18030 Roo.bootstrap.TimeField.footer
18049 * @class Roo.bootstrap.MonthField
18050 * @extends Roo.bootstrap.Input
18051 * Bootstrap MonthField class
18053 * @cfg {String} language default en
18056 * Create a new MonthField
18057 * @param {Object} config The config object
18060 Roo.bootstrap.MonthField = function(config){
18061 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18066 * Fires when this field show.
18067 * @param {Roo.bootstrap.MonthField} this
18068 * @param {Mixed} date The date value
18073 * Fires when this field hide.
18074 * @param {Roo.bootstrap.MonthField} this
18075 * @param {Mixed} date The date value
18080 * Fires when select a date.
18081 * @param {Roo.bootstrap.MonthField} this
18082 * @param {String} oldvalue The old value
18083 * @param {String} newvalue The new value
18089 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18091 onRender: function(ct, position)
18094 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18096 this.language = this.language || 'en';
18097 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18098 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18100 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18101 this.isInline = false;
18102 this.isInput = true;
18103 this.component = this.el.select('.add-on', true).first() || false;
18104 this.component = (this.component && this.component.length === 0) ? false : this.component;
18105 this.hasInput = this.component && this.inputEL().length;
18107 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18109 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18111 this.picker().on('mousedown', this.onMousedown, this);
18112 this.picker().on('click', this.onClick, this);
18114 this.picker().addClass('datepicker-dropdown');
18116 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18117 v.setStyle('width', '189px');
18124 if(this.isInline) {
18130 setValue: function(v, suppressEvent)
18132 var o = this.getValue();
18134 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18138 if(suppressEvent !== true){
18139 this.fireEvent('select', this, o, v);
18144 getValue: function()
18149 onClick: function(e)
18151 e.stopPropagation();
18152 e.preventDefault();
18154 var target = e.getTarget();
18156 if(target.nodeName.toLowerCase() === 'i'){
18157 target = Roo.get(target).dom.parentNode;
18160 var nodeName = target.nodeName;
18161 var className = target.className;
18162 var html = target.innerHTML;
18164 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18168 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18170 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18176 picker : function()
18178 return this.pickerEl;
18181 fillMonths: function()
18184 var months = this.picker().select('>.datepicker-months td', true).first();
18186 months.dom.innerHTML = '';
18192 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18195 months.createChild(month);
18204 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18205 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18208 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18209 e.removeClass('active');
18211 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18212 e.addClass('active');
18219 if(this.isInline) {
18223 this.picker().removeClass(['bottom', 'top']);
18225 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18227 * place to the top of element!
18231 this.picker().addClass('top');
18232 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18237 this.picker().addClass('bottom');
18239 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18242 onFocus : function()
18244 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18248 onBlur : function()
18250 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18252 var d = this.inputEl().getValue();
18261 this.picker().show();
18262 this.picker().select('>.datepicker-months', true).first().show();
18266 this.fireEvent('show', this, this.date);
18271 if(this.isInline) {
18274 this.picker().hide();
18275 this.fireEvent('hide', this, this.date);
18279 onMousedown: function(e)
18281 e.stopPropagation();
18282 e.preventDefault();
18287 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18291 fireKey: function(e)
18293 if (!this.picker().isVisible()){
18294 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18305 e.preventDefault();
18309 dir = e.keyCode == 37 ? -1 : 1;
18311 this.vIndex = this.vIndex + dir;
18313 if(this.vIndex < 0){
18317 if(this.vIndex > 11){
18321 if(isNaN(this.vIndex)){
18325 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18331 dir = e.keyCode == 38 ? -1 : 1;
18333 this.vIndex = this.vIndex + dir * 4;
18335 if(this.vIndex < 0){
18339 if(this.vIndex > 11){
18343 if(isNaN(this.vIndex)){
18347 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18352 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18353 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18357 e.preventDefault();
18360 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18361 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18377 this.picker().remove();
18382 Roo.apply(Roo.bootstrap.MonthField, {
18401 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18402 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18407 Roo.apply(Roo.bootstrap.MonthField, {
18411 cls: 'datepicker dropdown-menu roo-dynamic',
18415 cls: 'datepicker-months',
18419 cls: 'table-condensed',
18421 Roo.bootstrap.DateField.content
18441 * @class Roo.bootstrap.CheckBox
18442 * @extends Roo.bootstrap.Input
18443 * Bootstrap CheckBox class
18445 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18446 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18447 * @cfg {String} boxLabel The text that appears beside the checkbox
18448 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18449 * @cfg {Boolean} checked initnal the element
18450 * @cfg {Boolean} inline inline the element (default false)
18451 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18454 * Create a new CheckBox
18455 * @param {Object} config The config object
18458 Roo.bootstrap.CheckBox = function(config){
18459 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18464 * Fires when the element is checked or unchecked.
18465 * @param {Roo.bootstrap.CheckBox} this This input
18466 * @param {Boolean} checked The new checked value
18473 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18475 inputType: 'checkbox',
18483 getAutoCreate : function()
18485 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18491 cfg.cls = 'form-group ' + this.inputType; //input-group
18494 cfg.cls += ' ' + this.inputType + '-inline';
18500 type : this.inputType,
18501 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18502 cls : 'roo-' + this.inputType, //'form-box',
18503 placeholder : this.placeholder || ''
18507 if (this.weight) { // Validity check?
18508 cfg.cls += " " + this.inputType + "-" + this.weight;
18511 if (this.disabled) {
18512 input.disabled=true;
18516 input.checked = this.checked;
18520 input.name = this.name;
18524 input.cls += ' input-' + this.size;
18529 ['xs','sm','md','lg'].map(function(size){
18530 if (settings[size]) {
18531 cfg.cls += ' col-' + size + '-' + settings[size];
18535 var inputblock = input;
18537 if (this.before || this.after) {
18540 cls : 'input-group',
18545 inputblock.cn.push({
18547 cls : 'input-group-addon',
18552 inputblock.cn.push(input);
18555 inputblock.cn.push({
18557 cls : 'input-group-addon',
18564 if (align ==='left' && this.fieldLabel.length) {
18565 Roo.log("left and has label");
18571 cls : 'control-label col-md-' + this.labelWidth,
18572 html : this.fieldLabel
18576 cls : "col-md-" + (12 - this.labelWidth),
18583 } else if ( this.fieldLabel.length) {
18588 tag: this.boxLabel ? 'span' : 'label',
18590 cls: 'control-label box-input-label',
18591 //cls : 'input-group-addon',
18592 html : this.fieldLabel
18602 Roo.log(" no label && no align");
18603 cfg.cn = [ inputblock ] ;
18608 var boxLabelCfg = {
18610 //'for': id, // box label is handled by onclick - so no for...
18612 html: this.boxLabel
18616 boxLabelCfg.tooltip = this.tooltip;
18619 cfg.cn.push(boxLabelCfg);
18629 * return the real input element.
18631 inputEl: function ()
18633 return this.el.select('input.roo-' + this.inputType,true).first();
18636 labelEl: function()
18638 return this.el.select('label.control-label',true).first();
18640 /* depricated... */
18644 return this.labelEl();
18647 initEvents : function()
18649 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18651 this.inputEl().on('click', this.onClick, this);
18653 if (this.boxLabel) {
18654 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18657 this.startValue = this.getValue();
18660 Roo.bootstrap.CheckBox.register(this);
18664 onClick : function()
18666 this.setChecked(!this.checked);
18669 setChecked : function(state,suppressEvent)
18671 this.startValue = this.getValue();
18673 if(this.inputType == 'radio'){
18675 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18676 e.dom.checked = false;
18679 this.inputEl().dom.checked = true;
18681 this.inputEl().dom.value = this.inputValue;
18683 if(suppressEvent !== true){
18684 this.fireEvent('check', this, true);
18692 this.checked = state;
18694 this.inputEl().dom.checked = state;
18696 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18698 if(suppressEvent !== true){
18699 this.fireEvent('check', this, state);
18705 getValue : function()
18707 if(this.inputType == 'radio'){
18708 return this.getGroupValue();
18711 return this.inputEl().getValue();
18715 getGroupValue : function()
18717 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18721 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18724 setValue : function(v,suppressEvent)
18726 if(this.inputType == 'radio'){
18727 this.setGroupValue(v, suppressEvent);
18731 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18736 setGroupValue : function(v, suppressEvent)
18738 this.startValue = this.getValue();
18740 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18741 e.dom.checked = false;
18743 if(e.dom.value == v){
18744 e.dom.checked = true;
18748 if(suppressEvent !== true){
18749 this.fireEvent('check', this, true);
18757 validate : function()
18761 (this.inputType == 'radio' && this.validateRadio()) ||
18762 (this.inputType == 'checkbox' && this.validateCheckbox())
18768 this.markInvalid();
18772 validateRadio : function()
18776 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18777 if(!e.dom.checked){
18789 validateCheckbox : function()
18792 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18795 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18803 for(var i in group){
18808 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18815 * Mark this field as valid
18817 markValid : function()
18819 if(this.allowBlank){
18825 this.fireEvent('valid', this);
18827 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18830 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18837 if(this.inputType == 'radio'){
18838 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18839 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18840 e.findParent('.form-group', false, true).addClass(_this.validClass);
18847 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18848 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18852 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18858 for(var i in group){
18859 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18860 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18865 * Mark this field as invalid
18866 * @param {String} msg The validation message
18868 markInvalid : function(msg)
18870 if(this.allowBlank){
18876 this.fireEvent('invalid', this, msg);
18878 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18881 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18885 label.markInvalid();
18888 if(this.inputType == 'radio'){
18889 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18890 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18891 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18898 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18899 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18903 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18909 for(var i in group){
18910 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18911 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18918 Roo.apply(Roo.bootstrap.CheckBox, {
18923 * register a CheckBox Group
18924 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18926 register : function(checkbox)
18928 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18929 this.groups[checkbox.groupId] = {};
18932 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18936 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18940 * fetch a CheckBox Group based on the group ID
18941 * @param {string} the group ID
18942 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18944 get: function(groupId) {
18945 if (typeof(this.groups[groupId]) == 'undefined') {
18949 return this.groups[groupId] ;
18961 *<div class="radio">
18963 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18964 Option one is this and that—be sure to include why it's great
18971 *<label class="radio-inline">fieldLabel</label>
18972 *<label class="radio-inline">
18973 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18981 * @class Roo.bootstrap.Radio
18982 * @extends Roo.bootstrap.CheckBox
18983 * Bootstrap Radio class
18986 * Create a new Radio
18987 * @param {Object} config The config object
18990 Roo.bootstrap.Radio = function(config){
18991 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18995 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18997 inputType: 'radio',
19001 getAutoCreate : function()
19003 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19004 align = align || 'left'; // default...
19011 tag : this.inline ? 'span' : 'div',
19016 var inline = this.inline ? ' radio-inline' : '';
19020 // does not need for, as we wrap the input with it..
19022 cls : 'control-label box-label' + inline,
19025 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19029 //cls : 'control-label' + inline,
19030 html : this.fieldLabel,
19031 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19040 type : this.inputType,
19041 //value : (!this.checked) ? this.valueOff : this.inputValue,
19042 value : this.inputValue,
19044 placeholder : this.placeholder || '' // ?? needed????
19047 if (this.weight) { // Validity check?
19048 input.cls += " radio-" + this.weight;
19050 if (this.disabled) {
19051 input.disabled=true;
19055 input.checked = this.checked;
19059 input.name = this.name;
19063 input.cls += ' input-' + this.size;
19066 //?? can span's inline have a width??
19069 ['xs','sm','md','lg'].map(function(size){
19070 if (settings[size]) {
19071 cfg.cls += ' col-' + size + '-' + settings[size];
19075 var inputblock = input;
19077 if (this.before || this.after) {
19080 cls : 'input-group',
19085 inputblock.cn.push({
19087 cls : 'input-group-addon',
19091 inputblock.cn.push(input);
19093 inputblock.cn.push({
19095 cls : 'input-group-addon',
19103 if (this.fieldLabel && this.fieldLabel.length) {
19104 cfg.cn.push(fieldLabel);
19107 // normal bootstrap puts the input inside the label.
19108 // however with our styled version - it has to go after the input.
19110 //lbl.cn.push(inputblock);
19114 cls: 'radio' + inline,
19121 cfg.cn.push( lblwrap);
19126 html: this.boxLabel
19135 initEvents : function()
19137 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19139 this.inputEl().on('click', this.onClick, this);
19140 if (this.boxLabel) {
19141 //Roo.log('find label');
19142 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19147 inputEl: function ()
19149 return this.el.select('input.roo-radio',true).first();
19151 onClick : function()
19154 this.setChecked(true);
19157 setChecked : function(state,suppressEvent)
19160 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19161 v.dom.checked = false;
19164 Roo.log(this.inputEl().dom);
19165 this.checked = state;
19166 this.inputEl().dom.checked = state;
19168 if(suppressEvent !== true){
19169 this.fireEvent('check', this, state);
19172 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19176 getGroupValue : function()
19179 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19180 if(v.dom.checked == true){
19181 value = v.dom.value;
19189 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19190 * @return {Mixed} value The field value
19192 getValue : function(){
19193 return this.getGroupValue();
19199 //<script type="text/javascript">
19202 * Based Ext JS Library 1.1.1
19203 * Copyright(c) 2006-2007, Ext JS, LLC.
19209 * @class Roo.HtmlEditorCore
19210 * @extends Roo.Component
19211 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19213 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19216 Roo.HtmlEditorCore = function(config){
19219 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19224 * @event initialize
19225 * Fires when the editor is fully initialized (including the iframe)
19226 * @param {Roo.HtmlEditorCore} this
19231 * Fires when the editor is first receives the focus. Any insertion must wait
19232 * until after this event.
19233 * @param {Roo.HtmlEditorCore} this
19237 * @event beforesync
19238 * Fires before the textarea is updated with content from the editor iframe. Return false
19239 * to cancel the sync.
19240 * @param {Roo.HtmlEditorCore} this
19241 * @param {String} html
19245 * @event beforepush
19246 * Fires before the iframe editor is updated with content from the textarea. Return false
19247 * to cancel the push.
19248 * @param {Roo.HtmlEditorCore} this
19249 * @param {String} html
19254 * Fires when the textarea is updated with content from the editor iframe.
19255 * @param {Roo.HtmlEditorCore} this
19256 * @param {String} html
19261 * Fires when the iframe editor is updated with content from the textarea.
19262 * @param {Roo.HtmlEditorCore} this
19263 * @param {String} html
19268 * @event editorevent
19269 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19270 * @param {Roo.HtmlEditorCore} this
19276 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19278 // defaults : white / black...
19279 this.applyBlacklists();
19286 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19290 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19296 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19301 * @cfg {Number} height (in pixels)
19305 * @cfg {Number} width (in pixels)
19310 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19313 stylesheets: false,
19318 // private properties
19319 validationEvent : false,
19321 initialized : false,
19323 sourceEditMode : false,
19324 onFocus : Roo.emptyFn,
19326 hideMode:'offsets',
19330 // blacklist + whitelisted elements..
19337 * Protected method that will not generally be called directly. It
19338 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19339 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19341 getDocMarkup : function(){
19345 // inherit styels from page...??
19346 if (this.stylesheets === false) {
19348 Roo.get(document.head).select('style').each(function(node) {
19349 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19352 Roo.get(document.head).select('link').each(function(node) {
19353 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19356 } else if (!this.stylesheets.length) {
19358 st = '<style type="text/css">' +
19359 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19365 st += '<style type="text/css">' +
19366 'IMG { cursor: pointer } ' +
19370 return '<html><head>' + st +
19371 //<style type="text/css">' +
19372 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19374 ' </head><body class="roo-htmleditor-body"></body></html>';
19378 onRender : function(ct, position)
19381 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19382 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19385 this.el.dom.style.border = '0 none';
19386 this.el.dom.setAttribute('tabIndex', -1);
19387 this.el.addClass('x-hidden hide');
19391 if(Roo.isIE){ // fix IE 1px bogus margin
19392 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19396 this.frameId = Roo.id();
19400 var iframe = this.owner.wrap.createChild({
19402 cls: 'form-control', // bootstrap..
19404 name: this.frameId,
19405 frameBorder : 'no',
19406 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19411 this.iframe = iframe.dom;
19413 this.assignDocWin();
19415 this.doc.designMode = 'on';
19418 this.doc.write(this.getDocMarkup());
19422 var task = { // must defer to wait for browser to be ready
19424 //console.log("run task?" + this.doc.readyState);
19425 this.assignDocWin();
19426 if(this.doc.body || this.doc.readyState == 'complete'){
19428 this.doc.designMode="on";
19432 Roo.TaskMgr.stop(task);
19433 this.initEditor.defer(10, this);
19440 Roo.TaskMgr.start(task);
19445 onResize : function(w, h)
19447 Roo.log('resize: ' +w + ',' + h );
19448 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19452 if(typeof w == 'number'){
19454 this.iframe.style.width = w + 'px';
19456 if(typeof h == 'number'){
19458 this.iframe.style.height = h + 'px';
19460 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19467 * Toggles the editor between standard and source edit mode.
19468 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19470 toggleSourceEdit : function(sourceEditMode){
19472 this.sourceEditMode = sourceEditMode === true;
19474 if(this.sourceEditMode){
19476 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19479 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19480 //this.iframe.className = '';
19483 //this.setSize(this.owner.wrap.getSize());
19484 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19491 * Protected method that will not generally be called directly. If you need/want
19492 * custom HTML cleanup, this is the method you should override.
19493 * @param {String} html The HTML to be cleaned
19494 * return {String} The cleaned HTML
19496 cleanHtml : function(html){
19497 html = String(html);
19498 if(html.length > 5){
19499 if(Roo.isSafari){ // strip safari nonsense
19500 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19503 if(html == ' '){
19510 * HTML Editor -> Textarea
19511 * Protected method that will not generally be called directly. Syncs the contents
19512 * of the editor iframe with the textarea.
19514 syncValue : function(){
19515 if(this.initialized){
19516 var bd = (this.doc.body || this.doc.documentElement);
19517 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19518 var html = bd.innerHTML;
19520 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19521 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19523 html = '<div style="'+m[0]+'">' + html + '</div>';
19526 html = this.cleanHtml(html);
19527 // fix up the special chars.. normaly like back quotes in word...
19528 // however we do not want to do this with chinese..
19529 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19530 var cc = b.charCodeAt();
19532 (cc >= 0x4E00 && cc < 0xA000 ) ||
19533 (cc >= 0x3400 && cc < 0x4E00 ) ||
19534 (cc >= 0xf900 && cc < 0xfb00 )
19540 if(this.owner.fireEvent('beforesync', this, html) !== false){
19541 this.el.dom.value = html;
19542 this.owner.fireEvent('sync', this, html);
19548 * Protected method that will not generally be called directly. Pushes the value of the textarea
19549 * into the iframe editor.
19551 pushValue : function(){
19552 if(this.initialized){
19553 var v = this.el.dom.value.trim();
19555 // if(v.length < 1){
19559 if(this.owner.fireEvent('beforepush', this, v) !== false){
19560 var d = (this.doc.body || this.doc.documentElement);
19562 this.cleanUpPaste();
19563 this.el.dom.value = d.innerHTML;
19564 this.owner.fireEvent('push', this, v);
19570 deferFocus : function(){
19571 this.focus.defer(10, this);
19575 focus : function(){
19576 if(this.win && !this.sourceEditMode){
19583 assignDocWin: function()
19585 var iframe = this.iframe;
19588 this.doc = iframe.contentWindow.document;
19589 this.win = iframe.contentWindow;
19591 // if (!Roo.get(this.frameId)) {
19594 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19595 // this.win = Roo.get(this.frameId).dom.contentWindow;
19597 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19601 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19602 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19607 initEditor : function(){
19608 //console.log("INIT EDITOR");
19609 this.assignDocWin();
19613 this.doc.designMode="on";
19615 this.doc.write(this.getDocMarkup());
19618 var dbody = (this.doc.body || this.doc.documentElement);
19619 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19620 // this copies styles from the containing element into thsi one..
19621 // not sure why we need all of this..
19622 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19624 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19625 //ss['background-attachment'] = 'fixed'; // w3c
19626 dbody.bgProperties = 'fixed'; // ie
19627 //Roo.DomHelper.applyStyles(dbody, ss);
19628 Roo.EventManager.on(this.doc, {
19629 //'mousedown': this.onEditorEvent,
19630 'mouseup': this.onEditorEvent,
19631 'dblclick': this.onEditorEvent,
19632 'click': this.onEditorEvent,
19633 'keyup': this.onEditorEvent,
19638 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19640 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19641 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19643 this.initialized = true;
19645 this.owner.fireEvent('initialize', this);
19650 onDestroy : function(){
19656 //for (var i =0; i < this.toolbars.length;i++) {
19657 // // fixme - ask toolbars for heights?
19658 // this.toolbars[i].onDestroy();
19661 //this.wrap.dom.innerHTML = '';
19662 //this.wrap.remove();
19667 onFirstFocus : function(){
19669 this.assignDocWin();
19672 this.activated = true;
19675 if(Roo.isGecko){ // prevent silly gecko errors
19677 var s = this.win.getSelection();
19678 if(!s.focusNode || s.focusNode.nodeType != 3){
19679 var r = s.getRangeAt(0);
19680 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19685 this.execCmd('useCSS', true);
19686 this.execCmd('styleWithCSS', false);
19689 this.owner.fireEvent('activate', this);
19693 adjustFont: function(btn){
19694 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19695 //if(Roo.isSafari){ // safari
19698 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19699 if(Roo.isSafari){ // safari
19700 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19701 v = (v < 10) ? 10 : v;
19702 v = (v > 48) ? 48 : v;
19703 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19708 v = Math.max(1, v+adjust);
19710 this.execCmd('FontSize', v );
19713 onEditorEvent : function(e)
19715 this.owner.fireEvent('editorevent', this, e);
19716 // this.updateToolbar();
19717 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19720 insertTag : function(tg)
19722 // could be a bit smarter... -> wrap the current selected tRoo..
19723 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19725 range = this.createRange(this.getSelection());
19726 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19727 wrappingNode.appendChild(range.extractContents());
19728 range.insertNode(wrappingNode);
19735 this.execCmd("formatblock", tg);
19739 insertText : function(txt)
19743 var range = this.createRange();
19744 range.deleteContents();
19745 //alert(Sender.getAttribute('label'));
19747 range.insertNode(this.doc.createTextNode(txt));
19753 * Executes a Midas editor command on the editor document and performs necessary focus and
19754 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19755 * @param {String} cmd The Midas command
19756 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19758 relayCmd : function(cmd, value){
19760 this.execCmd(cmd, value);
19761 this.owner.fireEvent('editorevent', this);
19762 //this.updateToolbar();
19763 this.owner.deferFocus();
19767 * Executes a Midas editor command directly on the editor document.
19768 * For visual commands, you should use {@link #relayCmd} instead.
19769 * <b>This should only be called after the editor is initialized.</b>
19770 * @param {String} cmd The Midas command
19771 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19773 execCmd : function(cmd, value){
19774 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19781 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19783 * @param {String} text | dom node..
19785 insertAtCursor : function(text)
19790 if(!this.activated){
19796 var r = this.doc.selection.createRange();
19807 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19811 // from jquery ui (MIT licenced)
19813 var win = this.win;
19815 if (win.getSelection && win.getSelection().getRangeAt) {
19816 range = win.getSelection().getRangeAt(0);
19817 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19818 range.insertNode(node);
19819 } else if (win.document.selection && win.document.selection.createRange) {
19820 // no firefox support
19821 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19822 win.document.selection.createRange().pasteHTML(txt);
19824 // no firefox support
19825 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19826 this.execCmd('InsertHTML', txt);
19835 mozKeyPress : function(e){
19837 var c = e.getCharCode(), cmd;
19840 c = String.fromCharCode(c).toLowerCase();
19854 this.cleanUpPaste.defer(100, this);
19862 e.preventDefault();
19870 fixKeys : function(){ // load time branching for fastest keydown performance
19872 return function(e){
19873 var k = e.getKey(), r;
19876 r = this.doc.selection.createRange();
19879 r.pasteHTML('    ');
19886 r = this.doc.selection.createRange();
19888 var target = r.parentElement();
19889 if(!target || target.tagName.toLowerCase() != 'li'){
19891 r.pasteHTML('<br />');
19897 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19898 this.cleanUpPaste.defer(100, this);
19904 }else if(Roo.isOpera){
19905 return function(e){
19906 var k = e.getKey();
19910 this.execCmd('InsertHTML','    ');
19913 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19914 this.cleanUpPaste.defer(100, this);
19919 }else if(Roo.isSafari){
19920 return function(e){
19921 var k = e.getKey();
19925 this.execCmd('InsertText','\t');
19929 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19930 this.cleanUpPaste.defer(100, this);
19938 getAllAncestors: function()
19940 var p = this.getSelectedNode();
19943 a.push(p); // push blank onto stack..
19944 p = this.getParentElement();
19948 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19952 a.push(this.doc.body);
19956 lastSelNode : false,
19959 getSelection : function()
19961 this.assignDocWin();
19962 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19965 getSelectedNode: function()
19967 // this may only work on Gecko!!!
19969 // should we cache this!!!!
19974 var range = this.createRange(this.getSelection()).cloneRange();
19977 var parent = range.parentElement();
19979 var testRange = range.duplicate();
19980 testRange.moveToElementText(parent);
19981 if (testRange.inRange(range)) {
19984 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19987 parent = parent.parentElement;
19992 // is ancestor a text element.
19993 var ac = range.commonAncestorContainer;
19994 if (ac.nodeType == 3) {
19995 ac = ac.parentNode;
19998 var ar = ac.childNodes;
20001 var other_nodes = [];
20002 var has_other_nodes = false;
20003 for (var i=0;i<ar.length;i++) {
20004 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20007 // fullly contained node.
20009 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20014 // probably selected..
20015 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20016 other_nodes.push(ar[i]);
20020 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20025 has_other_nodes = true;
20027 if (!nodes.length && other_nodes.length) {
20028 nodes= other_nodes;
20030 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20036 createRange: function(sel)
20038 // this has strange effects when using with
20039 // top toolbar - not sure if it's a great idea.
20040 //this.editor.contentWindow.focus();
20041 if (typeof sel != "undefined") {
20043 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20045 return this.doc.createRange();
20048 return this.doc.createRange();
20051 getParentElement: function()
20054 this.assignDocWin();
20055 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20057 var range = this.createRange(sel);
20060 var p = range.commonAncestorContainer;
20061 while (p.nodeType == 3) { // text node
20072 * Range intersection.. the hard stuff...
20076 * [ -- selected range --- ]
20080 * if end is before start or hits it. fail.
20081 * if start is after end or hits it fail.
20083 * if either hits (but other is outside. - then it's not
20089 // @see http://www.thismuchiknow.co.uk/?p=64.
20090 rangeIntersectsNode : function(range, node)
20092 var nodeRange = node.ownerDocument.createRange();
20094 nodeRange.selectNode(node);
20096 nodeRange.selectNodeContents(node);
20099 var rangeStartRange = range.cloneRange();
20100 rangeStartRange.collapse(true);
20102 var rangeEndRange = range.cloneRange();
20103 rangeEndRange.collapse(false);
20105 var nodeStartRange = nodeRange.cloneRange();
20106 nodeStartRange.collapse(true);
20108 var nodeEndRange = nodeRange.cloneRange();
20109 nodeEndRange.collapse(false);
20111 return rangeStartRange.compareBoundaryPoints(
20112 Range.START_TO_START, nodeEndRange) == -1 &&
20113 rangeEndRange.compareBoundaryPoints(
20114 Range.START_TO_START, nodeStartRange) == 1;
20118 rangeCompareNode : function(range, node)
20120 var nodeRange = node.ownerDocument.createRange();
20122 nodeRange.selectNode(node);
20124 nodeRange.selectNodeContents(node);
20128 range.collapse(true);
20130 nodeRange.collapse(true);
20132 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20133 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20135 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20137 var nodeIsBefore = ss == 1;
20138 var nodeIsAfter = ee == -1;
20140 if (nodeIsBefore && nodeIsAfter) {
20143 if (!nodeIsBefore && nodeIsAfter) {
20144 return 1; //right trailed.
20147 if (nodeIsBefore && !nodeIsAfter) {
20148 return 2; // left trailed.
20154 // private? - in a new class?
20155 cleanUpPaste : function()
20157 // cleans up the whole document..
20158 Roo.log('cleanuppaste');
20160 this.cleanUpChildren(this.doc.body);
20161 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20162 if (clean != this.doc.body.innerHTML) {
20163 this.doc.body.innerHTML = clean;
20168 cleanWordChars : function(input) {// change the chars to hex code
20169 var he = Roo.HtmlEditorCore;
20171 var output = input;
20172 Roo.each(he.swapCodes, function(sw) {
20173 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20175 output = output.replace(swapper, sw[1]);
20182 cleanUpChildren : function (n)
20184 if (!n.childNodes.length) {
20187 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20188 this.cleanUpChild(n.childNodes[i]);
20195 cleanUpChild : function (node)
20198 //console.log(node);
20199 if (node.nodeName == "#text") {
20200 // clean up silly Windows -- stuff?
20203 if (node.nodeName == "#comment") {
20204 node.parentNode.removeChild(node);
20205 // clean up silly Windows -- stuff?
20208 var lcname = node.tagName.toLowerCase();
20209 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20210 // whitelist of tags..
20212 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20214 node.parentNode.removeChild(node);
20219 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20221 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20222 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20224 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20225 // remove_keep_children = true;
20228 if (remove_keep_children) {
20229 this.cleanUpChildren(node);
20230 // inserts everything just before this node...
20231 while (node.childNodes.length) {
20232 var cn = node.childNodes[0];
20233 node.removeChild(cn);
20234 node.parentNode.insertBefore(cn, node);
20236 node.parentNode.removeChild(node);
20240 if (!node.attributes || !node.attributes.length) {
20241 this.cleanUpChildren(node);
20245 function cleanAttr(n,v)
20248 if (v.match(/^\./) || v.match(/^\//)) {
20251 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20254 if (v.match(/^#/)) {
20257 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20258 node.removeAttribute(n);
20262 var cwhite = this.cwhite;
20263 var cblack = this.cblack;
20265 function cleanStyle(n,v)
20267 if (v.match(/expression/)) { //XSS?? should we even bother..
20268 node.removeAttribute(n);
20272 var parts = v.split(/;/);
20275 Roo.each(parts, function(p) {
20276 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20280 var l = p.split(':').shift().replace(/\s+/g,'');
20281 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20283 if ( cwhite.length && cblack.indexOf(l) > -1) {
20284 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20285 //node.removeAttribute(n);
20289 // only allow 'c whitelisted system attributes'
20290 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20291 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20292 //node.removeAttribute(n);
20302 if (clean.length) {
20303 node.setAttribute(n, clean.join(';'));
20305 node.removeAttribute(n);
20311 for (var i = node.attributes.length-1; i > -1 ; i--) {
20312 var a = node.attributes[i];
20315 if (a.name.toLowerCase().substr(0,2)=='on') {
20316 node.removeAttribute(a.name);
20319 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20320 node.removeAttribute(a.name);
20323 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20324 cleanAttr(a.name,a.value); // fixme..
20327 if (a.name == 'style') {
20328 cleanStyle(a.name,a.value);
20331 /// clean up MS crap..
20332 // tecnically this should be a list of valid class'es..
20335 if (a.name == 'class') {
20336 if (a.value.match(/^Mso/)) {
20337 node.className = '';
20340 if (a.value.match(/body/)) {
20341 node.className = '';
20352 this.cleanUpChildren(node);
20358 * Clean up MS wordisms...
20360 cleanWord : function(node)
20365 this.cleanWord(this.doc.body);
20368 if (node.nodeName == "#text") {
20369 // clean up silly Windows -- stuff?
20372 if (node.nodeName == "#comment") {
20373 node.parentNode.removeChild(node);
20374 // clean up silly Windows -- stuff?
20378 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20379 node.parentNode.removeChild(node);
20383 // remove - but keep children..
20384 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20385 while (node.childNodes.length) {
20386 var cn = node.childNodes[0];
20387 node.removeChild(cn);
20388 node.parentNode.insertBefore(cn, node);
20390 node.parentNode.removeChild(node);
20391 this.iterateChildren(node, this.cleanWord);
20395 if (node.className.length) {
20397 var cn = node.className.split(/\W+/);
20399 Roo.each(cn, function(cls) {
20400 if (cls.match(/Mso[a-zA-Z]+/)) {
20405 node.className = cna.length ? cna.join(' ') : '';
20407 node.removeAttribute("class");
20411 if (node.hasAttribute("lang")) {
20412 node.removeAttribute("lang");
20415 if (node.hasAttribute("style")) {
20417 var styles = node.getAttribute("style").split(";");
20419 Roo.each(styles, function(s) {
20420 if (!s.match(/:/)) {
20423 var kv = s.split(":");
20424 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20427 // what ever is left... we allow.
20430 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20431 if (!nstyle.length) {
20432 node.removeAttribute('style');
20435 this.iterateChildren(node, this.cleanWord);
20441 * iterateChildren of a Node, calling fn each time, using this as the scole..
20442 * @param {DomNode} node node to iterate children of.
20443 * @param {Function} fn method of this class to call on each item.
20445 iterateChildren : function(node, fn)
20447 if (!node.childNodes.length) {
20450 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20451 fn.call(this, node.childNodes[i])
20457 * cleanTableWidths.
20459 * Quite often pasting from word etc.. results in tables with column and widths.
20460 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20463 cleanTableWidths : function(node)
20468 this.cleanTableWidths(this.doc.body);
20473 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20476 Roo.log(node.tagName);
20477 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20478 this.iterateChildren(node, this.cleanTableWidths);
20481 if (node.hasAttribute('width')) {
20482 node.removeAttribute('width');
20486 if (node.hasAttribute("style")) {
20489 var styles = node.getAttribute("style").split(";");
20491 Roo.each(styles, function(s) {
20492 if (!s.match(/:/)) {
20495 var kv = s.split(":");
20496 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20499 // what ever is left... we allow.
20502 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20503 if (!nstyle.length) {
20504 node.removeAttribute('style');
20508 this.iterateChildren(node, this.cleanTableWidths);
20516 domToHTML : function(currentElement, depth, nopadtext) {
20518 depth = depth || 0;
20519 nopadtext = nopadtext || false;
20521 if (!currentElement) {
20522 return this.domToHTML(this.doc.body);
20525 //Roo.log(currentElement);
20527 var allText = false;
20528 var nodeName = currentElement.nodeName;
20529 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20531 if (nodeName == '#text') {
20533 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20538 if (nodeName != 'BODY') {
20541 // Prints the node tagName, such as <A>, <IMG>, etc
20544 for(i = 0; i < currentElement.attributes.length;i++) {
20546 var aname = currentElement.attributes.item(i).name;
20547 if (!currentElement.attributes.item(i).value.length) {
20550 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20553 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20562 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20565 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20570 // Traverse the tree
20572 var currentElementChild = currentElement.childNodes.item(i);
20573 var allText = true;
20574 var innerHTML = '';
20576 while (currentElementChild) {
20577 // Formatting code (indent the tree so it looks nice on the screen)
20578 var nopad = nopadtext;
20579 if (lastnode == 'SPAN') {
20583 if (currentElementChild.nodeName == '#text') {
20584 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20585 toadd = nopadtext ? toadd : toadd.trim();
20586 if (!nopad && toadd.length > 80) {
20587 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20589 innerHTML += toadd;
20592 currentElementChild = currentElement.childNodes.item(i);
20598 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20600 // Recursively traverse the tree structure of the child node
20601 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20602 lastnode = currentElementChild.nodeName;
20604 currentElementChild=currentElement.childNodes.item(i);
20610 // The remaining code is mostly for formatting the tree
20611 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20616 ret+= "</"+tagName+">";
20622 applyBlacklists : function()
20624 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20625 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20629 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20630 if (b.indexOf(tag) > -1) {
20633 this.white.push(tag);
20637 Roo.each(w, function(tag) {
20638 if (b.indexOf(tag) > -1) {
20641 if (this.white.indexOf(tag) > -1) {
20644 this.white.push(tag);
20649 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20650 if (w.indexOf(tag) > -1) {
20653 this.black.push(tag);
20657 Roo.each(b, function(tag) {
20658 if (w.indexOf(tag) > -1) {
20661 if (this.black.indexOf(tag) > -1) {
20664 this.black.push(tag);
20669 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20670 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20674 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20675 if (b.indexOf(tag) > -1) {
20678 this.cwhite.push(tag);
20682 Roo.each(w, function(tag) {
20683 if (b.indexOf(tag) > -1) {
20686 if (this.cwhite.indexOf(tag) > -1) {
20689 this.cwhite.push(tag);
20694 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20695 if (w.indexOf(tag) > -1) {
20698 this.cblack.push(tag);
20702 Roo.each(b, function(tag) {
20703 if (w.indexOf(tag) > -1) {
20706 if (this.cblack.indexOf(tag) > -1) {
20709 this.cblack.push(tag);
20714 setStylesheets : function(stylesheets)
20716 if(typeof(stylesheets) == 'string'){
20717 Roo.get(this.iframe.contentDocument.head).createChild({
20719 rel : 'stylesheet',
20728 Roo.each(stylesheets, function(s) {
20733 Roo.get(_this.iframe.contentDocument.head).createChild({
20735 rel : 'stylesheet',
20744 removeStylesheets : function()
20748 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20753 // hide stuff that is not compatible
20767 * @event specialkey
20771 * @cfg {String} fieldClass @hide
20774 * @cfg {String} focusClass @hide
20777 * @cfg {String} autoCreate @hide
20780 * @cfg {String} inputType @hide
20783 * @cfg {String} invalidClass @hide
20786 * @cfg {String} invalidText @hide
20789 * @cfg {String} msgFx @hide
20792 * @cfg {String} validateOnBlur @hide
20796 Roo.HtmlEditorCore.white = [
20797 'area', 'br', 'img', 'input', 'hr', 'wbr',
20799 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20800 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20801 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20802 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20803 'table', 'ul', 'xmp',
20805 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20808 'dir', 'menu', 'ol', 'ul', 'dl',
20814 Roo.HtmlEditorCore.black = [
20815 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20817 'base', 'basefont', 'bgsound', 'blink', 'body',
20818 'frame', 'frameset', 'head', 'html', 'ilayer',
20819 'iframe', 'layer', 'link', 'meta', 'object',
20820 'script', 'style' ,'title', 'xml' // clean later..
20822 Roo.HtmlEditorCore.clean = [
20823 'script', 'style', 'title', 'xml'
20825 Roo.HtmlEditorCore.remove = [
20830 Roo.HtmlEditorCore.ablack = [
20834 Roo.HtmlEditorCore.aclean = [
20835 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20839 Roo.HtmlEditorCore.pwhite= [
20840 'http', 'https', 'mailto'
20843 // white listed style attributes.
20844 Roo.HtmlEditorCore.cwhite= [
20845 // 'text-align', /// default is to allow most things..
20851 // black listed style attributes.
20852 Roo.HtmlEditorCore.cblack= [
20853 // 'font-size' -- this can be set by the project
20857 Roo.HtmlEditorCore.swapCodes =[
20876 * @class Roo.bootstrap.HtmlEditor
20877 * @extends Roo.bootstrap.TextArea
20878 * Bootstrap HtmlEditor class
20881 * Create a new HtmlEditor
20882 * @param {Object} config The config object
20885 Roo.bootstrap.HtmlEditor = function(config){
20886 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20887 if (!this.toolbars) {
20888 this.toolbars = [];
20890 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20893 * @event initialize
20894 * Fires when the editor is fully initialized (including the iframe)
20895 * @param {HtmlEditor} this
20900 * Fires when the editor is first receives the focus. Any insertion must wait
20901 * until after this event.
20902 * @param {HtmlEditor} this
20906 * @event beforesync
20907 * Fires before the textarea is updated with content from the editor iframe. Return false
20908 * to cancel the sync.
20909 * @param {HtmlEditor} this
20910 * @param {String} html
20914 * @event beforepush
20915 * Fires before the iframe editor is updated with content from the textarea. Return false
20916 * to cancel the push.
20917 * @param {HtmlEditor} this
20918 * @param {String} html
20923 * Fires when the textarea is updated with content from the editor iframe.
20924 * @param {HtmlEditor} this
20925 * @param {String} html
20930 * Fires when the iframe editor is updated with content from the textarea.
20931 * @param {HtmlEditor} this
20932 * @param {String} html
20936 * @event editmodechange
20937 * Fires when the editor switches edit modes
20938 * @param {HtmlEditor} this
20939 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20941 editmodechange: true,
20943 * @event editorevent
20944 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20945 * @param {HtmlEditor} this
20949 * @event firstfocus
20950 * Fires when on first focus - needed by toolbars..
20951 * @param {HtmlEditor} this
20956 * Auto save the htmlEditor value as a file into Events
20957 * @param {HtmlEditor} this
20961 * @event savedpreview
20962 * preview the saved version of htmlEditor
20963 * @param {HtmlEditor} this
20970 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20974 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20979 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20984 * @cfg {Number} height (in pixels)
20988 * @cfg {Number} width (in pixels)
20993 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20996 stylesheets: false,
21001 // private properties
21002 validationEvent : false,
21004 initialized : false,
21007 onFocus : Roo.emptyFn,
21009 hideMode:'offsets',
21012 tbContainer : false,
21014 toolbarContainer :function() {
21015 return this.wrap.select('.x-html-editor-tb',true).first();
21019 * Protected method that will not generally be called directly. It
21020 * is called when the editor creates its toolbar. Override this method if you need to
21021 * add custom toolbar buttons.
21022 * @param {HtmlEditor} editor
21024 createToolbar : function(){
21026 Roo.log("create toolbars");
21028 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21029 this.toolbars[0].render(this.toolbarContainer());
21033 // if (!editor.toolbars || !editor.toolbars.length) {
21034 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21037 // for (var i =0 ; i < editor.toolbars.length;i++) {
21038 // editor.toolbars[i] = Roo.factory(
21039 // typeof(editor.toolbars[i]) == 'string' ?
21040 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21041 // Roo.bootstrap.HtmlEditor);
21042 // editor.toolbars[i].init(editor);
21048 onRender : function(ct, position)
21050 // Roo.log("Call onRender: " + this.xtype);
21052 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21054 this.wrap = this.inputEl().wrap({
21055 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21058 this.editorcore.onRender(ct, position);
21060 if (this.resizable) {
21061 this.resizeEl = new Roo.Resizable(this.wrap, {
21065 minHeight : this.height,
21066 height: this.height,
21067 handles : this.resizable,
21070 resize : function(r, w, h) {
21071 _t.onResize(w,h); // -something
21077 this.createToolbar(this);
21080 if(!this.width && this.resizable){
21081 this.setSize(this.wrap.getSize());
21083 if (this.resizeEl) {
21084 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21085 // should trigger onReize..
21091 onResize : function(w, h)
21093 Roo.log('resize: ' +w + ',' + h );
21094 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21098 if(this.inputEl() ){
21099 if(typeof w == 'number'){
21100 var aw = w - this.wrap.getFrameWidth('lr');
21101 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21104 if(typeof h == 'number'){
21105 var tbh = -11; // fixme it needs to tool bar size!
21106 for (var i =0; i < this.toolbars.length;i++) {
21107 // fixme - ask toolbars for heights?
21108 tbh += this.toolbars[i].el.getHeight();
21109 //if (this.toolbars[i].footer) {
21110 // tbh += this.toolbars[i].footer.el.getHeight();
21118 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21119 ah -= 5; // knock a few pixes off for look..
21120 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21124 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21125 this.editorcore.onResize(ew,eh);
21130 * Toggles the editor between standard and source edit mode.
21131 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21133 toggleSourceEdit : function(sourceEditMode)
21135 this.editorcore.toggleSourceEdit(sourceEditMode);
21137 if(this.editorcore.sourceEditMode){
21138 Roo.log('editor - showing textarea');
21141 // Roo.log(this.syncValue());
21143 this.inputEl().removeClass(['hide', 'x-hidden']);
21144 this.inputEl().dom.removeAttribute('tabIndex');
21145 this.inputEl().focus();
21147 Roo.log('editor - hiding textarea');
21149 // Roo.log(this.pushValue());
21152 this.inputEl().addClass(['hide', 'x-hidden']);
21153 this.inputEl().dom.setAttribute('tabIndex', -1);
21154 //this.deferFocus();
21157 if(this.resizable){
21158 this.setSize(this.wrap.getSize());
21161 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21164 // private (for BoxComponent)
21165 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21167 // private (for BoxComponent)
21168 getResizeEl : function(){
21172 // private (for BoxComponent)
21173 getPositionEl : function(){
21178 initEvents : function(){
21179 this.originalValue = this.getValue();
21183 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21186 // markInvalid : Roo.emptyFn,
21188 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21191 // clearInvalid : Roo.emptyFn,
21193 setValue : function(v){
21194 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21195 this.editorcore.pushValue();
21200 deferFocus : function(){
21201 this.focus.defer(10, this);
21205 focus : function(){
21206 this.editorcore.focus();
21212 onDestroy : function(){
21218 for (var i =0; i < this.toolbars.length;i++) {
21219 // fixme - ask toolbars for heights?
21220 this.toolbars[i].onDestroy();
21223 this.wrap.dom.innerHTML = '';
21224 this.wrap.remove();
21229 onFirstFocus : function(){
21230 //Roo.log("onFirstFocus");
21231 this.editorcore.onFirstFocus();
21232 for (var i =0; i < this.toolbars.length;i++) {
21233 this.toolbars[i].onFirstFocus();
21239 syncValue : function()
21241 this.editorcore.syncValue();
21244 pushValue : function()
21246 this.editorcore.pushValue();
21250 // hide stuff that is not compatible
21264 * @event specialkey
21268 * @cfg {String} fieldClass @hide
21271 * @cfg {String} focusClass @hide
21274 * @cfg {String} autoCreate @hide
21277 * @cfg {String} inputType @hide
21280 * @cfg {String} invalidClass @hide
21283 * @cfg {String} invalidText @hide
21286 * @cfg {String} msgFx @hide
21289 * @cfg {String} validateOnBlur @hide
21298 Roo.namespace('Roo.bootstrap.htmleditor');
21300 * @class Roo.bootstrap.HtmlEditorToolbar1
21305 new Roo.bootstrap.HtmlEditor({
21308 new Roo.bootstrap.HtmlEditorToolbar1({
21309 disable : { fonts: 1 , format: 1, ..., ... , ...],
21315 * @cfg {Object} disable List of elements to disable..
21316 * @cfg {Array} btns List of additional buttons.
21320 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21323 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21326 Roo.apply(this, config);
21328 // default disabled, based on 'good practice'..
21329 this.disable = this.disable || {};
21330 Roo.applyIf(this.disable, {
21333 specialElements : true
21335 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21337 this.editor = config.editor;
21338 this.editorcore = config.editor.editorcore;
21340 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21342 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21343 // dont call parent... till later.
21345 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21350 editorcore : false,
21355 "h1","h2","h3","h4","h5","h6",
21357 "abbr", "acronym", "address", "cite", "samp", "var",
21361 onRender : function(ct, position)
21363 // Roo.log("Call onRender: " + this.xtype);
21365 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21367 this.el.dom.style.marginBottom = '0';
21369 var editorcore = this.editorcore;
21370 var editor= this.editor;
21373 var btn = function(id,cmd , toggle, handler){
21375 var event = toggle ? 'toggle' : 'click';
21380 xns: Roo.bootstrap,
21383 enableToggle:toggle !== false,
21385 pressed : toggle ? false : null,
21388 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21389 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21398 xns: Roo.bootstrap,
21399 glyphicon : 'font',
21403 xns: Roo.bootstrap,
21407 Roo.each(this.formats, function(f) {
21408 style.menu.items.push({
21410 xns: Roo.bootstrap,
21411 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21416 editorcore.insertTag(this.tagname);
21423 children.push(style);
21426 btn('bold',false,true);
21427 btn('italic',false,true);
21428 btn('align-left', 'justifyleft',true);
21429 btn('align-center', 'justifycenter',true);
21430 btn('align-right' , 'justifyright',true);
21431 btn('link', false, false, function(btn) {
21432 //Roo.log("create link?");
21433 var url = prompt(this.createLinkText, this.defaultLinkValue);
21434 if(url && url != 'http:/'+'/'){
21435 this.editorcore.relayCmd('createlink', url);
21438 btn('list','insertunorderedlist',true);
21439 btn('pencil', false,true, function(btn){
21442 this.toggleSourceEdit(btn.pressed);
21448 xns: Roo.bootstrap,
21453 xns: Roo.bootstrap,
21458 cog.menu.items.push({
21460 xns: Roo.bootstrap,
21461 html : Clean styles,
21466 editorcore.insertTag(this.tagname);
21475 this.xtype = 'NavSimplebar';
21477 for(var i=0;i< children.length;i++) {
21479 this.buttons.add(this.addxtypeChild(children[i]));
21483 editor.on('editorevent', this.updateToolbar, this);
21485 onBtnClick : function(id)
21487 this.editorcore.relayCmd(id);
21488 this.editorcore.focus();
21492 * Protected method that will not generally be called directly. It triggers
21493 * a toolbar update by reading the markup state of the current selection in the editor.
21495 updateToolbar: function(){
21497 if(!this.editorcore.activated){
21498 this.editor.onFirstFocus(); // is this neeed?
21502 var btns = this.buttons;
21503 var doc = this.editorcore.doc;
21504 btns.get('bold').setActive(doc.queryCommandState('bold'));
21505 btns.get('italic').setActive(doc.queryCommandState('italic'));
21506 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21508 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21509 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21510 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21512 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21513 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21516 var ans = this.editorcore.getAllAncestors();
21517 if (this.formatCombo) {
21520 var store = this.formatCombo.store;
21521 this.formatCombo.setValue("");
21522 for (var i =0; i < ans.length;i++) {
21523 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21525 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21533 // hides menus... - so this cant be on a menu...
21534 Roo.bootstrap.MenuMgr.hideAll();
21536 Roo.bootstrap.MenuMgr.hideAll();
21537 //this.editorsyncValue();
21539 onFirstFocus: function() {
21540 this.buttons.each(function(item){
21544 toggleSourceEdit : function(sourceEditMode){
21547 if(sourceEditMode){
21548 Roo.log("disabling buttons");
21549 this.buttons.each( function(item){
21550 if(item.cmd != 'pencil'){
21556 Roo.log("enabling buttons");
21557 if(this.editorcore.initialized){
21558 this.buttons.each( function(item){
21564 Roo.log("calling toggole on editor");
21565 // tell the editor that it's been pressed..
21566 this.editor.toggleSourceEdit(sourceEditMode);
21576 * @class Roo.bootstrap.Table.AbstractSelectionModel
21577 * @extends Roo.util.Observable
21578 * Abstract base class for grid SelectionModels. It provides the interface that should be
21579 * implemented by descendant classes. This class should not be directly instantiated.
21582 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21583 this.locked = false;
21584 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21588 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21589 /** @ignore Called by the grid automatically. Do not call directly. */
21590 init : function(grid){
21596 * Locks the selections.
21599 this.locked = true;
21603 * Unlocks the selections.
21605 unlock : function(){
21606 this.locked = false;
21610 * Returns true if the selections are locked.
21611 * @return {Boolean}
21613 isLocked : function(){
21614 return this.locked;
21618 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21619 * @class Roo.bootstrap.Table.RowSelectionModel
21620 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21621 * It supports multiple selections and keyboard selection/navigation.
21623 * @param {Object} config
21626 Roo.bootstrap.Table.RowSelectionModel = function(config){
21627 Roo.apply(this, config);
21628 this.selections = new Roo.util.MixedCollection(false, function(o){
21633 this.lastActive = false;
21637 * @event selectionchange
21638 * Fires when the selection changes
21639 * @param {SelectionModel} this
21641 "selectionchange" : true,
21643 * @event afterselectionchange
21644 * Fires after the selection changes (eg. by key press or clicking)
21645 * @param {SelectionModel} this
21647 "afterselectionchange" : true,
21649 * @event beforerowselect
21650 * Fires when a row is selected being selected, return false to cancel.
21651 * @param {SelectionModel} this
21652 * @param {Number} rowIndex The selected index
21653 * @param {Boolean} keepExisting False if other selections will be cleared
21655 "beforerowselect" : true,
21658 * Fires when a row is selected.
21659 * @param {SelectionModel} this
21660 * @param {Number} rowIndex The selected index
21661 * @param {Roo.data.Record} r The record
21663 "rowselect" : true,
21665 * @event rowdeselect
21666 * Fires when a row is deselected.
21667 * @param {SelectionModel} this
21668 * @param {Number} rowIndex The selected index
21670 "rowdeselect" : true
21672 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21673 this.locked = false;
21676 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21678 * @cfg {Boolean} singleSelect
21679 * True to allow selection of only one row at a time (defaults to false)
21681 singleSelect : false,
21684 initEvents : function(){
21686 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21687 this.grid.on("mousedown", this.handleMouseDown, this);
21688 }else{ // allow click to work like normal
21689 this.grid.on("rowclick", this.handleDragableRowClick, this);
21692 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21693 "up" : function(e){
21695 this.selectPrevious(e.shiftKey);
21696 }else if(this.last !== false && this.lastActive !== false){
21697 var last = this.last;
21698 this.selectRange(this.last, this.lastActive-1);
21699 this.grid.getView().focusRow(this.lastActive);
21700 if(last !== false){
21704 this.selectFirstRow();
21706 this.fireEvent("afterselectionchange", this);
21708 "down" : function(e){
21710 this.selectNext(e.shiftKey);
21711 }else if(this.last !== false && this.lastActive !== false){
21712 var last = this.last;
21713 this.selectRange(this.last, this.lastActive+1);
21714 this.grid.getView().focusRow(this.lastActive);
21715 if(last !== false){
21719 this.selectFirstRow();
21721 this.fireEvent("afterselectionchange", this);
21726 var view = this.grid.view;
21727 view.on("refresh", this.onRefresh, this);
21728 view.on("rowupdated", this.onRowUpdated, this);
21729 view.on("rowremoved", this.onRemove, this);
21733 onRefresh : function(){
21734 var ds = this.grid.dataSource, i, v = this.grid.view;
21735 var s = this.selections;
21736 s.each(function(r){
21737 if((i = ds.indexOfId(r.id)) != -1){
21746 onRemove : function(v, index, r){
21747 this.selections.remove(r);
21751 onRowUpdated : function(v, index, r){
21752 if(this.isSelected(r)){
21753 v.onRowSelect(index);
21759 * @param {Array} records The records to select
21760 * @param {Boolean} keepExisting (optional) True to keep existing selections
21762 selectRecords : function(records, keepExisting){
21764 this.clearSelections();
21766 var ds = this.grid.dataSource;
21767 for(var i = 0, len = records.length; i < len; i++){
21768 this.selectRow(ds.indexOf(records[i]), true);
21773 * Gets the number of selected rows.
21776 getCount : function(){
21777 return this.selections.length;
21781 * Selects the first row in the grid.
21783 selectFirstRow : function(){
21788 * Select the last row.
21789 * @param {Boolean} keepExisting (optional) True to keep existing selections
21791 selectLastRow : function(keepExisting){
21792 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21796 * Selects the row immediately following the last selected row.
21797 * @param {Boolean} keepExisting (optional) True to keep existing selections
21799 selectNext : function(keepExisting){
21800 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21801 this.selectRow(this.last+1, keepExisting);
21802 this.grid.getView().focusRow(this.last);
21807 * Selects the row that precedes the last selected row.
21808 * @param {Boolean} keepExisting (optional) True to keep existing selections
21810 selectPrevious : function(keepExisting){
21812 this.selectRow(this.last-1, keepExisting);
21813 this.grid.getView().focusRow(this.last);
21818 * Returns the selected records
21819 * @return {Array} Array of selected records
21821 getSelections : function(){
21822 return [].concat(this.selections.items);
21826 * Returns the first selected record.
21829 getSelected : function(){
21830 return this.selections.itemAt(0);
21835 * Clears all selections.
21837 clearSelections : function(fast){
21842 var ds = this.grid.dataSource;
21843 var s = this.selections;
21844 s.each(function(r){
21845 this.deselectRow(ds.indexOfId(r.id));
21849 this.selections.clear();
21856 * Selects all rows.
21858 selectAll : function(){
21862 this.selections.clear();
21863 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21864 this.selectRow(i, true);
21869 * Returns True if there is a selection.
21870 * @return {Boolean}
21872 hasSelection : function(){
21873 return this.selections.length > 0;
21877 * Returns True if the specified row is selected.
21878 * @param {Number/Record} record The record or index of the record to check
21879 * @return {Boolean}
21881 isSelected : function(index){
21882 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21883 return (r && this.selections.key(r.id) ? true : false);
21887 * Returns True if the specified record id is selected.
21888 * @param {String} id The id of record to check
21889 * @return {Boolean}
21891 isIdSelected : function(id){
21892 return (this.selections.key(id) ? true : false);
21896 handleMouseDown : function(e, t){
21897 var view = this.grid.getView(), rowIndex;
21898 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21901 if(e.shiftKey && this.last !== false){
21902 var last = this.last;
21903 this.selectRange(last, rowIndex, e.ctrlKey);
21904 this.last = last; // reset the last
21905 view.focusRow(rowIndex);
21907 var isSelected = this.isSelected(rowIndex);
21908 if(e.button !== 0 && isSelected){
21909 view.focusRow(rowIndex);
21910 }else if(e.ctrlKey && isSelected){
21911 this.deselectRow(rowIndex);
21912 }else if(!isSelected){
21913 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21914 view.focusRow(rowIndex);
21917 this.fireEvent("afterselectionchange", this);
21920 handleDragableRowClick : function(grid, rowIndex, e)
21922 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21923 this.selectRow(rowIndex, false);
21924 grid.view.focusRow(rowIndex);
21925 this.fireEvent("afterselectionchange", this);
21930 * Selects multiple rows.
21931 * @param {Array} rows Array of the indexes of the row to select
21932 * @param {Boolean} keepExisting (optional) True to keep existing selections
21934 selectRows : function(rows, keepExisting){
21936 this.clearSelections();
21938 for(var i = 0, len = rows.length; i < len; i++){
21939 this.selectRow(rows[i], true);
21944 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21945 * @param {Number} startRow The index of the first row in the range
21946 * @param {Number} endRow The index of the last row in the range
21947 * @param {Boolean} keepExisting (optional) True to retain existing selections
21949 selectRange : function(startRow, endRow, keepExisting){
21954 this.clearSelections();
21956 if(startRow <= endRow){
21957 for(var i = startRow; i <= endRow; i++){
21958 this.selectRow(i, true);
21961 for(var i = startRow; i >= endRow; i--){
21962 this.selectRow(i, true);
21968 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21969 * @param {Number} startRow The index of the first row in the range
21970 * @param {Number} endRow The index of the last row in the range
21972 deselectRange : function(startRow, endRow, preventViewNotify){
21976 for(var i = startRow; i <= endRow; i++){
21977 this.deselectRow(i, preventViewNotify);
21983 * @param {Number} row The index of the row to select
21984 * @param {Boolean} keepExisting (optional) True to keep existing selections
21986 selectRow : function(index, keepExisting, preventViewNotify){
21987 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
21990 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21991 if(!keepExisting || this.singleSelect){
21992 this.clearSelections();
21994 var r = this.grid.dataSource.getAt(index);
21995 this.selections.add(r);
21996 this.last = this.lastActive = index;
21997 if(!preventViewNotify){
21998 this.grid.getView().onRowSelect(index);
22000 this.fireEvent("rowselect", this, index, r);
22001 this.fireEvent("selectionchange", this);
22007 * @param {Number} row The index of the row to deselect
22009 deselectRow : function(index, preventViewNotify){
22013 if(this.last == index){
22016 if(this.lastActive == index){
22017 this.lastActive = false;
22019 var r = this.grid.dataSource.getAt(index);
22020 this.selections.remove(r);
22021 if(!preventViewNotify){
22022 this.grid.getView().onRowDeselect(index);
22024 this.fireEvent("rowdeselect", this, index);
22025 this.fireEvent("selectionchange", this);
22029 restoreLast : function(){
22031 this.last = this._last;
22036 acceptsNav : function(row, col, cm){
22037 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22041 onEditorKey : function(field, e){
22042 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22047 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22049 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22051 }else if(k == e.ENTER && !e.ctrlKey){
22055 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22057 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22059 }else if(k == e.ESC){
22063 g.startEditing(newCell[0], newCell[1]);
22068 * Ext JS Library 1.1.1
22069 * Copyright(c) 2006-2007, Ext JS, LLC.
22071 * Originally Released Under LGPL - original licence link has changed is not relivant.
22074 * <script type="text/javascript">
22078 * @class Roo.bootstrap.PagingToolbar
22079 * @extends Roo.bootstrap.NavSimplebar
22080 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22082 * Create a new PagingToolbar
22083 * @param {Object} config The config object
22084 * @param {Roo.data.Store} store
22086 Roo.bootstrap.PagingToolbar = function(config)
22088 // old args format still supported... - xtype is prefered..
22089 // created from xtype...
22091 this.ds = config.dataSource;
22093 if (config.store && !this.ds) {
22094 this.store= Roo.factory(config.store, Roo.data);
22095 this.ds = this.store;
22096 this.ds.xmodule = this.xmodule || false;
22099 this.toolbarItems = [];
22100 if (config.items) {
22101 this.toolbarItems = config.items;
22104 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22109 this.bind(this.ds);
22112 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22116 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22118 * @cfg {Roo.data.Store} dataSource
22119 * The underlying data store providing the paged data
22122 * @cfg {String/HTMLElement/Element} container
22123 * container The id or element that will contain the toolbar
22126 * @cfg {Boolean} displayInfo
22127 * True to display the displayMsg (defaults to false)
22130 * @cfg {Number} pageSize
22131 * The number of records to display per page (defaults to 20)
22135 * @cfg {String} displayMsg
22136 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22138 displayMsg : 'Displaying {0} - {1} of {2}',
22140 * @cfg {String} emptyMsg
22141 * The message to display when no records are found (defaults to "No data to display")
22143 emptyMsg : 'No data to display',
22145 * Customizable piece of the default paging text (defaults to "Page")
22148 beforePageText : "Page",
22150 * Customizable piece of the default paging text (defaults to "of %0")
22153 afterPageText : "of {0}",
22155 * Customizable piece of the default paging text (defaults to "First Page")
22158 firstText : "First Page",
22160 * Customizable piece of the default paging text (defaults to "Previous Page")
22163 prevText : "Previous Page",
22165 * Customizable piece of the default paging text (defaults to "Next Page")
22168 nextText : "Next Page",
22170 * Customizable piece of the default paging text (defaults to "Last Page")
22173 lastText : "Last Page",
22175 * Customizable piece of the default paging text (defaults to "Refresh")
22178 refreshText : "Refresh",
22182 onRender : function(ct, position)
22184 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22185 this.navgroup.parentId = this.id;
22186 this.navgroup.onRender(this.el, null);
22187 // add the buttons to the navgroup
22189 if(this.displayInfo){
22190 Roo.log(this.el.select('ul.navbar-nav',true).first());
22191 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22192 this.displayEl = this.el.select('.x-paging-info', true).first();
22193 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22194 // this.displayEl = navel.el.select('span',true).first();
22200 Roo.each(_this.buttons, function(e){ // this might need to use render????
22201 Roo.factory(e).onRender(_this.el, null);
22205 Roo.each(_this.toolbarItems, function(e) {
22206 _this.navgroup.addItem(e);
22210 this.first = this.navgroup.addItem({
22211 tooltip: this.firstText,
22213 icon : 'fa fa-backward',
22215 preventDefault: true,
22216 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22219 this.prev = this.navgroup.addItem({
22220 tooltip: this.prevText,
22222 icon : 'fa fa-step-backward',
22224 preventDefault: true,
22225 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22227 //this.addSeparator();
22230 var field = this.navgroup.addItem( {
22232 cls : 'x-paging-position',
22234 html : this.beforePageText +
22235 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22236 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22239 this.field = field.el.select('input', true).first();
22240 this.field.on("keydown", this.onPagingKeydown, this);
22241 this.field.on("focus", function(){this.dom.select();});
22244 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22245 //this.field.setHeight(18);
22246 //this.addSeparator();
22247 this.next = this.navgroup.addItem({
22248 tooltip: this.nextText,
22250 html : ' <i class="fa fa-step-forward">',
22252 preventDefault: true,
22253 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22255 this.last = this.navgroup.addItem({
22256 tooltip: this.lastText,
22257 icon : 'fa fa-forward',
22260 preventDefault: true,
22261 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22263 //this.addSeparator();
22264 this.loading = this.navgroup.addItem({
22265 tooltip: this.refreshText,
22266 icon: 'fa fa-refresh',
22267 preventDefault: true,
22268 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22274 updateInfo : function(){
22275 if(this.displayEl){
22276 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22277 var msg = count == 0 ?
22281 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22283 this.displayEl.update(msg);
22288 onLoad : function(ds, r, o){
22289 this.cursor = o.params ? o.params.start : 0;
22290 var d = this.getPageData(),
22294 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22295 this.field.dom.value = ap;
22296 this.first.setDisabled(ap == 1);
22297 this.prev.setDisabled(ap == 1);
22298 this.next.setDisabled(ap == ps);
22299 this.last.setDisabled(ap == ps);
22300 this.loading.enable();
22305 getPageData : function(){
22306 var total = this.ds.getTotalCount();
22309 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22310 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22315 onLoadError : function(){
22316 this.loading.enable();
22320 onPagingKeydown : function(e){
22321 var k = e.getKey();
22322 var d = this.getPageData();
22324 var v = this.field.dom.value, pageNum;
22325 if(!v || isNaN(pageNum = parseInt(v, 10))){
22326 this.field.dom.value = d.activePage;
22329 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22330 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22333 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))
22335 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22336 this.field.dom.value = pageNum;
22337 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22340 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22342 var v = this.field.dom.value, pageNum;
22343 var increment = (e.shiftKey) ? 10 : 1;
22344 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22347 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22348 this.field.dom.value = d.activePage;
22351 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22353 this.field.dom.value = parseInt(v, 10) + increment;
22354 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22355 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22362 beforeLoad : function(){
22364 this.loading.disable();
22369 onClick : function(which){
22378 ds.load({params:{start: 0, limit: this.pageSize}});
22381 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22384 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22387 var total = ds.getTotalCount();
22388 var extra = total % this.pageSize;
22389 var lastStart = extra ? (total - extra) : total-this.pageSize;
22390 ds.load({params:{start: lastStart, limit: this.pageSize}});
22393 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22399 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22400 * @param {Roo.data.Store} store The data store to unbind
22402 unbind : function(ds){
22403 ds.un("beforeload", this.beforeLoad, this);
22404 ds.un("load", this.onLoad, this);
22405 ds.un("loadexception", this.onLoadError, this);
22406 ds.un("remove", this.updateInfo, this);
22407 ds.un("add", this.updateInfo, this);
22408 this.ds = undefined;
22412 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22413 * @param {Roo.data.Store} store The data store to bind
22415 bind : function(ds){
22416 ds.on("beforeload", this.beforeLoad, this);
22417 ds.on("load", this.onLoad, this);
22418 ds.on("loadexception", this.onLoadError, this);
22419 ds.on("remove", this.updateInfo, this);
22420 ds.on("add", this.updateInfo, this);
22431 * @class Roo.bootstrap.MessageBar
22432 * @extends Roo.bootstrap.Component
22433 * Bootstrap MessageBar class
22434 * @cfg {String} html contents of the MessageBar
22435 * @cfg {String} weight (info | success | warning | danger) default info
22436 * @cfg {String} beforeClass insert the bar before the given class
22437 * @cfg {Boolean} closable (true | false) default false
22438 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22441 * Create a new Element
22442 * @param {Object} config The config object
22445 Roo.bootstrap.MessageBar = function(config){
22446 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22449 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22455 beforeClass: 'bootstrap-sticky-wrap',
22457 getAutoCreate : function(){
22461 cls: 'alert alert-dismissable alert-' + this.weight,
22466 html: this.html || ''
22472 cfg.cls += ' alert-messages-fixed';
22486 onRender : function(ct, position)
22488 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22491 var cfg = Roo.apply({}, this.getAutoCreate());
22495 cfg.cls += ' ' + this.cls;
22498 cfg.style = this.style;
22500 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22502 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22505 this.el.select('>button.close').on('click', this.hide, this);
22511 if (!this.rendered) {
22517 this.fireEvent('show', this);
22523 if (!this.rendered) {
22529 this.fireEvent('hide', this);
22532 update : function()
22534 // var e = this.el.dom.firstChild;
22536 // if(this.closable){
22537 // e = e.nextSibling;
22540 // e.data = this.html || '';
22542 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22558 * @class Roo.bootstrap.Graph
22559 * @extends Roo.bootstrap.Component
22560 * Bootstrap Graph class
22564 @cfg {String} graphtype bar | vbar | pie
22565 @cfg {number} g_x coodinator | centre x (pie)
22566 @cfg {number} g_y coodinator | centre y (pie)
22567 @cfg {number} g_r radius (pie)
22568 @cfg {number} g_height height of the chart (respected by all elements in the set)
22569 @cfg {number} g_width width of the chart (respected by all elements in the set)
22570 @cfg {Object} title The title of the chart
22573 -opts (object) options for the chart
22575 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22576 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22578 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.
22579 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22581 o stretch (boolean)
22583 -opts (object) options for the pie
22586 o startAngle (number)
22587 o endAngle (number)
22591 * Create a new Input
22592 * @param {Object} config The config object
22595 Roo.bootstrap.Graph = function(config){
22596 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22602 * The img click event for the img.
22603 * @param {Roo.EventObject} e
22609 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22620 //g_colors: this.colors,
22627 getAutoCreate : function(){
22638 onRender : function(ct,position){
22639 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22640 this.raphael = Raphael(this.el.dom);
22642 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22643 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22644 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22645 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22647 r.text(160, 10, "Single Series Chart").attr(txtattr);
22648 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22649 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22650 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22652 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22653 r.barchart(330, 10, 300, 220, data1);
22654 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22655 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22658 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22659 // r.barchart(30, 30, 560, 250, xdata, {
22660 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22661 // axis : "0 0 1 1",
22662 // axisxlabels : xdata
22663 // //yvalues : cols,
22666 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22668 // this.load(null,xdata,{
22669 // axis : "0 0 1 1",
22670 // axisxlabels : xdata
22675 load : function(graphtype,xdata,opts){
22676 this.raphael.clear();
22678 graphtype = this.graphtype;
22683 var r = this.raphael,
22684 fin = function () {
22685 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22687 fout = function () {
22688 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22690 pfin = function() {
22691 this.sector.stop();
22692 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22695 this.label[0].stop();
22696 this.label[0].attr({ r: 7.5 });
22697 this.label[1].attr({ "font-weight": 800 });
22700 pfout = function() {
22701 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22704 this.label[0].animate({ r: 5 }, 500, "bounce");
22705 this.label[1].attr({ "font-weight": 400 });
22711 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22714 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22717 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22718 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22720 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22727 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22732 setTitle: function(o)
22737 initEvents: function() {
22740 this.el.on('click', this.onClick, this);
22744 onClick : function(e)
22746 Roo.log('img onclick');
22747 this.fireEvent('click', this, e);
22759 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22762 * @class Roo.bootstrap.dash.NumberBox
22763 * @extends Roo.bootstrap.Component
22764 * Bootstrap NumberBox class
22765 * @cfg {String} headline Box headline
22766 * @cfg {String} content Box content
22767 * @cfg {String} icon Box icon
22768 * @cfg {String} footer Footer text
22769 * @cfg {String} fhref Footer href
22772 * Create a new NumberBox
22773 * @param {Object} config The config object
22777 Roo.bootstrap.dash.NumberBox = function(config){
22778 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22782 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22791 getAutoCreate : function(){
22795 cls : 'small-box ',
22803 cls : 'roo-headline',
22804 html : this.headline
22808 cls : 'roo-content',
22809 html : this.content
22823 cls : 'ion ' + this.icon
22832 cls : 'small-box-footer',
22833 href : this.fhref || '#',
22837 cfg.cn.push(footer);
22844 onRender : function(ct,position){
22845 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22852 setHeadline: function (value)
22854 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22857 setFooter: function (value, href)
22859 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22862 this.el.select('a.small-box-footer',true).first().attr('href', href);
22867 setContent: function (value)
22869 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22872 initEvents: function()
22886 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22889 * @class Roo.bootstrap.dash.TabBox
22890 * @extends Roo.bootstrap.Component
22891 * Bootstrap TabBox class
22892 * @cfg {String} title Title of the TabBox
22893 * @cfg {String} icon Icon of the TabBox
22894 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22895 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22898 * Create a new TabBox
22899 * @param {Object} config The config object
22903 Roo.bootstrap.dash.TabBox = function(config){
22904 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22909 * When a pane is added
22910 * @param {Roo.bootstrap.dash.TabPane} pane
22914 * @event activatepane
22915 * When a pane is activated
22916 * @param {Roo.bootstrap.dash.TabPane} pane
22918 "activatepane" : true
22926 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22931 tabScrollable : false,
22933 getChildContainer : function()
22935 return this.el.select('.tab-content', true).first();
22938 getAutoCreate : function(){
22942 cls: 'pull-left header',
22950 cls: 'fa ' + this.icon
22956 cls: 'nav nav-tabs pull-right',
22962 if(this.tabScrollable){
22969 cls: 'nav nav-tabs pull-right',
22980 cls: 'nav-tabs-custom',
22985 cls: 'tab-content no-padding',
22993 initEvents : function()
22995 //Roo.log('add add pane handler');
22996 this.on('addpane', this.onAddPane, this);
22999 * Updates the box title
23000 * @param {String} html to set the title to.
23002 setTitle : function(value)
23004 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23006 onAddPane : function(pane)
23008 this.panes.push(pane);
23009 //Roo.log('addpane');
23011 // tabs are rendere left to right..
23012 if(!this.showtabs){
23016 var ctr = this.el.select('.nav-tabs', true).first();
23019 var existing = ctr.select('.nav-tab',true);
23020 var qty = existing.getCount();;
23023 var tab = ctr.createChild({
23025 cls : 'nav-tab' + (qty ? '' : ' active'),
23033 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23036 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23038 pane.el.addClass('active');
23043 onTabClick : function(ev,un,ob,pane)
23045 //Roo.log('tab - prev default');
23046 ev.preventDefault();
23049 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23050 pane.tab.addClass('active');
23051 //Roo.log(pane.title);
23052 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23053 // technically we should have a deactivate event.. but maybe add later.
23054 // and it should not de-activate the selected tab...
23055 this.fireEvent('activatepane', pane);
23056 pane.el.addClass('active');
23057 pane.fireEvent('activate');
23062 getActivePane : function()
23065 Roo.each(this.panes, function(p) {
23066 if(p.el.hasClass('active')){
23087 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23089 * @class Roo.bootstrap.TabPane
23090 * @extends Roo.bootstrap.Component
23091 * Bootstrap TabPane class
23092 * @cfg {Boolean} active (false | true) Default false
23093 * @cfg {String} title title of panel
23097 * Create a new TabPane
23098 * @param {Object} config The config object
23101 Roo.bootstrap.dash.TabPane = function(config){
23102 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23108 * When a pane is activated
23109 * @param {Roo.bootstrap.dash.TabPane} pane
23116 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23121 // the tabBox that this is attached to.
23124 getAutoCreate : function()
23132 cfg.cls += ' active';
23137 initEvents : function()
23139 //Roo.log('trigger add pane handler');
23140 this.parent().fireEvent('addpane', this)
23144 * Updates the tab title
23145 * @param {String} html to set the title to.
23147 setTitle: function(str)
23153 this.tab.select('a', true).first().dom.innerHTML = str;
23170 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23173 * @class Roo.bootstrap.menu.Menu
23174 * @extends Roo.bootstrap.Component
23175 * Bootstrap Menu class - container for Menu
23176 * @cfg {String} html Text of the menu
23177 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23178 * @cfg {String} icon Font awesome icon
23179 * @cfg {String} pos Menu align to (top | bottom) default bottom
23183 * Create a new Menu
23184 * @param {Object} config The config object
23188 Roo.bootstrap.menu.Menu = function(config){
23189 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23193 * @event beforeshow
23194 * Fires before this menu is displayed
23195 * @param {Roo.bootstrap.menu.Menu} this
23199 * @event beforehide
23200 * Fires before this menu is hidden
23201 * @param {Roo.bootstrap.menu.Menu} this
23206 * Fires after this menu is displayed
23207 * @param {Roo.bootstrap.menu.Menu} this
23212 * Fires after this menu is hidden
23213 * @param {Roo.bootstrap.menu.Menu} this
23218 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23219 * @param {Roo.bootstrap.menu.Menu} this
23220 * @param {Roo.EventObject} e
23227 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23231 weight : 'default',
23236 getChildContainer : function() {
23237 if(this.isSubMenu){
23241 return this.el.select('ul.dropdown-menu', true).first();
23244 getAutoCreate : function()
23249 cls : 'roo-menu-text',
23257 cls : 'fa ' + this.icon
23268 cls : 'dropdown-button btn btn-' + this.weight,
23273 cls : 'dropdown-toggle btn btn-' + this.weight,
23283 cls : 'dropdown-menu'
23289 if(this.pos == 'top'){
23290 cfg.cls += ' dropup';
23293 if(this.isSubMenu){
23296 cls : 'dropdown-menu'
23303 onRender : function(ct, position)
23305 this.isSubMenu = ct.hasClass('dropdown-submenu');
23307 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23310 initEvents : function()
23312 if(this.isSubMenu){
23316 this.hidden = true;
23318 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23319 this.triggerEl.on('click', this.onTriggerPress, this);
23321 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23322 this.buttonEl.on('click', this.onClick, this);
23328 if(this.isSubMenu){
23332 return this.el.select('ul.dropdown-menu', true).first();
23335 onClick : function(e)
23337 this.fireEvent("click", this, e);
23340 onTriggerPress : function(e)
23342 if (this.isVisible()) {
23349 isVisible : function(){
23350 return !this.hidden;
23355 this.fireEvent("beforeshow", this);
23357 this.hidden = false;
23358 this.el.addClass('open');
23360 Roo.get(document).on("mouseup", this.onMouseUp, this);
23362 this.fireEvent("show", this);
23369 this.fireEvent("beforehide", this);
23371 this.hidden = true;
23372 this.el.removeClass('open');
23374 Roo.get(document).un("mouseup", this.onMouseUp);
23376 this.fireEvent("hide", this);
23379 onMouseUp : function()
23393 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23396 * @class Roo.bootstrap.menu.Item
23397 * @extends Roo.bootstrap.Component
23398 * Bootstrap MenuItem class
23399 * @cfg {Boolean} submenu (true | false) default false
23400 * @cfg {String} html text of the item
23401 * @cfg {String} href the link
23402 * @cfg {Boolean} disable (true | false) default false
23403 * @cfg {Boolean} preventDefault (true | false) default true
23404 * @cfg {String} icon Font awesome icon
23405 * @cfg {String} pos Submenu align to (left | right) default right
23409 * Create a new Item
23410 * @param {Object} config The config object
23414 Roo.bootstrap.menu.Item = function(config){
23415 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23419 * Fires when the mouse is hovering over this menu
23420 * @param {Roo.bootstrap.menu.Item} this
23421 * @param {Roo.EventObject} e
23426 * Fires when the mouse exits this menu
23427 * @param {Roo.bootstrap.menu.Item} this
23428 * @param {Roo.EventObject} e
23434 * The raw click event for the entire grid.
23435 * @param {Roo.EventObject} e
23441 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23446 preventDefault: true,
23451 getAutoCreate : function()
23456 cls : 'roo-menu-item-text',
23464 cls : 'fa ' + this.icon
23473 href : this.href || '#',
23480 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23484 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23486 if(this.pos == 'left'){
23487 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23494 initEvents : function()
23496 this.el.on('mouseover', this.onMouseOver, this);
23497 this.el.on('mouseout', this.onMouseOut, this);
23499 this.el.select('a', true).first().on('click', this.onClick, this);
23503 onClick : function(e)
23505 if(this.preventDefault){
23506 e.preventDefault();
23509 this.fireEvent("click", this, e);
23512 onMouseOver : function(e)
23514 if(this.submenu && this.pos == 'left'){
23515 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23518 this.fireEvent("mouseover", this, e);
23521 onMouseOut : function(e)
23523 this.fireEvent("mouseout", this, e);
23535 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23538 * @class Roo.bootstrap.menu.Separator
23539 * @extends Roo.bootstrap.Component
23540 * Bootstrap Separator class
23543 * Create a new Separator
23544 * @param {Object} config The config object
23548 Roo.bootstrap.menu.Separator = function(config){
23549 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23552 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23554 getAutoCreate : function(){
23575 * @class Roo.bootstrap.Tooltip
23576 * Bootstrap Tooltip class
23577 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23578 * to determine which dom element triggers the tooltip.
23580 * It needs to add support for additional attributes like tooltip-position
23583 * Create a new Toolti
23584 * @param {Object} config The config object
23587 Roo.bootstrap.Tooltip = function(config){
23588 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23591 Roo.apply(Roo.bootstrap.Tooltip, {
23593 * @function init initialize tooltip monitoring.
23597 currentTip : false,
23598 currentRegion : false,
23604 Roo.get(document).on('mouseover', this.enter ,this);
23605 Roo.get(document).on('mouseout', this.leave, this);
23608 this.currentTip = new Roo.bootstrap.Tooltip();
23611 enter : function(ev)
23613 var dom = ev.getTarget();
23615 //Roo.log(['enter',dom]);
23616 var el = Roo.fly(dom);
23617 if (this.currentEl) {
23619 //Roo.log(this.currentEl);
23620 //Roo.log(this.currentEl.contains(dom));
23621 if (this.currentEl == el) {
23624 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23630 if (this.currentTip.el) {
23631 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23636 // you can not look for children, as if el is the body.. then everythign is the child..
23637 if (!el.attr('tooltip')) { //
23638 if (!el.select("[tooltip]").elements.length) {
23641 // is the mouse over this child...?
23642 bindEl = el.select("[tooltip]").first();
23643 var xy = ev.getXY();
23644 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23645 //Roo.log("not in region.");
23648 //Roo.log("child element over..");
23651 this.currentEl = bindEl;
23652 this.currentTip.bind(bindEl);
23653 this.currentRegion = Roo.lib.Region.getRegion(dom);
23654 this.currentTip.enter();
23657 leave : function(ev)
23659 var dom = ev.getTarget();
23660 //Roo.log(['leave',dom]);
23661 if (!this.currentEl) {
23666 if (dom != this.currentEl.dom) {
23669 var xy = ev.getXY();
23670 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23673 // only activate leave if mouse cursor is outside... bounding box..
23678 if (this.currentTip) {
23679 this.currentTip.leave();
23681 //Roo.log('clear currentEl');
23682 this.currentEl = false;
23687 'left' : ['r-l', [-2,0], 'right'],
23688 'right' : ['l-r', [2,0], 'left'],
23689 'bottom' : ['t-b', [0,2], 'top'],
23690 'top' : [ 'b-t', [0,-2], 'bottom']
23696 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23701 delay : null, // can be { show : 300 , hide: 500}
23705 hoverState : null, //???
23707 placement : 'bottom',
23709 getAutoCreate : function(){
23716 cls : 'tooltip-arrow'
23719 cls : 'tooltip-inner'
23726 bind : function(el)
23732 enter : function () {
23734 if (this.timeout != null) {
23735 clearTimeout(this.timeout);
23738 this.hoverState = 'in';
23739 //Roo.log("enter - show");
23740 if (!this.delay || !this.delay.show) {
23745 this.timeout = setTimeout(function () {
23746 if (_t.hoverState == 'in') {
23749 }, this.delay.show);
23753 clearTimeout(this.timeout);
23755 this.hoverState = 'out';
23756 if (!this.delay || !this.delay.hide) {
23762 this.timeout = setTimeout(function () {
23763 //Roo.log("leave - timeout");
23765 if (_t.hoverState == 'out') {
23767 Roo.bootstrap.Tooltip.currentEl = false;
23775 this.render(document.body);
23778 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23780 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23782 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23784 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23786 var placement = typeof this.placement == 'function' ?
23787 this.placement.call(this, this.el, on_el) :
23790 var autoToken = /\s?auto?\s?/i;
23791 var autoPlace = autoToken.test(placement);
23793 placement = placement.replace(autoToken, '') || 'top';
23797 //this.el.setXY([0,0]);
23799 //this.el.dom.style.display='block';
23801 //this.el.appendTo(on_el);
23803 var p = this.getPosition();
23804 var box = this.el.getBox();
23810 var align = Roo.bootstrap.Tooltip.alignment[placement];
23812 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23814 if(placement == 'top' || placement == 'bottom'){
23816 placement = 'right';
23819 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23820 placement = 'left';
23824 align = Roo.bootstrap.Tooltip.alignment[placement];
23826 this.el.alignTo(this.bindEl, align[0],align[1]);
23827 //var arrow = this.el.select('.arrow',true).first();
23828 //arrow.set(align[2],
23830 this.el.addClass(placement);
23832 this.el.addClass('in fade');
23834 this.hoverState = null;
23836 if (this.el.hasClass('fade')) {
23847 //this.el.setXY([0,0]);
23848 this.el.removeClass('in');
23864 * @class Roo.bootstrap.LocationPicker
23865 * @extends Roo.bootstrap.Component
23866 * Bootstrap LocationPicker class
23867 * @cfg {Number} latitude Position when init default 0
23868 * @cfg {Number} longitude Position when init default 0
23869 * @cfg {Number} zoom default 15
23870 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23871 * @cfg {Boolean} mapTypeControl default false
23872 * @cfg {Boolean} disableDoubleClickZoom default false
23873 * @cfg {Boolean} scrollwheel default true
23874 * @cfg {Boolean} streetViewControl default false
23875 * @cfg {Number} radius default 0
23876 * @cfg {String} locationName
23877 * @cfg {Boolean} draggable default true
23878 * @cfg {Boolean} enableAutocomplete default false
23879 * @cfg {Boolean} enableReverseGeocode default true
23880 * @cfg {String} markerTitle
23883 * Create a new LocationPicker
23884 * @param {Object} config The config object
23888 Roo.bootstrap.LocationPicker = function(config){
23890 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23895 * Fires when the picker initialized.
23896 * @param {Roo.bootstrap.LocationPicker} this
23897 * @param {Google Location} location
23901 * @event positionchanged
23902 * Fires when the picker position changed.
23903 * @param {Roo.bootstrap.LocationPicker} this
23904 * @param {Google Location} location
23906 positionchanged : true,
23909 * Fires when the map resize.
23910 * @param {Roo.bootstrap.LocationPicker} this
23915 * Fires when the map show.
23916 * @param {Roo.bootstrap.LocationPicker} this
23921 * Fires when the map hide.
23922 * @param {Roo.bootstrap.LocationPicker} this
23927 * Fires when click the map.
23928 * @param {Roo.bootstrap.LocationPicker} this
23929 * @param {Map event} e
23933 * @event mapRightClick
23934 * Fires when right click the map.
23935 * @param {Roo.bootstrap.LocationPicker} this
23936 * @param {Map event} e
23938 mapRightClick : true,
23940 * @event markerClick
23941 * Fires when click the marker.
23942 * @param {Roo.bootstrap.LocationPicker} this
23943 * @param {Map event} e
23945 markerClick : true,
23947 * @event markerRightClick
23948 * Fires when right click the marker.
23949 * @param {Roo.bootstrap.LocationPicker} this
23950 * @param {Map event} e
23952 markerRightClick : true,
23954 * @event OverlayViewDraw
23955 * Fires when OverlayView Draw
23956 * @param {Roo.bootstrap.LocationPicker} this
23958 OverlayViewDraw : true,
23960 * @event OverlayViewOnAdd
23961 * Fires when OverlayView Draw
23962 * @param {Roo.bootstrap.LocationPicker} this
23964 OverlayViewOnAdd : true,
23966 * @event OverlayViewOnRemove
23967 * Fires when OverlayView Draw
23968 * @param {Roo.bootstrap.LocationPicker} this
23970 OverlayViewOnRemove : true,
23972 * @event OverlayViewShow
23973 * Fires when OverlayView Draw
23974 * @param {Roo.bootstrap.LocationPicker} this
23975 * @param {Pixel} cpx
23977 OverlayViewShow : true,
23979 * @event OverlayViewHide
23980 * Fires when OverlayView Draw
23981 * @param {Roo.bootstrap.LocationPicker} this
23983 OverlayViewHide : true,
23985 * @event loadexception
23986 * Fires when load google lib failed.
23987 * @param {Roo.bootstrap.LocationPicker} this
23989 loadexception : true
23994 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23996 gMapContext: false,
24002 mapTypeControl: false,
24003 disableDoubleClickZoom: false,
24005 streetViewControl: false,
24009 enableAutocomplete: false,
24010 enableReverseGeocode: true,
24013 getAutoCreate: function()
24018 cls: 'roo-location-picker'
24024 initEvents: function(ct, position)
24026 if(!this.el.getWidth() || this.isApplied()){
24030 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24035 initial: function()
24037 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24038 this.fireEvent('loadexception', this);
24042 if(!this.mapTypeId){
24043 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24046 this.gMapContext = this.GMapContext();
24048 this.initOverlayView();
24050 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24054 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24055 _this.setPosition(_this.gMapContext.marker.position);
24058 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24059 _this.fireEvent('mapClick', this, event);
24063 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24064 _this.fireEvent('mapRightClick', this, event);
24068 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24069 _this.fireEvent('markerClick', this, event);
24073 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24074 _this.fireEvent('markerRightClick', this, event);
24078 this.setPosition(this.gMapContext.location);
24080 this.fireEvent('initial', this, this.gMapContext.location);
24083 initOverlayView: function()
24087 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24091 _this.fireEvent('OverlayViewDraw', _this);
24096 _this.fireEvent('OverlayViewOnAdd', _this);
24099 onRemove: function()
24101 _this.fireEvent('OverlayViewOnRemove', _this);
24104 show: function(cpx)
24106 _this.fireEvent('OverlayViewShow', _this, cpx);
24111 _this.fireEvent('OverlayViewHide', _this);
24117 fromLatLngToContainerPixel: function(event)
24119 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24122 isApplied: function()
24124 return this.getGmapContext() == false ? false : true;
24127 getGmapContext: function()
24129 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24132 GMapContext: function()
24134 var position = new google.maps.LatLng(this.latitude, this.longitude);
24136 var _map = new google.maps.Map(this.el.dom, {
24139 mapTypeId: this.mapTypeId,
24140 mapTypeControl: this.mapTypeControl,
24141 disableDoubleClickZoom: this.disableDoubleClickZoom,
24142 scrollwheel: this.scrollwheel,
24143 streetViewControl: this.streetViewControl,
24144 locationName: this.locationName,
24145 draggable: this.draggable,
24146 enableAutocomplete: this.enableAutocomplete,
24147 enableReverseGeocode: this.enableReverseGeocode
24150 var _marker = new google.maps.Marker({
24151 position: position,
24153 title: this.markerTitle,
24154 draggable: this.draggable
24161 location: position,
24162 radius: this.radius,
24163 locationName: this.locationName,
24164 addressComponents: {
24165 formatted_address: null,
24166 addressLine1: null,
24167 addressLine2: null,
24169 streetNumber: null,
24173 stateOrProvince: null
24176 domContainer: this.el.dom,
24177 geodecoder: new google.maps.Geocoder()
24181 drawCircle: function(center, radius, options)
24183 if (this.gMapContext.circle != null) {
24184 this.gMapContext.circle.setMap(null);
24188 options = Roo.apply({}, options, {
24189 strokeColor: "#0000FF",
24190 strokeOpacity: .35,
24192 fillColor: "#0000FF",
24196 options.map = this.gMapContext.map;
24197 options.radius = radius;
24198 options.center = center;
24199 this.gMapContext.circle = new google.maps.Circle(options);
24200 return this.gMapContext.circle;
24206 setPosition: function(location)
24208 this.gMapContext.location = location;
24209 this.gMapContext.marker.setPosition(location);
24210 this.gMapContext.map.panTo(location);
24211 this.drawCircle(location, this.gMapContext.radius, {});
24215 if (this.gMapContext.settings.enableReverseGeocode) {
24216 this.gMapContext.geodecoder.geocode({
24217 latLng: this.gMapContext.location
24218 }, function(results, status) {
24220 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24221 _this.gMapContext.locationName = results[0].formatted_address;
24222 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24224 _this.fireEvent('positionchanged', this, location);
24231 this.fireEvent('positionchanged', this, location);
24236 google.maps.event.trigger(this.gMapContext.map, "resize");
24238 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24240 this.fireEvent('resize', this);
24243 setPositionByLatLng: function(latitude, longitude)
24245 this.setPosition(new google.maps.LatLng(latitude, longitude));
24248 getCurrentPosition: function()
24251 latitude: this.gMapContext.location.lat(),
24252 longitude: this.gMapContext.location.lng()
24256 getAddressName: function()
24258 return this.gMapContext.locationName;
24261 getAddressComponents: function()
24263 return this.gMapContext.addressComponents;
24266 address_component_from_google_geocode: function(address_components)
24270 for (var i = 0; i < address_components.length; i++) {
24271 var component = address_components[i];
24272 if (component.types.indexOf("postal_code") >= 0) {
24273 result.postalCode = component.short_name;
24274 } else if (component.types.indexOf("street_number") >= 0) {
24275 result.streetNumber = component.short_name;
24276 } else if (component.types.indexOf("route") >= 0) {
24277 result.streetName = component.short_name;
24278 } else if (component.types.indexOf("neighborhood") >= 0) {
24279 result.city = component.short_name;
24280 } else if (component.types.indexOf("locality") >= 0) {
24281 result.city = component.short_name;
24282 } else if (component.types.indexOf("sublocality") >= 0) {
24283 result.district = component.short_name;
24284 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24285 result.stateOrProvince = component.short_name;
24286 } else if (component.types.indexOf("country") >= 0) {
24287 result.country = component.short_name;
24291 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24292 result.addressLine2 = "";
24296 setZoomLevel: function(zoom)
24298 this.gMapContext.map.setZoom(zoom);
24311 this.fireEvent('show', this);
24322 this.fireEvent('hide', this);
24327 Roo.apply(Roo.bootstrap.LocationPicker, {
24329 OverlayView : function(map, options)
24331 options = options || {};
24345 * @class Roo.bootstrap.Alert
24346 * @extends Roo.bootstrap.Component
24347 * Bootstrap Alert class
24348 * @cfg {String} title The title of alert
24349 * @cfg {String} html The content of alert
24350 * @cfg {String} weight ( success | info | warning | danger )
24351 * @cfg {String} faicon font-awesomeicon
24354 * Create a new alert
24355 * @param {Object} config The config object
24359 Roo.bootstrap.Alert = function(config){
24360 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24364 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24371 getAutoCreate : function()
24380 cls : 'roo-alert-icon'
24385 cls : 'roo-alert-title',
24390 cls : 'roo-alert-text',
24397 cfg.cn[0].cls += ' fa ' + this.faicon;
24401 cfg.cls += ' alert-' + this.weight;
24407 initEvents: function()
24409 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24412 setTitle : function(str)
24414 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24417 setText : function(str)
24419 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24422 setWeight : function(weight)
24425 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24428 this.weight = weight;
24430 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24433 setIcon : function(icon)
24436 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24439 this.faicon = icon;
24441 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24462 * @class Roo.bootstrap.UploadCropbox
24463 * @extends Roo.bootstrap.Component
24464 * Bootstrap UploadCropbox class
24465 * @cfg {String} emptyText show when image has been loaded
24466 * @cfg {String} rotateNotify show when image too small to rotate
24467 * @cfg {Number} errorTimeout default 3000
24468 * @cfg {Number} minWidth default 300
24469 * @cfg {Number} minHeight default 300
24470 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24471 * @cfg {Boolean} isDocument (true|false) default false
24472 * @cfg {String} url action url
24473 * @cfg {String} paramName default 'imageUpload'
24474 * @cfg {String} method default POST
24475 * @cfg {Boolean} loadMask (true|false) default true
24476 * @cfg {Boolean} loadingText default 'Loading...'
24479 * Create a new UploadCropbox
24480 * @param {Object} config The config object
24483 Roo.bootstrap.UploadCropbox = function(config){
24484 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24488 * @event beforeselectfile
24489 * Fire before select file
24490 * @param {Roo.bootstrap.UploadCropbox} this
24492 "beforeselectfile" : true,
24495 * Fire after initEvent
24496 * @param {Roo.bootstrap.UploadCropbox} this
24501 * Fire after initEvent
24502 * @param {Roo.bootstrap.UploadCropbox} this
24503 * @param {String} data
24508 * Fire when preparing the file data
24509 * @param {Roo.bootstrap.UploadCropbox} this
24510 * @param {Object} file
24515 * Fire when get exception
24516 * @param {Roo.bootstrap.UploadCropbox} this
24517 * @param {XMLHttpRequest} xhr
24519 "exception" : true,
24521 * @event beforeloadcanvas
24522 * Fire before load the canvas
24523 * @param {Roo.bootstrap.UploadCropbox} this
24524 * @param {String} src
24526 "beforeloadcanvas" : true,
24529 * Fire when trash image
24530 * @param {Roo.bootstrap.UploadCropbox} this
24535 * Fire when download the image
24536 * @param {Roo.bootstrap.UploadCropbox} this
24540 * @event footerbuttonclick
24541 * Fire when footerbuttonclick
24542 * @param {Roo.bootstrap.UploadCropbox} this
24543 * @param {String} type
24545 "footerbuttonclick" : true,
24549 * @param {Roo.bootstrap.UploadCropbox} this
24554 * Fire when rotate the image
24555 * @param {Roo.bootstrap.UploadCropbox} this
24556 * @param {String} pos
24561 * Fire when inspect the file
24562 * @param {Roo.bootstrap.UploadCropbox} this
24563 * @param {Object} file
24568 * Fire when xhr upload the file
24569 * @param {Roo.bootstrap.UploadCropbox} this
24570 * @param {Object} data
24575 * Fire when arrange the file data
24576 * @param {Roo.bootstrap.UploadCropbox} this
24577 * @param {Object} formData
24582 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24585 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24587 emptyText : 'Click to upload image',
24588 rotateNotify : 'Image is too small to rotate',
24589 errorTimeout : 3000,
24603 cropType : 'image/jpeg',
24605 canvasLoaded : false,
24606 isDocument : false,
24608 paramName : 'imageUpload',
24610 loadingText : 'Loading...',
24613 getAutoCreate : function()
24617 cls : 'roo-upload-cropbox',
24621 cls : 'roo-upload-cropbox-selector',
24626 cls : 'roo-upload-cropbox-body',
24627 style : 'cursor:pointer',
24631 cls : 'roo-upload-cropbox-preview'
24635 cls : 'roo-upload-cropbox-thumb'
24639 cls : 'roo-upload-cropbox-empty-notify',
24640 html : this.emptyText
24644 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24645 html : this.rotateNotify
24651 cls : 'roo-upload-cropbox-footer',
24654 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24664 onRender : function(ct, position)
24666 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24668 if (this.buttons.length) {
24670 Roo.each(this.buttons, function(bb) {
24672 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24674 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24680 this.maskEl = this.el;
24684 initEvents : function()
24686 this.urlAPI = (window.createObjectURL && window) ||
24687 (window.URL && URL.revokeObjectURL && URL) ||
24688 (window.webkitURL && webkitURL);
24690 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24691 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24693 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24694 this.selectorEl.hide();
24696 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24697 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24699 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24700 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24701 this.thumbEl.hide();
24703 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24704 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24706 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24707 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24708 this.errorEl.hide();
24710 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24711 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24712 this.footerEl.hide();
24714 this.setThumbBoxSize();
24720 this.fireEvent('initial', this);
24727 window.addEventListener("resize", function() { _this.resize(); } );
24729 this.bodyEl.on('click', this.beforeSelectFile, this);
24732 this.bodyEl.on('touchstart', this.onTouchStart, this);
24733 this.bodyEl.on('touchmove', this.onTouchMove, this);
24734 this.bodyEl.on('touchend', this.onTouchEnd, this);
24738 this.bodyEl.on('mousedown', this.onMouseDown, this);
24739 this.bodyEl.on('mousemove', this.onMouseMove, this);
24740 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24741 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24742 Roo.get(document).on('mouseup', this.onMouseUp, this);
24745 this.selectorEl.on('change', this.onFileSelected, this);
24751 this.baseScale = 1;
24753 this.baseRotate = 1;
24754 this.dragable = false;
24755 this.pinching = false;
24758 this.cropData = false;
24759 this.notifyEl.dom.innerHTML = this.emptyText;
24761 this.selectorEl.dom.value = '';
24765 resize : function()
24767 if(this.fireEvent('resize', this) != false){
24768 this.setThumbBoxPosition();
24769 this.setCanvasPosition();
24773 onFooterButtonClick : function(e, el, o, type)
24776 case 'rotate-left' :
24777 this.onRotateLeft(e);
24779 case 'rotate-right' :
24780 this.onRotateRight(e);
24783 this.beforeSelectFile(e);
24798 this.fireEvent('footerbuttonclick', this, type);
24801 beforeSelectFile : function(e)
24803 e.preventDefault();
24805 if(this.fireEvent('beforeselectfile', this) != false){
24806 this.selectorEl.dom.click();
24810 onFileSelected : function(e)
24812 e.preventDefault();
24814 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24818 var file = this.selectorEl.dom.files[0];
24820 if(this.fireEvent('inspect', this, file) != false){
24821 this.prepare(file);
24826 trash : function(e)
24828 this.fireEvent('trash', this);
24831 download : function(e)
24833 this.fireEvent('download', this);
24836 loadCanvas : function(src)
24838 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24842 this.imageEl = document.createElement('img');
24846 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24848 this.imageEl.src = src;
24852 onLoadCanvas : function()
24854 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24855 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24857 this.bodyEl.un('click', this.beforeSelectFile, this);
24859 this.notifyEl.hide();
24860 this.thumbEl.show();
24861 this.footerEl.show();
24863 this.baseRotateLevel();
24865 if(this.isDocument){
24866 this.setThumbBoxSize();
24869 this.setThumbBoxPosition();
24871 this.baseScaleLevel();
24877 this.canvasLoaded = true;
24880 this.maskEl.unmask();
24885 setCanvasPosition : function()
24887 if(!this.canvasEl){
24891 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24892 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24894 this.previewEl.setLeft(pw);
24895 this.previewEl.setTop(ph);
24899 onMouseDown : function(e)
24903 this.dragable = true;
24904 this.pinching = false;
24906 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24907 this.dragable = false;
24911 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24912 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24916 onMouseMove : function(e)
24920 if(!this.canvasLoaded){
24924 if (!this.dragable){
24928 var minX = Math.ceil(this.thumbEl.getLeft(true));
24929 var minY = Math.ceil(this.thumbEl.getTop(true));
24931 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24932 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24934 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24935 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24937 x = x - this.mouseX;
24938 y = y - this.mouseY;
24940 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24941 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24943 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24944 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24946 this.previewEl.setLeft(bgX);
24947 this.previewEl.setTop(bgY);
24949 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24950 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24953 onMouseUp : function(e)
24957 this.dragable = false;
24960 onMouseWheel : function(e)
24964 this.startScale = this.scale;
24966 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24968 if(!this.zoomable()){
24969 this.scale = this.startScale;
24978 zoomable : function()
24980 var minScale = this.thumbEl.getWidth() / this.minWidth;
24982 if(this.minWidth < this.minHeight){
24983 minScale = this.thumbEl.getHeight() / this.minHeight;
24986 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24987 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24991 (this.rotate == 0 || this.rotate == 180) &&
24993 width > this.imageEl.OriginWidth ||
24994 height > this.imageEl.OriginHeight ||
24995 (width < this.minWidth && height < this.minHeight)
25003 (this.rotate == 90 || this.rotate == 270) &&
25005 width > this.imageEl.OriginWidth ||
25006 height > this.imageEl.OriginHeight ||
25007 (width < this.minHeight && height < this.minWidth)
25014 !this.isDocument &&
25015 (this.rotate == 0 || this.rotate == 180) &&
25017 width < this.minWidth ||
25018 width > this.imageEl.OriginWidth ||
25019 height < this.minHeight ||
25020 height > this.imageEl.OriginHeight
25027 !this.isDocument &&
25028 (this.rotate == 90 || this.rotate == 270) &&
25030 width < this.minHeight ||
25031 width > this.imageEl.OriginWidth ||
25032 height < this.minWidth ||
25033 height > this.imageEl.OriginHeight
25043 onRotateLeft : function(e)
25045 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25047 var minScale = this.thumbEl.getWidth() / this.minWidth;
25049 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25050 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25052 this.startScale = this.scale;
25054 while (this.getScaleLevel() < minScale){
25056 this.scale = this.scale + 1;
25058 if(!this.zoomable()){
25063 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25064 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25069 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25076 this.scale = this.startScale;
25078 this.onRotateFail();
25083 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25085 if(this.isDocument){
25086 this.setThumbBoxSize();
25087 this.setThumbBoxPosition();
25088 this.setCanvasPosition();
25093 this.fireEvent('rotate', this, 'left');
25097 onRotateRight : function(e)
25099 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25101 var minScale = this.thumbEl.getWidth() / this.minWidth;
25103 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25104 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25106 this.startScale = this.scale;
25108 while (this.getScaleLevel() < minScale){
25110 this.scale = this.scale + 1;
25112 if(!this.zoomable()){
25117 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25118 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25123 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25130 this.scale = this.startScale;
25132 this.onRotateFail();
25137 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25139 if(this.isDocument){
25140 this.setThumbBoxSize();
25141 this.setThumbBoxPosition();
25142 this.setCanvasPosition();
25147 this.fireEvent('rotate', this, 'right');
25150 onRotateFail : function()
25152 this.errorEl.show(true);
25156 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25161 this.previewEl.dom.innerHTML = '';
25163 var canvasEl = document.createElement("canvas");
25165 var contextEl = canvasEl.getContext("2d");
25167 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25168 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25169 var center = this.imageEl.OriginWidth / 2;
25171 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25172 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25173 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25174 center = this.imageEl.OriginHeight / 2;
25177 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25179 contextEl.translate(center, center);
25180 contextEl.rotate(this.rotate * Math.PI / 180);
25182 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25184 this.canvasEl = document.createElement("canvas");
25186 this.contextEl = this.canvasEl.getContext("2d");
25188 switch (this.rotate) {
25191 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25192 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25194 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25199 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25200 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25202 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25203 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);
25207 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25212 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25213 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25215 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25216 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);
25220 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);
25225 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25226 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25228 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25229 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25233 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);
25240 this.previewEl.appendChild(this.canvasEl);
25242 this.setCanvasPosition();
25247 if(!this.canvasLoaded){
25251 var imageCanvas = document.createElement("canvas");
25253 var imageContext = imageCanvas.getContext("2d");
25255 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25256 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25258 var center = imageCanvas.width / 2;
25260 imageContext.translate(center, center);
25262 imageContext.rotate(this.rotate * Math.PI / 180);
25264 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25266 var canvas = document.createElement("canvas");
25268 var context = canvas.getContext("2d");
25270 canvas.width = this.minWidth;
25271 canvas.height = this.minHeight;
25273 switch (this.rotate) {
25276 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25277 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25279 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25280 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25282 var targetWidth = this.minWidth - 2 * x;
25283 var targetHeight = this.minHeight - 2 * y;
25287 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25288 scale = targetWidth / width;
25291 if(x > 0 && y == 0){
25292 scale = targetHeight / height;
25295 if(x > 0 && y > 0){
25296 scale = targetWidth / width;
25298 if(width < height){
25299 scale = targetHeight / height;
25303 context.scale(scale, scale);
25305 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25306 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25308 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25309 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25311 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25316 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25317 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25319 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25320 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25322 var targetWidth = this.minWidth - 2 * x;
25323 var targetHeight = this.minHeight - 2 * y;
25327 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25328 scale = targetWidth / width;
25331 if(x > 0 && y == 0){
25332 scale = targetHeight / height;
25335 if(x > 0 && y > 0){
25336 scale = targetWidth / width;
25338 if(width < height){
25339 scale = targetHeight / height;
25343 context.scale(scale, scale);
25345 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25346 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25348 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25349 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25351 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25353 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25358 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25359 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25361 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25362 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25364 var targetWidth = this.minWidth - 2 * x;
25365 var targetHeight = this.minHeight - 2 * y;
25369 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25370 scale = targetWidth / width;
25373 if(x > 0 && y == 0){
25374 scale = targetHeight / height;
25377 if(x > 0 && y > 0){
25378 scale = targetWidth / width;
25380 if(width < height){
25381 scale = targetHeight / height;
25385 context.scale(scale, scale);
25387 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25388 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25390 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25391 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25393 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25394 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25396 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25401 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25402 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25404 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25405 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25407 var targetWidth = this.minWidth - 2 * x;
25408 var targetHeight = this.minHeight - 2 * y;
25412 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25413 scale = targetWidth / width;
25416 if(x > 0 && y == 0){
25417 scale = targetHeight / height;
25420 if(x > 0 && y > 0){
25421 scale = targetWidth / width;
25423 if(width < height){
25424 scale = targetHeight / height;
25428 context.scale(scale, scale);
25430 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25431 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25433 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25434 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25436 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25438 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25445 this.cropData = canvas.toDataURL(this.cropType);
25447 if(this.fireEvent('crop', this, this.cropData) !== false){
25448 this.process(this.file, this.cropData);
25455 setThumbBoxSize : function()
25459 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25460 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25461 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25463 this.minWidth = width;
25464 this.minHeight = height;
25466 if(this.rotate == 90 || this.rotate == 270){
25467 this.minWidth = height;
25468 this.minHeight = width;
25473 width = Math.ceil(this.minWidth * height / this.minHeight);
25475 if(this.minWidth > this.minHeight){
25477 height = Math.ceil(this.minHeight * width / this.minWidth);
25480 this.thumbEl.setStyle({
25481 width : width + 'px',
25482 height : height + 'px'
25489 setThumbBoxPosition : function()
25491 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25492 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25494 this.thumbEl.setLeft(x);
25495 this.thumbEl.setTop(y);
25499 baseRotateLevel : function()
25501 this.baseRotate = 1;
25504 typeof(this.exif) != 'undefined' &&
25505 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25506 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25508 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25511 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25515 baseScaleLevel : function()
25519 if(this.isDocument){
25521 if(this.baseRotate == 6 || this.baseRotate == 8){
25523 height = this.thumbEl.getHeight();
25524 this.baseScale = height / this.imageEl.OriginWidth;
25526 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25527 width = this.thumbEl.getWidth();
25528 this.baseScale = width / this.imageEl.OriginHeight;
25534 height = this.thumbEl.getHeight();
25535 this.baseScale = height / this.imageEl.OriginHeight;
25537 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25538 width = this.thumbEl.getWidth();
25539 this.baseScale = width / this.imageEl.OriginWidth;
25545 if(this.baseRotate == 6 || this.baseRotate == 8){
25547 width = this.thumbEl.getHeight();
25548 this.baseScale = width / this.imageEl.OriginHeight;
25550 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25551 height = this.thumbEl.getWidth();
25552 this.baseScale = height / this.imageEl.OriginHeight;
25555 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25556 height = this.thumbEl.getWidth();
25557 this.baseScale = height / this.imageEl.OriginHeight;
25559 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25560 width = this.thumbEl.getHeight();
25561 this.baseScale = width / this.imageEl.OriginWidth;
25568 width = this.thumbEl.getWidth();
25569 this.baseScale = width / this.imageEl.OriginWidth;
25571 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25572 height = this.thumbEl.getHeight();
25573 this.baseScale = height / this.imageEl.OriginHeight;
25576 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25578 height = this.thumbEl.getHeight();
25579 this.baseScale = height / this.imageEl.OriginHeight;
25581 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25582 width = this.thumbEl.getWidth();
25583 this.baseScale = width / this.imageEl.OriginWidth;
25591 getScaleLevel : function()
25593 return this.baseScale * Math.pow(1.1, this.scale);
25596 onTouchStart : function(e)
25598 if(!this.canvasLoaded){
25599 this.beforeSelectFile(e);
25603 var touches = e.browserEvent.touches;
25609 if(touches.length == 1){
25610 this.onMouseDown(e);
25614 if(touches.length != 2){
25620 for(var i = 0, finger; finger = touches[i]; i++){
25621 coords.push(finger.pageX, finger.pageY);
25624 var x = Math.pow(coords[0] - coords[2], 2);
25625 var y = Math.pow(coords[1] - coords[3], 2);
25627 this.startDistance = Math.sqrt(x + y);
25629 this.startScale = this.scale;
25631 this.pinching = true;
25632 this.dragable = false;
25636 onTouchMove : function(e)
25638 if(!this.pinching && !this.dragable){
25642 var touches = e.browserEvent.touches;
25649 this.onMouseMove(e);
25655 for(var i = 0, finger; finger = touches[i]; i++){
25656 coords.push(finger.pageX, finger.pageY);
25659 var x = Math.pow(coords[0] - coords[2], 2);
25660 var y = Math.pow(coords[1] - coords[3], 2);
25662 this.endDistance = Math.sqrt(x + y);
25664 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25666 if(!this.zoomable()){
25667 this.scale = this.startScale;
25675 onTouchEnd : function(e)
25677 this.pinching = false;
25678 this.dragable = false;
25682 process : function(file, crop)
25685 this.maskEl.mask(this.loadingText);
25688 this.xhr = new XMLHttpRequest();
25690 file.xhr = this.xhr;
25692 this.xhr.open(this.method, this.url, true);
25695 "Accept": "application/json",
25696 "Cache-Control": "no-cache",
25697 "X-Requested-With": "XMLHttpRequest"
25700 for (var headerName in headers) {
25701 var headerValue = headers[headerName];
25703 this.xhr.setRequestHeader(headerName, headerValue);
25709 this.xhr.onload = function()
25711 _this.xhrOnLoad(_this.xhr);
25714 this.xhr.onerror = function()
25716 _this.xhrOnError(_this.xhr);
25719 var formData = new FormData();
25721 formData.append('returnHTML', 'NO');
25724 formData.append('crop', crop);
25727 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25728 formData.append(this.paramName, file, file.name);
25731 if(typeof(file.filename) != 'undefined'){
25732 formData.append('filename', file.filename);
25735 if(typeof(file.mimetype) != 'undefined'){
25736 formData.append('mimetype', file.mimetype);
25739 if(this.fireEvent('arrange', this, formData) != false){
25740 this.xhr.send(formData);
25744 xhrOnLoad : function(xhr)
25747 this.maskEl.unmask();
25750 if (xhr.readyState !== 4) {
25751 this.fireEvent('exception', this, xhr);
25755 var response = Roo.decode(xhr.responseText);
25757 if(!response.success){
25758 this.fireEvent('exception', this, xhr);
25762 var response = Roo.decode(xhr.responseText);
25764 this.fireEvent('upload', this, response);
25768 xhrOnError : function()
25771 this.maskEl.unmask();
25774 Roo.log('xhr on error');
25776 var response = Roo.decode(xhr.responseText);
25782 prepare : function(file)
25785 this.maskEl.mask(this.loadingText);
25791 if(typeof(file) === 'string'){
25792 this.loadCanvas(file);
25796 if(!file || !this.urlAPI){
25801 this.cropType = file.type;
25805 if(this.fireEvent('prepare', this, this.file) != false){
25807 var reader = new FileReader();
25809 reader.onload = function (e) {
25810 if (e.target.error) {
25811 Roo.log(e.target.error);
25815 var buffer = e.target.result,
25816 dataView = new DataView(buffer),
25818 maxOffset = dataView.byteLength - 4,
25822 if (dataView.getUint16(0) === 0xffd8) {
25823 while (offset < maxOffset) {
25824 markerBytes = dataView.getUint16(offset);
25826 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25827 markerLength = dataView.getUint16(offset + 2) + 2;
25828 if (offset + markerLength > dataView.byteLength) {
25829 Roo.log('Invalid meta data: Invalid segment size.');
25833 if(markerBytes == 0xffe1){
25834 _this.parseExifData(
25841 offset += markerLength;
25851 var url = _this.urlAPI.createObjectURL(_this.file);
25853 _this.loadCanvas(url);
25858 reader.readAsArrayBuffer(this.file);
25864 parseExifData : function(dataView, offset, length)
25866 var tiffOffset = offset + 10,
25870 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25871 // No Exif data, might be XMP data instead
25875 // Check for the ASCII code for "Exif" (0x45786966):
25876 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25877 // No Exif data, might be XMP data instead
25880 if (tiffOffset + 8 > dataView.byteLength) {
25881 Roo.log('Invalid Exif data: Invalid segment size.');
25884 // Check for the two null bytes:
25885 if (dataView.getUint16(offset + 8) !== 0x0000) {
25886 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25889 // Check the byte alignment:
25890 switch (dataView.getUint16(tiffOffset)) {
25892 littleEndian = true;
25895 littleEndian = false;
25898 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25901 // Check for the TIFF tag marker (0x002A):
25902 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25903 Roo.log('Invalid Exif data: Missing TIFF marker.');
25906 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25907 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25909 this.parseExifTags(
25912 tiffOffset + dirOffset,
25917 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25922 if (dirOffset + 6 > dataView.byteLength) {
25923 Roo.log('Invalid Exif data: Invalid directory offset.');
25926 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25927 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25928 if (dirEndOffset + 4 > dataView.byteLength) {
25929 Roo.log('Invalid Exif data: Invalid directory size.');
25932 for (i = 0; i < tagsNumber; i += 1) {
25936 dirOffset + 2 + 12 * i, // tag offset
25940 // Return the offset to the next directory:
25941 return dataView.getUint32(dirEndOffset, littleEndian);
25944 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25946 var tag = dataView.getUint16(offset, littleEndian);
25948 this.exif[tag] = this.getExifValue(
25952 dataView.getUint16(offset + 2, littleEndian), // tag type
25953 dataView.getUint32(offset + 4, littleEndian), // tag length
25958 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25960 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25969 Roo.log('Invalid Exif data: Invalid tag type.');
25973 tagSize = tagType.size * length;
25974 // Determine if the value is contained in the dataOffset bytes,
25975 // or if the value at the dataOffset is a pointer to the actual data:
25976 dataOffset = tagSize > 4 ?
25977 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25978 if (dataOffset + tagSize > dataView.byteLength) {
25979 Roo.log('Invalid Exif data: Invalid data offset.');
25982 if (length === 1) {
25983 return tagType.getValue(dataView, dataOffset, littleEndian);
25986 for (i = 0; i < length; i += 1) {
25987 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25990 if (tagType.ascii) {
25992 // Concatenate the chars:
25993 for (i = 0; i < values.length; i += 1) {
25995 // Ignore the terminating NULL byte(s):
25996 if (c === '\u0000') {
26008 Roo.apply(Roo.bootstrap.UploadCropbox, {
26010 'Orientation': 0x0112
26014 1: 0, //'top-left',
26016 3: 180, //'bottom-right',
26017 // 4: 'bottom-left',
26019 6: 90, //'right-top',
26020 // 7: 'right-bottom',
26021 8: 270 //'left-bottom'
26025 // byte, 8-bit unsigned int:
26027 getValue: function (dataView, dataOffset) {
26028 return dataView.getUint8(dataOffset);
26032 // ascii, 8-bit byte:
26034 getValue: function (dataView, dataOffset) {
26035 return String.fromCharCode(dataView.getUint8(dataOffset));
26040 // short, 16 bit int:
26042 getValue: function (dataView, dataOffset, littleEndian) {
26043 return dataView.getUint16(dataOffset, littleEndian);
26047 // long, 32 bit int:
26049 getValue: function (dataView, dataOffset, littleEndian) {
26050 return dataView.getUint32(dataOffset, littleEndian);
26054 // rational = two long values, first is numerator, second is denominator:
26056 getValue: function (dataView, dataOffset, littleEndian) {
26057 return dataView.getUint32(dataOffset, littleEndian) /
26058 dataView.getUint32(dataOffset + 4, littleEndian);
26062 // slong, 32 bit signed int:
26064 getValue: function (dataView, dataOffset, littleEndian) {
26065 return dataView.getInt32(dataOffset, littleEndian);
26069 // srational, two slongs, first is numerator, second is denominator:
26071 getValue: function (dataView, dataOffset, littleEndian) {
26072 return dataView.getInt32(dataOffset, littleEndian) /
26073 dataView.getInt32(dataOffset + 4, littleEndian);
26083 cls : 'btn-group roo-upload-cropbox-rotate-left',
26084 action : 'rotate-left',
26088 cls : 'btn btn-default',
26089 html : '<i class="fa fa-undo"></i>'
26095 cls : 'btn-group roo-upload-cropbox-picture',
26096 action : 'picture',
26100 cls : 'btn btn-default',
26101 html : '<i class="fa fa-picture-o"></i>'
26107 cls : 'btn-group roo-upload-cropbox-rotate-right',
26108 action : 'rotate-right',
26112 cls : 'btn btn-default',
26113 html : '<i class="fa fa-repeat"></i>'
26121 cls : 'btn-group roo-upload-cropbox-rotate-left',
26122 action : 'rotate-left',
26126 cls : 'btn btn-default',
26127 html : '<i class="fa fa-undo"></i>'
26133 cls : 'btn-group roo-upload-cropbox-download',
26134 action : 'download',
26138 cls : 'btn btn-default',
26139 html : '<i class="fa fa-download"></i>'
26145 cls : 'btn-group roo-upload-cropbox-crop',
26150 cls : 'btn btn-default',
26151 html : '<i class="fa fa-crop"></i>'
26157 cls : 'btn-group roo-upload-cropbox-trash',
26162 cls : 'btn btn-default',
26163 html : '<i class="fa fa-trash"></i>'
26169 cls : 'btn-group roo-upload-cropbox-rotate-right',
26170 action : 'rotate-right',
26174 cls : 'btn btn-default',
26175 html : '<i class="fa fa-repeat"></i>'
26183 cls : 'btn-group roo-upload-cropbox-rotate-left',
26184 action : 'rotate-left',
26188 cls : 'btn btn-default',
26189 html : '<i class="fa fa-undo"></i>'
26195 cls : 'btn-group roo-upload-cropbox-rotate-right',
26196 action : 'rotate-right',
26200 cls : 'btn btn-default',
26201 html : '<i class="fa fa-repeat"></i>'
26214 * @class Roo.bootstrap.DocumentManager
26215 * @extends Roo.bootstrap.Component
26216 * Bootstrap DocumentManager class
26217 * @cfg {String} paramName default 'imageUpload'
26218 * @cfg {String} method default POST
26219 * @cfg {String} url action url
26220 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26221 * @cfg {Boolean} multiple multiple upload default true
26222 * @cfg {Number} thumbSize default 300
26223 * @cfg {String} fieldLabel
26224 * @cfg {Number} labelWidth default 4
26225 * @cfg {String} labelAlign (left|top) default left
26226 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26229 * Create a new DocumentManager
26230 * @param {Object} config The config object
26233 Roo.bootstrap.DocumentManager = function(config){
26234 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26239 * Fire when initial the DocumentManager
26240 * @param {Roo.bootstrap.DocumentManager} this
26245 * inspect selected file
26246 * @param {Roo.bootstrap.DocumentManager} this
26247 * @param {File} file
26252 * Fire when xhr load exception
26253 * @param {Roo.bootstrap.DocumentManager} this
26254 * @param {XMLHttpRequest} xhr
26256 "exception" : true,
26259 * prepare the form data
26260 * @param {Roo.bootstrap.DocumentManager} this
26261 * @param {Object} formData
26266 * Fire when remove the file
26267 * @param {Roo.bootstrap.DocumentManager} this
26268 * @param {Object} file
26273 * Fire after refresh the file
26274 * @param {Roo.bootstrap.DocumentManager} this
26279 * Fire after click the image
26280 * @param {Roo.bootstrap.DocumentManager} this
26281 * @param {Object} file
26286 * Fire when upload a image and editable set to true
26287 * @param {Roo.bootstrap.DocumentManager} this
26288 * @param {Object} file
26292 * @event beforeselectfile
26293 * Fire before select file
26294 * @param {Roo.bootstrap.DocumentManager} this
26296 "beforeselectfile" : true,
26299 * Fire before process file
26300 * @param {Roo.bootstrap.DocumentManager} this
26301 * @param {Object} file
26308 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26317 paramName : 'imageUpload',
26320 labelAlign : 'left',
26327 getAutoCreate : function()
26329 var managerWidget = {
26331 cls : 'roo-document-manager',
26335 cls : 'roo-document-manager-selector',
26340 cls : 'roo-document-manager-uploader',
26344 cls : 'roo-document-manager-upload-btn',
26345 html : '<i class="fa fa-plus"></i>'
26356 cls : 'column col-md-12',
26361 if(this.fieldLabel.length){
26366 cls : 'column col-md-12',
26367 html : this.fieldLabel
26371 cls : 'column col-md-12',
26376 if(this.labelAlign == 'left'){
26380 cls : 'column col-md-' + this.labelWidth,
26381 html : this.fieldLabel
26385 cls : 'column col-md-' + (12 - this.labelWidth),
26395 cls : 'row clearfix',
26403 initEvents : function()
26405 this.managerEl = this.el.select('.roo-document-manager', true).first();
26406 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26408 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26409 this.selectorEl.hide();
26412 this.selectorEl.attr('multiple', 'multiple');
26415 this.selectorEl.on('change', this.onFileSelected, this);
26417 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26418 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26420 this.uploader.on('click', this.onUploaderClick, this);
26422 this.renderProgressDialog();
26426 window.addEventListener("resize", function() { _this.refresh(); } );
26428 this.fireEvent('initial', this);
26431 renderProgressDialog : function()
26435 this.progressDialog = new Roo.bootstrap.Modal({
26436 cls : 'roo-document-manager-progress-dialog',
26437 allow_close : false,
26447 btnclick : function() {
26448 _this.uploadCancel();
26454 this.progressDialog.render(Roo.get(document.body));
26456 this.progress = new Roo.bootstrap.Progress({
26457 cls : 'roo-document-manager-progress',
26462 this.progress.render(this.progressDialog.getChildContainer());
26464 this.progressBar = new Roo.bootstrap.ProgressBar({
26465 cls : 'roo-document-manager-progress-bar',
26468 aria_valuemax : 12,
26472 this.progressBar.render(this.progress.getChildContainer());
26475 onUploaderClick : function(e)
26477 e.preventDefault();
26479 if(this.fireEvent('beforeselectfile', this) != false){
26480 this.selectorEl.dom.click();
26485 onFileSelected : function(e)
26487 e.preventDefault();
26489 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26493 Roo.each(this.selectorEl.dom.files, function(file){
26494 if(this.fireEvent('inspect', this, file) != false){
26495 this.files.push(file);
26505 this.selectorEl.dom.value = '';
26507 if(!this.files.length){
26511 if(this.boxes > 0 && this.files.length > this.boxes){
26512 this.files = this.files.slice(0, this.boxes);
26515 this.uploader.show();
26517 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26518 this.uploader.hide();
26527 Roo.each(this.files, function(file){
26529 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26530 var f = this.renderPreview(file);
26535 if(file.type.indexOf('image') != -1){
26536 this.delegates.push(
26538 _this.process(file);
26539 }).createDelegate(this)
26547 _this.process(file);
26548 }).createDelegate(this)
26553 this.files = files;
26555 this.delegates = this.delegates.concat(docs);
26557 if(!this.delegates.length){
26562 this.progressBar.aria_valuemax = this.delegates.length;
26569 arrange : function()
26571 if(!this.delegates.length){
26572 this.progressDialog.hide();
26577 var delegate = this.delegates.shift();
26579 this.progressDialog.show();
26581 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26583 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26588 refresh : function()
26590 this.uploader.show();
26592 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26593 this.uploader.hide();
26596 Roo.isTouch ? this.closable(false) : this.closable(true);
26598 this.fireEvent('refresh', this);
26601 onRemove : function(e, el, o)
26603 e.preventDefault();
26605 this.fireEvent('remove', this, o);
26609 remove : function(o)
26613 Roo.each(this.files, function(file){
26614 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26623 this.files = files;
26630 Roo.each(this.files, function(file){
26635 file.target.remove();
26644 onClick : function(e, el, o)
26646 e.preventDefault();
26648 this.fireEvent('click', this, o);
26652 closable : function(closable)
26654 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26656 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26668 xhrOnLoad : function(xhr)
26670 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26674 if (xhr.readyState !== 4) {
26676 this.fireEvent('exception', this, xhr);
26680 var response = Roo.decode(xhr.responseText);
26682 if(!response.success){
26684 this.fireEvent('exception', this, xhr);
26688 var file = this.renderPreview(response.data);
26690 this.files.push(file);
26696 xhrOnError : function()
26698 Roo.log('xhr on error');
26700 var response = Roo.decode(xhr.responseText);
26707 process : function(file)
26709 if(this.fireEvent('process', this, file) !== false){
26710 if(this.editable && file.type.indexOf('image') != -1){
26711 this.fireEvent('edit', this, file);
26715 this.uploadStart(file, false);
26722 uploadStart : function(file, crop)
26724 this.xhr = new XMLHttpRequest();
26726 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26731 file.xhr = this.xhr;
26733 this.managerEl.createChild({
26735 cls : 'roo-document-manager-loading',
26739 tooltip : file.name,
26740 cls : 'roo-document-manager-thumb',
26741 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26747 this.xhr.open(this.method, this.url, true);
26750 "Accept": "application/json",
26751 "Cache-Control": "no-cache",
26752 "X-Requested-With": "XMLHttpRequest"
26755 for (var headerName in headers) {
26756 var headerValue = headers[headerName];
26758 this.xhr.setRequestHeader(headerName, headerValue);
26764 this.xhr.onload = function()
26766 _this.xhrOnLoad(_this.xhr);
26769 this.xhr.onerror = function()
26771 _this.xhrOnError(_this.xhr);
26774 var formData = new FormData();
26776 formData.append('returnHTML', 'NO');
26779 formData.append('crop', crop);
26782 formData.append(this.paramName, file, file.name);
26784 if(this.fireEvent('prepare', this, formData) != false){
26785 this.xhr.send(formData);
26789 uploadCancel : function()
26796 this.delegates = [];
26798 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26805 renderPreview : function(file)
26807 if(typeof(file.target) != 'undefined' && file.target){
26811 var previewEl = this.managerEl.createChild({
26813 cls : 'roo-document-manager-preview',
26817 tooltip : file.filename,
26818 cls : 'roo-document-manager-thumb',
26819 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26824 html : '<i class="fa fa-times-circle"></i>'
26829 var close = previewEl.select('button.close', true).first();
26831 close.on('click', this.onRemove, this, file);
26833 file.target = previewEl;
26835 var image = previewEl.select('img', true).first();
26839 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26841 image.on('click', this.onClick, this, file);
26847 onPreviewLoad : function(file, image)
26849 if(typeof(file.target) == 'undefined' || !file.target){
26853 var width = image.dom.naturalWidth || image.dom.width;
26854 var height = image.dom.naturalHeight || image.dom.height;
26856 if(width > height){
26857 file.target.addClass('wide');
26861 file.target.addClass('tall');
26866 uploadFromSource : function(file, crop)
26868 this.xhr = new XMLHttpRequest();
26870 this.managerEl.createChild({
26872 cls : 'roo-document-manager-loading',
26876 tooltip : file.name,
26877 cls : 'roo-document-manager-thumb',
26878 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26884 this.xhr.open(this.method, this.url, true);
26887 "Accept": "application/json",
26888 "Cache-Control": "no-cache",
26889 "X-Requested-With": "XMLHttpRequest"
26892 for (var headerName in headers) {
26893 var headerValue = headers[headerName];
26895 this.xhr.setRequestHeader(headerName, headerValue);
26901 this.xhr.onload = function()
26903 _this.xhrOnLoad(_this.xhr);
26906 this.xhr.onerror = function()
26908 _this.xhrOnError(_this.xhr);
26911 var formData = new FormData();
26913 formData.append('returnHTML', 'NO');
26915 formData.append('crop', crop);
26917 if(typeof(file.filename) != 'undefined'){
26918 formData.append('filename', file.filename);
26921 if(typeof(file.mimetype) != 'undefined'){
26922 formData.append('mimetype', file.mimetype);
26925 if(this.fireEvent('prepare', this, formData) != false){
26926 this.xhr.send(formData);
26936 * @class Roo.bootstrap.DocumentViewer
26937 * @extends Roo.bootstrap.Component
26938 * Bootstrap DocumentViewer class
26941 * Create a new DocumentViewer
26942 * @param {Object} config The config object
26945 Roo.bootstrap.DocumentViewer = function(config){
26946 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26951 * Fire after initEvent
26952 * @param {Roo.bootstrap.DocumentViewer} this
26958 * @param {Roo.bootstrap.DocumentViewer} this
26963 * Fire after trash button
26964 * @param {Roo.bootstrap.DocumentViewer} this
26971 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26973 getAutoCreate : function()
26977 cls : 'roo-document-viewer',
26981 cls : 'roo-document-viewer-body',
26985 cls : 'roo-document-viewer-thumb',
26989 cls : 'roo-document-viewer-image'
26997 cls : 'roo-document-viewer-footer',
27000 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27008 cls : 'btn btn-default roo-document-viewer-trash',
27009 html : '<i class="fa fa-trash"></i>'
27022 initEvents : function()
27025 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27026 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27028 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27029 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27031 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27032 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27034 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27035 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27037 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27038 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27040 this.bodyEl.on('click', this.onClick, this);
27042 this.trashBtn.on('click', this.onTrash, this);
27046 initial : function()
27048 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27051 this.fireEvent('initial', this);
27055 onClick : function(e)
27057 e.preventDefault();
27059 this.fireEvent('click', this);
27062 onTrash : function(e)
27064 e.preventDefault();
27066 this.fireEvent('trash', this);
27078 * @class Roo.bootstrap.NavProgressBar
27079 * @extends Roo.bootstrap.Component
27080 * Bootstrap NavProgressBar class
27083 * Create a new nav progress bar
27084 * @param {Object} config The config object
27087 Roo.bootstrap.NavProgressBar = function(config){
27088 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27090 this.bullets = this.bullets || [];
27092 // Roo.bootstrap.NavProgressBar.register(this);
27096 * Fires when the active item changes
27097 * @param {Roo.bootstrap.NavProgressBar} this
27098 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27099 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27106 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27111 getAutoCreate : function()
27113 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27117 cls : 'roo-navigation-bar-group',
27121 cls : 'roo-navigation-top-bar'
27125 cls : 'roo-navigation-bullets-bar',
27129 cls : 'roo-navigation-bar'
27136 cls : 'roo-navigation-bottom-bar'
27146 initEvents: function()
27151 onRender : function(ct, position)
27153 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27155 if(this.bullets.length){
27156 Roo.each(this.bullets, function(b){
27165 addItem : function(cfg)
27167 var item = new Roo.bootstrap.NavProgressItem(cfg);
27169 item.parentId = this.id;
27170 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27173 var top = new Roo.bootstrap.Element({
27175 cls : 'roo-navigation-bar-text'
27178 var bottom = new Roo.bootstrap.Element({
27180 cls : 'roo-navigation-bar-text'
27183 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27184 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27186 var topText = new Roo.bootstrap.Element({
27188 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27191 var bottomText = new Roo.bootstrap.Element({
27193 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27196 topText.onRender(top.el, null);
27197 bottomText.onRender(bottom.el, null);
27200 item.bottomEl = bottom;
27203 this.barItems.push(item);
27208 getActive : function()
27210 var active = false;
27212 Roo.each(this.barItems, function(v){
27214 if (!v.isActive()) {
27226 setActiveItem : function(item)
27230 Roo.each(this.barItems, function(v){
27231 if (v.rid == item.rid) {
27235 if (v.isActive()) {
27236 v.setActive(false);
27241 item.setActive(true);
27243 this.fireEvent('changed', this, item, prev);
27246 getBarItem: function(rid)
27250 Roo.each(this.barItems, function(e) {
27251 if (e.rid != rid) {
27262 indexOfItem : function(item)
27266 Roo.each(this.barItems, function(v, i){
27268 if (v.rid != item.rid) {
27279 setActiveNext : function()
27281 var i = this.indexOfItem(this.getActive());
27283 if (i > this.barItems.length) {
27287 this.setActiveItem(this.barItems[i+1]);
27290 setActivePrev : function()
27292 var i = this.indexOfItem(this.getActive());
27298 this.setActiveItem(this.barItems[i-1]);
27301 format : function()
27303 if(!this.barItems.length){
27307 var width = 100 / this.barItems.length;
27309 Roo.each(this.barItems, function(i){
27310 i.el.setStyle('width', width + '%');
27311 i.topEl.el.setStyle('width', width + '%');
27312 i.bottomEl.el.setStyle('width', width + '%');
27321 * Nav Progress Item
27326 * @class Roo.bootstrap.NavProgressItem
27327 * @extends Roo.bootstrap.Component
27328 * Bootstrap NavProgressItem class
27329 * @cfg {String} rid the reference id
27330 * @cfg {Boolean} active (true|false) Is item active default false
27331 * @cfg {Boolean} disabled (true|false) Is item active default false
27332 * @cfg {String} html
27333 * @cfg {String} position (top|bottom) text position default bottom
27334 * @cfg {String} icon show icon instead of number
27337 * Create a new NavProgressItem
27338 * @param {Object} config The config object
27340 Roo.bootstrap.NavProgressItem = function(config){
27341 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27346 * The raw click event for the entire grid.
27347 * @param {Roo.bootstrap.NavProgressItem} this
27348 * @param {Roo.EventObject} e
27355 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27361 position : 'bottom',
27364 getAutoCreate : function()
27366 var iconCls = 'roo-navigation-bar-item-icon';
27368 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27372 cls: 'roo-navigation-bar-item',
27382 cfg.cls += ' active';
27385 cfg.cls += ' disabled';
27391 disable : function()
27393 this.setDisabled(true);
27396 enable : function()
27398 this.setDisabled(false);
27401 initEvents: function()
27403 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27405 this.iconEl.on('click', this.onClick, this);
27408 onClick : function(e)
27410 e.preventDefault();
27416 if(this.fireEvent('click', this, e) === false){
27420 this.parent().setActiveItem(this);
27423 isActive: function ()
27425 return this.active;
27428 setActive : function(state)
27430 if(this.active == state){
27434 this.active = state;
27437 this.el.addClass('active');
27441 this.el.removeClass('active');
27446 setDisabled : function(state)
27448 if(this.disabled == state){
27452 this.disabled = state;
27455 this.el.addClass('disabled');
27459 this.el.removeClass('disabled');
27462 tooltipEl : function()
27464 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27477 * @class Roo.bootstrap.FieldLabel
27478 * @extends Roo.bootstrap.Component
27479 * Bootstrap FieldLabel class
27480 * @cfg {String} html contents of the element
27481 * @cfg {String} tag tag of the element default label
27482 * @cfg {String} cls class of the element
27483 * @cfg {String} target label target
27484 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27485 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27486 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27487 * @cfg {String} iconTooltip default "This field is required"
27490 * Create a new FieldLabel
27491 * @param {Object} config The config object
27494 Roo.bootstrap.FieldLabel = function(config){
27495 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27500 * Fires after the field has been marked as invalid.
27501 * @param {Roo.form.FieldLabel} this
27502 * @param {String} msg The validation message
27507 * Fires after the field has been validated with no errors.
27508 * @param {Roo.form.FieldLabel} this
27514 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27521 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27522 validClass : 'text-success fa fa-lg fa-check',
27523 iconTooltip : 'This field is required',
27525 getAutoCreate : function(){
27529 cls : 'roo-bootstrap-field-label ' + this.cls,
27535 tooltip : this.iconTooltip
27547 initEvents: function()
27549 Roo.bootstrap.Element.superclass.initEvents.call(this);
27551 this.iconEl = this.el.select('i', true).first();
27553 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27555 Roo.bootstrap.FieldLabel.register(this);
27559 * Mark this field as valid
27561 markValid : function()
27563 this.iconEl.show();
27565 this.iconEl.removeClass(this.invalidClass);
27567 this.iconEl.addClass(this.validClass);
27569 this.fireEvent('valid', this);
27573 * Mark this field as invalid
27574 * @param {String} msg The validation message
27576 markInvalid : function(msg)
27578 this.iconEl.show();
27580 this.iconEl.removeClass(this.validClass);
27582 this.iconEl.addClass(this.invalidClass);
27584 this.fireEvent('invalid', this, msg);
27590 Roo.apply(Roo.bootstrap.FieldLabel, {
27595 * register a FieldLabel Group
27596 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27598 register : function(label)
27600 if(this.groups.hasOwnProperty(label.target)){
27604 this.groups[label.target] = label;
27608 * fetch a FieldLabel Group based on the target
27609 * @param {string} target
27610 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27612 get: function(target) {
27613 if (typeof(this.groups[target]) == 'undefined') {
27617 return this.groups[target] ;
27626 * page DateSplitField.
27632 * @class Roo.bootstrap.DateSplitField
27633 * @extends Roo.bootstrap.Component
27634 * Bootstrap DateSplitField class
27635 * @cfg {string} fieldLabel - the label associated
27636 * @cfg {Number} labelWidth set the width of label (0-12)
27637 * @cfg {String} labelAlign (top|left)
27638 * @cfg {Boolean} dayAllowBlank (true|false) default false
27639 * @cfg {Boolean} monthAllowBlank (true|false) default false
27640 * @cfg {Boolean} yearAllowBlank (true|false) default false
27641 * @cfg {string} dayPlaceholder
27642 * @cfg {string} monthPlaceholder
27643 * @cfg {string} yearPlaceholder
27644 * @cfg {string} dayFormat default 'd'
27645 * @cfg {string} monthFormat default 'm'
27646 * @cfg {string} yearFormat default 'Y'
27650 * Create a new DateSplitField
27651 * @param {Object} config The config object
27654 Roo.bootstrap.DateSplitField = function(config){
27655 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27661 * getting the data of years
27662 * @param {Roo.bootstrap.DateSplitField} this
27663 * @param {Object} years
27668 * getting the data of days
27669 * @param {Roo.bootstrap.DateSplitField} this
27670 * @param {Object} days
27675 * Fires after the field has been marked as invalid.
27676 * @param {Roo.form.Field} this
27677 * @param {String} msg The validation message
27682 * Fires after the field has been validated with no errors.
27683 * @param {Roo.form.Field} this
27689 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27692 labelAlign : 'top',
27694 dayAllowBlank : false,
27695 monthAllowBlank : false,
27696 yearAllowBlank : false,
27697 dayPlaceholder : '',
27698 monthPlaceholder : '',
27699 yearPlaceholder : '',
27703 isFormField : true,
27705 getAutoCreate : function()
27709 cls : 'row roo-date-split-field-group',
27714 cls : 'form-hidden-field roo-date-split-field-group-value',
27720 if(this.fieldLabel){
27723 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27727 html : this.fieldLabel
27733 Roo.each(['day', 'month', 'year'], function(t){
27736 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27743 inputEl: function ()
27745 return this.el.select('.roo-date-split-field-group-value', true).first();
27748 onRender : function(ct, position)
27752 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27754 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27756 this.dayField = new Roo.bootstrap.ComboBox({
27757 allowBlank : this.dayAllowBlank,
27758 alwaysQuery : true,
27759 displayField : 'value',
27762 forceSelection : true,
27764 placeholder : this.dayPlaceholder,
27765 selectOnFocus : true,
27766 tpl : '<div class="select2-result"><b>{value}</b></div>',
27767 triggerAction : 'all',
27769 valueField : 'value',
27770 store : new Roo.data.SimpleStore({
27771 data : (function() {
27773 _this.fireEvent('days', _this, days);
27776 fields : [ 'value' ]
27779 select : function (_self, record, index)
27781 _this.setValue(_this.getValue());
27786 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27788 this.monthField = new Roo.bootstrap.MonthField({
27789 after : '<i class=\"fa fa-calendar\"></i>',
27790 allowBlank : this.monthAllowBlank,
27791 placeholder : this.monthPlaceholder,
27794 render : function (_self)
27796 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27797 e.preventDefault();
27801 select : function (_self, oldvalue, newvalue)
27803 _this.setValue(_this.getValue());
27808 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27810 this.yearField = new Roo.bootstrap.ComboBox({
27811 allowBlank : this.yearAllowBlank,
27812 alwaysQuery : true,
27813 displayField : 'value',
27816 forceSelection : true,
27818 placeholder : this.yearPlaceholder,
27819 selectOnFocus : true,
27820 tpl : '<div class="select2-result"><b>{value}</b></div>',
27821 triggerAction : 'all',
27823 valueField : 'value',
27824 store : new Roo.data.SimpleStore({
27825 data : (function() {
27827 _this.fireEvent('years', _this, years);
27830 fields : [ 'value' ]
27833 select : function (_self, record, index)
27835 _this.setValue(_this.getValue());
27840 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27843 setValue : function(v, format)
27845 this.inputEl.dom.value = v;
27847 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27849 var d = Date.parseDate(v, f);
27856 this.setDay(d.format(this.dayFormat));
27857 this.setMonth(d.format(this.monthFormat));
27858 this.setYear(d.format(this.yearFormat));
27865 setDay : function(v)
27867 this.dayField.setValue(v);
27868 this.inputEl.dom.value = this.getValue();
27873 setMonth : function(v)
27875 this.monthField.setValue(v, true);
27876 this.inputEl.dom.value = this.getValue();
27881 setYear : function(v)
27883 this.yearField.setValue(v);
27884 this.inputEl.dom.value = this.getValue();
27889 getDay : function()
27891 return this.dayField.getValue();
27894 getMonth : function()
27896 return this.monthField.getValue();
27899 getYear : function()
27901 return this.yearField.getValue();
27904 getValue : function()
27906 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27908 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27918 this.inputEl.dom.value = '';
27923 validate : function()
27925 var d = this.dayField.validate();
27926 var m = this.monthField.validate();
27927 var y = this.yearField.validate();
27932 (!this.dayAllowBlank && !d) ||
27933 (!this.monthAllowBlank && !m) ||
27934 (!this.yearAllowBlank && !y)
27939 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27948 this.markInvalid();
27953 markValid : function()
27956 var label = this.el.select('label', true).first();
27957 var icon = this.el.select('i.fa-star', true).first();
27963 this.fireEvent('valid', this);
27967 * Mark this field as invalid
27968 * @param {String} msg The validation message
27970 markInvalid : function(msg)
27973 var label = this.el.select('label', true).first();
27974 var icon = this.el.select('i.fa-star', true).first();
27976 if(label && !icon){
27977 this.el.select('.roo-date-split-field-label', true).createChild({
27979 cls : 'text-danger fa fa-lg fa-star',
27980 tooltip : 'This field is required',
27981 style : 'margin-right:5px;'
27985 this.fireEvent('invalid', this, msg);
27988 clearInvalid : function()
27990 var label = this.el.select('label', true).first();
27991 var icon = this.el.select('i.fa-star', true).first();
27997 this.fireEvent('valid', this);
28000 getName: function()