4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1973 this.el.on('touchstart' , this.onTouch, this);
1975 this.el.on('click' , this.onClick, this);
1977 this.el.on("mouseover", this.onMouseOver, this);
1978 this.el.on("mouseout", this.onMouseOut, this);
1982 findTargetItem : function(e){
1983 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1987 //Roo.log(t); Roo.log(t.id);
1989 //Roo.log(this.menuitems);
1990 return this.menuitems.get(t.id);
1992 //return this.items.get(t.menuItemId);
1998 onTouch : function(e) {
2003 onClick : function(e){
2004 Roo.log("menu.onClick");
2005 var t = this.findTargetItem(e);
2006 if(!t || t.isContainer){
2011 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2012 if(t == this.activeItem && t.shouldDeactivate(e)){
2013 this.activeItem.deactivate();
2014 delete this.activeItem;
2018 this.setActiveItem(t, true);
2026 Roo.log('pass click event');
2030 this.fireEvent("click", this, t, e);
2034 onMouseOver : function(e){
2035 var t = this.findTargetItem(e);
2038 // if(t.canActivate && !t.disabled){
2039 // this.setActiveItem(t, true);
2043 this.fireEvent("mouseover", this, e, t);
2045 isVisible : function(){
2046 return !this.hidden;
2048 onMouseOut : function(e){
2049 var t = this.findTargetItem(e);
2052 // if(t == this.activeItem && t.shouldDeactivate(e)){
2053 // this.activeItem.deactivate();
2054 // delete this.activeItem;
2057 this.fireEvent("mouseout", this, e, t);
2062 * Displays this menu relative to another element
2063 * @param {String/HTMLElement/Roo.Element} element The element to align to
2064 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065 * the element (defaults to this.defaultAlign)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 show : function(el, pos, parentMenu){
2069 this.parentMenu = parentMenu;
2073 this.fireEvent("beforeshow", this);
2074 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2077 * Displays this menu at a specific xy position
2078 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2081 showAt : function(xy, parentMenu, /* private: */_e){
2082 this.parentMenu = parentMenu;
2087 this.fireEvent("beforeshow", this);
2088 //xy = this.el.adjustForConstraints(xy);
2092 this.hideMenuItems();
2093 this.hidden = false;
2094 this.triggerEl.addClass('open');
2096 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2102 this.fireEvent("show", this);
2108 this.doFocus.defer(50, this);
2112 doFocus : function(){
2114 this.focusEl.focus();
2119 * Hides this menu and optionally all parent menus
2120 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2122 hide : function(deep){
2124 this.hideMenuItems();
2125 if(this.el && this.isVisible()){
2126 this.fireEvent("beforehide", this);
2127 if(this.activeItem){
2128 this.activeItem.deactivate();
2129 this.activeItem = null;
2131 this.triggerEl.removeClass('open');;
2133 this.fireEvent("hide", this);
2135 if(deep === true && this.parentMenu){
2136 this.parentMenu.hide(true);
2140 onTriggerPress : function(e)
2143 Roo.log('trigger press');
2144 //Roo.log(e.getTarget());
2145 // Roo.log(this.triggerEl.dom);
2146 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2150 if (this.isVisible()) {
2155 this.show(this.triggerEl, false, false);
2164 hideMenuItems : function()
2166 //$(backdrop).remove()
2167 Roo.select('.open',true).each(function(aa) {
2169 aa.removeClass('open');
2170 //var parent = getParent($(this))
2171 //var relatedTarget = { relatedTarget: this }
2173 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174 //if (e.isDefaultPrevented()) return
2175 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2178 addxtypeChild : function (tree, cntr) {
2179 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2181 this.menuitems.add(comp);
2202 * @class Roo.bootstrap.MenuItem
2203 * @extends Roo.bootstrap.Component
2204 * Bootstrap MenuItem class
2205 * @cfg {String} html the menu label
2206 * @cfg {String} href the link
2207 * @cfg {Boolean} preventDefault (true | false) default true
2208 * @cfg {Boolean} isContainer (true | false) default false
2212 * Create a new MenuItem
2213 * @param {Object} config The config object
2217 Roo.bootstrap.MenuItem = function(config){
2218 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2223 * The raw click event for the entire grid.
2224 * @param {Roo.bootstrap.MenuItem} this
2225 * @param {Roo.EventObject} e
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2235 preventDefault: true,
2236 isContainer : false,
2238 getAutoCreate : function(){
2240 if(this.isContainer){
2243 cls: 'dropdown-menu-item'
2249 cls: 'dropdown-menu-item',
2258 if (this.parent().type == 'treeview') {
2259 cfg.cls = 'treeview-menu';
2262 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2267 initEvents: function() {
2269 //this.el.select('a').on('click', this.onClick, this);
2272 onClick : function(e)
2274 Roo.log('item on click ');
2275 //if(this.preventDefault){
2276 // e.preventDefault();
2278 //this.parent().hideMenuItems();
2280 this.fireEvent('click', this, e);
2299 * @class Roo.bootstrap.MenuSeparator
2300 * @extends Roo.bootstrap.Component
2301 * Bootstrap MenuSeparator class
2304 * Create a new MenuItem
2305 * @param {Object} config The config object
2309 Roo.bootstrap.MenuSeparator = function(config){
2310 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2315 getAutoCreate : function(){
2334 * @class Roo.bootstrap.Modal
2335 * @extends Roo.bootstrap.Component
2336 * Bootstrap Modal class
2337 * @cfg {String} title Title of dialog
2338 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2340 * @cfg {Boolean} specificTitle default false
2341 * @cfg {Array} buttons Array of buttons or standard button set..
2342 * @cfg {String} buttonPosition (left|right|center) default right
2343 * @cfg {Boolean} animate default true
2344 * @cfg {Boolean} allow_close default true
2347 * Create a new Modal Dialog
2348 * @param {Object} config The config object
2351 Roo.bootstrap.Modal = function(config){
2352 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2357 * The raw btnclick event for the button
2358 * @param {Roo.EventObject} e
2362 this.buttons = this.buttons || [];
2365 this.tmpl = Roo.factory(this.tmpl);
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2372 title : 'test dialog',
2382 specificTitle: false,
2384 buttonPosition: 'right',
2398 onRender : function(ct, position)
2400 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2403 var cfg = Roo.apply({}, this.getAutoCreate());
2406 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2408 //if (!cfg.name.length) {
2412 cfg.cls += ' ' + this.cls;
2415 cfg.style = this.style;
2417 this.el = Roo.get(document.body).createChild(cfg, position);
2419 //var type = this.el.dom.type;
2424 if(this.tabIndex !== undefined){
2425 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2429 this.bodyEl = this.el.select('.modal-body',true).first();
2430 this.closeEl = this.el.select('.modal-header .close', true).first();
2431 this.footerEl = this.el.select('.modal-footer',true).first();
2432 this.titleEl = this.el.select('.modal-title',true).first();
2436 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437 this.maskEl.enableDisplayMode("block");
2439 //this.el.addClass("x-dlg-modal");
2441 if (this.buttons.length) {
2442 Roo.each(this.buttons, function(bb) {
2443 var b = Roo.apply({}, bb);
2444 b.xns = b.xns || Roo.bootstrap;
2445 b.xtype = b.xtype || 'Button';
2446 if (typeof(b.listeners) == 'undefined') {
2447 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2450 var btn = Roo.factory(b);
2452 btn.onRender(this.el.select('.modal-footer div').first());
2456 // render the children.
2459 if(typeof(this.items) != 'undefined'){
2460 var items = this.items;
2463 for(var i =0;i < items.length;i++) {
2464 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2468 this.items = nitems;
2470 // where are these used - they used to be body/close/footer
2474 //this.el.addClass([this.fieldClass, this.cls]);
2478 getAutoCreate : function(){
2483 html : this.html || ''
2488 cls : 'modal-title',
2492 if(this.specificTitle){
2498 if (this.allow_close) {
2509 style : 'display: none',
2512 cls: "modal-dialog",
2515 cls : "modal-content",
2518 cls : 'modal-header',
2523 cls : 'modal-footer',
2527 cls: 'btn-' + this.buttonPosition
2544 modal.cls += ' fade';
2550 getChildContainer : function() {
2555 getButtonContainer : function() {
2556 return this.el.select('.modal-footer div',true).first();
2559 initEvents : function()
2561 if (this.allow_close) {
2562 this.closeEl.on('click', this.hide, this);
2567 window.addEventListener("resize", function() { _this.resize(); } );
2573 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2578 if (!this.rendered) {
2582 this.el.setStyle('display', 'block');
2586 (function(){ _this.el.addClass('in'); }).defer(50);
2588 this.el.addClass('in');
2591 // not sure how we can show data in here..
2593 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2596 Roo.get(document.body).addClass("x-body-masked");
2597 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2599 this.el.setStyle('zIndex', '10001');
2601 this.fireEvent('show', this);
2608 Roo.get(document.body).removeClass("x-body-masked");
2609 this.el.removeClass('in');
2613 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2615 this.el.setStyle('display', 'none');
2618 this.fireEvent('hide', this);
2621 addButton : function(str, cb)
2625 var b = Roo.apply({}, { html : str } );
2626 b.xns = b.xns || Roo.bootstrap;
2627 b.xtype = b.xtype || 'Button';
2628 if (typeof(b.listeners) == 'undefined') {
2629 b.listeners = { click : cb.createDelegate(this) };
2632 var btn = Roo.factory(b);
2634 btn.onRender(this.el.select('.modal-footer div').first());
2640 setDefaultButton : function(btn)
2642 //this.el.select('.modal-footer').()
2644 resizeTo: function(w,h)
2648 setContentSize : function(w, h)
2652 onButtonClick: function(btn,e)
2655 this.fireEvent('btnclick', btn.name, e);
2658 * Set the title of the Dialog
2659 * @param {String} str new Title
2661 setTitle: function(str) {
2662 this.titleEl.dom.innerHTML = str;
2665 * Set the body of the Dialog
2666 * @param {String} str new Title
2668 setBody: function(str) {
2669 this.bodyEl.dom.innerHTML = str;
2672 * Set the body of the Dialog using the template
2673 * @param {Obj} data - apply this data to the template and replace the body contents.
2675 applyBody: function(obj)
2678 Roo.log("Error - using apply Body without a template");
2681 this.tmpl.overwrite(this.bodyEl, obj);
2687 Roo.apply(Roo.bootstrap.Modal, {
2689 * Button config that displays a single OK button
2698 * Button config that displays Yes and No buttons
2714 * Button config that displays OK and Cancel buttons
2729 * Button config that displays Yes, No and Cancel buttons
2752 * messagebox - can be used as a replace
2756 * @class Roo.MessageBox
2757 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2766 // process text value...
2770 // Show a dialog using config options:
2772 title:'Save Changes?',
2773 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774 buttons: Roo.Msg.YESNOCANCEL,
2781 Roo.bootstrap.MessageBox = function(){
2782 var dlg, opt, mask, waitTimer;
2783 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784 var buttons, activeTextEl, bwidth;
2788 var handleButton = function(button){
2790 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2794 var handleHide = function(){
2796 dlg.el.removeClass(opt.cls);
2799 // Roo.TaskMgr.stop(waitTimer);
2800 // waitTimer = null;
2805 var updateButtons = function(b){
2808 buttons["ok"].hide();
2809 buttons["cancel"].hide();
2810 buttons["yes"].hide();
2811 buttons["no"].hide();
2812 //dlg.footer.dom.style.display = 'none';
2815 dlg.footerEl.dom.style.display = '';
2816 for(var k in buttons){
2817 if(typeof buttons[k] != "function"){
2820 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821 width += buttons[k].el.getWidth()+15;
2831 var handleEsc = function(d, k, e){
2832 if(opt && opt.closable !== false){
2842 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843 * @return {Roo.BasicDialog} The BasicDialog element
2845 getDialog : function(){
2847 dlg = new Roo.bootstrap.Modal( {
2850 //constraintoviewport:false,
2852 //collapsible : false,
2857 //buttonAlign:"center",
2858 closeClick : function(){
2859 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2862 handleButton("cancel");
2867 dlg.on("hide", handleHide);
2869 //dlg.addKeyListener(27, handleEsc);
2871 this.buttons = buttons;
2872 var bt = this.buttonText;
2873 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2878 bodyEl = dlg.bodyEl.createChild({
2880 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881 '<textarea class="roo-mb-textarea"></textarea>' +
2882 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2884 msgEl = bodyEl.dom.firstChild;
2885 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886 textboxEl.enableDisplayMode();
2887 textboxEl.addKeyListener([10,13], function(){
2888 if(dlg.isVisible() && opt && opt.buttons){
2891 }else if(opt.buttons.yes){
2892 handleButton("yes");
2896 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897 textareaEl.enableDisplayMode();
2898 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899 progressEl.enableDisplayMode();
2900 var pf = progressEl.dom.firstChild;
2902 pp = Roo.get(pf.firstChild);
2903 pp.setHeight(pf.offsetHeight);
2911 * Updates the message box body text
2912 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913 * the XHTML-compliant non-breaking space character '&#160;')
2914 * @return {Roo.MessageBox} This message box
2916 updateText : function(text){
2917 if(!dlg.isVisible() && !opt.width){
2918 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2920 msgEl.innerHTML = text || ' ';
2922 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2925 Math.min(opt.width || cw , this.maxWidth),
2926 Math.max(opt.minWidth || this.minWidth, bwidth)
2929 activeTextEl.setWidth(w);
2931 if(dlg.isVisible()){
2932 dlg.fixedcenter = false;
2934 // to big, make it scroll. = But as usual stupid IE does not support
2937 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2941 bodyEl.dom.style.height = '';
2942 bodyEl.dom.style.overflowY = '';
2945 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2947 bodyEl.dom.style.overflowX = '';
2950 dlg.setContentSize(w, bodyEl.getHeight());
2951 if(dlg.isVisible()){
2952 dlg.fixedcenter = true;
2958 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2959 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962 * @return {Roo.MessageBox} This message box
2964 updateProgress : function(value, text){
2966 this.updateText(text);
2968 if (pp) { // weird bug on my firefox - for some reason this is not defined
2969 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2975 * Returns true if the message box is currently displayed
2976 * @return {Boolean} True if the message box is visible, else false
2978 isVisible : function(){
2979 return dlg && dlg.isVisible();
2983 * Hides the message box if it is displayed
2986 if(this.isVisible()){
2992 * Displays a new message box, or reinitializes an existing message box, based on the config options
2993 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994 * The following config object properties are supported:
2996 Property Type Description
2997 ---------- --------------- ------------------------------------------------------------------------------------
2998 animEl String/Element An id or Element from which the message box should animate as it opens and
2999 closes (defaults to undefined)
3000 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable Boolean False to hide the top-right close button (defaults to true). Note that
3003 progress and wait dialogs will ignore this property and always hide the
3004 close button as they can only be closed programmatically.
3005 cls String A custom CSS class to apply to the message box element
3006 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3007 displayed (defaults to 75)
3008 fn Function A callback function to execute after closing the dialog. The arguments to the
3009 function will be btn (the name of the button that was clicked, if applicable,
3010 e.g. "ok"), and text (the value of the active text field, if applicable).
3011 Progress and wait dialogs will ignore this option since they do not respond to
3012 user actions and can only be closed programmatically, so any required function
3013 should be called by the same code after it closes the dialog.
3014 icon String A CSS class that provides a background image to be used as an icon for
3015 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3017 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3018 modal Boolean False to allow user interaction with the page while the message box is
3019 displayed (defaults to true)
3020 msg String A string that will replace the existing message box body text (defaults
3021 to the XHTML-compliant non-breaking space character ' ')
3022 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3023 progress Boolean True to display a progress bar (defaults to false)
3024 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3027 title String The title text
3028 value String The string value to set into the active textbox element if displayed
3029 wait Boolean True to display a progress bar (defaults to false)
3030 width Number The width of the dialog in pixels
3037 msg: 'Please enter your address:',
3039 buttons: Roo.MessageBox.OKCANCEL,
3042 animEl: 'addAddressBtn'
3045 * @param {Object} config Configuration options
3046 * @return {Roo.MessageBox} This message box
3048 show : function(options)
3051 // this causes nightmares if you show one dialog after another
3052 // especially on callbacks..
3054 if(this.isVisible()){
3057 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3059 Roo.log("New Dialog Message:" + options.msg )
3060 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3064 var d = this.getDialog();
3066 d.setTitle(opt.title || " ");
3067 d.closeEl.setDisplayed(opt.closable !== false);
3068 activeTextEl = textboxEl;
3069 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3074 textareaEl.setHeight(typeof opt.multiline == "number" ?
3075 opt.multiline : this.defaultTextHeight);
3076 activeTextEl = textareaEl;
3085 progressEl.setDisplayed(opt.progress === true);
3086 this.updateProgress(0);
3087 activeTextEl.dom.value = opt.value || "";
3089 dlg.setDefaultButton(activeTextEl);
3091 var bs = opt.buttons;
3095 }else if(bs && bs.yes){
3096 db = buttons["yes"];
3098 dlg.setDefaultButton(db);
3100 bwidth = updateButtons(opt.buttons);
3101 this.updateText(opt.msg);
3103 d.el.addClass(opt.cls);
3105 d.proxyDrag = opt.proxyDrag === true;
3106 d.modal = opt.modal !== false;
3107 d.mask = opt.modal !== false ? mask : false;
3109 // force it to the end of the z-index stack so it gets a cursor in FF
3110 document.body.appendChild(dlg.el.dom);
3111 d.animateTarget = null;
3112 d.show(options.animEl);
3118 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3119 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120 * and closing the message box when the process is complete.
3121 * @param {String} title The title bar text
3122 * @param {String} msg The message box body text
3123 * @return {Roo.MessageBox} This message box
3125 progress : function(title, msg){
3132 minWidth: this.minProgressWidth,
3139 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140 * If a callback function is passed it will be called after the user clicks the button, and the
3141 * id of the button that was clicked will be passed as the only parameter to the callback
3142 * (could also be the top-right close button).
3143 * @param {String} title The title bar text
3144 * @param {String} msg The message box body text
3145 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146 * @param {Object} scope (optional) The scope of the callback function
3147 * @return {Roo.MessageBox} This message box
3149 alert : function(title, msg, fn, scope){
3162 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3163 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164 * You are responsible for closing the message box when the process is complete.
3165 * @param {String} msg The message box body text
3166 * @param {String} title (optional) The title bar text
3167 * @return {Roo.MessageBox} This message box
3169 wait : function(msg, title){
3180 waitTimer = Roo.TaskMgr.start({
3182 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3190 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193 * @param {String} title The title bar text
3194 * @param {String} msg The message box body text
3195 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196 * @param {Object} scope (optional) The scope of the callback function
3197 * @return {Roo.MessageBox} This message box
3199 confirm : function(title, msg, fn, scope){
3203 buttons: this.YESNO,
3212 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3214 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215 * (could also be the top-right close button) and the text that was entered will be passed as the two
3216 * parameters to the callback.
3217 * @param {String} title The title bar text
3218 * @param {String} msg The message box body text
3219 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220 * @param {Object} scope (optional) The scope of the callback function
3221 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223 * @return {Roo.MessageBox} This message box
3225 prompt : function(title, msg, fn, scope, multiline){
3229 buttons: this.OKCANCEL,
3234 multiline: multiline,
3241 * Button config that displays a single OK button
3246 * Button config that displays Yes and No buttons
3249 YESNO : {yes:true, no:true},
3251 * Button config that displays OK and Cancel buttons
3254 OKCANCEL : {ok:true, cancel:true},
3256 * Button config that displays Yes, No and Cancel buttons
3259 YESNOCANCEL : {yes:true, no:true, cancel:true},
3262 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3265 defaultTextHeight : 75,
3267 * The maximum width in pixels of the message box (defaults to 600)
3272 * The minimum width in pixels of the message box (defaults to 100)
3277 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3278 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3281 minProgressWidth : 250,
3283 * An object containing the default button text strings that can be overriden for localized language support.
3284 * Supported properties are: ok, cancel, yes and no.
3285 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3298 * Shorthand for {@link Roo.MessageBox}
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3310 * @class Roo.bootstrap.Navbar
3311 * @extends Roo.bootstrap.Component
3312 * Bootstrap Navbar class
3315 * Create a new Navbar
3316 * @param {Object} config The config object
3320 Roo.bootstrap.Navbar = function(config){
3321 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3334 getAutoCreate : function(){
3337 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3341 initEvents :function ()
3343 //Roo.log(this.el.select('.navbar-toggle',true));
3344 this.el.select('.navbar-toggle',true).on('click', function() {
3345 // Roo.log('click');
3346 this.el.select('.navbar-collapse',true).toggleClass('in');
3354 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3356 var size = this.el.getSize();
3357 this.maskEl.setSize(size.width, size.height);
3358 this.maskEl.enableDisplayMode("block");
3367 getChildContainer : function()
3369 if (this.el.select('.collapse').getCount()) {
3370 return this.el.select('.collapse',true).first();
3403 * @class Roo.bootstrap.NavSimplebar
3404 * @extends Roo.bootstrap.Navbar
3405 * Bootstrap Sidebar class
3407 * @cfg {Boolean} inverse is inverted color
3409 * @cfg {String} type (nav | pills | tabs)
3410 * @cfg {Boolean} arrangement stacked | justified
3411 * @cfg {String} align (left | right) alignment
3413 * @cfg {Boolean} main (true|false) main nav bar? default false
3414 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3416 * @cfg {String} tag (header|footer|nav|div) default is nav
3422 * Create a new Sidebar
3423 * @param {Object} config The config object
3427 Roo.bootstrap.NavSimplebar = function(config){
3428 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3447 getAutoCreate : function(){
3451 tag : this.tag || 'div',
3464 this.type = this.type || 'nav';
3465 if (['tabs','pills'].indexOf(this.type)!==-1) {
3466 cfg.cn[0].cls += ' nav-' + this.type
3470 if (this.type!=='nav') {
3471 Roo.log('nav type must be nav/tabs/pills')
3473 cfg.cn[0].cls += ' navbar-nav'
3479 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480 cfg.cn[0].cls += ' nav-' + this.arrangement;
3484 if (this.align === 'right') {
3485 cfg.cn[0].cls += ' navbar-right';
3489 cfg.cls += ' navbar-inverse';
3516 * @class Roo.bootstrap.NavHeaderbar
3517 * @extends Roo.bootstrap.NavSimplebar
3518 * Bootstrap Sidebar class
3520 * @cfg {String} brand what is brand
3521 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522 * @cfg {String} brand_href href of the brand
3523 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3524 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3529 * Create a new Sidebar
3530 * @param {Object} config The config object
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3546 desktopCenter : false,
3549 getAutoCreate : function(){
3552 tag: this.nav || 'nav',
3559 if (this.desktopCenter) {
3560 cn.push({cls : 'container', cn : []});
3567 cls: 'navbar-header',
3572 cls: 'navbar-toggle',
3573 'data-toggle': 'collapse',
3578 html: 'Toggle navigation'
3600 cls: 'collapse navbar-collapse',
3604 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3606 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607 cfg.cls += ' navbar-' + this.position;
3609 // tag can override this..
3611 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3614 if (this.brand !== '') {
3617 href: this.brand_href ? this.brand_href : '#',
3618 cls: 'navbar-brand',
3626 cfg.cls += ' main-nav';
3634 getHeaderChildContainer : function()
3636 if (this.el.select('.navbar-header').getCount()) {
3637 return this.el.select('.navbar-header',true).first();
3640 return this.getChildContainer();
3644 initEvents : function()
3646 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3648 if (this.autohide) {
3653 Roo.get(document).on('scroll',function(e) {
3654 var ns = Roo.get(document).getScroll().top;
3655 var os = prevScroll;
3659 ft.removeClass('slideDown');
3660 ft.addClass('slideUp');
3663 ft.removeClass('slideUp');
3664 ft.addClass('slideDown');
3685 * @class Roo.bootstrap.NavSidebar
3686 * @extends Roo.bootstrap.Navbar
3687 * Bootstrap Sidebar class
3690 * Create a new Sidebar
3691 * @param {Object} config The config object
3695 Roo.bootstrap.NavSidebar = function(config){
3696 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3701 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3703 getAutoCreate : function(){
3708 cls: 'sidebar sidebar-nav'
3730 * @class Roo.bootstrap.NavGroup
3731 * @extends Roo.bootstrap.Component
3732 * Bootstrap NavGroup class
3733 * @cfg {String} align (left|right)
3734 * @cfg {Boolean} inverse
3735 * @cfg {String} type (nav|pills|tab) default nav
3736 * @cfg {String} navId - reference Id for navbar.
3740 * Create a new nav group
3741 * @param {Object} config The config object
3744 Roo.bootstrap.NavGroup = function(config){
3745 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3748 Roo.bootstrap.NavGroup.register(this);
3752 * Fires when the active item changes
3753 * @param {Roo.bootstrap.NavGroup} this
3754 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3773 getAutoCreate : function()
3775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3782 if (['tabs','pills'].indexOf(this.type)!==-1) {
3783 cfg.cls += ' nav-' + this.type
3785 if (this.type!=='nav') {
3786 Roo.log('nav type must be nav/tabs/pills')
3788 cfg.cls += ' navbar-nav'
3791 if (this.parent().sidebar) {
3794 cls: 'dashboard-menu sidebar-menu'
3800 if (this.form === true) {
3806 if (this.align === 'right') {
3807 cfg.cls += ' navbar-right';
3809 cfg.cls += ' navbar-left';
3813 if (this.align === 'right') {
3814 cfg.cls += ' navbar-right';
3818 cfg.cls += ' navbar-inverse';
3826 * sets the active Navigation item
3827 * @param {Roo.bootstrap.NavItem} the new current navitem
3829 setActiveItem : function(item)
3832 Roo.each(this.navItems, function(v){
3837 v.setActive(false, true);
3844 item.setActive(true, true);
3845 this.fireEvent('changed', this, item, prev);
3850 * gets the active Navigation item
3851 * @return {Roo.bootstrap.NavItem} the current navitem
3853 getActive : function()
3857 Roo.each(this.navItems, function(v){
3868 indexOfNav : function()
3872 Roo.each(this.navItems, function(v,i){
3883 * adds a Navigation item
3884 * @param {Roo.bootstrap.NavItem} the navitem to add
3886 addItem : function(cfg)
3888 var cn = new Roo.bootstrap.NavItem(cfg);
3890 cn.parentId = this.id;
3891 cn.onRender(this.el, null);
3895 * register a Navigation item
3896 * @param {Roo.bootstrap.NavItem} the navitem to add
3898 register : function(item)
3900 this.navItems.push( item);
3901 item.navId = this.navId;
3906 * clear all the Navigation item
3909 clearAll : function()
3912 this.el.dom.innerHTML = '';
3915 getNavItem: function(tabId)
3918 Roo.each(this.navItems, function(e) {
3919 if (e.tabId == tabId) {
3929 setActiveNext : function()
3931 var i = this.indexOfNav(this.getActive());
3932 if (i > this.navItems.length) {
3935 this.setActiveItem(this.navItems[i+1]);
3937 setActivePrev : function()
3939 var i = this.indexOfNav(this.getActive());
3943 this.setActiveItem(this.navItems[i-1]);
3945 clearWasActive : function(except) {
3946 Roo.each(this.navItems, function(e) {
3947 if (e.tabId != except.tabId && e.was_active) {
3948 e.was_active = false;
3955 getWasActive : function ()
3958 Roo.each(this.navItems, function(e) {
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3977 * register a Navigation Group
3978 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3980 register : function(navgrp)
3982 this.groups[navgrp.navId] = navgrp;
3986 * fetch a Navigation Group based on the navigation ID
3987 * @param {string} the navgroup to add
3988 * @returns {Roo.bootstrap.NavGroup} the navgroup
3990 get: function(navId) {
3991 if (typeof(this.groups[navId]) == 'undefined') {
3993 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3995 return this.groups[navId] ;
4010 * @class Roo.bootstrap.NavItem
4011 * @extends Roo.bootstrap.Component
4012 * Bootstrap Navbar.NavItem class
4013 * @cfg {String} href link to
4014 * @cfg {String} html content of button
4015 * @cfg {String} badge text inside badge
4016 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017 * @cfg {String} glyphicon name of glyphicon
4018 * @cfg {String} icon name of font awesome icon
4019 * @cfg {Boolean} active Is item active
4020 * @cfg {Boolean} disabled Is item disabled
4022 * @cfg {Boolean} preventDefault (true | false) default false
4023 * @cfg {String} tabId the tab that this item activates.
4024 * @cfg {String} tagtype (a|span) render as a href or span?
4025 * @cfg {Boolean} animateRef (true|false) link to element default false
4028 * Create a new Navbar Item
4029 * @param {Object} config The config object
4031 Roo.bootstrap.NavItem = function(config){
4032 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4037 * The raw click event for the entire grid.
4038 * @param {Roo.EventObject} e
4043 * Fires when the active item active state changes
4044 * @param {Roo.bootstrap.NavItem} this
4045 * @param {boolean} state the new state
4051 * Fires when scroll to element
4052 * @param {Roo.bootstrap.NavItem} this
4053 * @param {Object} options
4054 * @param {Roo.EventObject} e
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4070 preventDefault : false,
4077 getAutoCreate : function(){
4086 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4088 if (this.disabled) {
4089 cfg.cls += ' disabled';
4092 if (this.href || this.html || this.glyphicon || this.icon) {
4096 href : this.href || "#",
4097 html: this.html || ''
4102 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4105 if(this.glyphicon) {
4106 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4111 cfg.cn[0].html += " <span class='caret'></span>";
4115 if (this.badge !== '') {
4117 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4125 initEvents: function()
4127 if (typeof (this.menu) != 'undefined') {
4128 this.menu.parentType = this.xtype;
4129 this.menu.triggerEl = this.el;
4130 this.menu = this.addxtype(Roo.apply({}, this.menu));
4133 this.el.select('a',true).on('click', this.onClick, this);
4135 if(this.tagtype == 'span'){
4136 this.el.select('span',true).on('click', this.onClick, this);
4139 // at this point parent should be available..
4140 this.parent().register(this);
4143 onClick : function(e)
4146 this.preventDefault ||
4153 if (this.disabled) {
4157 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158 if (tg && tg.transition) {
4159 Roo.log("waiting for the transitionend");
4165 //Roo.log("fire event clicked");
4166 if(this.fireEvent('click', this, e) === false){
4170 if(this.tagtype == 'span'){
4174 //Roo.log(this.href);
4175 var ael = this.el.select('a',true).first();
4178 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181 return; // ignore... - it's a 'hash' to another page.
4185 this.scrollToElement(e);
4189 var p = this.parent();
4191 if (['tabs','pills'].indexOf(p.type)!==-1) {
4192 if (typeof(p.setActiveItem) !== 'undefined') {
4193 p.setActiveItem(this);
4197 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199 // remove the collapsed menu expand...
4200 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4204 isActive: function () {
4207 setActive : function(state, fire, is_was_active)
4209 if (this.active && !state && this.navId) {
4210 this.was_active = true;
4211 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4213 nv.clearWasActive(this);
4217 this.active = state;
4220 this.el.removeClass('active');
4221 } else if (!this.el.hasClass('active')) {
4222 this.el.addClass('active');
4225 this.fireEvent('changed', this, state);
4228 // show a panel if it's registered and related..
4230 if (!this.navId || !this.tabId || !state || is_was_active) {
4234 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4238 var pan = tg.getPanelByName(this.tabId);
4242 // if we can not flip to new panel - go back to old nav highlight..
4243 if (false == tg.showPanel(pan)) {
4244 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4246 var onav = nv.getWasActive();
4248 onav.setActive(true, false, true);
4257 // this should not be here...
4258 setDisabled : function(state)
4260 this.disabled = state;
4262 this.el.removeClass('disabled');
4263 } else if (!this.el.hasClass('disabled')) {
4264 this.el.addClass('disabled');
4270 * Fetch the element to display the tooltip on.
4271 * @return {Roo.Element} defaults to this.el
4273 tooltipEl : function()
4275 return this.el.select('' + this.tagtype + '', true).first();
4278 scrollToElement : function(e)
4280 var c = document.body;
4283 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4285 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286 c = document.documentElement;
4289 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4295 var o = target.calcOffsetsTo(c);
4302 this.fireEvent('scrollto', this, options, e);
4304 Roo.get(c).scrollTo('top', options.value, true);
4317 * <span> icon </span>
4318 * <span> text </span>
4319 * <span>badge </span>
4323 * @class Roo.bootstrap.NavSidebarItem
4324 * @extends Roo.bootstrap.NavItem
4325 * Bootstrap Navbar.NavSidebarItem class
4326 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4328 * Create a new Navbar Button
4329 * @param {Object} config The config object
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4337 * The raw click event for the entire grid.
4338 * @param {Roo.EventObject} e
4343 * Fires when the active item active state changes
4344 * @param {Roo.bootstrap.NavSidebarItem} this
4345 * @param {boolean} state the new state
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4355 badgeWeight : 'default',
4357 getAutoCreate : function(){
4362 href : this.href || '#',
4374 html : this.html || ''
4379 cfg.cls += ' active';
4382 if (this.disabled) {
4383 cfg.cls += ' disabled';
4387 if (this.glyphicon || this.icon) {
4388 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4389 a.cn.push({ tag : 'i', cls : c }) ;
4394 if (this.badge !== '') {
4396 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4400 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401 a.cls += 'dropdown-toggle treeview' ;
4412 initEvents : function()
4414 this.el.on('click', this.onClick, this);
4417 if(this.badge !== ''){
4419 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4424 onClick : function(e)
4431 if(this.preventDefault){
4435 this.fireEvent('click', this);
4438 disable : function()
4440 this.setDisabled(true);
4445 this.setDisabled(false);
4448 setDisabled : function(state)
4450 if(this.disabled == state){
4454 this.disabled = state;
4457 this.el.addClass('disabled');
4461 this.el.removeClass('disabled');
4466 setActive : function(state)
4468 if(this.active == state){
4472 this.active = state;
4475 this.el.addClass('active');
4479 this.el.removeClass('active');
4484 isActive: function ()
4489 setBadge : function(str)
4495 this.badgeEl.dom.innerHTML = str;
4512 * @class Roo.bootstrap.Row
4513 * @extends Roo.bootstrap.Component
4514 * Bootstrap Row class (contains columns...)
4518 * @param {Object} config The config object
4521 Roo.bootstrap.Row = function(config){
4522 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4527 getAutoCreate : function(){
4546 * @class Roo.bootstrap.Element
4547 * @extends Roo.bootstrap.Component
4548 * Bootstrap Element class
4549 * @cfg {String} html contents of the element
4550 * @cfg {String} tag tag of the element
4551 * @cfg {String} cls class of the element
4552 * @cfg {Boolean} preventDefault (true|false) default false
4553 * @cfg {Boolean} clickable (true|false) default false
4556 * Create a new Element
4557 * @param {Object} config The config object
4560 Roo.bootstrap.Element = function(config){
4561 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4567 * When a element is chick
4568 * @param {Roo.bootstrap.Element} this
4569 * @param {Roo.EventObject} e
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4580 preventDefault: false,
4583 getAutoCreate : function(){
4594 initEvents: function()
4596 Roo.bootstrap.Element.superclass.initEvents.call(this);
4599 this.el.on('click', this.onClick, this);
4604 onClick : function(e)
4606 if(this.preventDefault){
4610 this.fireEvent('click', this, e);
4613 getValue : function()
4615 return this.el.dom.innerHTML;
4618 setValue : function(value)
4620 this.el.dom.innerHTML = value;
4635 * @class Roo.bootstrap.Pagination
4636 * @extends Roo.bootstrap.Component
4637 * Bootstrap Pagination class
4638 * @cfg {String} size xs | sm | md | lg
4639 * @cfg {Boolean} inverse false | true
4642 * Create a new Pagination
4643 * @param {Object} config The config object
4646 Roo.bootstrap.Pagination = function(config){
4647 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4656 getAutoCreate : function(){
4662 cfg.cls += ' inverse';
4668 cfg.cls += " " + this.cls;
4686 * @class Roo.bootstrap.PaginationItem
4687 * @extends Roo.bootstrap.Component
4688 * Bootstrap PaginationItem class
4689 * @cfg {String} html text
4690 * @cfg {String} href the link
4691 * @cfg {Boolean} preventDefault (true | false) default true
4692 * @cfg {Boolean} active (true | false) default false
4693 * @cfg {Boolean} disabled default false
4697 * Create a new PaginationItem
4698 * @param {Object} config The config object
4702 Roo.bootstrap.PaginationItem = function(config){
4703 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4708 * The raw click event for the entire grid.
4709 * @param {Roo.EventObject} e
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4719 preventDefault: true,
4724 getAutoCreate : function(){
4730 href : this.href ? this.href : '#',
4731 html : this.html ? this.html : ''
4741 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4745 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4751 initEvents: function() {
4753 this.el.on('click', this.onClick, this);
4756 onClick : function(e)
4758 Roo.log('PaginationItem on click ');
4759 if(this.preventDefault){
4767 this.fireEvent('click', this, e);
4783 * @class Roo.bootstrap.Slider
4784 * @extends Roo.bootstrap.Component
4785 * Bootstrap Slider class
4788 * Create a new Slider
4789 * @param {Object} config The config object
4792 Roo.bootstrap.Slider = function(config){
4793 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4798 getAutoCreate : function(){
4802 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4806 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4818 * Ext JS Library 1.1.1
4819 * Copyright(c) 2006-2007, Ext JS, LLC.
4821 * Originally Released Under LGPL - original licence link has changed is not relivant.
4824 * <script type="text/javascript">
4829 * @class Roo.grid.ColumnModel
4830 * @extends Roo.util.Observable
4831 * This is the default implementation of a ColumnModel used by the Grid. It defines
4832 * the columns in the grid.
4835 var colModel = new Roo.grid.ColumnModel([
4836 {header: "Ticker", width: 60, sortable: true, locked: true},
4837 {header: "Company Name", width: 150, sortable: true},
4838 {header: "Market Cap.", width: 100, sortable: true},
4839 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840 {header: "Employees", width: 100, sortable: true, resizable: false}
4845 * The config options listed for this class are options which may appear in each
4846 * individual column definition.
4847 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4849 * @param {Object} config An Array of column config objects. See this class's
4850 * config objects for details.
4852 Roo.grid.ColumnModel = function(config){
4854 * The config passed into the constructor
4856 this.config = config;
4859 // if no id, create one
4860 // if the column does not have a dataIndex mapping,
4861 // map it to the order it is in the config
4862 for(var i = 0, len = config.length; i < len; i++){
4864 if(typeof c.dataIndex == "undefined"){
4867 if(typeof c.renderer == "string"){
4868 c.renderer = Roo.util.Format[c.renderer];
4870 if(typeof c.id == "undefined"){
4873 if(c.editor && c.editor.xtype){
4874 c.editor = Roo.factory(c.editor, Roo.grid);
4876 if(c.editor && c.editor.isFormField){
4877 c.editor = new Roo.grid.GridEditor(c.editor);
4879 this.lookup[c.id] = c;
4883 * The width of columns which have no width specified (defaults to 100)
4886 this.defaultWidth = 100;
4889 * Default sortable of columns which have no sortable specified (defaults to false)
4892 this.defaultSortable = false;
4896 * @event widthchange
4897 * Fires when the width of a column changes.
4898 * @param {ColumnModel} this
4899 * @param {Number} columnIndex The column index
4900 * @param {Number} newWidth The new width
4902 "widthchange": true,
4904 * @event headerchange
4905 * Fires when the text of a header changes.
4906 * @param {ColumnModel} this
4907 * @param {Number} columnIndex The column index
4908 * @param {Number} newText The new header text
4910 "headerchange": true,
4912 * @event hiddenchange
4913 * Fires when a column is hidden or "unhidden".
4914 * @param {ColumnModel} this
4915 * @param {Number} columnIndex The column index
4916 * @param {Boolean} hidden true if hidden, false otherwise
4918 "hiddenchange": true,
4920 * @event columnmoved
4921 * Fires when a column is moved.
4922 * @param {ColumnModel} this
4923 * @param {Number} oldIndex
4924 * @param {Number} newIndex
4926 "columnmoved" : true,
4928 * @event columlockchange
4929 * Fires when a column's locked state is changed
4930 * @param {ColumnModel} this
4931 * @param {Number} colIndex
4932 * @param {Boolean} locked true if locked
4934 "columnlockchange" : true
4936 Roo.grid.ColumnModel.superclass.constructor.call(this);
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4940 * @cfg {String} header The header text to display in the Grid view.
4943 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945 * specified, the column's index is used as an index into the Record's data Array.
4948 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4952 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953 * Defaults to the value of the {@link #defaultSortable} property.
4954 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4957 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4960 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4963 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4966 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4969 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4975 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4978 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4981 * @cfg {String} cursor (Optional)
4984 * @cfg {String} tooltip (Optional)
4987 * @cfg {Number} xs (Optional)
4990 * @cfg {Number} sm (Optional)
4993 * @cfg {Number} md (Optional)
4996 * @cfg {Number} lg (Optional)
4999 * Returns the id of the column at the specified index.
5000 * @param {Number} index The column index
5001 * @return {String} the id
5003 getColumnId : function(index){
5004 return this.config[index].id;
5008 * Returns the column for a specified id.
5009 * @param {String} id The column id
5010 * @return {Object} the column
5012 getColumnById : function(id){
5013 return this.lookup[id];
5018 * Returns the column for a specified dataIndex.
5019 * @param {String} dataIndex The column dataIndex
5020 * @return {Object|Boolean} the column or false if not found
5022 getColumnByDataIndex: function(dataIndex){
5023 var index = this.findColumnIndex(dataIndex);
5024 return index > -1 ? this.config[index] : false;
5028 * Returns the index for a specified column id.
5029 * @param {String} id The column id
5030 * @return {Number} the index, or -1 if not found
5032 getIndexById : function(id){
5033 for(var i = 0, len = this.config.length; i < len; i++){
5034 if(this.config[i].id == id){
5042 * Returns the index for a specified column dataIndex.
5043 * @param {String} dataIndex The column dataIndex
5044 * @return {Number} the index, or -1 if not found
5047 findColumnIndex : function(dataIndex){
5048 for(var i = 0, len = this.config.length; i < len; i++){
5049 if(this.config[i].dataIndex == dataIndex){
5057 moveColumn : function(oldIndex, newIndex){
5058 var c = this.config[oldIndex];
5059 this.config.splice(oldIndex, 1);
5060 this.config.splice(newIndex, 0, c);
5061 this.dataMap = null;
5062 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5065 isLocked : function(colIndex){
5066 return this.config[colIndex].locked === true;
5069 setLocked : function(colIndex, value, suppressEvent){
5070 if(this.isLocked(colIndex) == value){
5073 this.config[colIndex].locked = value;
5075 this.fireEvent("columnlockchange", this, colIndex, value);
5079 getTotalLockedWidth : function(){
5081 for(var i = 0; i < this.config.length; i++){
5082 if(this.isLocked(i) && !this.isHidden(i)){
5083 this.totalWidth += this.getColumnWidth(i);
5089 getLockedCount : function(){
5090 for(var i = 0, len = this.config.length; i < len; i++){
5091 if(!this.isLocked(i)){
5098 * Returns the number of columns.
5101 getColumnCount : function(visibleOnly){
5102 if(visibleOnly === true){
5104 for(var i = 0, len = this.config.length; i < len; i++){
5105 if(!this.isHidden(i)){
5111 return this.config.length;
5115 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116 * @param {Function} fn
5117 * @param {Object} scope (optional)
5118 * @return {Array} result
5120 getColumnsBy : function(fn, scope){
5122 for(var i = 0, len = this.config.length; i < len; i++){
5123 var c = this.config[i];
5124 if(fn.call(scope||this, c, i) === true){
5132 * Returns true if the specified column is sortable.
5133 * @param {Number} col The column index
5136 isSortable : function(col){
5137 if(typeof this.config[col].sortable == "undefined"){
5138 return this.defaultSortable;
5140 return this.config[col].sortable;
5144 * Returns the rendering (formatting) function defined for the column.
5145 * @param {Number} col The column index.
5146 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5148 getRenderer : function(col){
5149 if(!this.config[col].renderer){
5150 return Roo.grid.ColumnModel.defaultRenderer;
5152 return this.config[col].renderer;
5156 * Sets the rendering (formatting) function for a column.
5157 * @param {Number} col The column index
5158 * @param {Function} fn The function to use to process the cell's raw data
5159 * to return HTML markup for the grid view. The render function is called with
5160 * the following parameters:<ul>
5161 * <li>Data value.</li>
5162 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163 * <li>css A CSS style string to apply to the table cell.</li>
5164 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166 * <li>Row index</li>
5167 * <li>Column index</li>
5168 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5170 setRenderer : function(col, fn){
5171 this.config[col].renderer = fn;
5175 * Returns the width for the specified column.
5176 * @param {Number} col The column index
5179 getColumnWidth : function(col){
5180 return this.config[col].width * 1 || this.defaultWidth;
5184 * Sets the width for a column.
5185 * @param {Number} col The column index
5186 * @param {Number} width The new width
5188 setColumnWidth : function(col, width, suppressEvent){
5189 this.config[col].width = width;
5190 this.totalWidth = null;
5192 this.fireEvent("widthchange", this, col, width);
5197 * Returns the total width of all columns.
5198 * @param {Boolean} includeHidden True to include hidden column widths
5201 getTotalWidth : function(includeHidden){
5202 if(!this.totalWidth){
5203 this.totalWidth = 0;
5204 for(var i = 0, len = this.config.length; i < len; i++){
5205 if(includeHidden || !this.isHidden(i)){
5206 this.totalWidth += this.getColumnWidth(i);
5210 return this.totalWidth;
5214 * Returns the header for the specified column.
5215 * @param {Number} col The column index
5218 getColumnHeader : function(col){
5219 return this.config[col].header;
5223 * Sets the header for a column.
5224 * @param {Number} col The column index
5225 * @param {String} header The new header
5227 setColumnHeader : function(col, header){
5228 this.config[col].header = header;
5229 this.fireEvent("headerchange", this, col, header);
5233 * Returns the tooltip for the specified column.
5234 * @param {Number} col The column index
5237 getColumnTooltip : function(col){
5238 return this.config[col].tooltip;
5241 * Sets the tooltip for a column.
5242 * @param {Number} col The column index
5243 * @param {String} tooltip The new tooltip
5245 setColumnTooltip : function(col, tooltip){
5246 this.config[col].tooltip = tooltip;
5250 * Returns the dataIndex for the specified column.
5251 * @param {Number} col The column index
5254 getDataIndex : function(col){
5255 return this.config[col].dataIndex;
5259 * Sets the dataIndex for a column.
5260 * @param {Number} col The column index
5261 * @param {Number} dataIndex The new dataIndex
5263 setDataIndex : function(col, dataIndex){
5264 this.config[col].dataIndex = dataIndex;
5270 * Returns true if the cell is editable.
5271 * @param {Number} colIndex The column index
5272 * @param {Number} rowIndex The row index
5275 isCellEditable : function(colIndex, rowIndex){
5276 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5280 * Returns the editor defined for the cell/column.
5281 * return false or null to disable editing.
5282 * @param {Number} colIndex The column index
5283 * @param {Number} rowIndex The row index
5286 getCellEditor : function(colIndex, rowIndex){
5287 return this.config[colIndex].editor;
5291 * Sets if a column is editable.
5292 * @param {Number} col The column index
5293 * @param {Boolean} editable True if the column is editable
5295 setEditable : function(col, editable){
5296 this.config[col].editable = editable;
5301 * Returns true if the column is hidden.
5302 * @param {Number} colIndex The column index
5305 isHidden : function(colIndex){
5306 return this.config[colIndex].hidden;
5311 * Returns true if the column width cannot be changed
5313 isFixed : function(colIndex){
5314 return this.config[colIndex].fixed;
5318 * Returns true if the column can be resized
5321 isResizable : function(colIndex){
5322 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5325 * Sets if a column is hidden.
5326 * @param {Number} colIndex The column index
5327 * @param {Boolean} hidden True if the column is hidden
5329 setHidden : function(colIndex, hidden){
5330 this.config[colIndex].hidden = hidden;
5331 this.totalWidth = null;
5332 this.fireEvent("hiddenchange", this, colIndex, hidden);
5336 * Sets the editor for a column.
5337 * @param {Number} col The column index
5338 * @param {Object} editor The editor object
5340 setEditor : function(col, editor){
5341 this.config[col].editor = editor;
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346 if(typeof value == "string" && value.length < 1){
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5356 * Ext JS Library 1.1.1
5357 * Copyright(c) 2006-2007, Ext JS, LLC.
5359 * Originally Released Under LGPL - original licence link has changed is not relivant.
5362 * <script type="text/javascript">
5366 * @class Roo.LoadMask
5367 * A simple utility class for generically masking elements while loading data. If the element being masked has
5368 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5370 * element's UpdateManager load indicator and will be destroyed after the initial load.
5372 * Create a new LoadMask
5373 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374 * @param {Object} config The config object
5376 Roo.LoadMask = function(el, config){
5377 this.el = Roo.get(el);
5378 Roo.apply(this, config);
5380 this.store.on('beforeload', this.onBeforeLoad, this);
5381 this.store.on('load', this.onLoad, this);
5382 this.store.on('loadexception', this.onLoadException, this);
5383 this.removeMask = false;
5385 var um = this.el.getUpdateManager();
5386 um.showLoadIndicator = false; // disable the default indicator
5387 um.on('beforeupdate', this.onBeforeLoad, this);
5388 um.on('update', this.onLoad, this);
5389 um.on('failure', this.onLoad, this);
5390 this.removeMask = true;
5394 Roo.LoadMask.prototype = {
5396 * @cfg {Boolean} removeMask
5397 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5402 * The text to display in a centered loading message box (defaults to 'Loading...')
5406 * @cfg {String} msgCls
5407 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5409 msgCls : 'x-mask-loading',
5412 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5418 * Disables the mask to prevent it from being displayed
5420 disable : function(){
5421 this.disabled = true;
5425 * Enables the mask so that it can be displayed
5427 enable : function(){
5428 this.disabled = false;
5431 onLoadException : function()
5435 if (typeof(arguments[3]) != 'undefined') {
5436 Roo.MessageBox.alert("Error loading",arguments[3]);
5440 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5450 this.el.unmask(this.removeMask);
5455 this.el.unmask(this.removeMask);
5459 onBeforeLoad : function(){
5461 this.el.mask(this.msg, this.msgCls);
5466 destroy : function(){
5468 this.store.un('beforeload', this.onBeforeLoad, this);
5469 this.store.un('load', this.onLoad, this);
5470 this.store.un('loadexception', this.onLoadException, this);
5472 var um = this.el.getUpdateManager();
5473 um.un('beforeupdate', this.onBeforeLoad, this);
5474 um.un('update', this.onLoad, this);
5475 um.un('failure', this.onLoad, this);
5486 * @class Roo.bootstrap.Table
5487 * @extends Roo.bootstrap.Component
5488 * Bootstrap Table class
5489 * @cfg {String} cls table class
5490 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491 * @cfg {String} bgcolor Specifies the background color for a table
5492 * @cfg {Number} border Specifies whether the table cells should have borders or not
5493 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494 * @cfg {Number} cellspacing Specifies the space between cells
5495 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497 * @cfg {String} sortable Specifies that the table should be sortable
5498 * @cfg {String} summary Specifies a summary of the content of a table
5499 * @cfg {Number} width Specifies the width of a table
5500 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5502 * @cfg {boolean} striped Should the rows be alternative striped
5503 * @cfg {boolean} bordered Add borders to the table
5504 * @cfg {boolean} hover Add hover highlighting
5505 * @cfg {boolean} condensed Format condensed
5506 * @cfg {boolean} responsive Format condensed
5507 * @cfg {Boolean} loadMask (true|false) default false
5508 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510 * @cfg {Boolean} rowSelection (true|false) default false
5511 * @cfg {Boolean} cellSelection (true|false) default false
5512 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5516 * Create a new Table
5517 * @param {Object} config The config object
5520 Roo.bootstrap.Table = function(config){
5521 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5524 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5531 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532 this.sm = this.selModel;
5533 this.sm.xmodule = this.xmodule || false;
5535 if (this.cm && typeof(this.cm.config) == 'undefined') {
5536 this.colModel = new Roo.grid.ColumnModel(this.cm);
5537 this.cm = this.colModel;
5538 this.cm.xmodule = this.xmodule || false;
5541 this.store= Roo.factory(this.store, Roo.data);
5542 this.ds = this.store;
5543 this.ds.xmodule = this.xmodule || false;
5546 if (this.footer && this.store) {
5547 this.footer.dataSource = this.ds;
5548 this.footer = Roo.factory(this.footer);
5555 * Fires when a cell is clicked
5556 * @param {Roo.bootstrap.Table} this
5557 * @param {Roo.Element} el
5558 * @param {Number} rowIndex
5559 * @param {Number} columnIndex
5560 * @param {Roo.EventObject} e
5564 * @event celldblclick
5565 * Fires when a cell is double clicked
5566 * @param {Roo.bootstrap.Table} this
5567 * @param {Roo.Element} el
5568 * @param {Number} rowIndex
5569 * @param {Number} columnIndex
5570 * @param {Roo.EventObject} e
5572 "celldblclick" : true,
5575 * Fires when a row is clicked
5576 * @param {Roo.bootstrap.Table} this
5577 * @param {Roo.Element} el
5578 * @param {Number} rowIndex
5579 * @param {Roo.EventObject} e
5583 * @event rowdblclick
5584 * Fires when a row is double clicked
5585 * @param {Roo.bootstrap.Table} this
5586 * @param {Roo.Element} el
5587 * @param {Number} rowIndex
5588 * @param {Roo.EventObject} e
5590 "rowdblclick" : true,
5593 * Fires when a mouseover occur
5594 * @param {Roo.bootstrap.Table} this
5595 * @param {Roo.Element} el
5596 * @param {Number} rowIndex
5597 * @param {Number} columnIndex
5598 * @param {Roo.EventObject} e
5603 * Fires when a mouseout occur
5604 * @param {Roo.bootstrap.Table} this
5605 * @param {Roo.Element} el
5606 * @param {Number} rowIndex
5607 * @param {Number} columnIndex
5608 * @param {Roo.EventObject} e
5613 * Fires when a row is rendered, so you can change add a style to it.
5614 * @param {Roo.bootstrap.Table} this
5615 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5619 * @event rowsrendered
5620 * Fires when all the rows have been rendered
5621 * @param {Roo.bootstrap.Table} this
5623 'rowsrendered' : true
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5653 rowSelection : false,
5654 cellSelection : false,
5657 // Roo.Element - the tbody
5660 getAutoCreate : function(){
5661 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5670 cfg.cls += ' table-striped';
5674 cfg.cls += ' table-hover';
5676 if (this.bordered) {
5677 cfg.cls += ' table-bordered';
5679 if (this.condensed) {
5680 cfg.cls += ' table-condensed';
5682 if (this.responsive) {
5683 cfg.cls += ' table-responsive';
5687 cfg.cls+= ' ' +this.cls;
5690 // this lot should be simplifed...
5693 cfg.align=this.align;
5696 cfg.bgcolor=this.bgcolor;
5699 cfg.border=this.border;
5701 if (this.cellpadding) {
5702 cfg.cellpadding=this.cellpadding;
5704 if (this.cellspacing) {
5705 cfg.cellspacing=this.cellspacing;
5708 cfg.frame=this.frame;
5711 cfg.rules=this.rules;
5713 if (this.sortable) {
5714 cfg.sortable=this.sortable;
5717 cfg.summary=this.summary;
5720 cfg.width=this.width;
5723 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5726 if(this.store || this.cm){
5727 if(this.headerShow){
5728 cfg.cn.push(this.renderHeader());
5731 cfg.cn.push(this.renderBody());
5733 if(this.footerShow){
5734 cfg.cn.push(this.renderFooter());
5737 cfg.cls+= ' TableGrid';
5740 return { cn : [ cfg ] };
5743 initEvents : function()
5745 if(!this.store || !this.cm){
5749 //Roo.log('initEvents with ds!!!!');
5751 this.mainBody = this.el.select('tbody', true).first();
5756 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757 e.on('click', _this.sort, _this);
5760 this.el.on("click", this.onClick, this);
5761 this.el.on("dblclick", this.onDblClick, this);
5763 // why is this done????? = it breaks dialogs??
5764 //this.parent().el.setStyle('position', 'relative');
5768 this.footer.parentId = this.id;
5769 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5772 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5774 this.store.on('load', this.onLoad, this);
5775 this.store.on('beforeload', this.onBeforeLoad, this);
5776 this.store.on('update', this.onUpdate, this);
5777 this.store.on('add', this.onAdd, this);
5781 onMouseover : function(e, el)
5783 var cell = Roo.get(el);
5789 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790 cell = cell.findParent('td', false, true);
5793 var row = cell.findParent('tr', false, true);
5794 var cellIndex = cell.dom.cellIndex;
5795 var rowIndex = row.dom.rowIndex - 1; // start from 0
5797 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5801 onMouseout : function(e, el)
5803 var cell = Roo.get(el);
5809 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810 cell = cell.findParent('td', false, true);
5813 var row = cell.findParent('tr', false, true);
5814 var cellIndex = cell.dom.cellIndex;
5815 var rowIndex = row.dom.rowIndex - 1; // start from 0
5817 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5821 onClick : function(e, el)
5823 var cell = Roo.get(el);
5825 if(!cell || (!this.cellSelection && !this.rowSelection)){
5829 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830 cell = cell.findParent('td', false, true);
5833 if(!cell || typeof(cell) == 'undefined'){
5837 var row = cell.findParent('tr', false, true);
5839 if(!row || typeof(row) == 'undefined'){
5843 var cellIndex = cell.dom.cellIndex;
5844 var rowIndex = this.getRowIndex(row);
5846 // why??? - should these not be based on SelectionModel?
5847 if(this.cellSelection){
5848 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5851 if(this.rowSelection){
5852 this.fireEvent('rowclick', this, row, rowIndex, e);
5858 onDblClick : function(e,el)
5860 var cell = Roo.get(el);
5862 if(!cell || (!this.CellSelection && !this.RowSelection)){
5866 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867 cell = cell.findParent('td', false, true);
5870 if(!cell || typeof(cell) == 'undefined'){
5874 var row = cell.findParent('tr', false, true);
5876 if(!row || typeof(row) == 'undefined'){
5880 var cellIndex = cell.dom.cellIndex;
5881 var rowIndex = this.getRowIndex(row);
5883 if(this.CellSelection){
5884 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5887 if(this.RowSelection){
5888 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5892 sort : function(e,el)
5894 var col = Roo.get(el);
5896 if(!col.hasClass('sortable')){
5900 var sort = col.attr('sort');
5903 if(col.hasClass('glyphicon-arrow-up')){
5907 this.store.sortInfo = {field : sort, direction : dir};
5910 Roo.log("calling footer first");
5911 this.footer.onClick('first');
5914 this.store.load({ params : { start : 0 } });
5918 renderHeader : function()
5927 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5929 var config = cm.config[i];
5934 html: cm.getColumnHeader(i)
5939 if(typeof(config.lgHeader) != 'undefined'){
5940 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5943 if(typeof(config.mdHeader) != 'undefined'){
5944 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5947 if(typeof(config.smHeader) != 'undefined'){
5948 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5951 if(typeof(config.xsHeader) != 'undefined'){
5952 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5959 if(typeof(config.tooltip) != 'undefined'){
5960 c.tooltip = config.tooltip;
5963 if(typeof(config.colspan) != 'undefined'){
5964 c.colspan = config.colspan;
5967 if(typeof(config.hidden) != 'undefined' && config.hidden){
5968 c.style += ' display:none;';
5971 if(typeof(config.dataIndex) != 'undefined'){
5972 c.sort = config.dataIndex;
5975 if(typeof(config.sortable) != 'undefined' && config.sortable){
5979 if(typeof(config.align) != 'undefined' && config.align.length){
5980 c.style += ' text-align:' + config.align + ';';
5983 if(typeof(config.width) != 'undefined'){
5984 c.style += ' width:' + config.width + 'px;';
5987 if(typeof(config.cls) != 'undefined'){
5988 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5991 ['xs','sm','md','lg'].map(function(size){
5993 if(typeof(config[size]) == 'undefined'){
5997 if (!config[size]) { // 0 = hidden
5998 cfg.cls += ' hidden-' + size;
6002 cfg.cls += ' col-' + size + '-' + config[size];
6012 renderBody : function()
6022 colspan : this.cm.getColumnCount()
6032 renderFooter : function()
6042 colspan : this.cm.getColumnCount()
6056 Roo.log('ds onload');
6061 var ds = this.store;
6063 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6066 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6070 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6075 var tbody = this.mainBody;
6077 if(ds.getCount() > 0){
6078 ds.data.each(function(d,rowIndex){
6079 var row = this.renderRow(cm, ds, rowIndex);
6081 tbody.createChild(row);
6085 if(row.cellObjects.length){
6086 Roo.each(row.cellObjects, function(r){
6087 _this.renderCellObject(r);
6094 Roo.each(this.el.select('tbody td', true).elements, function(e){
6095 e.on('mouseover', _this.onMouseover, _this);
6098 Roo.each(this.el.select('tbody td', true).elements, function(e){
6099 e.on('mouseout', _this.onMouseout, _this);
6101 this.fireEvent('rowsrendered', this);
6102 //if(this.loadMask){
6103 // this.maskEl.hide();
6108 onUpdate : function(ds,record)
6110 this.refreshRow(record);
6113 onRemove : function(ds, record, index, isUpdate){
6114 if(isUpdate !== true){
6115 this.fireEvent("beforerowremoved", this, index, record);
6117 var bt = this.mainBody.dom;
6119 var rows = this.el.select('tbody > tr', true).elements;
6121 if(typeof(rows[index]) != 'undefined'){
6122 bt.removeChild(rows[index].dom);
6125 // if(bt.rows[index]){
6126 // bt.removeChild(bt.rows[index]);
6129 if(isUpdate !== true){
6130 //this.stripeRows(index);
6131 //this.syncRowHeights(index, index);
6133 this.fireEvent("rowremoved", this, index, record);
6137 onAdd : function(ds, records, rowIndex)
6139 //Roo.log('on Add called');
6140 // - note this does not handle multiple adding very well..
6141 var bt = this.mainBody.dom;
6142 for (var i =0 ; i < records.length;i++) {
6143 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144 //Roo.log(records[i]);
6145 //Roo.log(this.store.getAt(rowIndex+i));
6146 this.insertRow(this.store, rowIndex + i, false);
6153 refreshRow : function(record){
6154 var ds = this.store, index;
6155 if(typeof record == 'number'){
6157 record = ds.getAt(index);
6159 index = ds.indexOf(record);
6161 this.insertRow(ds, index, true);
6162 this.onRemove(ds, record, index+1, true);
6163 //this.syncRowHeights(index, index);
6165 this.fireEvent("rowupdated", this, index, record);
6168 insertRow : function(dm, rowIndex, isUpdate){
6171 this.fireEvent("beforerowsinserted", this, rowIndex);
6173 //var s = this.getScrollState();
6174 var row = this.renderRow(this.cm, this.store, rowIndex);
6175 // insert before rowIndex..
6176 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6180 if(row.cellObjects.length){
6181 Roo.each(row.cellObjects, function(r){
6182 _this.renderCellObject(r);
6187 this.fireEvent("rowsinserted", this, rowIndex);
6188 //this.syncRowHeights(firstRow, lastRow);
6189 //this.stripeRows(firstRow);
6196 getRowDom : function(rowIndex)
6198 var rows = this.el.select('tbody > tr', true).elements;
6200 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6203 // returns the object tree for a tr..
6206 renderRow : function(cm, ds, rowIndex)
6209 var d = ds.getAt(rowIndex);
6216 var cellObjects = [];
6218 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219 var config = cm.config[i];
6221 var renderer = cm.getRenderer(i);
6225 if(typeof(renderer) !== 'undefined'){
6226 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6228 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229 // and are rendered into the cells after the row is rendered - using the id for the element.
6231 if(typeof(value) === 'object'){
6241 rowIndex : rowIndex,
6246 this.fireEvent('rowclass', this, rowcfg);
6250 cls : rowcfg.rowClass,
6252 html: (typeof(value) === 'object') ? '' : value
6259 if(typeof(config.colspan) != 'undefined'){
6260 td.colspan = config.colspan;
6263 if(typeof(config.hidden) != 'undefined' && config.hidden){
6264 td.style += ' display:none;';
6267 if(typeof(config.align) != 'undefined' && config.align.length){
6268 td.style += ' text-align:' + config.align + ';';
6271 if(typeof(config.width) != 'undefined'){
6272 td.style += ' width:' + config.width + 'px;';
6275 if(typeof(config.cursor) != 'undefined'){
6276 td.style += ' cursor:' + config.cursor + ';';
6279 if(typeof(config.cls) != 'undefined'){
6280 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6283 ['xs','sm','md','lg'].map(function(size){
6285 if(typeof(config[size]) == 'undefined'){
6289 if (!config[size]) { // 0 = hidden
6290 td.cls += ' hidden-' + size;
6294 td.cls += ' col-' + size + '-' + config[size];
6302 row.cellObjects = cellObjects;
6310 onBeforeLoad : function()
6312 //Roo.log('ds onBeforeLoad');
6316 //if(this.loadMask){
6317 // this.maskEl.show();
6325 this.el.select('tbody', true).first().dom.innerHTML = '';
6328 * Show or hide a row.
6329 * @param {Number} rowIndex to show or hide
6330 * @param {Boolean} state hide
6332 setRowVisibility : function(rowIndex, state)
6334 var bt = this.mainBody.dom;
6336 var rows = this.el.select('tbody > tr', true).elements;
6338 if(typeof(rows[rowIndex]) == 'undefined'){
6341 rows[rowIndex].dom.style.display = state ? '' : 'none';
6345 getSelectionModel : function(){
6347 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6349 return this.selModel;
6352 * Render the Roo.bootstrap object from renderder
6354 renderCellObject : function(r)
6358 var t = r.cfg.render(r.container);
6361 Roo.each(r.cfg.cn, function(c){
6363 container: t.getChildContainer(),
6366 _this.renderCellObject(child);
6371 getRowIndex : function(row)
6375 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6398 * @class Roo.bootstrap.TableCell
6399 * @extends Roo.bootstrap.Component
6400 * Bootstrap TableCell class
6401 * @cfg {String} html cell contain text
6402 * @cfg {String} cls cell class
6403 * @cfg {String} tag cell tag (td|th) default td
6404 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405 * @cfg {String} align Aligns the content in a cell
6406 * @cfg {String} axis Categorizes cells
6407 * @cfg {String} bgcolor Specifies the background color of a cell
6408 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409 * @cfg {Number} colspan Specifies the number of columns a cell should span
6410 * @cfg {String} headers Specifies one or more header cells a cell is related to
6411 * @cfg {Number} height Sets the height of a cell
6412 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413 * @cfg {Number} rowspan Sets the number of rows a cell should span
6414 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415 * @cfg {String} valign Vertical aligns the content in a cell
6416 * @cfg {Number} width Specifies the width of a cell
6419 * Create a new TableCell
6420 * @param {Object} config The config object
6423 Roo.bootstrap.TableCell = function(config){
6424 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6447 getAutoCreate : function(){
6448 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6468 cfg.align=this.align
6474 cfg.bgcolor=this.bgcolor
6477 cfg.charoff=this.charoff
6480 cfg.colspan=this.colspan
6483 cfg.headers=this.headers
6486 cfg.height=this.height
6489 cfg.nowrap=this.nowrap
6492 cfg.rowspan=this.rowspan
6495 cfg.scope=this.scope
6498 cfg.valign=this.valign
6501 cfg.width=this.width
6520 * @class Roo.bootstrap.TableRow
6521 * @extends Roo.bootstrap.Component
6522 * Bootstrap TableRow class
6523 * @cfg {String} cls row class
6524 * @cfg {String} align Aligns the content in a table row
6525 * @cfg {String} bgcolor Specifies a background color for a table row
6526 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527 * @cfg {String} valign Vertical aligns the content in a table row
6530 * Create a new TableRow
6531 * @param {Object} config The config object
6534 Roo.bootstrap.TableRow = function(config){
6535 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6546 getAutoCreate : function(){
6547 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6557 cfg.align = this.align;
6560 cfg.bgcolor = this.bgcolor;
6563 cfg.charoff = this.charoff;
6566 cfg.valign = this.valign;
6584 * @class Roo.bootstrap.TableBody
6585 * @extends Roo.bootstrap.Component
6586 * Bootstrap TableBody class
6587 * @cfg {String} cls element class
6588 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589 * @cfg {String} align Aligns the content inside the element
6590 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6594 * Create a new TableBody
6595 * @param {Object} config The config object
6598 Roo.bootstrap.TableBody = function(config){
6599 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6610 getAutoCreate : function(){
6611 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6625 cfg.align = this.align;
6628 cfg.charoff = this.charoff;
6631 cfg.valign = this.valign;
6638 // initEvents : function()
6645 // this.store = Roo.factory(this.store, Roo.data);
6646 // this.store.on('load', this.onLoad, this);
6648 // this.store.load();
6652 // onLoad: function ()
6654 // this.fireEvent('load', this);
6664 * Ext JS Library 1.1.1
6665 * Copyright(c) 2006-2007, Ext JS, LLC.
6667 * Originally Released Under LGPL - original licence link has changed is not relivant.
6670 * <script type="text/javascript">
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6676 * @class Roo.form.Action
6677 * Internal Class used to handle form actions
6679 * @param {Roo.form.BasicForm} el The form element or its id
6680 * @param {Object} config Configuration options
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6688 this.options = options || {};
6691 * Client Validation Failed
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6696 * Server Validation Failed
6699 Roo.form.Action.SERVER_INVALID = 'server';
6701 * Connect to Server Failed
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6706 * Reading Data from Server Failed
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6711 Roo.form.Action.prototype = {
6713 failureType : undefined,
6714 response : undefined,
6718 run : function(options){
6723 success : function(response){
6728 handleResponse : function(response){
6732 // default connection failure
6733 failure : function(response){
6735 this.response = response;
6736 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737 this.form.afterAction(this, false);
6740 processResponse : function(response){
6741 this.response = response;
6742 if(!response.responseText){
6745 this.result = this.handleResponse(response);
6749 // utility functions used internally
6750 getUrl : function(appendParams){
6751 var url = this.options.url || this.form.url || this.form.el.dom.action;
6753 var p = this.getParams();
6755 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6761 getMethod : function(){
6762 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6765 getParams : function(){
6766 var bp = this.form.baseParams;
6767 var p = this.options.params;
6769 if(typeof p == "object"){
6770 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771 }else if(typeof p == 'string' && bp){
6772 p += '&' + Roo.urlEncode(bp);
6775 p = Roo.urlEncode(bp);
6780 createCallback : function(){
6782 success: this.success,
6783 failure: this.failure,
6785 timeout: (this.form.timeout*1000),
6786 upload: this.form.fileUpload ? this.success : undefined
6791 Roo.form.Action.Submit = function(form, options){
6792 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6798 haveProgress : false,
6799 uploadComplete : false,
6801 // uploadProgress indicator.
6802 uploadProgress : function()
6804 if (!this.form.progressUrl) {
6808 if (!this.haveProgress) {
6809 Roo.MessageBox.progress("Uploading", "Uploading");
6811 if (this.uploadComplete) {
6812 Roo.MessageBox.hide();
6816 this.haveProgress = true;
6818 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6820 var c = new Roo.data.Connection();
6822 url : this.form.progressUrl,
6827 success : function(req){
6828 //console.log(data);
6832 rdata = Roo.decode(req.responseText)
6834 Roo.log("Invalid data from server..");
6838 if (!rdata || !rdata.success) {
6840 Roo.MessageBox.alert(Roo.encode(rdata));
6843 var data = rdata.data;
6845 if (this.uploadComplete) {
6846 Roo.MessageBox.hide();
6851 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6855 this.uploadProgress.defer(2000,this);
6858 failure: function(data) {
6859 Roo.log('progress url failed ');
6870 // run get Values on the form, so it syncs any secondary forms.
6871 this.form.getValues();
6873 var o = this.options;
6874 var method = this.getMethod();
6875 var isPost = method == 'POST';
6876 if(o.clientValidation === false || this.form.isValid()){
6878 if (this.form.progressUrl) {
6879 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880 (new Date() * 1) + '' + Math.random());
6885 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886 form:this.form.el.dom,
6887 url:this.getUrl(!isPost),
6889 params:isPost ? this.getParams() : null,
6890 isUpload: this.form.fileUpload
6893 this.uploadProgress();
6895 }else if (o.clientValidation !== false){ // client validation failed
6896 this.failureType = Roo.form.Action.CLIENT_INVALID;
6897 this.form.afterAction(this, false);
6901 success : function(response)
6903 this.uploadComplete= true;
6904 if (this.haveProgress) {
6905 Roo.MessageBox.hide();
6909 var result = this.processResponse(response);
6910 if(result === true || result.success){
6911 this.form.afterAction(this, true);
6915 this.form.markInvalid(result.errors);
6916 this.failureType = Roo.form.Action.SERVER_INVALID;
6918 this.form.afterAction(this, false);
6920 failure : function(response)
6922 this.uploadComplete= true;
6923 if (this.haveProgress) {
6924 Roo.MessageBox.hide();
6927 this.response = response;
6928 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929 this.form.afterAction(this, false);
6932 handleResponse : function(response){
6933 if(this.form.errorReader){
6934 var rs = this.form.errorReader.read(response);
6937 for(var i = 0, len = rs.records.length; i < len; i++) {
6938 var r = rs.records[i];
6942 if(errors.length < 1){
6946 success : rs.success,
6952 ret = Roo.decode(response.responseText);
6956 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6966 Roo.form.Action.Load = function(form, options){
6967 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968 this.reader = this.form.reader;
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6976 Roo.Ajax.request(Roo.apply(
6977 this.createCallback(), {
6978 method:this.getMethod(),
6979 url:this.getUrl(false),
6980 params:this.getParams()
6984 success : function(response){
6986 var result = this.processResponse(response);
6987 if(result === true || !result.success || !result.data){
6988 this.failureType = Roo.form.Action.LOAD_FAILURE;
6989 this.form.afterAction(this, false);
6992 this.form.clearInvalid();
6993 this.form.setValues(result.data);
6994 this.form.afterAction(this, true);
6997 handleResponse : function(response){
6998 if(this.form.reader){
6999 var rs = this.form.reader.read(response);
7000 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7002 success : rs.success,
7006 return Roo.decode(response.responseText);
7010 Roo.form.Action.ACTION_TYPES = {
7011 'load' : Roo.form.Action.Load,
7012 'submit' : Roo.form.Action.Submit
7021 * @class Roo.bootstrap.Form
7022 * @extends Roo.bootstrap.Component
7023 * Bootstrap Form class
7024 * @cfg {String} method GET | POST (default POST)
7025 * @cfg {String} labelAlign top | left (default top)
7026 * @cfg {String} align left | right - for navbars
7027 * @cfg {Boolean} loadMask load mask when submit (default true)
7032 * @param {Object} config The config object
7036 Roo.bootstrap.Form = function(config){
7037 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7040 * @event clientvalidation
7041 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042 * @param {Form} this
7043 * @param {Boolean} valid true if the form has passed client-side validation
7045 clientvalidation: true,
7047 * @event beforeaction
7048 * Fires before any action is performed. Return false to cancel the action.
7049 * @param {Form} this
7050 * @param {Action} action The action to be performed
7054 * @event actionfailed
7055 * Fires when an action fails.
7056 * @param {Form} this
7057 * @param {Action} action The action that failed
7059 actionfailed : true,
7061 * @event actioncomplete
7062 * Fires when an action is completed.
7063 * @param {Form} this
7064 * @param {Action} action The action that completed
7066 actioncomplete : true
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7074 * @cfg {String} method
7075 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7080 * The URL to use for form actions if one isn't supplied in the action options.
7083 * @cfg {Boolean} fileUpload
7084 * Set to true if this form is a file upload.
7088 * @cfg {Object} baseParams
7089 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7093 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7097 * @cfg {Sting} align (left|right) for navbar forms
7102 activeAction : null,
7105 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106 * element by passing it or its id or mask the form itself by passing in true.
7109 waitMsgTarget : false,
7113 getAutoCreate : function(){
7117 method : this.method || 'POST',
7118 id : this.id || Roo.id(),
7121 if (this.parent().xtype.match(/^Nav/)) {
7122 cfg.cls = 'navbar-form navbar-' + this.align;
7126 if (this.labelAlign == 'left' ) {
7127 cfg.cls += ' form-horizontal';
7133 initEvents : function()
7135 this.el.on('submit', this.onSubmit, this);
7136 // this was added as random key presses on the form where triggering form submit.
7137 this.el.on('keypress', function(e) {
7138 if (e.getCharCode() != 13) {
7141 // we might need to allow it for textareas.. and some other items.
7142 // check e.getTarget().
7144 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7148 Roo.log("keypress blocked");
7156 onSubmit : function(e){
7161 * Returns true if client-side validation on the form is successful.
7164 isValid : function(){
7165 var items = this.getItems();
7167 items.each(function(f){
7176 * Returns true if any fields in this form have changed since their original load.
7179 isDirty : function(){
7181 var items = this.getItems();
7182 items.each(function(f){
7192 * Performs a predefined action (submit or load) or custom actions you define on this form.
7193 * @param {String} actionName The name of the action type
7194 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7195 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196 * accept other config options):
7198 Property Type Description
7199 ---------------- --------------- ----------------------------------------------------------------------------------
7200 url String The url for the action (defaults to the form's url)
7201 method String The form method to use (defaults to the form's method, or POST if not defined)
7202 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7204 validate the form on the client (defaults to false)
7206 * @return {BasicForm} this
7208 doAction : function(action, options){
7209 if(typeof action == 'string'){
7210 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7212 if(this.fireEvent('beforeaction', this, action) !== false){
7213 this.beforeAction(action);
7214 action.run.defer(100, action);
7220 beforeAction : function(action){
7221 var o = action.options;
7224 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7226 // not really supported yet.. ??
7228 //if(this.waitMsgTarget === true){
7229 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230 //}else if(this.waitMsgTarget){
7231 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7234 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7240 afterAction : function(action, success){
7241 this.activeAction = null;
7242 var o = action.options;
7244 //if(this.waitMsgTarget === true){
7246 //}else if(this.waitMsgTarget){
7247 // this.waitMsgTarget.unmask();
7249 // Roo.MessageBox.updateProgress(1);
7250 // Roo.MessageBox.hide();
7257 Roo.callback(o.success, o.scope, [this, action]);
7258 this.fireEvent('actioncomplete', this, action);
7262 // failure condition..
7263 // we have a scenario where updates need confirming.
7264 // eg. if a locking scenario exists..
7265 // we look for { errors : { needs_confirm : true }} in the response.
7267 (typeof(action.result) != 'undefined') &&
7268 (typeof(action.result.errors) != 'undefined') &&
7269 (typeof(action.result.errors.needs_confirm) != 'undefined')
7272 Roo.log("not supported yet");
7275 Roo.MessageBox.confirm(
7276 "Change requires confirmation",
7277 action.result.errorMsg,
7282 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7292 Roo.callback(o.failure, o.scope, [this, action]);
7293 // show an error message if no failed handler is set..
7294 if (!this.hasListener('actionfailed')) {
7295 Roo.log("need to add dialog support");
7297 Roo.MessageBox.alert("Error",
7298 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299 action.result.errorMsg :
7300 "Saving Failed, please check your entries or try again"
7305 this.fireEvent('actionfailed', this, action);
7310 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311 * @param {String} id The value to search for
7314 findField : function(id){
7315 var items = this.getItems();
7316 var field = items.get(id);
7318 items.each(function(f){
7319 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7326 return field || null;
7329 * Mark fields in this form invalid in bulk.
7330 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331 * @return {BasicForm} this
7333 markInvalid : function(errors){
7334 if(errors instanceof Array){
7335 for(var i = 0, len = errors.length; i < len; i++){
7336 var fieldError = errors[i];
7337 var f = this.findField(fieldError.id);
7339 f.markInvalid(fieldError.msg);
7345 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346 field.markInvalid(errors[id]);
7350 //Roo.each(this.childForms || [], function (f) {
7351 // f.markInvalid(errors);
7358 * Set values for fields in this form in bulk.
7359 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360 * @return {BasicForm} this
7362 setValues : function(values){
7363 if(values instanceof Array){ // array of objects
7364 for(var i = 0, len = values.length; i < len; i++){
7366 var f = this.findField(v.id);
7368 f.setValue(v.value);
7369 if(this.trackResetOnLoad){
7370 f.originalValue = f.getValue();
7374 }else{ // object hash
7377 if(typeof values[id] != 'function' && (field = this.findField(id))){
7379 if (field.setFromData &&
7381 field.displayField &&
7382 // combos' with local stores can
7383 // be queried via setValue()
7384 // to set their value..
7385 (field.store && !field.store.isLocal)
7389 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391 field.setFromData(sd);
7394 field.setValue(values[id]);
7398 if(this.trackResetOnLoad){
7399 field.originalValue = field.getValue();
7405 //Roo.each(this.childForms || [], function (f) {
7406 // f.setValues(values);
7413 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414 * they are returned as an array.
7415 * @param {Boolean} asString
7418 getValues : function(asString){
7419 //if (this.childForms) {
7420 // copy values from the child forms
7421 // Roo.each(this.childForms, function (f) {
7422 // this.setValues(f.getValues());
7428 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429 if(asString === true){
7432 return Roo.urlDecode(fs);
7436 * Returns the fields in this form as an object with key/value pairs.
7437 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7440 getFieldValues : function(with_hidden)
7442 var items = this.getItems();
7444 items.each(function(f){
7448 var v = f.getValue();
7449 if (f.inputType =='radio') {
7450 if (typeof(ret[f.getName()]) == 'undefined') {
7451 ret[f.getName()] = ''; // empty..
7454 if (!f.el.dom.checked) {
7462 // not sure if this supported any more..
7463 if ((typeof(v) == 'object') && f.getRawValue) {
7464 v = f.getRawValue() ; // dates..
7466 // combo boxes where name != hiddenName...
7467 if (f.name != f.getName()) {
7468 ret[f.name] = f.getRawValue();
7470 ret[f.getName()] = v;
7477 * Clears all invalid messages in this form.
7478 * @return {BasicForm} this
7480 clearInvalid : function(){
7481 var items = this.getItems();
7483 items.each(function(f){
7494 * @return {BasicForm} this
7497 var items = this.getItems();
7498 items.each(function(f){
7502 Roo.each(this.childForms || [], function (f) {
7509 getItems : function()
7511 var r=new Roo.util.MixedCollection(false, function(o){
7512 return o.id || (o.id = Roo.id());
7514 var iter = function(el) {
7521 Roo.each(el.items,function(e) {
7541 * Ext JS Library 1.1.1
7542 * Copyright(c) 2006-2007, Ext JS, LLC.
7544 * Originally Released Under LGPL - original licence link has changed is not relivant.
7547 * <script type="text/javascript">
7550 * @class Roo.form.VTypes
7551 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7554 Roo.form.VTypes = function(){
7555 // closure these in so they are only created once.
7556 var alpha = /^[a-zA-Z_]+$/;
7557 var alphanum = /^[a-zA-Z0-9_]+$/;
7558 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7561 // All these messages and functions are configurable
7564 * The function used to validate email addresses
7565 * @param {String} value The email address
7567 'email' : function(v){
7568 return email.test(v);
7571 * The error text to display when the email validation function returns false
7574 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7576 * The keystroke filter mask to be applied on email input
7579 'emailMask' : /[a-z0-9_\.\-@]/i,
7582 * The function used to validate URLs
7583 * @param {String} value The URL
7585 'url' : function(v){
7589 * The error text to display when the url validation function returns false
7592 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7595 * The function used to validate alpha values
7596 * @param {String} value The value
7598 'alpha' : function(v){
7599 return alpha.test(v);
7602 * The error text to display when the alpha validation function returns false
7605 'alphaText' : 'This field should only contain letters and _',
7607 * The keystroke filter mask to be applied on alpha input
7610 'alphaMask' : /[a-z_]/i,
7613 * The function used to validate alphanumeric values
7614 * @param {String} value The value
7616 'alphanum' : function(v){
7617 return alphanum.test(v);
7620 * The error text to display when the alphanumeric validation function returns false
7623 'alphanumText' : 'This field should only contain letters, numbers and _',
7625 * The keystroke filter mask to be applied on alphanumeric input
7628 'alphanumMask' : /[a-z0-9_]/i
7638 * @class Roo.bootstrap.Input
7639 * @extends Roo.bootstrap.Component
7640 * Bootstrap Input class
7641 * @cfg {Boolean} disabled is it disabled
7642 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643 * @cfg {String} name name of the input
7644 * @cfg {string} fieldLabel - the label associated
7645 * @cfg {string} placeholder - placeholder to put in text.
7646 * @cfg {string} before - input group add on before
7647 * @cfg {string} after - input group add on after
7648 * @cfg {string} size - (lg|sm) or leave empty..
7649 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651 * @cfg {Number} md colspan out of 12 for computer-sized screens
7652 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653 * @cfg {string} value default value of the input
7654 * @cfg {Number} labelWidth set the width of label (0-12)
7655 * @cfg {String} labelAlign (top|left)
7656 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7659 * @cfg {String} align (left|center|right) Default left
7660 * @cfg {Boolean} forceFeedback (true|false) Default false
7666 * Create a new Input
7667 * @param {Object} config The config object
7670 Roo.bootstrap.Input = function(config){
7671 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7676 * Fires when this field receives input focus.
7677 * @param {Roo.form.Field} this
7682 * Fires when this field loses input focus.
7683 * @param {Roo.form.Field} this
7688 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7689 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690 * @param {Roo.form.Field} this
7691 * @param {Roo.EventObject} e The event object
7696 * Fires just before the field blurs if the field value has changed.
7697 * @param {Roo.form.Field} this
7698 * @param {Mixed} newValue The new value
7699 * @param {Mixed} oldValue The original value
7704 * Fires after the field has been marked as invalid.
7705 * @param {Roo.form.Field} this
7706 * @param {String} msg The validation message
7711 * Fires after the field has been validated with no errors.
7712 * @param {Roo.form.Field} this
7717 * Fires after the key up
7718 * @param {Roo.form.Field} this
7719 * @param {Roo.EventObject} e The event Object
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7727 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728 automatic validation (defaults to "keyup").
7730 validationEvent : "keyup",
7732 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7734 validateOnBlur : true,
7736 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7738 validationDelay : 250,
7740 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7742 focusClass : "x-form-focus", // not needed???
7746 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7748 invalidClass : "has-warning",
7751 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7753 validClass : "has-success",
7756 * @cfg {Boolean} hasFeedback (true|false) default true
7761 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7763 invalidFeedbackClass : "glyphicon-warning-sign",
7766 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7768 validFeedbackClass : "glyphicon-ok",
7771 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7773 selectOnFocus : false,
7776 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7780 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7785 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7787 disableKeyFilter : false,
7790 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7794 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7798 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7800 blankText : "This field is required",
7803 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7807 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7809 maxLength : Number.MAX_VALUE,
7811 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7813 minLengthText : "The minimum length for this field is {0}",
7815 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7817 maxLengthText : "The maximum length for this field is {0}",
7821 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822 * If available, this function will be called only after the basic validators all return true, and will be passed the
7823 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7827 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7833 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7837 autocomplete: false,
7856 formatedValue : false,
7857 forceFeedback : false,
7859 parentLabelAlign : function()
7862 while (parent.parent()) {
7863 parent = parent.parent();
7864 if (typeof(parent.labelAlign) !='undefined') {
7865 return parent.labelAlign;
7872 getAutoCreate : function(){
7874 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7880 if(this.inputType != 'hidden'){
7881 cfg.cls = 'form-group' //input-group
7887 type : this.inputType,
7889 cls : 'form-control',
7890 placeholder : this.placeholder || '',
7891 autocomplete : this.autocomplete || 'new-password'
7896 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7899 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900 input.maxLength = this.maxLength;
7903 if (this.disabled) {
7904 input.disabled=true;
7907 if (this.readOnly) {
7908 input.readonly=true;
7912 input.name = this.name;
7915 input.cls += ' input-' + this.size;
7918 ['xs','sm','md','lg'].map(function(size){
7919 if (settings[size]) {
7920 cfg.cls += ' col-' + size + '-' + settings[size];
7924 var inputblock = input;
7928 cls: 'glyphicon form-control-feedback'
7931 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7934 cls : 'has-feedback',
7942 if (this.before || this.after) {
7945 cls : 'input-group',
7949 if (this.before && typeof(this.before) == 'string') {
7951 inputblock.cn.push({
7953 cls : 'roo-input-before input-group-addon',
7957 if (this.before && typeof(this.before) == 'object') {
7958 this.before = Roo.factory(this.before);
7959 Roo.log(this.before);
7960 inputblock.cn.push({
7962 cls : 'roo-input-before input-group-' +
7963 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7967 inputblock.cn.push(input);
7969 if (this.after && typeof(this.after) == 'string') {
7970 inputblock.cn.push({
7972 cls : 'roo-input-after input-group-addon',
7976 if (this.after && typeof(this.after) == 'object') {
7977 this.after = Roo.factory(this.after);
7978 Roo.log(this.after);
7979 inputblock.cn.push({
7981 cls : 'roo-input-after input-group-' +
7982 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7986 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987 inputblock.cls += ' has-feedback';
7988 inputblock.cn.push(feedback);
7992 if (align ==='left' && this.fieldLabel.length) {
7993 Roo.log("left and has label");
7999 cls : 'control-label col-sm-' + this.labelWidth,
8000 html : this.fieldLabel
8004 cls : "col-sm-" + (12 - this.labelWidth),
8011 } else if ( this.fieldLabel.length) {
8017 //cls : 'input-group-addon',
8018 html : this.fieldLabel
8028 Roo.log(" no label && no align");
8037 Roo.log('input-parentType: ' + this.parentType);
8039 if (this.parentType === 'Navbar' && this.parent().bar) {
8040 cfg.cls += ' navbar-form';
8048 * return the real input element.
8050 inputEl: function ()
8052 return this.el.select('input.form-control',true).first();
8055 tooltipEl : function()
8057 return this.inputEl();
8060 setDisabled : function(v)
8062 var i = this.inputEl().dom;
8064 i.removeAttribute('disabled');
8068 i.setAttribute('disabled','true');
8070 initEvents : function()
8073 this.inputEl().on("keydown" , this.fireKey, this);
8074 this.inputEl().on("focus", this.onFocus, this);
8075 this.inputEl().on("blur", this.onBlur, this);
8077 this.inputEl().relayEvent('keyup', this);
8079 // reference to original value for reset
8080 this.originalValue = this.getValue();
8081 //Roo.form.TextField.superclass.initEvents.call(this);
8082 if(this.validationEvent == 'keyup'){
8083 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084 this.inputEl().on('keyup', this.filterValidation, this);
8086 else if(this.validationEvent !== false){
8087 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8090 if(this.selectOnFocus){
8091 this.on("focus", this.preFocus, this);
8094 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095 this.inputEl().on("keypress", this.filterKeys, this);
8098 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8099 this.el.on("click", this.autoSize, this);
8102 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8106 if (typeof(this.before) == 'object') {
8107 this.before.render(this.el.select('.roo-input-before',true).first());
8109 if (typeof(this.after) == 'object') {
8110 this.after.render(this.el.select('.roo-input-after',true).first());
8115 filterValidation : function(e){
8116 if(!e.isNavKeyPress()){
8117 this.validationTask.delay(this.validationDelay);
8121 * Validates the field value
8122 * @return {Boolean} True if the value is valid, else false
8124 validate : function(){
8125 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126 if(this.disabled || this.validateValue(this.getRawValue())){
8137 * Validates a value according to the field's validation rules and marks the field as invalid
8138 * if the validation fails
8139 * @param {Mixed} value The value to validate
8140 * @return {Boolean} True if the value is valid, else false
8142 validateValue : function(value){
8143 if(value.length < 1) { // if it's blank
8144 if(this.allowBlank){
8150 if(value.length < this.minLength){
8153 if(value.length > this.maxLength){
8157 var vt = Roo.form.VTypes;
8158 if(!vt[this.vtype](value, this)){
8162 if(typeof this.validator == "function"){
8163 var msg = this.validator(value);
8169 if(this.regex && !this.regex.test(value)){
8179 fireKey : function(e){
8180 //Roo.log('field ' + e.getKey());
8181 if(e.isNavKeyPress()){
8182 this.fireEvent("specialkey", this, e);
8185 focus : function (selectText){
8187 this.inputEl().focus();
8188 if(selectText === true){
8189 this.inputEl().dom.select();
8195 onFocus : function(){
8196 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197 // this.el.addClass(this.focusClass);
8200 this.hasFocus = true;
8201 this.startValue = this.getValue();
8202 this.fireEvent("focus", this);
8206 beforeBlur : Roo.emptyFn,
8210 onBlur : function(){
8212 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213 //this.el.removeClass(this.focusClass);
8215 this.hasFocus = false;
8216 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8219 var v = this.getValue();
8220 if(String(v) !== String(this.startValue)){
8221 this.fireEvent('change', this, v, this.startValue);
8223 this.fireEvent("blur", this);
8227 * Resets the current field value to the originally loaded value and clears any validation messages
8230 this.setValue(this.originalValue);
8234 * Returns the name of the field
8235 * @return {Mixed} name The name field
8237 getName: function(){
8241 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8242 * @return {Mixed} value The field value
8244 getValue : function(){
8246 var v = this.inputEl().getValue();
8251 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8252 * @return {Mixed} value The field value
8254 getRawValue : function(){
8255 var v = this.inputEl().getValue();
8261 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8262 * @param {Mixed} value The value to set
8264 setRawValue : function(v){
8265 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8268 selectText : function(start, end){
8269 var v = this.getRawValue();
8271 start = start === undefined ? 0 : start;
8272 end = end === undefined ? v.length : end;
8273 var d = this.inputEl().dom;
8274 if(d.setSelectionRange){
8275 d.setSelectionRange(start, end);
8276 }else if(d.createTextRange){
8277 var range = d.createTextRange();
8278 range.moveStart("character", start);
8279 range.moveEnd("character", v.length-end);
8286 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8287 * @param {Mixed} value The value to set
8289 setValue : function(v){
8292 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8298 processValue : function(value){
8299 if(this.stripCharsRe){
8300 var newValue = value.replace(this.stripCharsRe, '');
8301 if(newValue !== value){
8302 this.setRawValue(newValue);
8309 preFocus : function(){
8311 if(this.selectOnFocus){
8312 this.inputEl().dom.select();
8315 filterKeys : function(e){
8317 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8320 var c = e.getCharCode(), cc = String.fromCharCode(c);
8321 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8324 if(!this.maskRe.test(cc)){
8329 * Clear any invalid styles/messages for this field
8331 clearInvalid : function(){
8333 if(!this.el || this.preventMark){ // not rendered
8336 this.el.removeClass(this.invalidClass);
8338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8340 var feedback = this.el.select('.form-control-feedback', true).first();
8343 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8348 this.fireEvent('valid', this);
8352 * Mark this field as valid
8354 markValid : function()
8356 if(!this.el || this.preventMark){ // not rendered
8360 this.el.removeClass([this.invalidClass, this.validClass]);
8362 var feedback = this.el.select('.form-control-feedback', true).first();
8365 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8368 if(this.disabled || this.allowBlank){
8372 var formGroup = this.el.findParent('.form-group', false, true);
8376 var label = formGroup.select('label', true).first();
8377 var icon = formGroup.select('i.fa-star', true).first();
8384 this.el.addClass(this.validClass);
8386 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8388 var feedback = this.el.select('.form-control-feedback', true).first();
8391 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8392 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8397 this.fireEvent('valid', this);
8401 * Mark this field as invalid
8402 * @param {String} msg The validation message
8404 markInvalid : function(msg)
8406 if(!this.el || this.preventMark){ // not rendered
8410 this.el.removeClass([this.invalidClass, this.validClass]);
8412 var feedback = this.el.select('.form-control-feedback', true).first();
8415 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8418 if(this.disabled || this.allowBlank){
8422 var formGroup = this.el.findParent('.form-group', false, true);
8425 var label = formGroup.select('label', true).first();
8426 var icon = formGroup.select('i.fa-star', true).first();
8428 if(!this.getValue().length && label && !icon){
8429 this.el.findParent('.form-group', false, true).createChild({
8431 cls : 'text-danger fa fa-lg fa-star',
8432 tooltip : 'This field is required',
8433 style : 'margin-right:5px;'
8439 this.el.addClass(this.invalidClass);
8441 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8443 var feedback = this.el.select('.form-control-feedback', true).first();
8446 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8448 if(this.getValue().length || this.forceFeedback){
8449 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8456 this.fireEvent('invalid', this, msg);
8459 SafariOnKeyDown : function(event)
8461 // this is a workaround for a password hang bug on chrome/ webkit.
8463 var isSelectAll = false;
8465 if(this.inputEl().dom.selectionEnd > 0){
8466 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8468 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8469 event.preventDefault();
8474 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8476 event.preventDefault();
8477 // this is very hacky as keydown always get's upper case.
8479 var cc = String.fromCharCode(event.getCharCode());
8480 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8484 adjustWidth : function(tag, w){
8485 tag = tag.toLowerCase();
8486 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8487 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8491 if(tag == 'textarea'){
8494 }else if(Roo.isOpera){
8498 if(tag == 'textarea'){
8517 * @class Roo.bootstrap.TextArea
8518 * @extends Roo.bootstrap.Input
8519 * Bootstrap TextArea class
8520 * @cfg {Number} cols Specifies the visible width of a text area
8521 * @cfg {Number} rows Specifies the visible number of lines in a text area
8522 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8523 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8524 * @cfg {string} html text
8527 * Create a new TextArea
8528 * @param {Object} config The config object
8531 Roo.bootstrap.TextArea = function(config){
8532 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8536 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8546 getAutoCreate : function(){
8548 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8559 value : this.value || '',
8560 html: this.html || '',
8561 cls : 'form-control',
8562 placeholder : this.placeholder || ''
8566 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8567 input.maxLength = this.maxLength;
8571 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8575 input.cols = this.cols;
8578 if (this.readOnly) {
8579 input.readonly = true;
8583 input.name = this.name;
8587 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8591 ['xs','sm','md','lg'].map(function(size){
8592 if (settings[size]) {
8593 cfg.cls += ' col-' + size + '-' + settings[size];
8597 var inputblock = input;
8599 if(this.hasFeedback && !this.allowBlank){
8603 cls: 'glyphicon form-control-feedback'
8607 cls : 'has-feedback',
8616 if (this.before || this.after) {
8619 cls : 'input-group',
8623 inputblock.cn.push({
8625 cls : 'input-group-addon',
8630 inputblock.cn.push(input);
8632 if(this.hasFeedback && !this.allowBlank){
8633 inputblock.cls += ' has-feedback';
8634 inputblock.cn.push(feedback);
8638 inputblock.cn.push({
8640 cls : 'input-group-addon',
8647 if (align ==='left' && this.fieldLabel.length) {
8648 Roo.log("left and has label");
8654 cls : 'control-label col-sm-' + this.labelWidth,
8655 html : this.fieldLabel
8659 cls : "col-sm-" + (12 - this.labelWidth),
8666 } else if ( this.fieldLabel.length) {
8672 //cls : 'input-group-addon',
8673 html : this.fieldLabel
8683 Roo.log(" no label && no align");
8693 if (this.disabled) {
8694 input.disabled=true;
8701 * return the real textarea element.
8703 inputEl: function ()
8705 return this.el.select('textarea.form-control',true).first();
8709 * Clear any invalid styles/messages for this field
8711 clearInvalid : function()
8714 if(!this.el || this.preventMark){ // not rendered
8718 var label = this.el.select('label', true).first();
8719 var icon = this.el.select('i.fa-star', true).first();
8725 this.el.removeClass(this.invalidClass);
8727 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8729 var feedback = this.el.select('.form-control-feedback', true).first();
8732 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8737 this.fireEvent('valid', this);
8741 * Mark this field as valid
8743 markValid : function()
8745 if(!this.el || this.preventMark){ // not rendered
8749 this.el.removeClass([this.invalidClass, this.validClass]);
8751 var feedback = this.el.select('.form-control-feedback', true).first();
8754 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8757 if(this.disabled || this.allowBlank){
8761 var label = this.el.select('label', true).first();
8762 var icon = this.el.select('i.fa-star', true).first();
8768 this.el.addClass(this.validClass);
8770 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8772 var feedback = this.el.select('.form-control-feedback', true).first();
8775 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8776 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8781 this.fireEvent('valid', this);
8785 * Mark this field as invalid
8786 * @param {String} msg The validation message
8788 markInvalid : function(msg)
8790 if(!this.el || this.preventMark){ // not rendered
8794 this.el.removeClass([this.invalidClass, this.validClass]);
8796 var feedback = this.el.select('.form-control-feedback', true).first();
8799 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8802 if(this.disabled || this.allowBlank){
8806 var label = this.el.select('label', true).first();
8807 var icon = this.el.select('i.fa-star', true).first();
8809 if(!this.getValue().length && label && !icon){
8810 this.el.createChild({
8812 cls : 'text-danger fa fa-lg fa-star',
8813 tooltip : 'This field is required',
8814 style : 'margin-right:5px;'
8818 this.el.addClass(this.invalidClass);
8820 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8822 var feedback = this.el.select('.form-control-feedback', true).first();
8825 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8827 if(this.getValue().length || this.forceFeedback){
8828 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8835 this.fireEvent('invalid', this, msg);
8843 * trigger field - base class for combo..
8848 * @class Roo.bootstrap.TriggerField
8849 * @extends Roo.bootstrap.Input
8850 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8851 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8852 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8853 * for which you can provide a custom implementation. For example:
8855 var trigger = new Roo.bootstrap.TriggerField();
8856 trigger.onTriggerClick = myTriggerFn;
8857 trigger.applyTo('my-field');
8860 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8861 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8862 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8863 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8864 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8867 * Create a new TriggerField.
8868 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8869 * to the base TextField)
8871 Roo.bootstrap.TriggerField = function(config){
8872 this.mimicing = false;
8873 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8876 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8878 * @cfg {String} triggerClass A CSS class to apply to the trigger
8881 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8886 * @cfg {Boolean} removable (true|false) special filter default false
8890 /** @cfg {Boolean} grow @hide */
8891 /** @cfg {Number} growMin @hide */
8892 /** @cfg {Number} growMax @hide */
8898 autoSize: Roo.emptyFn,
8905 actionMode : 'wrap',
8910 getAutoCreate : function(){
8912 var align = this.labelAlign || this.parentLabelAlign();
8917 cls: 'form-group' //input-group
8924 type : this.inputType,
8925 cls : 'form-control',
8926 autocomplete: 'new-password',
8927 placeholder : this.placeholder || ''
8931 input.name = this.name;
8934 input.cls += ' input-' + this.size;
8937 if (this.disabled) {
8938 input.disabled=true;
8941 var inputblock = input;
8943 if(this.hasFeedback && !this.allowBlank){
8947 cls: 'glyphicon form-control-feedback'
8950 if(this.removable && !this.editable && !this.tickable){
8952 cls : 'has-feedback',
8958 cls : 'roo-combo-removable-btn close'
8965 cls : 'has-feedback',
8974 if(this.removable && !this.editable && !this.tickable){
8976 cls : 'roo-removable',
8982 cls : 'roo-combo-removable-btn close'
8989 if (this.before || this.after) {
8992 cls : 'input-group',
8996 inputblock.cn.push({
8998 cls : 'input-group-addon',
9003 inputblock.cn.push(input);
9005 if(this.hasFeedback && !this.allowBlank){
9006 inputblock.cls += ' has-feedback';
9007 inputblock.cn.push(feedback);
9011 inputblock.cn.push({
9013 cls : 'input-group-addon',
9026 cls: 'form-hidden-field'
9034 Roo.log('multiple');
9042 cls: 'form-hidden-field'
9046 cls: 'select2-choices',
9050 cls: 'select2-search-field',
9063 cls: 'select2-container input-group',
9068 // cls: 'typeahead typeahead-long dropdown-menu',
9069 // style: 'display:none'
9074 if(!this.multiple && this.showToggleBtn){
9080 if (this.caret != false) {
9083 cls: 'fa fa-' + this.caret
9090 cls : 'input-group-addon btn dropdown-toggle',
9095 cls: 'combobox-clear',
9109 combobox.cls += ' select2-container-multi';
9112 if (align ==='left' && this.fieldLabel.length) {
9114 Roo.log("left and has label");
9120 cls : 'control-label col-sm-' + this.labelWidth,
9121 html : this.fieldLabel
9125 cls : "col-sm-" + (12 - this.labelWidth),
9132 } else if ( this.fieldLabel.length) {
9138 //cls : 'input-group-addon',
9139 html : this.fieldLabel
9149 Roo.log(" no label && no align");
9156 ['xs','sm','md','lg'].map(function(size){
9157 if (settings[size]) {
9158 cfg.cls += ' col-' + size + '-' + settings[size];
9169 onResize : function(w, h){
9170 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9171 // if(typeof w == 'number'){
9172 // var x = w - this.trigger.getWidth();
9173 // this.inputEl().setWidth(this.adjustWidth('input', x));
9174 // this.trigger.setStyle('left', x+'px');
9179 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9182 getResizeEl : function(){
9183 return this.inputEl();
9187 getPositionEl : function(){
9188 return this.inputEl();
9192 alignErrorIcon : function(){
9193 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9197 initEvents : function(){
9201 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9202 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9203 if(!this.multiple && this.showToggleBtn){
9204 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9205 if(this.hideTrigger){
9206 this.trigger.setDisplayed(false);
9208 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9212 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9215 if(this.removable && !this.editable && !this.tickable){
9216 var close = this.closeTriggerEl();
9219 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9220 close.on('click', this.removeBtnClick, this, close);
9224 //this.trigger.addClassOnOver('x-form-trigger-over');
9225 //this.trigger.addClassOnClick('x-form-trigger-click');
9228 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9232 closeTriggerEl : function()
9234 var close = this.el.select('.roo-combo-removable-btn', true).first();
9235 return close ? close : false;
9238 removeBtnClick : function(e, h, el)
9242 if(this.fireEvent("remove", this) !== false){
9247 createList : function()
9249 this.list = Roo.get(document.body).createChild({
9251 cls: 'typeahead typeahead-long dropdown-menu',
9252 style: 'display:none'
9255 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9260 initTrigger : function(){
9265 onDestroy : function(){
9267 this.trigger.removeAllListeners();
9268 // this.trigger.remove();
9271 // this.wrap.remove();
9273 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9277 onFocus : function(){
9278 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9281 this.wrap.addClass('x-trigger-wrap-focus');
9282 this.mimicing = true;
9283 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9284 if(this.monitorTab){
9285 this.el.on("keydown", this.checkTab, this);
9292 checkTab : function(e){
9293 if(e.getKey() == e.TAB){
9299 onBlur : function(){
9304 mimicBlur : function(e, t){
9306 if(!this.wrap.contains(t) && this.validateBlur()){
9313 triggerBlur : function(){
9314 this.mimicing = false;
9315 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9316 if(this.monitorTab){
9317 this.el.un("keydown", this.checkTab, this);
9319 //this.wrap.removeClass('x-trigger-wrap-focus');
9320 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9324 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9325 validateBlur : function(e, t){
9330 onDisable : function(){
9331 this.inputEl().dom.disabled = true;
9332 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9334 // this.wrap.addClass('x-item-disabled');
9339 onEnable : function(){
9340 this.inputEl().dom.disabled = false;
9341 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9343 // this.el.removeClass('x-item-disabled');
9348 onShow : function(){
9349 var ae = this.getActionEl();
9352 ae.dom.style.display = '';
9353 ae.dom.style.visibility = 'visible';
9359 onHide : function(){
9360 var ae = this.getActionEl();
9361 ae.dom.style.display = 'none';
9365 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9366 * by an implementing function.
9368 * @param {EventObject} e
9370 onTriggerClick : Roo.emptyFn
9374 * Ext JS Library 1.1.1
9375 * Copyright(c) 2006-2007, Ext JS, LLC.
9377 * Originally Released Under LGPL - original licence link has changed is not relivant.
9380 * <script type="text/javascript">
9385 * @class Roo.data.SortTypes
9387 * Defines the default sorting (casting?) comparison functions used when sorting data.
9389 Roo.data.SortTypes = {
9391 * Default sort that does nothing
9392 * @param {Mixed} s The value being converted
9393 * @return {Mixed} The comparison value
9400 * The regular expression used to strip tags
9404 stripTagsRE : /<\/?[^>]+>/gi,
9407 * Strips all HTML tags to sort on text only
9408 * @param {Mixed} s The value being converted
9409 * @return {String} The comparison value
9411 asText : function(s){
9412 return String(s).replace(this.stripTagsRE, "");
9416 * Strips all HTML tags to sort on text only - Case insensitive
9417 * @param {Mixed} s The value being converted
9418 * @return {String} The comparison value
9420 asUCText : function(s){
9421 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9425 * Case insensitive string
9426 * @param {Mixed} s The value being converted
9427 * @return {String} The comparison value
9429 asUCString : function(s) {
9430 return String(s).toUpperCase();
9435 * @param {Mixed} s The value being converted
9436 * @return {Number} The comparison value
9438 asDate : function(s) {
9442 if(s instanceof Date){
9445 return Date.parse(String(s));
9450 * @param {Mixed} s The value being converted
9451 * @return {Float} The comparison value
9453 asFloat : function(s) {
9454 var val = parseFloat(String(s).replace(/,/g, ""));
9463 * @param {Mixed} s The value being converted
9464 * @return {Number} The comparison value
9466 asInt : function(s) {
9467 var val = parseInt(String(s).replace(/,/g, ""));
9475 * Ext JS Library 1.1.1
9476 * Copyright(c) 2006-2007, Ext JS, LLC.
9478 * Originally Released Under LGPL - original licence link has changed is not relivant.
9481 * <script type="text/javascript">
9485 * @class Roo.data.Record
9486 * Instances of this class encapsulate both record <em>definition</em> information, and record
9487 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9488 * to access Records cached in an {@link Roo.data.Store} object.<br>
9490 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9491 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9494 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9496 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9497 * {@link #create}. The parameters are the same.
9498 * @param {Array} data An associative Array of data values keyed by the field name.
9499 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9500 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9501 * not specified an integer id is generated.
9503 Roo.data.Record = function(data, id){
9504 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9509 * Generate a constructor for a specific record layout.
9510 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9511 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9512 * Each field definition object may contain the following properties: <ul>
9513 * <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,
9514 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9515 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9516 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9517 * is being used, then this is a string containing the javascript expression to reference the data relative to
9518 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9519 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9520 * this may be omitted.</p></li>
9521 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9522 * <ul><li>auto (Default, implies no conversion)</li>
9527 * <li>date</li></ul></p></li>
9528 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9529 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9530 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9531 * by the Reader into an object that will be stored in the Record. It is passed the
9532 * following parameters:<ul>
9533 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9535 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9537 * <br>usage:<br><pre><code>
9538 var TopicRecord = Roo.data.Record.create(
9539 {name: 'title', mapping: 'topic_title'},
9540 {name: 'author', mapping: 'username'},
9541 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9542 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9543 {name: 'lastPoster', mapping: 'user2'},
9544 {name: 'excerpt', mapping: 'post_text'}
9547 var myNewRecord = new TopicRecord({
9548 title: 'Do my job please',
9551 lastPost: new Date(),
9552 lastPoster: 'Animal',
9553 excerpt: 'No way dude!'
9555 myStore.add(myNewRecord);
9560 Roo.data.Record.create = function(o){
9562 f.superclass.constructor.apply(this, arguments);
9564 Roo.extend(f, Roo.data.Record);
9565 var p = f.prototype;
9566 p.fields = new Roo.util.MixedCollection(false, function(field){
9569 for(var i = 0, len = o.length; i < len; i++){
9570 p.fields.add(new Roo.data.Field(o[i]));
9572 f.getField = function(name){
9573 return p.fields.get(name);
9578 Roo.data.Record.AUTO_ID = 1000;
9579 Roo.data.Record.EDIT = 'edit';
9580 Roo.data.Record.REJECT = 'reject';
9581 Roo.data.Record.COMMIT = 'commit';
9583 Roo.data.Record.prototype = {
9585 * Readonly flag - true if this record has been modified.
9594 join : function(store){
9599 * Set the named field to the specified value.
9600 * @param {String} name The name of the field to set.
9601 * @param {Object} value The value to set the field to.
9603 set : function(name, value){
9604 if(this.data[name] == value){
9611 if(typeof this.modified[name] == 'undefined'){
9612 this.modified[name] = this.data[name];
9614 this.data[name] = value;
9615 if(!this.editing && this.store){
9616 this.store.afterEdit(this);
9621 * Get the value of the named field.
9622 * @param {String} name The name of the field to get the value of.
9623 * @return {Object} The value of the field.
9625 get : function(name){
9626 return this.data[name];
9630 beginEdit : function(){
9631 this.editing = true;
9636 cancelEdit : function(){
9637 this.editing = false;
9638 delete this.modified;
9642 endEdit : function(){
9643 this.editing = false;
9644 if(this.dirty && this.store){
9645 this.store.afterEdit(this);
9650 * Usually called by the {@link Roo.data.Store} which owns the Record.
9651 * Rejects all changes made to the Record since either creation, or the last commit operation.
9652 * Modified fields are reverted to their original values.
9654 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9655 * of reject operations.
9657 reject : function(){
9658 var m = this.modified;
9660 if(typeof m[n] != "function"){
9661 this.data[n] = m[n];
9665 delete this.modified;
9666 this.editing = false;
9668 this.store.afterReject(this);
9673 * Usually called by the {@link Roo.data.Store} which owns the Record.
9674 * Commits all changes made to the Record since either creation, or the last commit operation.
9676 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9677 * of commit operations.
9679 commit : function(){
9681 delete this.modified;
9682 this.editing = false;
9684 this.store.afterCommit(this);
9689 hasError : function(){
9690 return this.error != null;
9694 clearError : function(){
9699 * Creates a copy of this record.
9700 * @param {String} id (optional) A new record id if you don't want to use this record's id
9703 copy : function(newId) {
9704 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9708 * Ext JS Library 1.1.1
9709 * Copyright(c) 2006-2007, Ext JS, LLC.
9711 * Originally Released Under LGPL - original licence link has changed is not relivant.
9714 * <script type="text/javascript">
9720 * @class Roo.data.Store
9721 * @extends Roo.util.Observable
9722 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9723 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9725 * 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
9726 * has no knowledge of the format of the data returned by the Proxy.<br>
9728 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9729 * instances from the data object. These records are cached and made available through accessor functions.
9731 * Creates a new Store.
9732 * @param {Object} config A config object containing the objects needed for the Store to access data,
9733 * and read the data into Records.
9735 Roo.data.Store = function(config){
9736 this.data = new Roo.util.MixedCollection(false);
9737 this.data.getKey = function(o){
9740 this.baseParams = {};
9747 "multisort" : "_multisort"
9750 if(config && config.data){
9751 this.inlineData = config.data;
9755 Roo.apply(this, config);
9757 if(this.reader){ // reader passed
9758 this.reader = Roo.factory(this.reader, Roo.data);
9759 this.reader.xmodule = this.xmodule || false;
9760 if(!this.recordType){
9761 this.recordType = this.reader.recordType;
9763 if(this.reader.onMetaChange){
9764 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9768 if(this.recordType){
9769 this.fields = this.recordType.prototype.fields;
9775 * @event datachanged
9776 * Fires when the data cache has changed, and a widget which is using this Store
9777 * as a Record cache should refresh its view.
9778 * @param {Store} this
9783 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9784 * @param {Store} this
9785 * @param {Object} meta The JSON metadata
9790 * Fires when Records have been added to the Store
9791 * @param {Store} this
9792 * @param {Roo.data.Record[]} records The array of Records added
9793 * @param {Number} index The index at which the record(s) were added
9798 * Fires when a Record has been removed from the Store
9799 * @param {Store} this
9800 * @param {Roo.data.Record} record The Record that was removed
9801 * @param {Number} index The index at which the record was removed
9806 * Fires when a Record has been updated
9807 * @param {Store} this
9808 * @param {Roo.data.Record} record The Record that was updated
9809 * @param {String} operation The update operation being performed. Value may be one of:
9811 Roo.data.Record.EDIT
9812 Roo.data.Record.REJECT
9813 Roo.data.Record.COMMIT
9819 * Fires when the data cache has been cleared.
9820 * @param {Store} this
9825 * Fires before a request is made for a new data object. If the beforeload handler returns false
9826 * the load action will be canceled.
9827 * @param {Store} this
9828 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9832 * @event beforeloadadd
9833 * Fires after a new set of Records has been loaded.
9834 * @param {Store} this
9835 * @param {Roo.data.Record[]} records The Records that were loaded
9836 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9838 beforeloadadd : true,
9841 * Fires after a new set of Records has been loaded, before they are added to the store.
9842 * @param {Store} this
9843 * @param {Roo.data.Record[]} records The Records that were loaded
9844 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9845 * @params {Object} return from reader
9849 * @event loadexception
9850 * Fires if an exception occurs in the Proxy during loading.
9851 * Called with the signature of the Proxy's "loadexception" event.
9852 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9855 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9856 * @param {Object} load options
9857 * @param {Object} jsonData from your request (normally this contains the Exception)
9859 loadexception : true
9863 this.proxy = Roo.factory(this.proxy, Roo.data);
9864 this.proxy.xmodule = this.xmodule || false;
9865 this.relayEvents(this.proxy, ["loadexception"]);
9867 this.sortToggle = {};
9868 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9870 Roo.data.Store.superclass.constructor.call(this);
9872 if(this.inlineData){
9873 this.loadData(this.inlineData);
9874 delete this.inlineData;
9878 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9880 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9881 * without a remote query - used by combo/forms at present.
9885 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9888 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9891 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9892 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9895 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9896 * on any HTTP request
9899 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9902 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9906 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9907 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9912 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9913 * loaded or when a record is removed. (defaults to false).
9915 pruneModifiedRecords : false,
9921 * Add Records to the Store and fires the add event.
9922 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9924 add : function(records){
9925 records = [].concat(records);
9926 for(var i = 0, len = records.length; i < len; i++){
9927 records[i].join(this);
9929 var index = this.data.length;
9930 this.data.addAll(records);
9931 this.fireEvent("add", this, records, index);
9935 * Remove a Record from the Store and fires the remove event.
9936 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9938 remove : function(record){
9939 var index = this.data.indexOf(record);
9940 this.data.removeAt(index);
9941 if(this.pruneModifiedRecords){
9942 this.modified.remove(record);
9944 this.fireEvent("remove", this, record, index);
9948 * Remove all Records from the Store and fires the clear event.
9950 removeAll : function(){
9952 if(this.pruneModifiedRecords){
9955 this.fireEvent("clear", this);
9959 * Inserts Records to the Store at the given index and fires the add event.
9960 * @param {Number} index The start index at which to insert the passed Records.
9961 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9963 insert : function(index, records){
9964 records = [].concat(records);
9965 for(var i = 0, len = records.length; i < len; i++){
9966 this.data.insert(index, records[i]);
9967 records[i].join(this);
9969 this.fireEvent("add", this, records, index);
9973 * Get the index within the cache of the passed Record.
9974 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9975 * @return {Number} The index of the passed Record. Returns -1 if not found.
9977 indexOf : function(record){
9978 return this.data.indexOf(record);
9982 * Get the index within the cache of the Record with the passed id.
9983 * @param {String} id The id of the Record to find.
9984 * @return {Number} The index of the Record. Returns -1 if not found.
9986 indexOfId : function(id){
9987 return this.data.indexOfKey(id);
9991 * Get the Record with the specified id.
9992 * @param {String} id The id of the Record to find.
9993 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9995 getById : function(id){
9996 return this.data.key(id);
10000 * Get the Record at the specified index.
10001 * @param {Number} index The index of the Record to find.
10002 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10004 getAt : function(index){
10005 return this.data.itemAt(index);
10009 * Returns a range of Records between specified indices.
10010 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10011 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10012 * @return {Roo.data.Record[]} An array of Records
10014 getRange : function(start, end){
10015 return this.data.getRange(start, end);
10019 storeOptions : function(o){
10020 o = Roo.apply({}, o);
10023 this.lastOptions = o;
10027 * Loads the Record cache from the configured Proxy using the configured Reader.
10029 * If using remote paging, then the first load call must specify the <em>start</em>
10030 * and <em>limit</em> properties in the options.params property to establish the initial
10031 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10033 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10034 * and this call will return before the new data has been loaded. Perform any post-processing
10035 * in a callback function, or in a "load" event handler.</strong>
10037 * @param {Object} options An object containing properties which control loading options:<ul>
10038 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10039 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10040 * passed the following arguments:<ul>
10041 * <li>r : Roo.data.Record[]</li>
10042 * <li>options: Options object from the load call</li>
10043 * <li>success: Boolean success indicator</li></ul></li>
10044 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10045 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10048 load : function(options){
10049 options = options || {};
10050 if(this.fireEvent("beforeload", this, options) !== false){
10051 this.storeOptions(options);
10052 var p = Roo.apply(options.params || {}, this.baseParams);
10053 // if meta was not loaded from remote source.. try requesting it.
10054 if (!this.reader.metaFromRemote) {
10055 p._requestMeta = 1;
10057 if(this.sortInfo && this.remoteSort){
10058 var pn = this.paramNames;
10059 p[pn["sort"]] = this.sortInfo.field;
10060 p[pn["dir"]] = this.sortInfo.direction;
10062 if (this.multiSort) {
10063 var pn = this.paramNames;
10064 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10067 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10072 * Reloads the Record cache from the configured Proxy using the configured Reader and
10073 * the options from the last load operation performed.
10074 * @param {Object} options (optional) An object containing properties which may override the options
10075 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10076 * the most recently used options are reused).
10078 reload : function(options){
10079 this.load(Roo.applyIf(options||{}, this.lastOptions));
10083 // Called as a callback by the Reader during a load operation.
10084 loadRecords : function(o, options, success){
10085 if(!o || success === false){
10086 if(success !== false){
10087 this.fireEvent("load", this, [], options, o);
10089 if(options.callback){
10090 options.callback.call(options.scope || this, [], options, false);
10094 // if data returned failure - throw an exception.
10095 if (o.success === false) {
10096 // show a message if no listener is registered.
10097 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10098 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10100 // loadmask wil be hooked into this..
10101 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10104 var r = o.records, t = o.totalRecords || r.length;
10106 this.fireEvent("beforeloadadd", this, r, options, o);
10108 if(!options || options.add !== true){
10109 if(this.pruneModifiedRecords){
10110 this.modified = [];
10112 for(var i = 0, len = r.length; i < len; i++){
10116 this.data = this.snapshot;
10117 delete this.snapshot;
10120 this.data.addAll(r);
10121 this.totalLength = t;
10123 this.fireEvent("datachanged", this);
10125 this.totalLength = Math.max(t, this.data.length+r.length);
10128 this.fireEvent("load", this, r, options, o);
10129 if(options.callback){
10130 options.callback.call(options.scope || this, r, options, true);
10136 * Loads data from a passed data block. A Reader which understands the format of the data
10137 * must have been configured in the constructor.
10138 * @param {Object} data The data block from which to read the Records. The format of the data expected
10139 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10140 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10142 loadData : function(o, append){
10143 var r = this.reader.readRecords(o);
10144 this.loadRecords(r, {add: append}, true);
10148 * Gets the number of cached records.
10150 * <em>If using paging, this may not be the total size of the dataset. If the data object
10151 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10152 * the data set size</em>
10154 getCount : function(){
10155 return this.data.length || 0;
10159 * Gets the total number of records in the dataset as returned by the server.
10161 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10162 * the dataset size</em>
10164 getTotalCount : function(){
10165 return this.totalLength || 0;
10169 * Returns the sort state of the Store as an object with two properties:
10171 field {String} The name of the field by which the Records are sorted
10172 direction {String} The sort order, "ASC" or "DESC"
10175 getSortState : function(){
10176 return this.sortInfo;
10180 applySort : function(){
10181 if(this.sortInfo && !this.remoteSort){
10182 var s = this.sortInfo, f = s.field;
10183 var st = this.fields.get(f).sortType;
10184 var fn = function(r1, r2){
10185 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10186 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10188 this.data.sort(s.direction, fn);
10189 if(this.snapshot && this.snapshot != this.data){
10190 this.snapshot.sort(s.direction, fn);
10196 * Sets the default sort column and order to be used by the next load operation.
10197 * @param {String} fieldName The name of the field to sort by.
10198 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10200 setDefaultSort : function(field, dir){
10201 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10205 * Sort the Records.
10206 * If remote sorting is used, the sort is performed on the server, and the cache is
10207 * reloaded. If local sorting is used, the cache is sorted internally.
10208 * @param {String} fieldName The name of the field to sort by.
10209 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10211 sort : function(fieldName, dir){
10212 var f = this.fields.get(fieldName);
10214 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10216 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10217 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10222 this.sortToggle[f.name] = dir;
10223 this.sortInfo = {field: f.name, direction: dir};
10224 if(!this.remoteSort){
10226 this.fireEvent("datachanged", this);
10228 this.load(this.lastOptions);
10233 * Calls the specified function for each of the Records in the cache.
10234 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10235 * Returning <em>false</em> aborts and exits the iteration.
10236 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10238 each : function(fn, scope){
10239 this.data.each(fn, scope);
10243 * Gets all records modified since the last commit. Modified records are persisted across load operations
10244 * (e.g., during paging).
10245 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10247 getModifiedRecords : function(){
10248 return this.modified;
10252 createFilterFn : function(property, value, anyMatch){
10253 if(!value.exec){ // not a regex
10254 value = String(value);
10255 if(value.length == 0){
10258 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10260 return function(r){
10261 return value.test(r.data[property]);
10266 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10267 * @param {String} property A field on your records
10268 * @param {Number} start The record index to start at (defaults to 0)
10269 * @param {Number} end The last record index to include (defaults to length - 1)
10270 * @return {Number} The sum
10272 sum : function(property, start, end){
10273 var rs = this.data.items, v = 0;
10274 start = start || 0;
10275 end = (end || end === 0) ? end : rs.length-1;
10277 for(var i = start; i <= end; i++){
10278 v += (rs[i].data[property] || 0);
10284 * Filter the records by a specified property.
10285 * @param {String} field A field on your records
10286 * @param {String/RegExp} value Either a string that the field
10287 * should start with or a RegExp to test against the field
10288 * @param {Boolean} anyMatch True to match any part not just the beginning
10290 filter : function(property, value, anyMatch){
10291 var fn = this.createFilterFn(property, value, anyMatch);
10292 return fn ? this.filterBy(fn) : this.clearFilter();
10296 * Filter by a function. The specified function will be called with each
10297 * record in this data source. If the function returns true the record is included,
10298 * otherwise it is filtered.
10299 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10300 * @param {Object} scope (optional) The scope of the function (defaults to this)
10302 filterBy : function(fn, scope){
10303 this.snapshot = this.snapshot || this.data;
10304 this.data = this.queryBy(fn, scope||this);
10305 this.fireEvent("datachanged", this);
10309 * Query the records by a specified property.
10310 * @param {String} field A field on your records
10311 * @param {String/RegExp} value Either a string that the field
10312 * should start with or a RegExp to test against the field
10313 * @param {Boolean} anyMatch True to match any part not just the beginning
10314 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10316 query : function(property, value, anyMatch){
10317 var fn = this.createFilterFn(property, value, anyMatch);
10318 return fn ? this.queryBy(fn) : this.data.clone();
10322 * Query by a function. The specified function will be called with each
10323 * record in this data source. If the function returns true the record is included
10325 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10326 * @param {Object} scope (optional) The scope of the function (defaults to this)
10327 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10329 queryBy : function(fn, scope){
10330 var data = this.snapshot || this.data;
10331 return data.filterBy(fn, scope||this);
10335 * Collects unique values for a particular dataIndex from this store.
10336 * @param {String} dataIndex The property to collect
10337 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10338 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10339 * @return {Array} An array of the unique values
10341 collect : function(dataIndex, allowNull, bypassFilter){
10342 var d = (bypassFilter === true && this.snapshot) ?
10343 this.snapshot.items : this.data.items;
10344 var v, sv, r = [], l = {};
10345 for(var i = 0, len = d.length; i < len; i++){
10346 v = d[i].data[dataIndex];
10348 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10357 * Revert to a view of the Record cache with no filtering applied.
10358 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10360 clearFilter : function(suppressEvent){
10361 if(this.snapshot && this.snapshot != this.data){
10362 this.data = this.snapshot;
10363 delete this.snapshot;
10364 if(suppressEvent !== true){
10365 this.fireEvent("datachanged", this);
10371 afterEdit : function(record){
10372 if(this.modified.indexOf(record) == -1){
10373 this.modified.push(record);
10375 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10379 afterReject : function(record){
10380 this.modified.remove(record);
10381 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10385 afterCommit : function(record){
10386 this.modified.remove(record);
10387 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10391 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10392 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10394 commitChanges : function(){
10395 var m = this.modified.slice(0);
10396 this.modified = [];
10397 for(var i = 0, len = m.length; i < len; i++){
10403 * Cancel outstanding changes on all changed records.
10405 rejectChanges : function(){
10406 var m = this.modified.slice(0);
10407 this.modified = [];
10408 for(var i = 0, len = m.length; i < len; i++){
10413 onMetaChange : function(meta, rtype, o){
10414 this.recordType = rtype;
10415 this.fields = rtype.prototype.fields;
10416 delete this.snapshot;
10417 this.sortInfo = meta.sortInfo || this.sortInfo;
10418 this.modified = [];
10419 this.fireEvent('metachange', this, this.reader.meta);
10422 moveIndex : function(data, type)
10424 var index = this.indexOf(data);
10426 var newIndex = index + type;
10430 this.insert(newIndex, data);
10435 * Ext JS Library 1.1.1
10436 * Copyright(c) 2006-2007, Ext JS, LLC.
10438 * Originally Released Under LGPL - original licence link has changed is not relivant.
10441 * <script type="text/javascript">
10445 * @class Roo.data.SimpleStore
10446 * @extends Roo.data.Store
10447 * Small helper class to make creating Stores from Array data easier.
10448 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10449 * @cfg {Array} fields An array of field definition objects, or field name strings.
10450 * @cfg {Array} data The multi-dimensional array of data
10452 * @param {Object} config
10454 Roo.data.SimpleStore = function(config){
10455 Roo.data.SimpleStore.superclass.constructor.call(this, {
10457 reader: new Roo.data.ArrayReader({
10460 Roo.data.Record.create(config.fields)
10462 proxy : new Roo.data.MemoryProxy(config.data)
10466 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10468 * Ext JS Library 1.1.1
10469 * Copyright(c) 2006-2007, Ext JS, LLC.
10471 * Originally Released Under LGPL - original licence link has changed is not relivant.
10474 * <script type="text/javascript">
10479 * @extends Roo.data.Store
10480 * @class Roo.data.JsonStore
10481 * Small helper class to make creating Stores for JSON data easier. <br/>
10483 var store = new Roo.data.JsonStore({
10484 url: 'get-images.php',
10486 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10489 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10490 * JsonReader and HttpProxy (unless inline data is provided).</b>
10491 * @cfg {Array} fields An array of field definition objects, or field name strings.
10493 * @param {Object} config
10495 Roo.data.JsonStore = function(c){
10496 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10497 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10498 reader: new Roo.data.JsonReader(c, c.fields)
10501 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10503 * Ext JS Library 1.1.1
10504 * Copyright(c) 2006-2007, Ext JS, LLC.
10506 * Originally Released Under LGPL - original licence link has changed is not relivant.
10509 * <script type="text/javascript">
10513 Roo.data.Field = function(config){
10514 if(typeof config == "string"){
10515 config = {name: config};
10517 Roo.apply(this, config);
10520 this.type = "auto";
10523 var st = Roo.data.SortTypes;
10524 // named sortTypes are supported, here we look them up
10525 if(typeof this.sortType == "string"){
10526 this.sortType = st[this.sortType];
10529 // set default sortType for strings and dates
10530 if(!this.sortType){
10533 this.sortType = st.asUCString;
10536 this.sortType = st.asDate;
10539 this.sortType = st.none;
10544 var stripRe = /[\$,%]/g;
10546 // prebuilt conversion function for this field, instead of
10547 // switching every time we're reading a value
10549 var cv, dateFormat = this.dateFormat;
10554 cv = function(v){ return v; };
10557 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10561 return v !== undefined && v !== null && v !== '' ?
10562 parseInt(String(v).replace(stripRe, ""), 10) : '';
10567 return v !== undefined && v !== null && v !== '' ?
10568 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10573 cv = function(v){ return v === true || v === "true" || v == 1; };
10580 if(v instanceof Date){
10584 if(dateFormat == "timestamp"){
10585 return new Date(v*1000);
10587 return Date.parseDate(v, dateFormat);
10589 var parsed = Date.parse(v);
10590 return parsed ? new Date(parsed) : null;
10599 Roo.data.Field.prototype = {
10607 * Ext JS Library 1.1.1
10608 * Copyright(c) 2006-2007, Ext JS, LLC.
10610 * Originally Released Under LGPL - original licence link has changed is not relivant.
10613 * <script type="text/javascript">
10616 // Base class for reading structured data from a data source. This class is intended to be
10617 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10620 * @class Roo.data.DataReader
10621 * Base class for reading structured data from a data source. This class is intended to be
10622 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10625 Roo.data.DataReader = function(meta, recordType){
10629 this.recordType = recordType instanceof Array ?
10630 Roo.data.Record.create(recordType) : recordType;
10633 Roo.data.DataReader.prototype = {
10635 * Create an empty record
10636 * @param {Object} data (optional) - overlay some values
10637 * @return {Roo.data.Record} record created.
10639 newRow : function(d) {
10641 this.recordType.prototype.fields.each(function(c) {
10643 case 'int' : da[c.name] = 0; break;
10644 case 'date' : da[c.name] = new Date(); break;
10645 case 'float' : da[c.name] = 0.0; break;
10646 case 'boolean' : da[c.name] = false; break;
10647 default : da[c.name] = ""; break;
10651 return new this.recordType(Roo.apply(da, d));
10656 * Ext JS Library 1.1.1
10657 * Copyright(c) 2006-2007, Ext JS, LLC.
10659 * Originally Released Under LGPL - original licence link has changed is not relivant.
10662 * <script type="text/javascript">
10666 * @class Roo.data.DataProxy
10667 * @extends Roo.data.Observable
10668 * This class is an abstract base class for implementations which provide retrieval of
10669 * unformatted data objects.<br>
10671 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10672 * (of the appropriate type which knows how to parse the data object) to provide a block of
10673 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10675 * Custom implementations must implement the load method as described in
10676 * {@link Roo.data.HttpProxy#load}.
10678 Roo.data.DataProxy = function(){
10681 * @event beforeload
10682 * Fires before a network request is made to retrieve a data object.
10683 * @param {Object} This DataProxy object.
10684 * @param {Object} params The params parameter to the load function.
10689 * Fires before the load method's callback is called.
10690 * @param {Object} This DataProxy object.
10691 * @param {Object} o The data object.
10692 * @param {Object} arg The callback argument object passed to the load function.
10696 * @event loadexception
10697 * Fires if an Exception occurs during data retrieval.
10698 * @param {Object} This DataProxy object.
10699 * @param {Object} o The data object.
10700 * @param {Object} arg The callback argument object passed to the load function.
10701 * @param {Object} e The Exception.
10703 loadexception : true
10705 Roo.data.DataProxy.superclass.constructor.call(this);
10708 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10711 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10715 * Ext JS Library 1.1.1
10716 * Copyright(c) 2006-2007, Ext JS, LLC.
10718 * Originally Released Under LGPL - original licence link has changed is not relivant.
10721 * <script type="text/javascript">
10724 * @class Roo.data.MemoryProxy
10725 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10726 * to the Reader when its load method is called.
10728 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10730 Roo.data.MemoryProxy = function(data){
10734 Roo.data.MemoryProxy.superclass.constructor.call(this);
10738 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10740 * Load data from the requested source (in this case an in-memory
10741 * data object passed to the constructor), read the data object into
10742 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10743 * process that block using the passed callback.
10744 * @param {Object} params This parameter is not used by the MemoryProxy class.
10745 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10746 * object into a block of Roo.data.Records.
10747 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10748 * The function must be passed <ul>
10749 * <li>The Record block object</li>
10750 * <li>The "arg" argument from the load function</li>
10751 * <li>A boolean success indicator</li>
10753 * @param {Object} scope The scope in which to call the callback
10754 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10756 load : function(params, reader, callback, scope, arg){
10757 params = params || {};
10760 result = reader.readRecords(this.data);
10762 this.fireEvent("loadexception", this, arg, null, e);
10763 callback.call(scope, null, arg, false);
10766 callback.call(scope, result, arg, true);
10770 update : function(params, records){
10775 * Ext JS Library 1.1.1
10776 * Copyright(c) 2006-2007, Ext JS, LLC.
10778 * Originally Released Under LGPL - original licence link has changed is not relivant.
10781 * <script type="text/javascript">
10784 * @class Roo.data.HttpProxy
10785 * @extends Roo.data.DataProxy
10786 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10787 * configured to reference a certain URL.<br><br>
10789 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10790 * from which the running page was served.<br><br>
10792 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10794 * Be aware that to enable the browser to parse an XML document, the server must set
10795 * the Content-Type header in the HTTP response to "text/xml".
10797 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10798 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10799 * will be used to make the request.
10801 Roo.data.HttpProxy = function(conn){
10802 Roo.data.HttpProxy.superclass.constructor.call(this);
10803 // is conn a conn config or a real conn?
10805 this.useAjax = !conn || !conn.events;
10809 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10810 // thse are take from connection...
10813 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10816 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10817 * extra parameters to each request made by this object. (defaults to undefined)
10820 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10821 * to each request made by this object. (defaults to undefined)
10824 * @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)
10827 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10830 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10836 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10840 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10841 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10842 * a finer-grained basis than the DataProxy events.
10844 getConnection : function(){
10845 return this.useAjax ? Roo.Ajax : this.conn;
10849 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10850 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10851 * process that block using the passed callback.
10852 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10853 * for the request to the remote server.
10854 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10855 * object into a block of Roo.data.Records.
10856 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10857 * The function must be passed <ul>
10858 * <li>The Record block object</li>
10859 * <li>The "arg" argument from the load function</li>
10860 * <li>A boolean success indicator</li>
10862 * @param {Object} scope The scope in which to call the callback
10863 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10865 load : function(params, reader, callback, scope, arg){
10866 if(this.fireEvent("beforeload", this, params) !== false){
10868 params : params || {},
10870 callback : callback,
10875 callback : this.loadResponse,
10879 Roo.applyIf(o, this.conn);
10880 if(this.activeRequest){
10881 Roo.Ajax.abort(this.activeRequest);
10883 this.activeRequest = Roo.Ajax.request(o);
10885 this.conn.request(o);
10888 callback.call(scope||this, null, arg, false);
10893 loadResponse : function(o, success, response){
10894 delete this.activeRequest;
10896 this.fireEvent("loadexception", this, o, response);
10897 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10902 result = o.reader.read(response);
10904 this.fireEvent("loadexception", this, o, response, e);
10905 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10909 this.fireEvent("load", this, o, o.request.arg);
10910 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10914 update : function(dataSet){
10919 updateResponse : function(dataSet){
10924 * Ext JS Library 1.1.1
10925 * Copyright(c) 2006-2007, Ext JS, LLC.
10927 * Originally Released Under LGPL - original licence link has changed is not relivant.
10930 * <script type="text/javascript">
10934 * @class Roo.data.ScriptTagProxy
10935 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10936 * other than the originating domain of the running page.<br><br>
10938 * <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
10939 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10941 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10942 * source code that is used as the source inside a <script> tag.<br><br>
10944 * In order for the browser to process the returned data, the server must wrap the data object
10945 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10946 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10947 * depending on whether the callback name was passed:
10950 boolean scriptTag = false;
10951 String cb = request.getParameter("callback");
10954 response.setContentType("text/javascript");
10956 response.setContentType("application/x-json");
10958 Writer out = response.getWriter();
10960 out.write(cb + "(");
10962 out.print(dataBlock.toJsonString());
10969 * @param {Object} config A configuration object.
10971 Roo.data.ScriptTagProxy = function(config){
10972 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10973 Roo.apply(this, config);
10974 this.head = document.getElementsByTagName("head")[0];
10977 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10979 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10981 * @cfg {String} url The URL from which to request the data object.
10984 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10988 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10989 * the server the name of the callback function set up by the load call to process the returned data object.
10990 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10991 * javascript output which calls this named function passing the data object as its only parameter.
10993 callbackParam : "callback",
10995 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10996 * name to the request.
11001 * Load data from the configured URL, read the data object into
11002 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11003 * process that block using the passed callback.
11004 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11005 * for the request to the remote server.
11006 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11007 * object into a block of Roo.data.Records.
11008 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11009 * The function must be passed <ul>
11010 * <li>The Record block object</li>
11011 * <li>The "arg" argument from the load function</li>
11012 * <li>A boolean success indicator</li>
11014 * @param {Object} scope The scope in which to call the callback
11015 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11017 load : function(params, reader, callback, scope, arg){
11018 if(this.fireEvent("beforeload", this, params) !== false){
11020 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11022 var url = this.url;
11023 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11025 url += "&_dc=" + (new Date().getTime());
11027 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11030 cb : "stcCallback"+transId,
11031 scriptId : "stcScript"+transId,
11035 callback : callback,
11041 window[trans.cb] = function(o){
11042 conn.handleResponse(o, trans);
11045 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11047 if(this.autoAbort !== false){
11051 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11053 var script = document.createElement("script");
11054 script.setAttribute("src", url);
11055 script.setAttribute("type", "text/javascript");
11056 script.setAttribute("id", trans.scriptId);
11057 this.head.appendChild(script);
11059 this.trans = trans;
11061 callback.call(scope||this, null, arg, false);
11066 isLoading : function(){
11067 return this.trans ? true : false;
11071 * Abort the current server request.
11073 abort : function(){
11074 if(this.isLoading()){
11075 this.destroyTrans(this.trans);
11080 destroyTrans : function(trans, isLoaded){
11081 this.head.removeChild(document.getElementById(trans.scriptId));
11082 clearTimeout(trans.timeoutId);
11084 window[trans.cb] = undefined;
11086 delete window[trans.cb];
11089 // if hasn't been loaded, wait for load to remove it to prevent script error
11090 window[trans.cb] = function(){
11091 window[trans.cb] = undefined;
11093 delete window[trans.cb];
11100 handleResponse : function(o, trans){
11101 this.trans = false;
11102 this.destroyTrans(trans, true);
11105 result = trans.reader.readRecords(o);
11107 this.fireEvent("loadexception", this, o, trans.arg, e);
11108 trans.callback.call(trans.scope||window, null, trans.arg, false);
11111 this.fireEvent("load", this, o, trans.arg);
11112 trans.callback.call(trans.scope||window, result, trans.arg, true);
11116 handleFailure : function(trans){
11117 this.trans = false;
11118 this.destroyTrans(trans, false);
11119 this.fireEvent("loadexception", this, null, trans.arg);
11120 trans.callback.call(trans.scope||window, null, trans.arg, false);
11124 * Ext JS Library 1.1.1
11125 * Copyright(c) 2006-2007, Ext JS, LLC.
11127 * Originally Released Under LGPL - original licence link has changed is not relivant.
11130 * <script type="text/javascript">
11134 * @class Roo.data.JsonReader
11135 * @extends Roo.data.DataReader
11136 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11137 * based on mappings in a provided Roo.data.Record constructor.
11139 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11140 * in the reply previously.
11145 var RecordDef = Roo.data.Record.create([
11146 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11147 {name: 'occupation'} // This field will use "occupation" as the mapping.
11149 var myReader = new Roo.data.JsonReader({
11150 totalProperty: "results", // The property which contains the total dataset size (optional)
11151 root: "rows", // The property which contains an Array of row objects
11152 id: "id" // The property within each row object that provides an ID for the record (optional)
11156 * This would consume a JSON file like this:
11158 { 'results': 2, 'rows': [
11159 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11160 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11163 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11164 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11165 * paged from the remote server.
11166 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11167 * @cfg {String} root name of the property which contains the Array of row objects.
11168 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11169 * @cfg {Array} fields Array of field definition objects
11171 * Create a new JsonReader
11172 * @param {Object} meta Metadata configuration options
11173 * @param {Object} recordType Either an Array of field definition objects,
11174 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11176 Roo.data.JsonReader = function(meta, recordType){
11179 // set some defaults:
11180 Roo.applyIf(meta, {
11181 totalProperty: 'total',
11182 successProperty : 'success',
11187 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11189 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11192 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11193 * Used by Store query builder to append _requestMeta to params.
11196 metaFromRemote : false,
11198 * This method is only used by a DataProxy which has retrieved data from a remote server.
11199 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11200 * @return {Object} data A data block which is used by an Roo.data.Store object as
11201 * a cache of Roo.data.Records.
11203 read : function(response){
11204 var json = response.responseText;
11206 var o = /* eval:var:o */ eval("("+json+")");
11208 throw {message: "JsonReader.read: Json object not found"};
11214 this.metaFromRemote = true;
11215 this.meta = o.metaData;
11216 this.recordType = Roo.data.Record.create(o.metaData.fields);
11217 this.onMetaChange(this.meta, this.recordType, o);
11219 return this.readRecords(o);
11222 // private function a store will implement
11223 onMetaChange : function(meta, recordType, o){
11230 simpleAccess: function(obj, subsc) {
11237 getJsonAccessor: function(){
11239 return function(expr) {
11241 return(re.test(expr))
11242 ? new Function("obj", "return obj." + expr)
11247 return Roo.emptyFn;
11252 * Create a data block containing Roo.data.Records from an XML document.
11253 * @param {Object} o An object which contains an Array of row objects in the property specified
11254 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11255 * which contains the total size of the dataset.
11256 * @return {Object} data A data block which is used by an Roo.data.Store object as
11257 * a cache of Roo.data.Records.
11259 readRecords : function(o){
11261 * After any data loads, the raw JSON data is available for further custom processing.
11265 var s = this.meta, Record = this.recordType,
11266 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11268 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11270 if(s.totalProperty) {
11271 this.getTotal = this.getJsonAccessor(s.totalProperty);
11273 if(s.successProperty) {
11274 this.getSuccess = this.getJsonAccessor(s.successProperty);
11276 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11278 var g = this.getJsonAccessor(s.id);
11279 this.getId = function(rec) {
11281 return (r === undefined || r === "") ? null : r;
11284 this.getId = function(){return null;};
11287 for(var jj = 0; jj < fl; jj++){
11289 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11290 this.ef[jj] = this.getJsonAccessor(map);
11294 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11295 if(s.totalProperty){
11296 var vt = parseInt(this.getTotal(o), 10);
11301 if(s.successProperty){
11302 var vs = this.getSuccess(o);
11303 if(vs === false || vs === 'false'){
11308 for(var i = 0; i < c; i++){
11311 var id = this.getId(n);
11312 for(var j = 0; j < fl; j++){
11314 var v = this.ef[j](n);
11316 Roo.log('missing convert for ' + f.name);
11320 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11322 var record = new Record(values, id);
11324 records[i] = record;
11330 totalRecords : totalRecords
11335 * Ext JS Library 1.1.1
11336 * Copyright(c) 2006-2007, Ext JS, LLC.
11338 * Originally Released Under LGPL - original licence link has changed is not relivant.
11341 * <script type="text/javascript">
11345 * @class Roo.data.ArrayReader
11346 * @extends Roo.data.DataReader
11347 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11348 * Each element of that Array represents a row of data fields. The
11349 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11350 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11354 var RecordDef = Roo.data.Record.create([
11355 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11356 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11358 var myReader = new Roo.data.ArrayReader({
11359 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11363 * This would consume an Array like this:
11365 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11367 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11369 * Create a new JsonReader
11370 * @param {Object} meta Metadata configuration options.
11371 * @param {Object} recordType Either an Array of field definition objects
11372 * as specified to {@link Roo.data.Record#create},
11373 * or an {@link Roo.data.Record} object
11374 * created using {@link Roo.data.Record#create}.
11376 Roo.data.ArrayReader = function(meta, recordType){
11377 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11380 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11382 * Create a data block containing Roo.data.Records from an XML document.
11383 * @param {Object} o An Array of row objects which represents the dataset.
11384 * @return {Object} data A data block which is used by an Roo.data.Store object as
11385 * a cache of Roo.data.Records.
11387 readRecords : function(o){
11388 var sid = this.meta ? this.meta.id : null;
11389 var recordType = this.recordType, fields = recordType.prototype.fields;
11392 for(var i = 0; i < root.length; i++){
11395 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11396 for(var j = 0, jlen = fields.length; j < jlen; j++){
11397 var f = fields.items[j];
11398 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11399 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11401 values[f.name] = v;
11403 var record = new recordType(values, id);
11405 records[records.length] = record;
11409 totalRecords : records.length
11418 * @class Roo.bootstrap.ComboBox
11419 * @extends Roo.bootstrap.TriggerField
11420 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11421 * @cfg {Boolean} append (true|false) default false
11422 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11423 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11424 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11425 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11426 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11427 * @cfg {Boolean} animate default true
11428 * @cfg {Boolean} emptyResultText only for touch device
11429 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11431 * Create a new ComboBox.
11432 * @param {Object} config Configuration options
11434 Roo.bootstrap.ComboBox = function(config){
11435 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11439 * Fires when the dropdown list is expanded
11440 * @param {Roo.bootstrap.ComboBox} combo This combo box
11445 * Fires when the dropdown list is collapsed
11446 * @param {Roo.bootstrap.ComboBox} combo This combo box
11450 * @event beforeselect
11451 * Fires before a list item is selected. Return false to cancel the selection.
11452 * @param {Roo.bootstrap.ComboBox} combo This combo box
11453 * @param {Roo.data.Record} record The data record returned from the underlying store
11454 * @param {Number} index The index of the selected item in the dropdown list
11456 'beforeselect' : true,
11459 * Fires when a list item is selected
11460 * @param {Roo.bootstrap.ComboBox} combo This combo box
11461 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11462 * @param {Number} index The index of the selected item in the dropdown list
11466 * @event beforequery
11467 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11468 * The event object passed has these properties:
11469 * @param {Roo.bootstrap.ComboBox} combo This combo box
11470 * @param {String} query The query
11471 * @param {Boolean} forceAll true to force "all" query
11472 * @param {Boolean} cancel true to cancel the query
11473 * @param {Object} e The query event object
11475 'beforequery': true,
11478 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11479 * @param {Roo.bootstrap.ComboBox} combo This combo box
11484 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11485 * @param {Roo.bootstrap.ComboBox} combo This combo box
11486 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11491 * Fires when the remove value from the combobox array
11492 * @param {Roo.bootstrap.ComboBox} combo This combo box
11496 * @event specialfilter
11497 * Fires when specialfilter
11498 * @param {Roo.bootstrap.ComboBox} combo This combo box
11500 'specialfilter' : true,
11503 * Fires when tick the element
11504 * @param {Roo.bootstrap.ComboBox} combo This combo box
11511 this.tickItems = [];
11513 this.selectedIndex = -1;
11514 if(this.mode == 'local'){
11515 if(config.queryDelay === undefined){
11516 this.queryDelay = 10;
11518 if(config.minChars === undefined){
11524 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11527 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11528 * rendering into an Roo.Editor, defaults to false)
11531 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11532 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11535 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11538 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11539 * the dropdown list (defaults to undefined, with no header element)
11543 * @cfg {String/Roo.Template} tpl The template to use to render the output
11547 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11549 listWidth: undefined,
11551 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11552 * mode = 'remote' or 'text' if mode = 'local')
11554 displayField: undefined,
11557 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11558 * mode = 'remote' or 'value' if mode = 'local').
11559 * Note: use of a valueField requires the user make a selection
11560 * in order for a value to be mapped.
11562 valueField: undefined,
11566 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11567 * field's data value (defaults to the underlying DOM element's name)
11569 hiddenName: undefined,
11571 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11575 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11577 selectedClass: 'active',
11580 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11584 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11585 * anchor positions (defaults to 'tl-bl')
11587 listAlign: 'tl-bl?',
11589 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11593 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11594 * query specified by the allQuery config option (defaults to 'query')
11596 triggerAction: 'query',
11598 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11599 * (defaults to 4, does not apply if editable = false)
11603 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11604 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11608 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11609 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11613 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11614 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11618 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11619 * when editable = true (defaults to false)
11621 selectOnFocus:false,
11623 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11625 queryParam: 'query',
11627 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11628 * when mode = 'remote' (defaults to 'Loading...')
11630 loadingText: 'Loading...',
11632 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11636 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11640 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11641 * traditional select (defaults to true)
11645 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11649 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11653 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11654 * listWidth has a higher value)
11658 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11659 * allow the user to set arbitrary text into the field (defaults to false)
11661 forceSelection:false,
11663 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11664 * if typeAhead = true (defaults to 250)
11666 typeAheadDelay : 250,
11668 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11669 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11671 valueNotFoundText : undefined,
11673 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11675 blockFocus : false,
11678 * @cfg {Boolean} disableClear Disable showing of clear button.
11680 disableClear : false,
11682 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11684 alwaysQuery : false,
11687 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11692 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11694 invalidClass : "has-warning",
11697 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11699 validClass : "has-success",
11702 * @cfg {Boolean} specialFilter (true|false) special filter default false
11704 specialFilter : false,
11707 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11709 mobileTouchView : true,
11721 btnPosition : 'right',
11722 triggerList : true,
11723 showToggleBtn : true,
11725 emptyResultText: 'Empty',
11726 triggerText : 'Select',
11728 // element that contains real text value.. (when hidden is used..)
11730 getAutoCreate : function()
11738 if(Roo.isTouch && this.mobileTouchView){
11739 cfg = this.getAutoCreateTouchView();
11746 if(!this.tickable){
11747 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11752 * ComboBox with tickable selections
11755 var align = this.labelAlign || this.parentLabelAlign();
11758 cls : 'form-group roo-combobox-tickable' //input-group
11763 cls : 'tickable-buttons',
11768 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11769 html : this.triggerText
11775 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11782 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11789 buttons.cn.unshift({
11791 cls: 'select2-search-field-input'
11797 Roo.each(buttons.cn, function(c){
11799 c.cls += ' btn-' + _this.size;
11802 if (_this.disabled) {
11813 cls: 'form-hidden-field'
11817 cls: 'select2-choices',
11821 cls: 'select2-search-field',
11833 cls: 'select2-container input-group select2-container-multi',
11838 // cls: 'typeahead typeahead-long dropdown-menu',
11839 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11844 if(this.hasFeedback && !this.allowBlank){
11848 cls: 'glyphicon form-control-feedback'
11851 combobox.cn.push(feedback);
11854 if (align ==='left' && this.fieldLabel.length) {
11856 Roo.log("left and has label");
11862 cls : 'control-label col-sm-' + this.labelWidth,
11863 html : this.fieldLabel
11867 cls : "col-sm-" + (12 - this.labelWidth),
11874 } else if ( this.fieldLabel.length) {
11880 //cls : 'input-group-addon',
11881 html : this.fieldLabel
11891 Roo.log(" no label && no align");
11898 ['xs','sm','md','lg'].map(function(size){
11899 if (settings[size]) {
11900 cfg.cls += ' col-' + size + '-' + settings[size];
11908 _initEventsCalled : false,
11911 initEvents: function()
11914 if (this._initEventsCalled) { // as we call render... prevent looping...
11917 this._initEventsCalled = true;
11920 throw "can not find store for combo";
11923 this.store = Roo.factory(this.store, Roo.data);
11925 // if we are building from html. then this element is so complex, that we can not really
11926 // use the rendered HTML.
11927 // so we have to trash and replace the previous code.
11928 if (Roo.XComponent.build_from_html) {
11930 // remove this element....
11931 var e = this.el.dom, k=0;
11932 while (e ) { e = e.previousSibling; ++k;}
11937 this.rendered = false;
11939 this.render(this.parent().getChildContainer(true), k);
11950 if(Roo.isTouch && this.mobileTouchView){
11951 this.initTouchView();
11956 this.initTickableEvents();
11960 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11962 if(this.hiddenName){
11964 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11966 this.hiddenField.dom.value =
11967 this.hiddenValue !== undefined ? this.hiddenValue :
11968 this.value !== undefined ? this.value : '';
11970 // prevent input submission
11971 this.el.dom.removeAttribute('name');
11972 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11977 // this.el.dom.setAttribute('autocomplete', 'off');
11980 var cls = 'x-combo-list';
11982 //this.list = new Roo.Layer({
11983 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11989 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11990 _this.list.setWidth(lw);
11993 this.list.on('mouseover', this.onViewOver, this);
11994 this.list.on('mousemove', this.onViewMove, this);
11996 this.list.on('scroll', this.onViewScroll, this);
11999 this.list.swallowEvent('mousewheel');
12000 this.assetHeight = 0;
12003 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12004 this.assetHeight += this.header.getHeight();
12007 this.innerList = this.list.createChild({cls:cls+'-inner'});
12008 this.innerList.on('mouseover', this.onViewOver, this);
12009 this.innerList.on('mousemove', this.onViewMove, this);
12010 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12012 if(this.allowBlank && !this.pageSize && !this.disableClear){
12013 this.footer = this.list.createChild({cls:cls+'-ft'});
12014 this.pageTb = new Roo.Toolbar(this.footer);
12018 this.footer = this.list.createChild({cls:cls+'-ft'});
12019 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12020 {pageSize: this.pageSize});
12024 if (this.pageTb && this.allowBlank && !this.disableClear) {
12026 this.pageTb.add(new Roo.Toolbar.Fill(), {
12027 cls: 'x-btn-icon x-btn-clear',
12029 handler: function()
12032 _this.clearValue();
12033 _this.onSelect(false, -1);
12038 this.assetHeight += this.footer.getHeight();
12043 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12046 this.view = new Roo.View(this.list, this.tpl, {
12047 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12049 //this.view.wrapEl.setDisplayed(false);
12050 this.view.on('click', this.onViewClick, this);
12054 this.store.on('beforeload', this.onBeforeLoad, this);
12055 this.store.on('load', this.onLoad, this);
12056 this.store.on('loadexception', this.onLoadException, this);
12058 if(this.resizable){
12059 this.resizer = new Roo.Resizable(this.list, {
12060 pinned:true, handles:'se'
12062 this.resizer.on('resize', function(r, w, h){
12063 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12064 this.listWidth = w;
12065 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12066 this.restrictHeight();
12068 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12071 if(!this.editable){
12072 this.editable = true;
12073 this.setEditable(false);
12078 if (typeof(this.events.add.listeners) != 'undefined') {
12080 this.addicon = this.wrap.createChild(
12081 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12083 this.addicon.on('click', function(e) {
12084 this.fireEvent('add', this);
12087 if (typeof(this.events.edit.listeners) != 'undefined') {
12089 this.editicon = this.wrap.createChild(
12090 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12091 if (this.addicon) {
12092 this.editicon.setStyle('margin-left', '40px');
12094 this.editicon.on('click', function(e) {
12096 // we fire even if inothing is selected..
12097 this.fireEvent('edit', this, this.lastData );
12103 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12104 "up" : function(e){
12105 this.inKeyMode = true;
12109 "down" : function(e){
12110 if(!this.isExpanded()){
12111 this.onTriggerClick();
12113 this.inKeyMode = true;
12118 "enter" : function(e){
12119 // this.onViewClick();
12123 if(this.fireEvent("specialkey", this, e)){
12124 this.onViewClick(false);
12130 "esc" : function(e){
12134 "tab" : function(e){
12137 if(this.fireEvent("specialkey", this, e)){
12138 this.onViewClick(false);
12146 doRelay : function(foo, bar, hname){
12147 if(hname == 'down' || this.scope.isExpanded()){
12148 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12157 this.queryDelay = Math.max(this.queryDelay || 10,
12158 this.mode == 'local' ? 10 : 250);
12161 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12163 if(this.typeAhead){
12164 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12166 if(this.editable !== false){
12167 this.inputEl().on("keyup", this.onKeyUp, this);
12169 if(this.forceSelection){
12170 this.inputEl().on('blur', this.doForce, this);
12174 this.choices = this.el.select('ul.select2-choices', true).first();
12175 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12179 initTickableEvents: function()
12183 if(this.hiddenName){
12185 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12187 this.hiddenField.dom.value =
12188 this.hiddenValue !== undefined ? this.hiddenValue :
12189 this.value !== undefined ? this.value : '';
12191 // prevent input submission
12192 this.el.dom.removeAttribute('name');
12193 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12198 // this.list = this.el.select('ul.dropdown-menu',true).first();
12200 this.choices = this.el.select('ul.select2-choices', true).first();
12201 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12202 if(this.triggerList){
12203 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12206 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12207 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12209 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12210 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12212 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12213 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12215 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12216 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12217 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12220 this.cancelBtn.hide();
12225 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12226 _this.list.setWidth(lw);
12229 this.list.on('mouseover', this.onViewOver, this);
12230 this.list.on('mousemove', this.onViewMove, this);
12232 this.list.on('scroll', this.onViewScroll, this);
12235 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>';
12238 this.view = new Roo.View(this.list, this.tpl, {
12239 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12242 //this.view.wrapEl.setDisplayed(false);
12243 this.view.on('click', this.onViewClick, this);
12247 this.store.on('beforeload', this.onBeforeLoad, this);
12248 this.store.on('load', this.onLoad, this);
12249 this.store.on('loadexception', this.onLoadException, this);
12252 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12253 "up" : function(e){
12254 this.inKeyMode = true;
12258 "down" : function(e){
12259 this.inKeyMode = true;
12263 "enter" : function(e){
12264 if(this.fireEvent("specialkey", this, e)){
12265 this.onViewClick(false);
12271 "esc" : function(e){
12272 this.onTickableFooterButtonClick(e, false, false);
12275 "tab" : function(e){
12276 this.fireEvent("specialkey", this, e);
12278 this.onTickableFooterButtonClick(e, false, false);
12285 doRelay : function(e, fn, key){
12286 if(this.scope.isExpanded()){
12287 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12296 this.queryDelay = Math.max(this.queryDelay || 10,
12297 this.mode == 'local' ? 10 : 250);
12300 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12302 if(this.typeAhead){
12303 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12306 if(this.editable !== false){
12307 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12312 onDestroy : function(){
12314 this.view.setStore(null);
12315 this.view.el.removeAllListeners();
12316 this.view.el.remove();
12317 this.view.purgeListeners();
12320 this.list.dom.innerHTML = '';
12324 this.store.un('beforeload', this.onBeforeLoad, this);
12325 this.store.un('load', this.onLoad, this);
12326 this.store.un('loadexception', this.onLoadException, this);
12328 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12332 fireKey : function(e){
12333 if(e.isNavKeyPress() && !this.list.isVisible()){
12334 this.fireEvent("specialkey", this, e);
12339 onResize: function(w, h){
12340 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12342 // if(typeof w != 'number'){
12343 // // we do not handle it!?!?
12346 // var tw = this.trigger.getWidth();
12347 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12348 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12350 // this.inputEl().setWidth( this.adjustWidth('input', x));
12352 // //this.trigger.setStyle('left', x+'px');
12354 // if(this.list && this.listWidth === undefined){
12355 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12356 // this.list.setWidth(lw);
12357 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12365 * Allow or prevent the user from directly editing the field text. If false is passed,
12366 * the user will only be able to select from the items defined in the dropdown list. This method
12367 * is the runtime equivalent of setting the 'editable' config option at config time.
12368 * @param {Boolean} value True to allow the user to directly edit the field text
12370 setEditable : function(value){
12371 if(value == this.editable){
12374 this.editable = value;
12376 this.inputEl().dom.setAttribute('readOnly', true);
12377 this.inputEl().on('mousedown', this.onTriggerClick, this);
12378 this.inputEl().addClass('x-combo-noedit');
12380 this.inputEl().dom.setAttribute('readOnly', false);
12381 this.inputEl().un('mousedown', this.onTriggerClick, this);
12382 this.inputEl().removeClass('x-combo-noedit');
12388 onBeforeLoad : function(combo,opts){
12389 if(!this.hasFocus){
12393 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12395 this.restrictHeight();
12396 this.selectedIndex = -1;
12400 onLoad : function(){
12402 this.hasQuery = false;
12404 if(!this.hasFocus){
12408 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12409 this.loading.hide();
12412 if(this.store.getCount() > 0){
12414 this.restrictHeight();
12415 if(this.lastQuery == this.allQuery){
12416 if(this.editable && !this.tickable){
12417 this.inputEl().dom.select();
12421 !this.selectByValue(this.value, true) &&
12424 !this.store.lastOptions ||
12425 typeof(this.store.lastOptions.add) == 'undefined' ||
12426 this.store.lastOptions.add != true
12429 this.select(0, true);
12432 if(this.autoFocus){
12435 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12436 this.taTask.delay(this.typeAheadDelay);
12440 this.onEmptyResults();
12446 onLoadException : function()
12448 this.hasQuery = false;
12450 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12451 this.loading.hide();
12454 if(this.tickable && this.editable){
12459 // only causes errors at present
12460 //Roo.log(this.store.reader.jsonData);
12461 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12463 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12469 onTypeAhead : function(){
12470 if(this.store.getCount() > 0){
12471 var r = this.store.getAt(0);
12472 var newValue = r.data[this.displayField];
12473 var len = newValue.length;
12474 var selStart = this.getRawValue().length;
12476 if(selStart != len){
12477 this.setRawValue(newValue);
12478 this.selectText(selStart, newValue.length);
12484 onSelect : function(record, index){
12486 if(this.fireEvent('beforeselect', this, record, index) !== false){
12488 this.setFromData(index > -1 ? record.data : false);
12491 this.fireEvent('select', this, record, index);
12496 * Returns the currently selected field value or empty string if no value is set.
12497 * @return {String} value The selected value
12499 getValue : function(){
12502 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12505 if(this.valueField){
12506 return typeof this.value != 'undefined' ? this.value : '';
12508 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12513 * Clears any text/value currently set in the field
12515 clearValue : function(){
12516 if(this.hiddenField){
12517 this.hiddenField.dom.value = '';
12520 this.setRawValue('');
12521 this.lastSelectionText = '';
12522 this.lastData = false;
12524 var close = this.closeTriggerEl();
12533 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12534 * will be displayed in the field. If the value does not match the data value of an existing item,
12535 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12536 * Otherwise the field will be blank (although the value will still be set).
12537 * @param {String} value The value to match
12539 setValue : function(v){
12546 if(this.valueField){
12547 var r = this.findRecord(this.valueField, v);
12549 text = r.data[this.displayField];
12550 }else if(this.valueNotFoundText !== undefined){
12551 text = this.valueNotFoundText;
12554 this.lastSelectionText = text;
12555 if(this.hiddenField){
12556 this.hiddenField.dom.value = v;
12558 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12561 var close = this.closeTriggerEl();
12564 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12568 * @property {Object} the last set data for the element
12573 * Sets the value of the field based on a object which is related to the record format for the store.
12574 * @param {Object} value the value to set as. or false on reset?
12576 setFromData : function(o){
12583 var dv = ''; // display value
12584 var vv = ''; // value value..
12586 if (this.displayField) {
12587 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12589 // this is an error condition!!!
12590 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12593 if(this.valueField){
12594 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12597 var close = this.closeTriggerEl();
12600 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12603 if(this.hiddenField){
12604 this.hiddenField.dom.value = vv;
12606 this.lastSelectionText = dv;
12607 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12611 // no hidden field.. - we store the value in 'value', but still display
12612 // display field!!!!
12613 this.lastSelectionText = dv;
12614 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12621 reset : function(){
12622 // overridden so that last data is reset..
12629 this.setValue(this.originalValue);
12630 this.clearInvalid();
12631 this.lastData = false;
12633 this.view.clearSelections();
12637 findRecord : function(prop, value){
12639 if(this.store.getCount() > 0){
12640 this.store.each(function(r){
12641 if(r.data[prop] == value){
12651 getName: function()
12653 // returns hidden if it's set..
12654 if (!this.rendered) {return ''};
12655 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12659 onViewMove : function(e, t){
12660 this.inKeyMode = false;
12664 onViewOver : function(e, t){
12665 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12668 var item = this.view.findItemFromChild(t);
12671 var index = this.view.indexOf(item);
12672 this.select(index, false);
12677 onViewClick : function(view, doFocus, el, e)
12679 var index = this.view.getSelectedIndexes()[0];
12681 var r = this.store.getAt(index);
12685 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12692 Roo.each(this.tickItems, function(v,k){
12694 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12696 _this.tickItems.splice(k, 1);
12698 if(typeof(e) == 'undefined' && view == false){
12699 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12711 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12712 this.tickItems.push(r.data);
12715 if(typeof(e) == 'undefined' && view == false){
12716 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12723 this.onSelect(r, index);
12725 if(doFocus !== false && !this.blockFocus){
12726 this.inputEl().focus();
12731 restrictHeight : function(){
12732 //this.innerList.dom.style.height = '';
12733 //var inner = this.innerList.dom;
12734 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12735 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12736 //this.list.beginUpdate();
12737 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12738 this.list.alignTo(this.inputEl(), this.listAlign);
12739 this.list.alignTo(this.inputEl(), this.listAlign);
12740 //this.list.endUpdate();
12744 onEmptyResults : function(){
12746 if(this.tickable && this.editable){
12747 this.restrictHeight();
12755 * Returns true if the dropdown list is expanded, else false.
12757 isExpanded : function(){
12758 return this.list.isVisible();
12762 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12763 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12764 * @param {String} value The data value of the item to select
12765 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12766 * selected item if it is not currently in view (defaults to true)
12767 * @return {Boolean} True if the value matched an item in the list, else false
12769 selectByValue : function(v, scrollIntoView){
12770 if(v !== undefined && v !== null){
12771 var r = this.findRecord(this.valueField || this.displayField, v);
12773 this.select(this.store.indexOf(r), scrollIntoView);
12781 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12782 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12783 * @param {Number} index The zero-based index of the list item to select
12784 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12785 * selected item if it is not currently in view (defaults to true)
12787 select : function(index, scrollIntoView){
12788 this.selectedIndex = index;
12789 this.view.select(index);
12790 if(scrollIntoView !== false){
12791 var el = this.view.getNode(index);
12793 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12796 this.list.scrollChildIntoView(el, false);
12802 selectNext : function(){
12803 var ct = this.store.getCount();
12805 if(this.selectedIndex == -1){
12807 }else if(this.selectedIndex < ct-1){
12808 this.select(this.selectedIndex+1);
12814 selectPrev : function(){
12815 var ct = this.store.getCount();
12817 if(this.selectedIndex == -1){
12819 }else if(this.selectedIndex != 0){
12820 this.select(this.selectedIndex-1);
12826 onKeyUp : function(e){
12827 if(this.editable !== false && !e.isSpecialKey()){
12828 this.lastKey = e.getKey();
12829 this.dqTask.delay(this.queryDelay);
12834 validateBlur : function(){
12835 return !this.list || !this.list.isVisible();
12839 initQuery : function(){
12841 var v = this.getRawValue();
12843 if(this.tickable && this.editable){
12844 v = this.tickableInputEl().getValue();
12851 doForce : function(){
12852 if(this.inputEl().dom.value.length > 0){
12853 this.inputEl().dom.value =
12854 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12860 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12861 * query allowing the query action to be canceled if needed.
12862 * @param {String} query The SQL query to execute
12863 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12864 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12865 * saved in the current store (defaults to false)
12867 doQuery : function(q, forceAll){
12869 if(q === undefined || q === null){
12874 forceAll: forceAll,
12878 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12883 forceAll = qe.forceAll;
12884 if(forceAll === true || (q.length >= this.minChars)){
12886 this.hasQuery = true;
12888 if(this.lastQuery != q || this.alwaysQuery){
12889 this.lastQuery = q;
12890 if(this.mode == 'local'){
12891 this.selectedIndex = -1;
12893 this.store.clearFilter();
12896 if(this.specialFilter){
12897 this.fireEvent('specialfilter', this);
12902 this.store.filter(this.displayField, q);
12905 this.store.fireEvent("datachanged", this.store);
12912 this.store.baseParams[this.queryParam] = q;
12914 var options = {params : this.getParams(q)};
12917 options.add = true;
12918 options.params.start = this.page * this.pageSize;
12921 this.store.load(options);
12924 * this code will make the page width larger, at the beginning, the list not align correctly,
12925 * we should expand the list on onLoad
12926 * so command out it
12931 this.selectedIndex = -1;
12936 this.loadNext = false;
12940 getParams : function(q){
12942 //p[this.queryParam] = q;
12946 p.limit = this.pageSize;
12952 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12954 collapse : function(){
12955 if(!this.isExpanded()){
12962 this.hasFocus = false;
12964 this.cancelBtn.hide();
12965 this.trigger.show();
12968 this.tickableInputEl().dom.value = '';
12969 this.tickableInputEl().blur();
12974 Roo.get(document).un('mousedown', this.collapseIf, this);
12975 Roo.get(document).un('mousewheel', this.collapseIf, this);
12976 if (!this.editable) {
12977 Roo.get(document).un('keydown', this.listKeyPress, this);
12979 this.fireEvent('collapse', this);
12983 collapseIf : function(e){
12984 var in_combo = e.within(this.el);
12985 var in_list = e.within(this.list);
12986 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12988 if (in_combo || in_list || is_list) {
12989 //e.stopPropagation();
12994 this.onTickableFooterButtonClick(e, false, false);
13002 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13004 expand : function(){
13006 if(this.isExpanded() || !this.hasFocus){
13010 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13011 this.list.setWidth(lw);
13018 this.restrictHeight();
13022 this.tickItems = Roo.apply([], this.item);
13025 this.cancelBtn.show();
13026 this.trigger.hide();
13029 this.tickableInputEl().focus();
13034 Roo.get(document).on('mousedown', this.collapseIf, this);
13035 Roo.get(document).on('mousewheel', this.collapseIf, this);
13036 if (!this.editable) {
13037 Roo.get(document).on('keydown', this.listKeyPress, this);
13040 this.fireEvent('expand', this);
13044 // Implements the default empty TriggerField.onTriggerClick function
13045 onTriggerClick : function(e)
13047 Roo.log('trigger click');
13049 if(this.disabled || !this.triggerList){
13054 this.loadNext = false;
13056 if(this.isExpanded()){
13058 if (!this.blockFocus) {
13059 this.inputEl().focus();
13063 this.hasFocus = true;
13064 if(this.triggerAction == 'all') {
13065 this.doQuery(this.allQuery, true);
13067 this.doQuery(this.getRawValue());
13069 if (!this.blockFocus) {
13070 this.inputEl().focus();
13075 onTickableTriggerClick : function(e)
13082 this.loadNext = false;
13083 this.hasFocus = true;
13085 if(this.triggerAction == 'all') {
13086 this.doQuery(this.allQuery, true);
13088 this.doQuery(this.getRawValue());
13092 onSearchFieldClick : function(e)
13094 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13095 this.onTickableFooterButtonClick(e, false, false);
13099 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13104 this.loadNext = false;
13105 this.hasFocus = true;
13107 if(this.triggerAction == 'all') {
13108 this.doQuery(this.allQuery, true);
13110 this.doQuery(this.getRawValue());
13114 listKeyPress : function(e)
13116 //Roo.log('listkeypress');
13117 // scroll to first matching element based on key pres..
13118 if (e.isSpecialKey()) {
13121 var k = String.fromCharCode(e.getKey()).toUpperCase();
13124 var csel = this.view.getSelectedNodes();
13125 var cselitem = false;
13127 var ix = this.view.indexOf(csel[0]);
13128 cselitem = this.store.getAt(ix);
13129 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13135 this.store.each(function(v) {
13137 // start at existing selection.
13138 if (cselitem.id == v.id) {
13144 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13145 match = this.store.indexOf(v);
13151 if (match === false) {
13152 return true; // no more action?
13155 this.view.select(match);
13156 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13157 sn.scrollIntoView(sn.dom.parentNode, false);
13160 onViewScroll : function(e, t){
13162 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){
13166 this.hasQuery = true;
13168 this.loading = this.list.select('.loading', true).first();
13170 if(this.loading === null){
13171 this.list.createChild({
13173 cls: 'loading select2-more-results select2-active',
13174 html: 'Loading more results...'
13177 this.loading = this.list.select('.loading', true).first();
13179 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13181 this.loading.hide();
13184 this.loading.show();
13189 this.loadNext = true;
13191 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13196 addItem : function(o)
13198 var dv = ''; // display value
13200 if (this.displayField) {
13201 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13203 // this is an error condition!!!
13204 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13211 var choice = this.choices.createChild({
13213 cls: 'select2-search-choice',
13222 cls: 'select2-search-choice-close',
13227 }, this.searchField);
13229 var close = choice.select('a.select2-search-choice-close', true).first();
13231 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13239 this.inputEl().dom.value = '';
13244 onRemoveItem : function(e, _self, o)
13246 e.preventDefault();
13248 this.lastItem = Roo.apply([], this.item);
13250 var index = this.item.indexOf(o.data) * 1;
13253 Roo.log('not this item?!');
13257 this.item.splice(index, 1);
13262 this.fireEvent('remove', this, e);
13268 syncValue : function()
13270 if(!this.item.length){
13277 Roo.each(this.item, function(i){
13278 if(_this.valueField){
13279 value.push(i[_this.valueField]);
13286 this.value = value.join(',');
13288 if(this.hiddenField){
13289 this.hiddenField.dom.value = this.value;
13292 this.store.fireEvent("datachanged", this.store);
13295 clearItem : function()
13297 if(!this.multiple){
13303 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13311 if(this.tickable && !Roo.isTouch){
13312 this.view.refresh();
13316 inputEl: function ()
13318 if(Roo.isTouch && this.mobileTouchView){
13319 return this.el.select('input.form-control',true).first();
13323 return this.searchField;
13326 return this.el.select('input.form-control',true).first();
13330 onTickableFooterButtonClick : function(e, btn, el)
13332 e.preventDefault();
13334 this.lastItem = Roo.apply([], this.item);
13336 if(btn && btn.name == 'cancel'){
13337 this.tickItems = Roo.apply([], this.item);
13346 Roo.each(this.tickItems, function(o){
13354 validate : function()
13356 var v = this.getRawValue();
13359 v = this.getValue();
13362 if(this.disabled || this.allowBlank || v.length){
13367 this.markInvalid();
13371 tickableInputEl : function()
13373 if(!this.tickable || !this.editable){
13374 return this.inputEl();
13377 return this.inputEl().select('.select2-search-field-input', true).first();
13381 getAutoCreateTouchView : function()
13386 cls: 'form-group' //input-group
13392 type : this.inputType,
13393 cls : 'form-control x-combo-noedit',
13394 autocomplete: 'new-password',
13395 placeholder : this.placeholder || '',
13400 input.name = this.name;
13404 input.cls += ' input-' + this.size;
13407 if (this.disabled) {
13408 input.disabled = true;
13419 inputblock.cls += ' input-group';
13421 inputblock.cn.unshift({
13423 cls : 'input-group-addon',
13428 if(this.removable && !this.multiple){
13429 inputblock.cls += ' roo-removable';
13431 inputblock.cn.push({
13434 cls : 'roo-combo-removable-btn close'
13438 if(this.hasFeedback && !this.allowBlank){
13440 inputblock.cls += ' has-feedback';
13442 inputblock.cn.push({
13444 cls: 'glyphicon form-control-feedback'
13451 inputblock.cls += (this.before) ? '' : ' input-group';
13453 inputblock.cn.push({
13455 cls : 'input-group-addon',
13466 cls: 'form-hidden-field'
13480 cls: 'form-hidden-field'
13484 cls: 'select2-choices',
13488 cls: 'select2-search-field',
13501 cls: 'select2-container input-group',
13508 combobox.cls += ' select2-container-multi';
13511 var align = this.labelAlign || this.parentLabelAlign();
13515 if(this.fieldLabel.length){
13517 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13518 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13523 cls : 'control-label ' + lw,
13524 html : this.fieldLabel
13536 var settings = this;
13538 ['xs','sm','md','lg'].map(function(size){
13539 if (settings[size]) {
13540 cfg.cls += ' col-' + size + '-' + settings[size];
13547 initTouchView : function()
13549 this.renderTouchView();
13551 this.touchViewEl.on('scroll', function(){
13552 this.el.dom.scrollTop = 0;
13555 this.inputEl().on("click", this.showTouchView, this);
13556 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13557 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13559 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13561 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13562 this.store.on('load', this.onTouchViewLoad, this);
13563 this.store.on('loadexception', this.onTouchViewLoadException, this);
13565 if(this.hiddenName){
13567 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13569 this.hiddenField.dom.value =
13570 this.hiddenValue !== undefined ? this.hiddenValue :
13571 this.value !== undefined ? this.value : '';
13573 this.el.dom.removeAttribute('name');
13574 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13578 this.choices = this.el.select('ul.select2-choices', true).first();
13579 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13582 if(this.removable && !this.multiple){
13583 var close = this.closeTriggerEl();
13585 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13586 close.on('click', this.removeBtnClick, this, close);
13595 renderTouchView : function()
13597 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13598 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13600 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13601 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13603 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13604 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13605 this.touchViewBodyEl.setStyle('overflow', 'auto');
13607 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13608 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13610 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13611 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13615 showTouchView : function()
13617 this.touchViewHeaderEl.hide();
13619 if(this.fieldLabel.length){
13620 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13621 this.touchViewHeaderEl.show();
13624 this.touchViewEl.show();
13626 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13627 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13629 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13631 if(this.fieldLabel.length){
13632 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13635 this.touchViewBodyEl.setHeight(bodyHeight);
13639 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13641 this.touchViewEl.addClass('in');
13644 this.doTouchViewQuery();
13648 hideTouchView : function()
13650 this.touchViewEl.removeClass('in');
13654 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13656 this.touchViewEl.setStyle('display', 'none');
13661 setTouchViewValue : function()
13668 Roo.each(this.tickItems, function(o){
13673 this.hideTouchView();
13676 doTouchViewQuery : function()
13685 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13689 if(!this.alwaysQuery || this.mode == 'local'){
13690 this.onTouchViewLoad();
13697 onTouchViewBeforeLoad : function(combo,opts)
13703 onTouchViewLoad : function()
13705 if(this.store.getCount() < 1){
13706 this.onTouchViewEmptyResults();
13710 this.clearTouchView();
13712 var rawValue = this.getRawValue();
13714 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13716 this.tickItems = [];
13718 this.store.data.each(function(d, rowIndex){
13719 var row = this.touchViewListGroup.createChild(template);
13721 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13722 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13725 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13726 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13729 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13730 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13731 this.tickItems.push(d.data);
13734 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13738 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13740 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13742 if(this.fieldLabel.length){
13743 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13746 var listHeight = this.touchViewListGroup.getHeight();
13750 if(firstChecked && listHeight > bodyHeight){
13751 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13756 onTouchViewLoadException : function()
13758 this.hideTouchView();
13761 onTouchViewEmptyResults : function()
13763 this.clearTouchView();
13765 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13767 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13771 clearTouchView : function()
13773 this.touchViewListGroup.dom.innerHTML = '';
13776 onTouchViewClick : function(e, el, o)
13778 e.preventDefault();
13781 var rowIndex = o.rowIndex;
13783 var r = this.store.getAt(rowIndex);
13785 if(!this.multiple){
13786 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13787 c.dom.removeAttribute('checked');
13790 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13792 this.setFromData(r.data);
13794 var close = this.closeTriggerEl();
13800 this.hideTouchView();
13802 this.fireEvent('select', this, r, rowIndex);
13807 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13808 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13809 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13813 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13814 this.addItem(r.data);
13815 this.tickItems.push(r.data);
13821 * @cfg {Boolean} grow
13825 * @cfg {Number} growMin
13829 * @cfg {Number} growMax
13838 Roo.apply(Roo.bootstrap.ComboBox, {
13842 cls: 'modal-header',
13864 cls: 'list-group-item',
13868 cls: 'roo-combobox-list-group-item-value'
13872 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13886 listItemCheckbox : {
13888 cls: 'list-group-item',
13892 cls: 'roo-combobox-list-group-item-value'
13896 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13912 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13917 cls: 'modal-footer',
13925 cls: 'col-xs-6 text-left',
13928 cls: 'btn btn-danger roo-touch-view-cancel',
13934 cls: 'col-xs-6 text-right',
13937 cls: 'btn btn-success roo-touch-view-ok',
13948 Roo.apply(Roo.bootstrap.ComboBox, {
13950 touchViewTemplate : {
13952 cls: 'modal fade roo-combobox-touch-view',
13956 cls: 'modal-dialog',
13960 cls: 'modal-content',
13962 Roo.bootstrap.ComboBox.header,
13963 Roo.bootstrap.ComboBox.body,
13964 Roo.bootstrap.ComboBox.footer
13973 * Ext JS Library 1.1.1
13974 * Copyright(c) 2006-2007, Ext JS, LLC.
13976 * Originally Released Under LGPL - original licence link has changed is not relivant.
13979 * <script type="text/javascript">
13984 * @extends Roo.util.Observable
13985 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13986 * This class also supports single and multi selection modes. <br>
13987 * Create a data model bound view:
13989 var store = new Roo.data.Store(...);
13991 var view = new Roo.View({
13993 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13995 singleSelect: true,
13996 selectedClass: "ydataview-selected",
14000 // listen for node click?
14001 view.on("click", function(vw, index, node, e){
14002 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14006 dataModel.load("foobar.xml");
14008 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14010 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14011 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14013 * Note: old style constructor is still suported (container, template, config)
14016 * Create a new View
14017 * @param {Object} config The config object
14020 Roo.View = function(config, depreciated_tpl, depreciated_config){
14022 this.parent = false;
14024 if (typeof(depreciated_tpl) == 'undefined') {
14025 // new way.. - universal constructor.
14026 Roo.apply(this, config);
14027 this.el = Roo.get(this.el);
14030 this.el = Roo.get(config);
14031 this.tpl = depreciated_tpl;
14032 Roo.apply(this, depreciated_config);
14034 this.wrapEl = this.el.wrap().wrap();
14035 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14038 if(typeof(this.tpl) == "string"){
14039 this.tpl = new Roo.Template(this.tpl);
14041 // support xtype ctors..
14042 this.tpl = new Roo.factory(this.tpl, Roo);
14046 this.tpl.compile();
14051 * @event beforeclick
14052 * Fires before a click is processed. Returns false to cancel the default action.
14053 * @param {Roo.View} this
14054 * @param {Number} index The index of the target node
14055 * @param {HTMLElement} node The target node
14056 * @param {Roo.EventObject} e The raw event object
14058 "beforeclick" : true,
14061 * Fires when a template node is clicked.
14062 * @param {Roo.View} this
14063 * @param {Number} index The index of the target node
14064 * @param {HTMLElement} node The target node
14065 * @param {Roo.EventObject} e The raw event object
14070 * Fires when a template node is double clicked.
14071 * @param {Roo.View} this
14072 * @param {Number} index The index of the target node
14073 * @param {HTMLElement} node The target node
14074 * @param {Roo.EventObject} e The raw event object
14078 * @event contextmenu
14079 * Fires when a template node is right clicked.
14080 * @param {Roo.View} this
14081 * @param {Number} index The index of the target node
14082 * @param {HTMLElement} node The target node
14083 * @param {Roo.EventObject} e The raw event object
14085 "contextmenu" : true,
14087 * @event selectionchange
14088 * Fires when the selected nodes change.
14089 * @param {Roo.View} this
14090 * @param {Array} selections Array of the selected nodes
14092 "selectionchange" : true,
14095 * @event beforeselect
14096 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14097 * @param {Roo.View} this
14098 * @param {HTMLElement} node The node to be selected
14099 * @param {Array} selections Array of currently selected nodes
14101 "beforeselect" : true,
14103 * @event preparedata
14104 * Fires on every row to render, to allow you to change the data.
14105 * @param {Roo.View} this
14106 * @param {Object} data to be rendered (change this)
14108 "preparedata" : true
14116 "click": this.onClick,
14117 "dblclick": this.onDblClick,
14118 "contextmenu": this.onContextMenu,
14122 this.selections = [];
14124 this.cmp = new Roo.CompositeElementLite([]);
14126 this.store = Roo.factory(this.store, Roo.data);
14127 this.setStore(this.store, true);
14130 if ( this.footer && this.footer.xtype) {
14132 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14134 this.footer.dataSource = this.store;
14135 this.footer.container = fctr;
14136 this.footer = Roo.factory(this.footer, Roo);
14137 fctr.insertFirst(this.el);
14139 // this is a bit insane - as the paging toolbar seems to detach the el..
14140 // dom.parentNode.parentNode.parentNode
14141 // they get detached?
14145 Roo.View.superclass.constructor.call(this);
14150 Roo.extend(Roo.View, Roo.util.Observable, {
14153 * @cfg {Roo.data.Store} store Data store to load data from.
14158 * @cfg {String|Roo.Element} el The container element.
14163 * @cfg {String|Roo.Template} tpl The template used by this View
14167 * @cfg {String} dataName the named area of the template to use as the data area
14168 * Works with domtemplates roo-name="name"
14172 * @cfg {String} selectedClass The css class to add to selected nodes
14174 selectedClass : "x-view-selected",
14176 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14181 * @cfg {String} text to display on mask (default Loading)
14185 * @cfg {Boolean} multiSelect Allow multiple selection
14187 multiSelect : false,
14189 * @cfg {Boolean} singleSelect Allow single selection
14191 singleSelect: false,
14194 * @cfg {Boolean} toggleSelect - selecting
14196 toggleSelect : false,
14199 * @cfg {Boolean} tickable - selecting
14204 * Returns the element this view is bound to.
14205 * @return {Roo.Element}
14207 getEl : function(){
14208 return this.wrapEl;
14214 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14216 refresh : function(){
14217 //Roo.log('refresh');
14220 // if we are using something like 'domtemplate', then
14221 // the what gets used is:
14222 // t.applySubtemplate(NAME, data, wrapping data..)
14223 // the outer template then get' applied with
14224 // the store 'extra data'
14225 // and the body get's added to the
14226 // roo-name="data" node?
14227 // <span class='roo-tpl-{name}'></span> ?????
14231 this.clearSelections();
14232 this.el.update("");
14234 var records = this.store.getRange();
14235 if(records.length < 1) {
14237 // is this valid?? = should it render a template??
14239 this.el.update(this.emptyText);
14243 if (this.dataName) {
14244 this.el.update(t.apply(this.store.meta)); //????
14245 el = this.el.child('.roo-tpl-' + this.dataName);
14248 for(var i = 0, len = records.length; i < len; i++){
14249 var data = this.prepareData(records[i].data, i, records[i]);
14250 this.fireEvent("preparedata", this, data, i, records[i]);
14252 var d = Roo.apply({}, data);
14255 Roo.apply(d, {'roo-id' : Roo.id()});
14259 Roo.each(this.parent.item, function(item){
14260 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14263 Roo.apply(d, {'roo-data-checked' : 'checked'});
14267 html[html.length] = Roo.util.Format.trim(
14269 t.applySubtemplate(this.dataName, d, this.store.meta) :
14276 el.update(html.join(""));
14277 this.nodes = el.dom.childNodes;
14278 this.updateIndexes(0);
14283 * Function to override to reformat the data that is sent to
14284 * the template for each node.
14285 * DEPRICATED - use the preparedata event handler.
14286 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14287 * a JSON object for an UpdateManager bound view).
14289 prepareData : function(data, index, record)
14291 this.fireEvent("preparedata", this, data, index, record);
14295 onUpdate : function(ds, record){
14296 // Roo.log('on update');
14297 this.clearSelections();
14298 var index = this.store.indexOf(record);
14299 var n = this.nodes[index];
14300 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14301 n.parentNode.removeChild(n);
14302 this.updateIndexes(index, index);
14308 onAdd : function(ds, records, index)
14310 //Roo.log(['on Add', ds, records, index] );
14311 this.clearSelections();
14312 if(this.nodes.length == 0){
14316 var n = this.nodes[index];
14317 for(var i = 0, len = records.length; i < len; i++){
14318 var d = this.prepareData(records[i].data, i, records[i]);
14320 this.tpl.insertBefore(n, d);
14323 this.tpl.append(this.el, d);
14326 this.updateIndexes(index);
14329 onRemove : function(ds, record, index){
14330 // Roo.log('onRemove');
14331 this.clearSelections();
14332 var el = this.dataName ?
14333 this.el.child('.roo-tpl-' + this.dataName) :
14336 el.dom.removeChild(this.nodes[index]);
14337 this.updateIndexes(index);
14341 * Refresh an individual node.
14342 * @param {Number} index
14344 refreshNode : function(index){
14345 this.onUpdate(this.store, this.store.getAt(index));
14348 updateIndexes : function(startIndex, endIndex){
14349 var ns = this.nodes;
14350 startIndex = startIndex || 0;
14351 endIndex = endIndex || ns.length - 1;
14352 for(var i = startIndex; i <= endIndex; i++){
14353 ns[i].nodeIndex = i;
14358 * Changes the data store this view uses and refresh the view.
14359 * @param {Store} store
14361 setStore : function(store, initial){
14362 if(!initial && this.store){
14363 this.store.un("datachanged", this.refresh);
14364 this.store.un("add", this.onAdd);
14365 this.store.un("remove", this.onRemove);
14366 this.store.un("update", this.onUpdate);
14367 this.store.un("clear", this.refresh);
14368 this.store.un("beforeload", this.onBeforeLoad);
14369 this.store.un("load", this.onLoad);
14370 this.store.un("loadexception", this.onLoad);
14374 store.on("datachanged", this.refresh, this);
14375 store.on("add", this.onAdd, this);
14376 store.on("remove", this.onRemove, this);
14377 store.on("update", this.onUpdate, this);
14378 store.on("clear", this.refresh, this);
14379 store.on("beforeload", this.onBeforeLoad, this);
14380 store.on("load", this.onLoad, this);
14381 store.on("loadexception", this.onLoad, this);
14389 * onbeforeLoad - masks the loading area.
14392 onBeforeLoad : function(store,opts)
14394 //Roo.log('onBeforeLoad');
14396 this.el.update("");
14398 this.el.mask(this.mask ? this.mask : "Loading" );
14400 onLoad : function ()
14407 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14408 * @param {HTMLElement} node
14409 * @return {HTMLElement} The template node
14411 findItemFromChild : function(node){
14412 var el = this.dataName ?
14413 this.el.child('.roo-tpl-' + this.dataName,true) :
14416 if(!node || node.parentNode == el){
14419 var p = node.parentNode;
14420 while(p && p != el){
14421 if(p.parentNode == el){
14430 onClick : function(e){
14431 var item = this.findItemFromChild(e.getTarget());
14433 var index = this.indexOf(item);
14434 if(this.onItemClick(item, index, e) !== false){
14435 this.fireEvent("click", this, index, item, e);
14438 this.clearSelections();
14443 onContextMenu : function(e){
14444 var item = this.findItemFromChild(e.getTarget());
14446 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14451 onDblClick : function(e){
14452 var item = this.findItemFromChild(e.getTarget());
14454 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14458 onItemClick : function(item, index, e)
14460 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14463 if (this.toggleSelect) {
14464 var m = this.isSelected(item) ? 'unselect' : 'select';
14467 _t[m](item, true, false);
14470 if(this.multiSelect || this.singleSelect){
14471 if(this.multiSelect && e.shiftKey && this.lastSelection){
14472 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14474 this.select(item, this.multiSelect && e.ctrlKey);
14475 this.lastSelection = item;
14478 if(!this.tickable){
14479 e.preventDefault();
14487 * Get the number of selected nodes.
14490 getSelectionCount : function(){
14491 return this.selections.length;
14495 * Get the currently selected nodes.
14496 * @return {Array} An array of HTMLElements
14498 getSelectedNodes : function(){
14499 return this.selections;
14503 * Get the indexes of the selected nodes.
14506 getSelectedIndexes : function(){
14507 var indexes = [], s = this.selections;
14508 for(var i = 0, len = s.length; i < len; i++){
14509 indexes.push(s[i].nodeIndex);
14515 * Clear all selections
14516 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14518 clearSelections : function(suppressEvent){
14519 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14520 this.cmp.elements = this.selections;
14521 this.cmp.removeClass(this.selectedClass);
14522 this.selections = [];
14523 if(!suppressEvent){
14524 this.fireEvent("selectionchange", this, this.selections);
14530 * Returns true if the passed node is selected
14531 * @param {HTMLElement/Number} node The node or node index
14532 * @return {Boolean}
14534 isSelected : function(node){
14535 var s = this.selections;
14539 node = this.getNode(node);
14540 return s.indexOf(node) !== -1;
14545 * @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
14546 * @param {Boolean} keepExisting (optional) true to keep existing selections
14547 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14549 select : function(nodeInfo, keepExisting, suppressEvent){
14550 if(nodeInfo instanceof Array){
14552 this.clearSelections(true);
14554 for(var i = 0, len = nodeInfo.length; i < len; i++){
14555 this.select(nodeInfo[i], true, true);
14559 var node = this.getNode(nodeInfo);
14560 if(!node || this.isSelected(node)){
14561 return; // already selected.
14564 this.clearSelections(true);
14567 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14568 Roo.fly(node).addClass(this.selectedClass);
14569 this.selections.push(node);
14570 if(!suppressEvent){
14571 this.fireEvent("selectionchange", this, this.selections);
14579 * @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
14580 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14581 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14583 unselect : function(nodeInfo, keepExisting, suppressEvent)
14585 if(nodeInfo instanceof Array){
14586 Roo.each(this.selections, function(s) {
14587 this.unselect(s, nodeInfo);
14591 var node = this.getNode(nodeInfo);
14592 if(!node || !this.isSelected(node)){
14593 //Roo.log("not selected");
14594 return; // not selected.
14598 Roo.each(this.selections, function(s) {
14600 Roo.fly(node).removeClass(this.selectedClass);
14607 this.selections= ns;
14608 this.fireEvent("selectionchange", this, this.selections);
14612 * Gets a template node.
14613 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14614 * @return {HTMLElement} The node or null if it wasn't found
14616 getNode : function(nodeInfo){
14617 if(typeof nodeInfo == "string"){
14618 return document.getElementById(nodeInfo);
14619 }else if(typeof nodeInfo == "number"){
14620 return this.nodes[nodeInfo];
14626 * Gets a range template nodes.
14627 * @param {Number} startIndex
14628 * @param {Number} endIndex
14629 * @return {Array} An array of nodes
14631 getNodes : function(start, end){
14632 var ns = this.nodes;
14633 start = start || 0;
14634 end = typeof end == "undefined" ? ns.length - 1 : end;
14637 for(var i = start; i <= end; i++){
14641 for(var i = start; i >= end; i--){
14649 * Finds the index of the passed node
14650 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14651 * @return {Number} The index of the node or -1
14653 indexOf : function(node){
14654 node = this.getNode(node);
14655 if(typeof node.nodeIndex == "number"){
14656 return node.nodeIndex;
14658 var ns = this.nodes;
14659 for(var i = 0, len = ns.length; i < len; i++){
14670 * based on jquery fullcalendar
14674 Roo.bootstrap = Roo.bootstrap || {};
14676 * @class Roo.bootstrap.Calendar
14677 * @extends Roo.bootstrap.Component
14678 * Bootstrap Calendar class
14679 * @cfg {Boolean} loadMask (true|false) default false
14680 * @cfg {Object} header generate the user specific header of the calendar, default false
14683 * Create a new Container
14684 * @param {Object} config The config object
14689 Roo.bootstrap.Calendar = function(config){
14690 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14694 * Fires when a date is selected
14695 * @param {DatePicker} this
14696 * @param {Date} date The selected date
14700 * @event monthchange
14701 * Fires when the displayed month changes
14702 * @param {DatePicker} this
14703 * @param {Date} date The selected month
14705 'monthchange': true,
14707 * @event evententer
14708 * Fires when mouse over an event
14709 * @param {Calendar} this
14710 * @param {event} Event
14712 'evententer': true,
14714 * @event eventleave
14715 * Fires when the mouse leaves an
14716 * @param {Calendar} this
14719 'eventleave': true,
14721 * @event eventclick
14722 * Fires when the mouse click an
14723 * @param {Calendar} this
14732 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14735 * @cfg {Number} startDay
14736 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14744 getAutoCreate : function(){
14747 var fc_button = function(name, corner, style, content ) {
14748 return Roo.apply({},{
14750 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14752 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14755 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14766 style : 'width:100%',
14773 cls : 'fc-header-left',
14775 fc_button('prev', 'left', 'arrow', '‹' ),
14776 fc_button('next', 'right', 'arrow', '›' ),
14777 { tag: 'span', cls: 'fc-header-space' },
14778 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14786 cls : 'fc-header-center',
14790 cls: 'fc-header-title',
14793 html : 'month / year'
14801 cls : 'fc-header-right',
14803 /* fc_button('month', 'left', '', 'month' ),
14804 fc_button('week', '', '', 'week' ),
14805 fc_button('day', 'right', '', 'day' )
14817 header = this.header;
14820 var cal_heads = function() {
14822 // fixme - handle this.
14824 for (var i =0; i < Date.dayNames.length; i++) {
14825 var d = Date.dayNames[i];
14828 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14829 html : d.substring(0,3)
14833 ret[0].cls += ' fc-first';
14834 ret[6].cls += ' fc-last';
14837 var cal_cell = function(n) {
14840 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14845 cls: 'fc-day-number',
14849 cls: 'fc-day-content',
14853 style: 'position: relative;' // height: 17px;
14865 var cal_rows = function() {
14868 for (var r = 0; r < 6; r++) {
14875 for (var i =0; i < Date.dayNames.length; i++) {
14876 var d = Date.dayNames[i];
14877 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14880 row.cn[0].cls+=' fc-first';
14881 row.cn[0].cn[0].style = 'min-height:90px';
14882 row.cn[6].cls+=' fc-last';
14886 ret[0].cls += ' fc-first';
14887 ret[4].cls += ' fc-prev-last';
14888 ret[5].cls += ' fc-last';
14895 cls: 'fc-border-separate',
14896 style : 'width:100%',
14904 cls : 'fc-first fc-last',
14922 cls : 'fc-content',
14923 style : "position: relative;",
14926 cls : 'fc-view fc-view-month fc-grid',
14927 style : 'position: relative',
14928 unselectable : 'on',
14931 cls : 'fc-event-container',
14932 style : 'position:absolute;z-index:8;top:0;left:0;'
14950 initEvents : function()
14953 throw "can not find store for calendar";
14959 style: "text-align:center",
14963 style: "background-color:white;width:50%;margin:250 auto",
14967 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14978 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14980 var size = this.el.select('.fc-content', true).first().getSize();
14981 this.maskEl.setSize(size.width, size.height);
14982 this.maskEl.enableDisplayMode("block");
14983 if(!this.loadMask){
14984 this.maskEl.hide();
14987 this.store = Roo.factory(this.store, Roo.data);
14988 this.store.on('load', this.onLoad, this);
14989 this.store.on('beforeload', this.onBeforeLoad, this);
14993 this.cells = this.el.select('.fc-day',true);
14994 //Roo.log(this.cells);
14995 this.textNodes = this.el.query('.fc-day-number');
14996 this.cells.addClassOnOver('fc-state-hover');
14998 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14999 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15000 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15001 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15003 this.on('monthchange', this.onMonthChange, this);
15005 this.update(new Date().clearTime());
15008 resize : function() {
15009 var sz = this.el.getSize();
15011 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15012 this.el.select('.fc-day-content div',true).setHeight(34);
15017 showPrevMonth : function(e){
15018 this.update(this.activeDate.add("mo", -1));
15020 showToday : function(e){
15021 this.update(new Date().clearTime());
15024 showNextMonth : function(e){
15025 this.update(this.activeDate.add("mo", 1));
15029 showPrevYear : function(){
15030 this.update(this.activeDate.add("y", -1));
15034 showNextYear : function(){
15035 this.update(this.activeDate.add("y", 1));
15040 update : function(date)
15042 var vd = this.activeDate;
15043 this.activeDate = date;
15044 // if(vd && this.el){
15045 // var t = date.getTime();
15046 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15047 // Roo.log('using add remove');
15049 // this.fireEvent('monthchange', this, date);
15051 // this.cells.removeClass("fc-state-highlight");
15052 // this.cells.each(function(c){
15053 // if(c.dateValue == t){
15054 // c.addClass("fc-state-highlight");
15055 // setTimeout(function(){
15056 // try{c.dom.firstChild.focus();}catch(e){}
15066 var days = date.getDaysInMonth();
15068 var firstOfMonth = date.getFirstDateOfMonth();
15069 var startingPos = firstOfMonth.getDay()-this.startDay;
15071 if(startingPos < this.startDay){
15075 var pm = date.add(Date.MONTH, -1);
15076 var prevStart = pm.getDaysInMonth()-startingPos;
15078 this.cells = this.el.select('.fc-day',true);
15079 this.textNodes = this.el.query('.fc-day-number');
15080 this.cells.addClassOnOver('fc-state-hover');
15082 var cells = this.cells.elements;
15083 var textEls = this.textNodes;
15085 Roo.each(cells, function(cell){
15086 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15089 days += startingPos;
15091 // convert everything to numbers so it's fast
15092 var day = 86400000;
15093 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15096 //Roo.log(prevStart);
15098 var today = new Date().clearTime().getTime();
15099 var sel = date.clearTime().getTime();
15100 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15101 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15102 var ddMatch = this.disabledDatesRE;
15103 var ddText = this.disabledDatesText;
15104 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15105 var ddaysText = this.disabledDaysText;
15106 var format = this.format;
15108 var setCellClass = function(cal, cell){
15112 //Roo.log('set Cell Class');
15114 var t = d.getTime();
15118 cell.dateValue = t;
15120 cell.className += " fc-today";
15121 cell.className += " fc-state-highlight";
15122 cell.title = cal.todayText;
15125 // disable highlight in other month..
15126 //cell.className += " fc-state-highlight";
15131 cell.className = " fc-state-disabled";
15132 cell.title = cal.minText;
15136 cell.className = " fc-state-disabled";
15137 cell.title = cal.maxText;
15141 if(ddays.indexOf(d.getDay()) != -1){
15142 cell.title = ddaysText;
15143 cell.className = " fc-state-disabled";
15146 if(ddMatch && format){
15147 var fvalue = d.dateFormat(format);
15148 if(ddMatch.test(fvalue)){
15149 cell.title = ddText.replace("%0", fvalue);
15150 cell.className = " fc-state-disabled";
15154 if (!cell.initialClassName) {
15155 cell.initialClassName = cell.dom.className;
15158 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15163 for(; i < startingPos; i++) {
15164 textEls[i].innerHTML = (++prevStart);
15165 d.setDate(d.getDate()+1);
15167 cells[i].className = "fc-past fc-other-month";
15168 setCellClass(this, cells[i]);
15173 for(; i < days; i++){
15174 intDay = i - startingPos + 1;
15175 textEls[i].innerHTML = (intDay);
15176 d.setDate(d.getDate()+1);
15178 cells[i].className = ''; // "x-date-active";
15179 setCellClass(this, cells[i]);
15183 for(; i < 42; i++) {
15184 textEls[i].innerHTML = (++extraDays);
15185 d.setDate(d.getDate()+1);
15187 cells[i].className = "fc-future fc-other-month";
15188 setCellClass(this, cells[i]);
15191 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15193 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15195 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15196 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15198 if(totalRows != 6){
15199 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15200 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15203 this.fireEvent('monthchange', this, date);
15207 if(!this.internalRender){
15208 var main = this.el.dom.firstChild;
15209 var w = main.offsetWidth;
15210 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15211 Roo.fly(main).setWidth(w);
15212 this.internalRender = true;
15213 // opera does not respect the auto grow header center column
15214 // then, after it gets a width opera refuses to recalculate
15215 // without a second pass
15216 if(Roo.isOpera && !this.secondPass){
15217 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15218 this.secondPass = true;
15219 this.update.defer(10, this, [date]);
15226 findCell : function(dt) {
15227 dt = dt.clearTime().getTime();
15229 this.cells.each(function(c){
15230 //Roo.log("check " +c.dateValue + '?=' + dt);
15231 if(c.dateValue == dt){
15241 findCells : function(ev) {
15242 var s = ev.start.clone().clearTime().getTime();
15244 var e= ev.end.clone().clearTime().getTime();
15247 this.cells.each(function(c){
15248 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15250 if(c.dateValue > e){
15253 if(c.dateValue < s){
15262 // findBestRow: function(cells)
15266 // for (var i =0 ; i < cells.length;i++) {
15267 // ret = Math.max(cells[i].rows || 0,ret);
15274 addItem : function(ev)
15276 // look for vertical location slot in
15277 var cells = this.findCells(ev);
15279 // ev.row = this.findBestRow(cells);
15281 // work out the location.
15285 for(var i =0; i < cells.length; i++) {
15287 cells[i].row = cells[0].row;
15290 cells[i].row = cells[i].row + 1;
15300 if (crow.start.getY() == cells[i].getY()) {
15302 crow.end = cells[i];
15319 cells[0].events.push(ev);
15321 this.calevents.push(ev);
15324 clearEvents: function() {
15326 if(!this.calevents){
15330 Roo.each(this.cells.elements, function(c){
15336 Roo.each(this.calevents, function(e) {
15337 Roo.each(e.els, function(el) {
15338 el.un('mouseenter' ,this.onEventEnter, this);
15339 el.un('mouseleave' ,this.onEventLeave, this);
15344 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15350 renderEvents: function()
15354 this.cells.each(function(c) {
15363 if(c.row != c.events.length){
15364 r = 4 - (4 - (c.row - c.events.length));
15367 c.events = ev.slice(0, r);
15368 c.more = ev.slice(r);
15370 if(c.more.length && c.more.length == 1){
15371 c.events.push(c.more.pop());
15374 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15378 this.cells.each(function(c) {
15380 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15383 for (var e = 0; e < c.events.length; e++){
15384 var ev = c.events[e];
15385 var rows = ev.rows;
15387 for(var i = 0; i < rows.length; i++) {
15389 // how many rows should it span..
15392 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15393 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15395 unselectable : "on",
15398 cls: 'fc-event-inner',
15402 // cls: 'fc-event-time',
15403 // html : cells.length > 1 ? '' : ev.time
15407 cls: 'fc-event-title',
15408 html : String.format('{0}', ev.title)
15415 cls: 'ui-resizable-handle ui-resizable-e',
15416 html : '  '
15423 cfg.cls += ' fc-event-start';
15425 if ((i+1) == rows.length) {
15426 cfg.cls += ' fc-event-end';
15429 var ctr = _this.el.select('.fc-event-container',true).first();
15430 var cg = ctr.createChild(cfg);
15432 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15433 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15435 var r = (c.more.length) ? 1 : 0;
15436 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15437 cg.setWidth(ebox.right - sbox.x -2);
15439 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15440 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15441 cg.on('click', _this.onEventClick, _this, ev);
15452 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15453 style : 'position: absolute',
15454 unselectable : "on",
15457 cls: 'fc-event-inner',
15461 cls: 'fc-event-title',
15469 cls: 'ui-resizable-handle ui-resizable-e',
15470 html : '  '
15476 var ctr = _this.el.select('.fc-event-container',true).first();
15477 var cg = ctr.createChild(cfg);
15479 var sbox = c.select('.fc-day-content',true).first().getBox();
15480 var ebox = c.select('.fc-day-content',true).first().getBox();
15482 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15483 cg.setWidth(ebox.right - sbox.x -2);
15485 cg.on('click', _this.onMoreEventClick, _this, c.more);
15495 onEventEnter: function (e, el,event,d) {
15496 this.fireEvent('evententer', this, el, event);
15499 onEventLeave: function (e, el,event,d) {
15500 this.fireEvent('eventleave', this, el, event);
15503 onEventClick: function (e, el,event,d) {
15504 this.fireEvent('eventclick', this, el, event);
15507 onMonthChange: function () {
15511 onMoreEventClick: function(e, el, more)
15515 this.calpopover.placement = 'right';
15516 this.calpopover.setTitle('More');
15518 this.calpopover.setContent('');
15520 var ctr = this.calpopover.el.select('.popover-content', true).first();
15522 Roo.each(more, function(m){
15524 cls : 'fc-event-hori fc-event-draggable',
15527 var cg = ctr.createChild(cfg);
15529 cg.on('click', _this.onEventClick, _this, m);
15532 this.calpopover.show(el);
15537 onLoad: function ()
15539 this.calevents = [];
15542 if(this.store.getCount() > 0){
15543 this.store.data.each(function(d){
15546 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15547 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15548 time : d.data.start_time,
15549 title : d.data.title,
15550 description : d.data.description,
15551 venue : d.data.venue
15556 this.renderEvents();
15558 if(this.calevents.length && this.loadMask){
15559 this.maskEl.hide();
15563 onBeforeLoad: function()
15565 this.clearEvents();
15567 this.maskEl.show();
15581 * @class Roo.bootstrap.Popover
15582 * @extends Roo.bootstrap.Component
15583 * Bootstrap Popover class
15584 * @cfg {String} html contents of the popover (or false to use children..)
15585 * @cfg {String} title of popover (or false to hide)
15586 * @cfg {String} placement how it is placed
15587 * @cfg {String} trigger click || hover (or false to trigger manually)
15588 * @cfg {String} over what (parent or false to trigger manually.)
15589 * @cfg {Number} delay - delay before showing
15592 * Create a new Popover
15593 * @param {Object} config The config object
15596 Roo.bootstrap.Popover = function(config){
15597 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15600 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15602 title: 'Fill in a title',
15605 placement : 'right',
15606 trigger : 'hover', // hover
15612 can_build_overlaid : false,
15614 getChildContainer : function()
15616 return this.el.select('.popover-content',true).first();
15619 getAutoCreate : function(){
15620 Roo.log('make popover?');
15622 cls : 'popover roo-dynamic',
15623 style: 'display:block',
15629 cls : 'popover-inner',
15633 cls: 'popover-title',
15637 cls : 'popover-content',
15648 setTitle: function(str)
15651 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15653 setContent: function(str)
15656 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15658 // as it get's added to the bottom of the page.
15659 onRender : function(ct, position)
15661 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15663 var cfg = Roo.apply({}, this.getAutoCreate());
15667 cfg.cls += ' ' + this.cls;
15670 cfg.style = this.style;
15672 //Roo.log("adding to ");
15673 this.el = Roo.get(document.body).createChild(cfg, position);
15679 initEvents : function()
15681 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15682 this.el.enableDisplayMode('block');
15684 if (this.over === false) {
15687 if (this.triggers === false) {
15690 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15691 var triggers = this.trigger ? this.trigger.split(' ') : [];
15692 Roo.each(triggers, function(trigger) {
15694 if (trigger == 'click') {
15695 on_el.on('click', this.toggle, this);
15696 } else if (trigger != 'manual') {
15697 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15698 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15700 on_el.on(eventIn ,this.enter, this);
15701 on_el.on(eventOut, this.leave, this);
15712 toggle : function () {
15713 this.hoverState == 'in' ? this.leave() : this.enter();
15716 enter : function () {
15719 clearTimeout(this.timeout);
15721 this.hoverState = 'in';
15723 if (!this.delay || !this.delay.show) {
15728 this.timeout = setTimeout(function () {
15729 if (_t.hoverState == 'in') {
15732 }, this.delay.show)
15734 leave : function() {
15735 clearTimeout(this.timeout);
15737 this.hoverState = 'out';
15739 if (!this.delay || !this.delay.hide) {
15744 this.timeout = setTimeout(function () {
15745 if (_t.hoverState == 'out') {
15748 }, this.delay.hide)
15751 show : function (on_el)
15754 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15757 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15758 if (this.html !== false) {
15759 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15761 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15762 if (!this.title.length) {
15763 this.el.select('.popover-title',true).hide();
15766 var placement = typeof this.placement == 'function' ?
15767 this.placement.call(this, this.el, on_el) :
15770 var autoToken = /\s?auto?\s?/i;
15771 var autoPlace = autoToken.test(placement);
15773 placement = placement.replace(autoToken, '') || 'top';
15777 //this.el.setXY([0,0]);
15779 this.el.dom.style.display='block';
15780 this.el.addClass(placement);
15782 //this.el.appendTo(on_el);
15784 var p = this.getPosition();
15785 var box = this.el.getBox();
15790 var align = Roo.bootstrap.Popover.alignment[placement];
15791 this.el.alignTo(on_el, align[0],align[1]);
15792 //var arrow = this.el.select('.arrow',true).first();
15793 //arrow.set(align[2],
15795 this.el.addClass('in');
15798 if (this.el.hasClass('fade')) {
15805 this.el.setXY([0,0]);
15806 this.el.removeClass('in');
15808 this.hoverState = null;
15814 Roo.bootstrap.Popover.alignment = {
15815 'left' : ['r-l', [-10,0], 'right'],
15816 'right' : ['l-r', [10,0], 'left'],
15817 'bottom' : ['t-b', [0,10], 'top'],
15818 'top' : [ 'b-t', [0,-10], 'bottom']
15829 * @class Roo.bootstrap.Progress
15830 * @extends Roo.bootstrap.Component
15831 * Bootstrap Progress class
15832 * @cfg {Boolean} striped striped of the progress bar
15833 * @cfg {Boolean} active animated of the progress bar
15837 * Create a new Progress
15838 * @param {Object} config The config object
15841 Roo.bootstrap.Progress = function(config){
15842 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15845 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15850 getAutoCreate : function(){
15858 cfg.cls += ' progress-striped';
15862 cfg.cls += ' active';
15881 * @class Roo.bootstrap.ProgressBar
15882 * @extends Roo.bootstrap.Component
15883 * Bootstrap ProgressBar class
15884 * @cfg {Number} aria_valuenow aria-value now
15885 * @cfg {Number} aria_valuemin aria-value min
15886 * @cfg {Number} aria_valuemax aria-value max
15887 * @cfg {String} label label for the progress bar
15888 * @cfg {String} panel (success | info | warning | danger )
15889 * @cfg {String} role role of the progress bar
15890 * @cfg {String} sr_only text
15894 * Create a new ProgressBar
15895 * @param {Object} config The config object
15898 Roo.bootstrap.ProgressBar = function(config){
15899 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15902 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15906 aria_valuemax : 100,
15912 getAutoCreate : function()
15917 cls: 'progress-bar',
15918 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15930 cfg.role = this.role;
15933 if(this.aria_valuenow){
15934 cfg['aria-valuenow'] = this.aria_valuenow;
15937 if(this.aria_valuemin){
15938 cfg['aria-valuemin'] = this.aria_valuemin;
15941 if(this.aria_valuemax){
15942 cfg['aria-valuemax'] = this.aria_valuemax;
15945 if(this.label && !this.sr_only){
15946 cfg.html = this.label;
15950 cfg.cls += ' progress-bar-' + this.panel;
15956 update : function(aria_valuenow)
15958 this.aria_valuenow = aria_valuenow;
15960 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15975 * @class Roo.bootstrap.TabGroup
15976 * @extends Roo.bootstrap.Column
15977 * Bootstrap Column class
15978 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15979 * @cfg {Boolean} carousel true to make the group behave like a carousel
15980 * @cfg {Boolean} bullets show bullets for the panels
15981 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15982 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15983 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15986 * Create a new TabGroup
15987 * @param {Object} config The config object
15990 Roo.bootstrap.TabGroup = function(config){
15991 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15993 this.navId = Roo.id();
15996 Roo.bootstrap.TabGroup.register(this);
16000 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16003 transition : false,
16008 slideOnTouch : false,
16010 getAutoCreate : function()
16012 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16014 cfg.cls += ' tab-content';
16016 Roo.log('get auto create...............');
16018 if (this.carousel) {
16019 cfg.cls += ' carousel slide';
16022 cls : 'carousel-inner'
16025 if(this.bullets && !Roo.isTouch){
16028 cls : 'carousel-bullets',
16032 if(this.bullets_cls){
16033 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16036 for (var i = 0; i < this.bullets; i++){
16038 cls : 'bullet bullet-' + i
16046 cfg.cn[0].cn = bullets;
16053 initEvents: function()
16055 Roo.log('-------- init events on tab group ---------');
16061 if(Roo.isTouch && this.slideOnTouch){
16062 this.el.on("touchstart", this.onTouchStart, this);
16065 if(this.autoslide){
16068 this.slideFn = window.setInterval(function() {
16069 _this.showPanelNext();
16075 onTouchStart : function(e, el, o)
16077 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16081 this.showPanelNext();
16084 getChildContainer : function()
16086 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16090 * register a Navigation item
16091 * @param {Roo.bootstrap.NavItem} the navitem to add
16093 register : function(item)
16095 this.tabs.push( item);
16096 item.navId = this.navId; // not really needed..
16101 getActivePanel : function()
16104 Roo.each(this.tabs, function(t) {
16114 getPanelByName : function(n)
16117 Roo.each(this.tabs, function(t) {
16118 if (t.tabId == n) {
16126 indexOfPanel : function(p)
16129 Roo.each(this.tabs, function(t,i) {
16130 if (t.tabId == p.tabId) {
16139 * show a specific panel
16140 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16141 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16143 showPanel : function (pan)
16145 if(this.transition){
16146 Roo.log("waiting for the transitionend");
16150 if (typeof(pan) == 'number') {
16151 pan = this.tabs[pan];
16153 if (typeof(pan) == 'string') {
16154 pan = this.getPanelByName(pan);
16156 if (pan.tabId == this.getActivePanel().tabId) {
16159 var cur = this.getActivePanel();
16161 if (false === cur.fireEvent('beforedeactivate')) {
16165 if(this.bullets > 0 && !Roo.isTouch){
16166 this.setActiveBullet(this.indexOfPanel(pan));
16169 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16171 this.transition = true;
16172 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16173 var lr = dir == 'next' ? 'left' : 'right';
16174 pan.el.addClass(dir); // or prev
16175 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16176 cur.el.addClass(lr); // or right
16177 pan.el.addClass(lr);
16180 cur.el.on('transitionend', function() {
16181 Roo.log("trans end?");
16183 pan.el.removeClass([lr,dir]);
16184 pan.setActive(true);
16186 cur.el.removeClass([lr]);
16187 cur.setActive(false);
16189 _this.transition = false;
16191 }, this, { single: true } );
16196 cur.setActive(false);
16197 pan.setActive(true);
16202 showPanelNext : function()
16204 var i = this.indexOfPanel(this.getActivePanel());
16206 if (i >= this.tabs.length - 1 && !this.autoslide) {
16210 if (i >= this.tabs.length - 1 && this.autoslide) {
16214 this.showPanel(this.tabs[i+1]);
16217 showPanelPrev : function()
16219 var i = this.indexOfPanel(this.getActivePanel());
16221 if (i < 1 && !this.autoslide) {
16225 if (i < 1 && this.autoslide) {
16226 i = this.tabs.length;
16229 this.showPanel(this.tabs[i-1]);
16233 addBullet: function()
16235 if(!this.bullets || Roo.isTouch){
16238 var ctr = this.el.select('.carousel-bullets',true).first();
16239 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16240 var bullet = ctr.createChild({
16241 cls : 'bullet bullet-' + i
16242 },ctr.dom.lastChild);
16244 bullet.on('click', (function(e, el, o, ii, t){
16246 e.preventDefault();
16248 this.showPanel(ii);
16250 if(this.autoslide && this.slideFn){
16251 clearInterval(this.slideFn);
16252 this.slideFn = window.setInterval(function() {
16253 this.showPanelNext();
16257 }).createDelegate(this, [i, bullet], true));
16262 setActiveBullet : function(i)
16268 Roo.each(this.el.select('.bullet', true).elements, function(el){
16269 el.removeClass('selected');
16272 var bullet = this.el.select('.bullet-' + i, true).first();
16278 bullet.addClass('selected');
16289 Roo.apply(Roo.bootstrap.TabGroup, {
16293 * register a Navigation Group
16294 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16296 register : function(navgrp)
16298 this.groups[navgrp.navId] = navgrp;
16302 * fetch a Navigation Group based on the navigation ID
16303 * if one does not exist , it will get created.
16304 * @param {string} the navgroup to add
16305 * @returns {Roo.bootstrap.NavGroup} the navgroup
16307 get: function(navId) {
16308 if (typeof(this.groups[navId]) == 'undefined') {
16309 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16311 return this.groups[navId] ;
16326 * @class Roo.bootstrap.TabPanel
16327 * @extends Roo.bootstrap.Component
16328 * Bootstrap TabPanel class
16329 * @cfg {Boolean} active panel active
16330 * @cfg {String} html panel content
16331 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16332 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16336 * Create a new TabPanel
16337 * @param {Object} config The config object
16340 Roo.bootstrap.TabPanel = function(config){
16341 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16345 * Fires when the active status changes
16346 * @param {Roo.bootstrap.TabPanel} this
16347 * @param {Boolean} state the new state
16352 * @event beforedeactivate
16353 * Fires before a tab is de-activated - can be used to do validation on a form.
16354 * @param {Roo.bootstrap.TabPanel} this
16355 * @return {Boolean} false if there is an error
16358 'beforedeactivate': true
16361 this.tabId = this.tabId || Roo.id();
16365 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16372 getAutoCreate : function(){
16375 // item is needed for carousel - not sure if it has any effect otherwise
16376 cls: 'tab-pane item',
16377 html: this.html || ''
16381 cfg.cls += ' active';
16385 cfg.tabId = this.tabId;
16392 initEvents: function()
16394 Roo.log('-------- init events on tab panel ---------');
16396 var p = this.parent();
16397 this.navId = this.navId || p.navId;
16399 if (typeof(this.navId) != 'undefined') {
16400 // not really needed.. but just in case.. parent should be a NavGroup.
16401 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16402 Roo.log(['register', tg, this]);
16405 var i = tg.tabs.length - 1;
16407 if(this.active && tg.bullets > 0 && i < tg.bullets){
16408 tg.setActiveBullet(i);
16415 onRender : function(ct, position)
16417 // Roo.log("Call onRender: " + this.xtype);
16419 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16427 setActive: function(state)
16429 Roo.log("panel - set active " + this.tabId + "=" + state);
16431 this.active = state;
16433 this.el.removeClass('active');
16435 } else if (!this.el.hasClass('active')) {
16436 this.el.addClass('active');
16439 this.fireEvent('changed', this, state);
16456 * @class Roo.bootstrap.DateField
16457 * @extends Roo.bootstrap.Input
16458 * Bootstrap DateField class
16459 * @cfg {Number} weekStart default 0
16460 * @cfg {String} viewMode default empty, (months|years)
16461 * @cfg {String} minViewMode default empty, (months|years)
16462 * @cfg {Number} startDate default -Infinity
16463 * @cfg {Number} endDate default Infinity
16464 * @cfg {Boolean} todayHighlight default false
16465 * @cfg {Boolean} todayBtn default false
16466 * @cfg {Boolean} calendarWeeks default false
16467 * @cfg {Object} daysOfWeekDisabled default empty
16468 * @cfg {Boolean} singleMode default false (true | false)
16470 * @cfg {Boolean} keyboardNavigation default true
16471 * @cfg {String} language default en
16474 * Create a new DateField
16475 * @param {Object} config The config object
16478 Roo.bootstrap.DateField = function(config){
16479 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16483 * Fires when this field show.
16484 * @param {Roo.bootstrap.DateField} this
16485 * @param {Mixed} date The date value
16490 * Fires when this field hide.
16491 * @param {Roo.bootstrap.DateField} this
16492 * @param {Mixed} date The date value
16497 * Fires when select a date.
16498 * @param {Roo.bootstrap.DateField} this
16499 * @param {Mixed} date The date value
16503 * @event beforeselect
16504 * Fires when before select a date.
16505 * @param {Roo.bootstrap.DateField} this
16506 * @param {Mixed} date The date value
16508 beforeselect : true
16512 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16515 * @cfg {String} format
16516 * The default date format string which can be overriden for localization support. The format must be
16517 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16521 * @cfg {String} altFormats
16522 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16523 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16525 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16533 todayHighlight : false,
16539 keyboardNavigation: true,
16541 calendarWeeks: false,
16543 startDate: -Infinity,
16547 daysOfWeekDisabled: [],
16551 singleMode : false,
16553 UTCDate: function()
16555 return new Date(Date.UTC.apply(Date, arguments));
16558 UTCToday: function()
16560 var today = new Date();
16561 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16564 getDate: function() {
16565 var d = this.getUTCDate();
16566 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16569 getUTCDate: function() {
16573 setDate: function(d) {
16574 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16577 setUTCDate: function(d) {
16579 this.setValue(this.formatDate(this.date));
16582 onRender: function(ct, position)
16585 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16587 this.language = this.language || 'en';
16588 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16589 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16591 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16592 this.format = this.format || 'm/d/y';
16593 this.isInline = false;
16594 this.isInput = true;
16595 this.component = this.el.select('.add-on', true).first() || false;
16596 this.component = (this.component && this.component.length === 0) ? false : this.component;
16597 this.hasInput = this.component && this.inputEL().length;
16599 if (typeof(this.minViewMode === 'string')) {
16600 switch (this.minViewMode) {
16602 this.minViewMode = 1;
16605 this.minViewMode = 2;
16608 this.minViewMode = 0;
16613 if (typeof(this.viewMode === 'string')) {
16614 switch (this.viewMode) {
16627 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16629 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16631 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16633 this.picker().on('mousedown', this.onMousedown, this);
16634 this.picker().on('click', this.onClick, this);
16636 this.picker().addClass('datepicker-dropdown');
16638 this.startViewMode = this.viewMode;
16640 if(this.singleMode){
16641 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16642 v.setVisibilityMode(Roo.Element.DISPLAY);
16646 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16647 v.setStyle('width', '189px');
16651 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16652 if(!this.calendarWeeks){
16657 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16658 v.attr('colspan', function(i, val){
16659 return parseInt(val) + 1;
16664 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16666 this.setStartDate(this.startDate);
16667 this.setEndDate(this.endDate);
16669 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16676 if(this.isInline) {
16681 picker : function()
16683 return this.pickerEl;
16684 // return this.el.select('.datepicker', true).first();
16687 fillDow: function()
16689 var dowCnt = this.weekStart;
16698 if(this.calendarWeeks){
16706 while (dowCnt < this.weekStart + 7) {
16710 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16714 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16717 fillMonths: function()
16720 var months = this.picker().select('>.datepicker-months td', true).first();
16722 months.dom.innerHTML = '';
16728 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16731 months.createChild(month);
16738 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;
16740 if (this.date < this.startDate) {
16741 this.viewDate = new Date(this.startDate);
16742 } else if (this.date > this.endDate) {
16743 this.viewDate = new Date(this.endDate);
16745 this.viewDate = new Date(this.date);
16753 var d = new Date(this.viewDate),
16754 year = d.getUTCFullYear(),
16755 month = d.getUTCMonth(),
16756 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16757 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16758 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16759 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16760 currentDate = this.date && this.date.valueOf(),
16761 today = this.UTCToday();
16763 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16765 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16767 // this.picker.select('>tfoot th.today').
16768 // .text(dates[this.language].today)
16769 // .toggle(this.todayBtn !== false);
16771 this.updateNavArrows();
16774 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16776 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16778 prevMonth.setUTCDate(day);
16780 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16782 var nextMonth = new Date(prevMonth);
16784 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16786 nextMonth = nextMonth.valueOf();
16788 var fillMonths = false;
16790 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16792 while(prevMonth.valueOf() < nextMonth) {
16795 if (prevMonth.getUTCDay() === this.weekStart) {
16797 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16805 if(this.calendarWeeks){
16806 // ISO 8601: First week contains first thursday.
16807 // ISO also states week starts on Monday, but we can be more abstract here.
16809 // Start of current week: based on weekstart/current date
16810 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16811 // Thursday of this week
16812 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16813 // First Thursday of year, year from thursday
16814 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16815 // Calendar week: ms between thursdays, div ms per day, div 7 days
16816 calWeek = (th - yth) / 864e5 / 7 + 1;
16818 fillMonths.cn.push({
16826 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16828 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16831 if (this.todayHighlight &&
16832 prevMonth.getUTCFullYear() == today.getFullYear() &&
16833 prevMonth.getUTCMonth() == today.getMonth() &&
16834 prevMonth.getUTCDate() == today.getDate()) {
16835 clsName += ' today';
16838 if (currentDate && prevMonth.valueOf() === currentDate) {
16839 clsName += ' active';
16842 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16843 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16844 clsName += ' disabled';
16847 fillMonths.cn.push({
16849 cls: 'day ' + clsName,
16850 html: prevMonth.getDate()
16853 prevMonth.setDate(prevMonth.getDate()+1);
16856 var currentYear = this.date && this.date.getUTCFullYear();
16857 var currentMonth = this.date && this.date.getUTCMonth();
16859 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16861 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16862 v.removeClass('active');
16864 if(currentYear === year && k === currentMonth){
16865 v.addClass('active');
16868 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16869 v.addClass('disabled');
16875 year = parseInt(year/10, 10) * 10;
16877 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16879 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16882 for (var i = -1; i < 11; i++) {
16883 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16885 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16893 showMode: function(dir)
16896 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16899 Roo.each(this.picker().select('>div',true).elements, function(v){
16900 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16903 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16908 if(this.isInline) {
16912 this.picker().removeClass(['bottom', 'top']);
16914 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16916 * place to the top of element!
16920 this.picker().addClass('top');
16921 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16926 this.picker().addClass('bottom');
16928 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16931 parseDate : function(value)
16933 if(!value || value instanceof Date){
16936 var v = Date.parseDate(value, this.format);
16937 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16938 v = Date.parseDate(value, 'Y-m-d');
16940 if(!v && this.altFormats){
16941 if(!this.altFormatsArray){
16942 this.altFormatsArray = this.altFormats.split("|");
16944 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16945 v = Date.parseDate(value, this.altFormatsArray[i]);
16951 formatDate : function(date, fmt)
16953 return (!date || !(date instanceof Date)) ?
16954 date : date.dateFormat(fmt || this.format);
16957 onFocus : function()
16959 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16963 onBlur : function()
16965 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16967 var d = this.inputEl().getValue();
16976 this.picker().show();
16980 this.fireEvent('show', this, this.date);
16985 if(this.isInline) {
16988 this.picker().hide();
16989 this.viewMode = this.startViewMode;
16992 this.fireEvent('hide', this, this.date);
16996 onMousedown: function(e)
16998 e.stopPropagation();
16999 e.preventDefault();
17004 Roo.bootstrap.DateField.superclass.keyup.call(this);
17008 setValue: function(v)
17010 if(this.fireEvent('beforeselect', this, v) !== false){
17011 var d = new Date(this.parseDate(v) ).clearTime();
17013 if(isNaN(d.getTime())){
17014 this.date = this.viewDate = '';
17015 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17019 v = this.formatDate(d);
17021 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17023 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17027 this.fireEvent('select', this, this.date);
17031 getValue: function()
17033 return this.formatDate(this.date);
17036 fireKey: function(e)
17038 if (!this.picker().isVisible()){
17039 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17045 var dateChanged = false,
17047 newDate, newViewDate;
17052 e.preventDefault();
17056 if (!this.keyboardNavigation) {
17059 dir = e.keyCode == 37 ? -1 : 1;
17062 newDate = this.moveYear(this.date, dir);
17063 newViewDate = this.moveYear(this.viewDate, dir);
17064 } else if (e.shiftKey){
17065 newDate = this.moveMonth(this.date, dir);
17066 newViewDate = this.moveMonth(this.viewDate, dir);
17068 newDate = new Date(this.date);
17069 newDate.setUTCDate(this.date.getUTCDate() + dir);
17070 newViewDate = new Date(this.viewDate);
17071 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17073 if (this.dateWithinRange(newDate)){
17074 this.date = newDate;
17075 this.viewDate = newViewDate;
17076 this.setValue(this.formatDate(this.date));
17078 e.preventDefault();
17079 dateChanged = true;
17084 if (!this.keyboardNavigation) {
17087 dir = e.keyCode == 38 ? -1 : 1;
17089 newDate = this.moveYear(this.date, dir);
17090 newViewDate = this.moveYear(this.viewDate, dir);
17091 } else if (e.shiftKey){
17092 newDate = this.moveMonth(this.date, dir);
17093 newViewDate = this.moveMonth(this.viewDate, dir);
17095 newDate = new Date(this.date);
17096 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17097 newViewDate = new Date(this.viewDate);
17098 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17100 if (this.dateWithinRange(newDate)){
17101 this.date = newDate;
17102 this.viewDate = newViewDate;
17103 this.setValue(this.formatDate(this.date));
17105 e.preventDefault();
17106 dateChanged = true;
17110 this.setValue(this.formatDate(this.date));
17112 e.preventDefault();
17115 this.setValue(this.formatDate(this.date));
17129 onClick: function(e)
17131 e.stopPropagation();
17132 e.preventDefault();
17134 var target = e.getTarget();
17136 if(target.nodeName.toLowerCase() === 'i'){
17137 target = Roo.get(target).dom.parentNode;
17140 var nodeName = target.nodeName;
17141 var className = target.className;
17142 var html = target.innerHTML;
17143 //Roo.log(nodeName);
17145 switch(nodeName.toLowerCase()) {
17147 switch(className) {
17153 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17154 switch(this.viewMode){
17156 this.viewDate = this.moveMonth(this.viewDate, dir);
17160 this.viewDate = this.moveYear(this.viewDate, dir);
17166 var date = new Date();
17167 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17169 this.setValue(this.formatDate(this.date));
17176 if (className.indexOf('disabled') < 0) {
17177 this.viewDate.setUTCDate(1);
17178 if (className.indexOf('month') > -1) {
17179 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17181 var year = parseInt(html, 10) || 0;
17182 this.viewDate.setUTCFullYear(year);
17186 if(this.singleMode){
17187 this.setValue(this.formatDate(this.viewDate));
17198 //Roo.log(className);
17199 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17200 var day = parseInt(html, 10) || 1;
17201 var year = this.viewDate.getUTCFullYear(),
17202 month = this.viewDate.getUTCMonth();
17204 if (className.indexOf('old') > -1) {
17211 } else if (className.indexOf('new') > -1) {
17219 //Roo.log([year,month,day]);
17220 this.date = this.UTCDate(year, month, day,0,0,0,0);
17221 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17223 //Roo.log(this.formatDate(this.date));
17224 this.setValue(this.formatDate(this.date));
17231 setStartDate: function(startDate)
17233 this.startDate = startDate || -Infinity;
17234 if (this.startDate !== -Infinity) {
17235 this.startDate = this.parseDate(this.startDate);
17238 this.updateNavArrows();
17241 setEndDate: function(endDate)
17243 this.endDate = endDate || Infinity;
17244 if (this.endDate !== Infinity) {
17245 this.endDate = this.parseDate(this.endDate);
17248 this.updateNavArrows();
17251 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17253 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17254 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17255 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17257 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17258 return parseInt(d, 10);
17261 this.updateNavArrows();
17264 updateNavArrows: function()
17266 if(this.singleMode){
17270 var d = new Date(this.viewDate),
17271 year = d.getUTCFullYear(),
17272 month = d.getUTCMonth();
17274 Roo.each(this.picker().select('.prev', true).elements, function(v){
17276 switch (this.viewMode) {
17279 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17285 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17292 Roo.each(this.picker().select('.next', true).elements, function(v){
17294 switch (this.viewMode) {
17297 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17303 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17311 moveMonth: function(date, dir)
17316 var new_date = new Date(date.valueOf()),
17317 day = new_date.getUTCDate(),
17318 month = new_date.getUTCMonth(),
17319 mag = Math.abs(dir),
17321 dir = dir > 0 ? 1 : -1;
17324 // If going back one month, make sure month is not current month
17325 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17327 return new_date.getUTCMonth() == month;
17329 // If going forward one month, make sure month is as expected
17330 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17332 return new_date.getUTCMonth() != new_month;
17334 new_month = month + dir;
17335 new_date.setUTCMonth(new_month);
17336 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17337 if (new_month < 0 || new_month > 11) {
17338 new_month = (new_month + 12) % 12;
17341 // For magnitudes >1, move one month at a time...
17342 for (var i=0; i<mag; i++) {
17343 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17344 new_date = this.moveMonth(new_date, dir);
17346 // ...then reset the day, keeping it in the new month
17347 new_month = new_date.getUTCMonth();
17348 new_date.setUTCDate(day);
17350 return new_month != new_date.getUTCMonth();
17353 // Common date-resetting loop -- if date is beyond end of month, make it
17356 new_date.setUTCDate(--day);
17357 new_date.setUTCMonth(new_month);
17362 moveYear: function(date, dir)
17364 return this.moveMonth(date, dir*12);
17367 dateWithinRange: function(date)
17369 return date >= this.startDate && date <= this.endDate;
17375 this.picker().remove();
17380 Roo.apply(Roo.bootstrap.DateField, {
17391 html: '<i class="fa fa-arrow-left"/>'
17401 html: '<i class="fa fa-arrow-right"/>'
17443 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17444 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17445 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17446 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17447 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17460 navFnc: 'FullYear',
17465 navFnc: 'FullYear',
17470 Roo.apply(Roo.bootstrap.DateField, {
17474 cls: 'datepicker dropdown-menu roo-dynamic',
17478 cls: 'datepicker-days',
17482 cls: 'table-condensed',
17484 Roo.bootstrap.DateField.head,
17488 Roo.bootstrap.DateField.footer
17495 cls: 'datepicker-months',
17499 cls: 'table-condensed',
17501 Roo.bootstrap.DateField.head,
17502 Roo.bootstrap.DateField.content,
17503 Roo.bootstrap.DateField.footer
17510 cls: 'datepicker-years',
17514 cls: 'table-condensed',
17516 Roo.bootstrap.DateField.head,
17517 Roo.bootstrap.DateField.content,
17518 Roo.bootstrap.DateField.footer
17537 * @class Roo.bootstrap.TimeField
17538 * @extends Roo.bootstrap.Input
17539 * Bootstrap DateField class
17543 * Create a new TimeField
17544 * @param {Object} config The config object
17547 Roo.bootstrap.TimeField = function(config){
17548 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17552 * Fires when this field show.
17553 * @param {Roo.bootstrap.DateField} thisthis
17554 * @param {Mixed} date The date value
17559 * Fires when this field hide.
17560 * @param {Roo.bootstrap.DateField} this
17561 * @param {Mixed} date The date value
17566 * Fires when select a date.
17567 * @param {Roo.bootstrap.DateField} this
17568 * @param {Mixed} date The date value
17574 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17577 * @cfg {String} format
17578 * The default time format string which can be overriden for localization support. The format must be
17579 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17583 onRender: function(ct, position)
17586 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17588 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17590 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17592 this.pop = this.picker().select('>.datepicker-time',true).first();
17593 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17595 this.picker().on('mousedown', this.onMousedown, this);
17596 this.picker().on('click', this.onClick, this);
17598 this.picker().addClass('datepicker-dropdown');
17603 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17604 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17605 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17606 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17607 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17608 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17612 fireKey: function(e){
17613 if (!this.picker().isVisible()){
17614 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17620 e.preventDefault();
17628 this.onTogglePeriod();
17631 this.onIncrementMinutes();
17634 this.onDecrementMinutes();
17643 onClick: function(e) {
17644 e.stopPropagation();
17645 e.preventDefault();
17648 picker : function()
17650 return this.el.select('.datepicker', true).first();
17653 fillTime: function()
17655 var time = this.pop.select('tbody', true).first();
17657 time.dom.innerHTML = '';
17672 cls: 'hours-up glyphicon glyphicon-chevron-up'
17692 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17713 cls: 'timepicker-hour',
17728 cls: 'timepicker-minute',
17743 cls: 'btn btn-primary period',
17765 cls: 'hours-down glyphicon glyphicon-chevron-down'
17785 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17803 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17810 var hours = this.time.getHours();
17811 var minutes = this.time.getMinutes();
17824 hours = hours - 12;
17828 hours = '0' + hours;
17832 minutes = '0' + minutes;
17835 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17836 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17837 this.pop.select('button', true).first().dom.innerHTML = period;
17843 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17845 var cls = ['bottom'];
17847 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17854 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17859 this.picker().addClass(cls.join('-'));
17863 Roo.each(cls, function(c){
17865 _this.picker().setTop(_this.inputEl().getHeight());
17869 _this.picker().setTop(0 - _this.picker().getHeight());
17874 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17878 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17885 onFocus : function()
17887 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17891 onBlur : function()
17893 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17899 this.picker().show();
17904 this.fireEvent('show', this, this.date);
17909 this.picker().hide();
17912 this.fireEvent('hide', this, this.date);
17915 setTime : function()
17918 this.setValue(this.time.format(this.format));
17920 this.fireEvent('select', this, this.date);
17925 onMousedown: function(e){
17926 e.stopPropagation();
17927 e.preventDefault();
17930 onIncrementHours: function()
17932 Roo.log('onIncrementHours');
17933 this.time = this.time.add(Date.HOUR, 1);
17938 onDecrementHours: function()
17940 Roo.log('onDecrementHours');
17941 this.time = this.time.add(Date.HOUR, -1);
17945 onIncrementMinutes: function()
17947 Roo.log('onIncrementMinutes');
17948 this.time = this.time.add(Date.MINUTE, 1);
17952 onDecrementMinutes: function()
17954 Roo.log('onDecrementMinutes');
17955 this.time = this.time.add(Date.MINUTE, -1);
17959 onTogglePeriod: function()
17961 Roo.log('onTogglePeriod');
17962 this.time = this.time.add(Date.HOUR, 12);
17969 Roo.apply(Roo.bootstrap.TimeField, {
17999 cls: 'btn btn-info ok',
18011 Roo.apply(Roo.bootstrap.TimeField, {
18015 cls: 'datepicker dropdown-menu',
18019 cls: 'datepicker-time',
18023 cls: 'table-condensed',
18025 Roo.bootstrap.TimeField.content,
18026 Roo.bootstrap.TimeField.footer
18045 * @class Roo.bootstrap.MonthField
18046 * @extends Roo.bootstrap.Input
18047 * Bootstrap MonthField class
18049 * @cfg {String} language default en
18052 * Create a new MonthField
18053 * @param {Object} config The config object
18056 Roo.bootstrap.MonthField = function(config){
18057 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18062 * Fires when this field show.
18063 * @param {Roo.bootstrap.MonthField} this
18064 * @param {Mixed} date The date value
18069 * Fires when this field hide.
18070 * @param {Roo.bootstrap.MonthField} this
18071 * @param {Mixed} date The date value
18076 * Fires when select a date.
18077 * @param {Roo.bootstrap.MonthField} this
18078 * @param {String} oldvalue The old value
18079 * @param {String} newvalue The new value
18085 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18087 onRender: function(ct, position)
18090 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18092 this.language = this.language || 'en';
18093 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18094 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18096 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18097 this.isInline = false;
18098 this.isInput = true;
18099 this.component = this.el.select('.add-on', true).first() || false;
18100 this.component = (this.component && this.component.length === 0) ? false : this.component;
18101 this.hasInput = this.component && this.inputEL().length;
18103 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18105 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18107 this.picker().on('mousedown', this.onMousedown, this);
18108 this.picker().on('click', this.onClick, this);
18110 this.picker().addClass('datepicker-dropdown');
18112 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18113 v.setStyle('width', '189px');
18120 if(this.isInline) {
18126 setValue: function(v, suppressEvent)
18128 var o = this.getValue();
18130 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18134 if(suppressEvent !== true){
18135 this.fireEvent('select', this, o, v);
18140 getValue: function()
18145 onClick: function(e)
18147 e.stopPropagation();
18148 e.preventDefault();
18150 var target = e.getTarget();
18152 if(target.nodeName.toLowerCase() === 'i'){
18153 target = Roo.get(target).dom.parentNode;
18156 var nodeName = target.nodeName;
18157 var className = target.className;
18158 var html = target.innerHTML;
18160 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18164 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18166 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18172 picker : function()
18174 return this.pickerEl;
18177 fillMonths: function()
18180 var months = this.picker().select('>.datepicker-months td', true).first();
18182 months.dom.innerHTML = '';
18188 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18191 months.createChild(month);
18200 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18201 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18204 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18205 e.removeClass('active');
18207 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18208 e.addClass('active');
18215 if(this.isInline) {
18219 this.picker().removeClass(['bottom', 'top']);
18221 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18223 * place to the top of element!
18227 this.picker().addClass('top');
18228 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18233 this.picker().addClass('bottom');
18235 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18238 onFocus : function()
18240 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18244 onBlur : function()
18246 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18248 var d = this.inputEl().getValue();
18257 this.picker().show();
18258 this.picker().select('>.datepicker-months', true).first().show();
18262 this.fireEvent('show', this, this.date);
18267 if(this.isInline) {
18270 this.picker().hide();
18271 this.fireEvent('hide', this, this.date);
18275 onMousedown: function(e)
18277 e.stopPropagation();
18278 e.preventDefault();
18283 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18287 fireKey: function(e)
18289 if (!this.picker().isVisible()){
18290 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18301 e.preventDefault();
18305 dir = e.keyCode == 37 ? -1 : 1;
18307 this.vIndex = this.vIndex + dir;
18309 if(this.vIndex < 0){
18313 if(this.vIndex > 11){
18317 if(isNaN(this.vIndex)){
18321 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18327 dir = e.keyCode == 38 ? -1 : 1;
18329 this.vIndex = this.vIndex + dir * 4;
18331 if(this.vIndex < 0){
18335 if(this.vIndex > 11){
18339 if(isNaN(this.vIndex)){
18343 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18348 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18349 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18353 e.preventDefault();
18356 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18357 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18373 this.picker().remove();
18378 Roo.apply(Roo.bootstrap.MonthField, {
18397 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18398 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18403 Roo.apply(Roo.bootstrap.MonthField, {
18407 cls: 'datepicker dropdown-menu roo-dynamic',
18411 cls: 'datepicker-months',
18415 cls: 'table-condensed',
18417 Roo.bootstrap.DateField.content
18437 * @class Roo.bootstrap.CheckBox
18438 * @extends Roo.bootstrap.Input
18439 * Bootstrap CheckBox class
18441 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18442 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18443 * @cfg {String} boxLabel The text that appears beside the checkbox
18444 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18445 * @cfg {Boolean} checked initnal the element
18446 * @cfg {Boolean} inline inline the element (default false)
18447 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18450 * Create a new CheckBox
18451 * @param {Object} config The config object
18454 Roo.bootstrap.CheckBox = function(config){
18455 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18460 * Fires when the element is checked or unchecked.
18461 * @param {Roo.bootstrap.CheckBox} this This input
18462 * @param {Boolean} checked The new checked value
18469 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18471 inputType: 'checkbox',
18479 getAutoCreate : function()
18481 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18487 cfg.cls = 'form-group ' + this.inputType; //input-group
18490 cfg.cls += ' ' + this.inputType + '-inline';
18496 type : this.inputType,
18497 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18498 cls : 'roo-' + this.inputType, //'form-box',
18499 placeholder : this.placeholder || ''
18503 if (this.weight) { // Validity check?
18504 cfg.cls += " " + this.inputType + "-" + this.weight;
18507 if (this.disabled) {
18508 input.disabled=true;
18512 input.checked = this.checked;
18516 input.name = this.name;
18520 input.cls += ' input-' + this.size;
18525 ['xs','sm','md','lg'].map(function(size){
18526 if (settings[size]) {
18527 cfg.cls += ' col-' + size + '-' + settings[size];
18531 var inputblock = input;
18533 if (this.before || this.after) {
18536 cls : 'input-group',
18541 inputblock.cn.push({
18543 cls : 'input-group-addon',
18548 inputblock.cn.push(input);
18551 inputblock.cn.push({
18553 cls : 'input-group-addon',
18560 if (align ==='left' && this.fieldLabel.length) {
18561 Roo.log("left and has label");
18567 cls : 'control-label col-md-' + this.labelWidth,
18568 html : this.fieldLabel
18572 cls : "col-md-" + (12 - this.labelWidth),
18579 } else if ( this.fieldLabel.length) {
18584 tag: this.boxLabel ? 'span' : 'label',
18586 cls: 'control-label box-input-label',
18587 //cls : 'input-group-addon',
18588 html : this.fieldLabel
18598 Roo.log(" no label && no align");
18599 cfg.cn = [ inputblock ] ;
18604 var boxLabelCfg = {
18606 //'for': id, // box label is handled by onclick - so no for...
18608 html: this.boxLabel
18612 boxLabelCfg.tooltip = this.tooltip;
18615 cfg.cn.push(boxLabelCfg);
18625 * return the real input element.
18627 inputEl: function ()
18629 return this.el.select('input.roo-' + this.inputType,true).first();
18632 labelEl: function()
18634 return this.el.select('label.control-label',true).first();
18636 /* depricated... */
18640 return this.labelEl();
18643 initEvents : function()
18645 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18647 this.inputEl().on('click', this.onClick, this);
18649 if (this.boxLabel) {
18650 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18653 this.startValue = this.getValue();
18656 Roo.bootstrap.CheckBox.register(this);
18660 onClick : function()
18662 this.setChecked(!this.checked);
18665 setChecked : function(state,suppressEvent)
18667 this.startValue = this.getValue();
18669 if(this.inputType == 'radio'){
18671 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18672 e.dom.checked = false;
18675 this.inputEl().dom.checked = true;
18677 this.inputEl().dom.value = this.inputValue;
18679 if(suppressEvent !== true){
18680 this.fireEvent('check', this, true);
18688 this.checked = state;
18690 this.inputEl().dom.checked = state;
18692 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18694 if(suppressEvent !== true){
18695 this.fireEvent('check', this, state);
18701 getValue : function()
18703 if(this.inputType == 'radio'){
18704 return this.getGroupValue();
18707 return this.inputEl().getValue();
18711 getGroupValue : function()
18713 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18717 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18720 setValue : function(v,suppressEvent)
18722 if(this.inputType == 'radio'){
18723 this.setGroupValue(v, suppressEvent);
18727 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18732 setGroupValue : function(v, suppressEvent)
18734 this.startValue = this.getValue();
18736 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18737 e.dom.checked = false;
18739 if(e.dom.value == v){
18740 e.dom.checked = true;
18744 if(suppressEvent !== true){
18745 this.fireEvent('check', this, true);
18753 validate : function()
18757 (this.inputType == 'radio' && this.validateRadio()) ||
18758 (this.inputType == 'checkbox' && this.validateCheckbox())
18764 this.markInvalid();
18768 validateRadio : function()
18772 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18773 if(!e.dom.checked){
18785 validateCheckbox : function()
18788 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18791 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18799 for(var i in group){
18804 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18811 * Mark this field as valid
18813 markValid : function()
18815 if(this.allowBlank){
18821 this.fireEvent('valid', this);
18823 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18826 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18833 if(this.inputType == 'radio'){
18834 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18835 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18836 e.findParent('.form-group', false, true).addClass(_this.validClass);
18843 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18844 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18848 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18854 for(var i in group){
18855 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18856 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18861 * Mark this field as invalid
18862 * @param {String} msg The validation message
18864 markInvalid : function(msg)
18866 if(this.allowBlank){
18872 this.fireEvent('invalid', this, msg);
18874 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18877 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18881 label.markInvalid();
18884 if(this.inputType == 'radio'){
18885 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18886 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18887 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18894 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18895 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18899 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18905 for(var i in group){
18906 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18907 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18914 Roo.apply(Roo.bootstrap.CheckBox, {
18919 * register a CheckBox Group
18920 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18922 register : function(checkbox)
18924 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18925 this.groups[checkbox.groupId] = {};
18928 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18932 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18936 * fetch a CheckBox Group based on the group ID
18937 * @param {string} the group ID
18938 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18940 get: function(groupId) {
18941 if (typeof(this.groups[groupId]) == 'undefined') {
18945 return this.groups[groupId] ;
18957 *<div class="radio">
18959 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18960 Option one is this and that—be sure to include why it's great
18967 *<label class="radio-inline">fieldLabel</label>
18968 *<label class="radio-inline">
18969 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18977 * @class Roo.bootstrap.Radio
18978 * @extends Roo.bootstrap.CheckBox
18979 * Bootstrap Radio class
18982 * Create a new Radio
18983 * @param {Object} config The config object
18986 Roo.bootstrap.Radio = function(config){
18987 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18991 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18993 inputType: 'radio',
18997 getAutoCreate : function()
18999 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19000 align = align || 'left'; // default...
19007 tag : this.inline ? 'span' : 'div',
19012 var inline = this.inline ? ' radio-inline' : '';
19016 // does not need for, as we wrap the input with it..
19018 cls : 'control-label box-label' + inline,
19021 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19025 //cls : 'control-label' + inline,
19026 html : this.fieldLabel,
19027 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19036 type : this.inputType,
19037 //value : (!this.checked) ? this.valueOff : this.inputValue,
19038 value : this.inputValue,
19040 placeholder : this.placeholder || '' // ?? needed????
19043 if (this.weight) { // Validity check?
19044 input.cls += " radio-" + this.weight;
19046 if (this.disabled) {
19047 input.disabled=true;
19051 input.checked = this.checked;
19055 input.name = this.name;
19059 input.cls += ' input-' + this.size;
19062 //?? can span's inline have a width??
19065 ['xs','sm','md','lg'].map(function(size){
19066 if (settings[size]) {
19067 cfg.cls += ' col-' + size + '-' + settings[size];
19071 var inputblock = input;
19073 if (this.before || this.after) {
19076 cls : 'input-group',
19081 inputblock.cn.push({
19083 cls : 'input-group-addon',
19087 inputblock.cn.push(input);
19089 inputblock.cn.push({
19091 cls : 'input-group-addon',
19099 if (this.fieldLabel && this.fieldLabel.length) {
19100 cfg.cn.push(fieldLabel);
19103 // normal bootstrap puts the input inside the label.
19104 // however with our styled version - it has to go after the input.
19106 //lbl.cn.push(inputblock);
19110 cls: 'radio' + inline,
19117 cfg.cn.push( lblwrap);
19122 html: this.boxLabel
19131 initEvents : function()
19133 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19135 this.inputEl().on('click', this.onClick, this);
19136 if (this.boxLabel) {
19137 //Roo.log('find label');
19138 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19143 inputEl: function ()
19145 return this.el.select('input.roo-radio',true).first();
19147 onClick : function()
19150 this.setChecked(true);
19153 setChecked : function(state,suppressEvent)
19156 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19157 v.dom.checked = false;
19160 Roo.log(this.inputEl().dom);
19161 this.checked = state;
19162 this.inputEl().dom.checked = state;
19164 if(suppressEvent !== true){
19165 this.fireEvent('check', this, state);
19168 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19172 getGroupValue : function()
19175 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19176 if(v.dom.checked == true){
19177 value = v.dom.value;
19185 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19186 * @return {Mixed} value The field value
19188 getValue : function(){
19189 return this.getGroupValue();
19195 //<script type="text/javascript">
19198 * Based Ext JS Library 1.1.1
19199 * Copyright(c) 2006-2007, Ext JS, LLC.
19205 * @class Roo.HtmlEditorCore
19206 * @extends Roo.Component
19207 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19209 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19212 Roo.HtmlEditorCore = function(config){
19215 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19220 * @event initialize
19221 * Fires when the editor is fully initialized (including the iframe)
19222 * @param {Roo.HtmlEditorCore} this
19227 * Fires when the editor is first receives the focus. Any insertion must wait
19228 * until after this event.
19229 * @param {Roo.HtmlEditorCore} this
19233 * @event beforesync
19234 * Fires before the textarea is updated with content from the editor iframe. Return false
19235 * to cancel the sync.
19236 * @param {Roo.HtmlEditorCore} this
19237 * @param {String} html
19241 * @event beforepush
19242 * Fires before the iframe editor is updated with content from the textarea. Return false
19243 * to cancel the push.
19244 * @param {Roo.HtmlEditorCore} this
19245 * @param {String} html
19250 * Fires when the textarea is updated with content from the editor iframe.
19251 * @param {Roo.HtmlEditorCore} this
19252 * @param {String} html
19257 * Fires when the iframe editor is updated with content from the textarea.
19258 * @param {Roo.HtmlEditorCore} this
19259 * @param {String} html
19264 * @event editorevent
19265 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19266 * @param {Roo.HtmlEditorCore} this
19272 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19274 // defaults : white / black...
19275 this.applyBlacklists();
19282 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19286 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19292 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19297 * @cfg {Number} height (in pixels)
19301 * @cfg {Number} width (in pixels)
19306 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19309 stylesheets: false,
19314 // private properties
19315 validationEvent : false,
19317 initialized : false,
19319 sourceEditMode : false,
19320 onFocus : Roo.emptyFn,
19322 hideMode:'offsets',
19326 // blacklist + whitelisted elements..
19333 * Protected method that will not generally be called directly. It
19334 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19335 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19337 getDocMarkup : function(){
19341 // inherit styels from page...??
19342 if (this.stylesheets === false) {
19344 Roo.get(document.head).select('style').each(function(node) {
19345 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19348 Roo.get(document.head).select('link').each(function(node) {
19349 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19352 } else if (!this.stylesheets.length) {
19354 st = '<style type="text/css">' +
19355 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19361 st += '<style type="text/css">' +
19362 'IMG { cursor: pointer } ' +
19366 return '<html><head>' + st +
19367 //<style type="text/css">' +
19368 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19370 ' </head><body class="roo-htmleditor-body"></body></html>';
19374 onRender : function(ct, position)
19377 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19378 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19381 this.el.dom.style.border = '0 none';
19382 this.el.dom.setAttribute('tabIndex', -1);
19383 this.el.addClass('x-hidden hide');
19387 if(Roo.isIE){ // fix IE 1px bogus margin
19388 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19392 this.frameId = Roo.id();
19396 var iframe = this.owner.wrap.createChild({
19398 cls: 'form-control', // bootstrap..
19400 name: this.frameId,
19401 frameBorder : 'no',
19402 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19407 this.iframe = iframe.dom;
19409 this.assignDocWin();
19411 this.doc.designMode = 'on';
19414 this.doc.write(this.getDocMarkup());
19418 var task = { // must defer to wait for browser to be ready
19420 //console.log("run task?" + this.doc.readyState);
19421 this.assignDocWin();
19422 if(this.doc.body || this.doc.readyState == 'complete'){
19424 this.doc.designMode="on";
19428 Roo.TaskMgr.stop(task);
19429 this.initEditor.defer(10, this);
19436 Roo.TaskMgr.start(task);
19441 onResize : function(w, h)
19443 Roo.log('resize: ' +w + ',' + h );
19444 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19448 if(typeof w == 'number'){
19450 this.iframe.style.width = w + 'px';
19452 if(typeof h == 'number'){
19454 this.iframe.style.height = h + 'px';
19456 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19463 * Toggles the editor between standard and source edit mode.
19464 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19466 toggleSourceEdit : function(sourceEditMode){
19468 this.sourceEditMode = sourceEditMode === true;
19470 if(this.sourceEditMode){
19472 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19475 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19476 //this.iframe.className = '';
19479 //this.setSize(this.owner.wrap.getSize());
19480 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19487 * Protected method that will not generally be called directly. If you need/want
19488 * custom HTML cleanup, this is the method you should override.
19489 * @param {String} html The HTML to be cleaned
19490 * return {String} The cleaned HTML
19492 cleanHtml : function(html){
19493 html = String(html);
19494 if(html.length > 5){
19495 if(Roo.isSafari){ // strip safari nonsense
19496 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19499 if(html == ' '){
19506 * HTML Editor -> Textarea
19507 * Protected method that will not generally be called directly. Syncs the contents
19508 * of the editor iframe with the textarea.
19510 syncValue : function(){
19511 if(this.initialized){
19512 var bd = (this.doc.body || this.doc.documentElement);
19513 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19514 var html = bd.innerHTML;
19516 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19517 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19519 html = '<div style="'+m[0]+'">' + html + '</div>';
19522 html = this.cleanHtml(html);
19523 // fix up the special chars.. normaly like back quotes in word...
19524 // however we do not want to do this with chinese..
19525 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19526 var cc = b.charCodeAt();
19528 (cc >= 0x4E00 && cc < 0xA000 ) ||
19529 (cc >= 0x3400 && cc < 0x4E00 ) ||
19530 (cc >= 0xf900 && cc < 0xfb00 )
19536 if(this.owner.fireEvent('beforesync', this, html) !== false){
19537 this.el.dom.value = html;
19538 this.owner.fireEvent('sync', this, html);
19544 * Protected method that will not generally be called directly. Pushes the value of the textarea
19545 * into the iframe editor.
19547 pushValue : function(){
19548 if(this.initialized){
19549 var v = this.el.dom.value.trim();
19551 // if(v.length < 1){
19555 if(this.owner.fireEvent('beforepush', this, v) !== false){
19556 var d = (this.doc.body || this.doc.documentElement);
19558 this.cleanUpPaste();
19559 this.el.dom.value = d.innerHTML;
19560 this.owner.fireEvent('push', this, v);
19566 deferFocus : function(){
19567 this.focus.defer(10, this);
19571 focus : function(){
19572 if(this.win && !this.sourceEditMode){
19579 assignDocWin: function()
19581 var iframe = this.iframe;
19584 this.doc = iframe.contentWindow.document;
19585 this.win = iframe.contentWindow;
19587 // if (!Roo.get(this.frameId)) {
19590 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19591 // this.win = Roo.get(this.frameId).dom.contentWindow;
19593 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19597 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19598 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19603 initEditor : function(){
19604 //console.log("INIT EDITOR");
19605 this.assignDocWin();
19609 this.doc.designMode="on";
19611 this.doc.write(this.getDocMarkup());
19614 var dbody = (this.doc.body || this.doc.documentElement);
19615 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19616 // this copies styles from the containing element into thsi one..
19617 // not sure why we need all of this..
19618 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19620 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19621 //ss['background-attachment'] = 'fixed'; // w3c
19622 dbody.bgProperties = 'fixed'; // ie
19623 //Roo.DomHelper.applyStyles(dbody, ss);
19624 Roo.EventManager.on(this.doc, {
19625 //'mousedown': this.onEditorEvent,
19626 'mouseup': this.onEditorEvent,
19627 'dblclick': this.onEditorEvent,
19628 'click': this.onEditorEvent,
19629 'keyup': this.onEditorEvent,
19634 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19636 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19637 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19639 this.initialized = true;
19641 this.owner.fireEvent('initialize', this);
19646 onDestroy : function(){
19652 //for (var i =0; i < this.toolbars.length;i++) {
19653 // // fixme - ask toolbars for heights?
19654 // this.toolbars[i].onDestroy();
19657 //this.wrap.dom.innerHTML = '';
19658 //this.wrap.remove();
19663 onFirstFocus : function(){
19665 this.assignDocWin();
19668 this.activated = true;
19671 if(Roo.isGecko){ // prevent silly gecko errors
19673 var s = this.win.getSelection();
19674 if(!s.focusNode || s.focusNode.nodeType != 3){
19675 var r = s.getRangeAt(0);
19676 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19681 this.execCmd('useCSS', true);
19682 this.execCmd('styleWithCSS', false);
19685 this.owner.fireEvent('activate', this);
19689 adjustFont: function(btn){
19690 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19691 //if(Roo.isSafari){ // safari
19694 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19695 if(Roo.isSafari){ // safari
19696 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19697 v = (v < 10) ? 10 : v;
19698 v = (v > 48) ? 48 : v;
19699 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19704 v = Math.max(1, v+adjust);
19706 this.execCmd('FontSize', v );
19709 onEditorEvent : function(e)
19711 this.owner.fireEvent('editorevent', this, e);
19712 // this.updateToolbar();
19713 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19716 insertTag : function(tg)
19718 // could be a bit smarter... -> wrap the current selected tRoo..
19719 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19721 range = this.createRange(this.getSelection());
19722 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19723 wrappingNode.appendChild(range.extractContents());
19724 range.insertNode(wrappingNode);
19731 this.execCmd("formatblock", tg);
19735 insertText : function(txt)
19739 var range = this.createRange();
19740 range.deleteContents();
19741 //alert(Sender.getAttribute('label'));
19743 range.insertNode(this.doc.createTextNode(txt));
19749 * Executes a Midas editor command on the editor document and performs necessary focus and
19750 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19751 * @param {String} cmd The Midas command
19752 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19754 relayCmd : function(cmd, value){
19756 this.execCmd(cmd, value);
19757 this.owner.fireEvent('editorevent', this);
19758 //this.updateToolbar();
19759 this.owner.deferFocus();
19763 * Executes a Midas editor command directly on the editor document.
19764 * For visual commands, you should use {@link #relayCmd} instead.
19765 * <b>This should only be called after the editor is initialized.</b>
19766 * @param {String} cmd The Midas command
19767 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19769 execCmd : function(cmd, value){
19770 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19777 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19779 * @param {String} text | dom node..
19781 insertAtCursor : function(text)
19786 if(!this.activated){
19792 var r = this.doc.selection.createRange();
19803 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19807 // from jquery ui (MIT licenced)
19809 var win = this.win;
19811 if (win.getSelection && win.getSelection().getRangeAt) {
19812 range = win.getSelection().getRangeAt(0);
19813 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19814 range.insertNode(node);
19815 } else if (win.document.selection && win.document.selection.createRange) {
19816 // no firefox support
19817 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19818 win.document.selection.createRange().pasteHTML(txt);
19820 // no firefox support
19821 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19822 this.execCmd('InsertHTML', txt);
19831 mozKeyPress : function(e){
19833 var c = e.getCharCode(), cmd;
19836 c = String.fromCharCode(c).toLowerCase();
19850 this.cleanUpPaste.defer(100, this);
19858 e.preventDefault();
19866 fixKeys : function(){ // load time branching for fastest keydown performance
19868 return function(e){
19869 var k = e.getKey(), r;
19872 r = this.doc.selection.createRange();
19875 r.pasteHTML('    ');
19882 r = this.doc.selection.createRange();
19884 var target = r.parentElement();
19885 if(!target || target.tagName.toLowerCase() != 'li'){
19887 r.pasteHTML('<br />');
19893 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19894 this.cleanUpPaste.defer(100, this);
19900 }else if(Roo.isOpera){
19901 return function(e){
19902 var k = e.getKey();
19906 this.execCmd('InsertHTML','    ');
19909 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19910 this.cleanUpPaste.defer(100, this);
19915 }else if(Roo.isSafari){
19916 return function(e){
19917 var k = e.getKey();
19921 this.execCmd('InsertText','\t');
19925 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19926 this.cleanUpPaste.defer(100, this);
19934 getAllAncestors: function()
19936 var p = this.getSelectedNode();
19939 a.push(p); // push blank onto stack..
19940 p = this.getParentElement();
19944 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19948 a.push(this.doc.body);
19952 lastSelNode : false,
19955 getSelection : function()
19957 this.assignDocWin();
19958 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19961 getSelectedNode: function()
19963 // this may only work on Gecko!!!
19965 // should we cache this!!!!
19970 var range = this.createRange(this.getSelection()).cloneRange();
19973 var parent = range.parentElement();
19975 var testRange = range.duplicate();
19976 testRange.moveToElementText(parent);
19977 if (testRange.inRange(range)) {
19980 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19983 parent = parent.parentElement;
19988 // is ancestor a text element.
19989 var ac = range.commonAncestorContainer;
19990 if (ac.nodeType == 3) {
19991 ac = ac.parentNode;
19994 var ar = ac.childNodes;
19997 var other_nodes = [];
19998 var has_other_nodes = false;
19999 for (var i=0;i<ar.length;i++) {
20000 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20003 // fullly contained node.
20005 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20010 // probably selected..
20011 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20012 other_nodes.push(ar[i]);
20016 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20021 has_other_nodes = true;
20023 if (!nodes.length && other_nodes.length) {
20024 nodes= other_nodes;
20026 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20032 createRange: function(sel)
20034 // this has strange effects when using with
20035 // top toolbar - not sure if it's a great idea.
20036 //this.editor.contentWindow.focus();
20037 if (typeof sel != "undefined") {
20039 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20041 return this.doc.createRange();
20044 return this.doc.createRange();
20047 getParentElement: function()
20050 this.assignDocWin();
20051 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20053 var range = this.createRange(sel);
20056 var p = range.commonAncestorContainer;
20057 while (p.nodeType == 3) { // text node
20068 * Range intersection.. the hard stuff...
20072 * [ -- selected range --- ]
20076 * if end is before start or hits it. fail.
20077 * if start is after end or hits it fail.
20079 * if either hits (but other is outside. - then it's not
20085 // @see http://www.thismuchiknow.co.uk/?p=64.
20086 rangeIntersectsNode : function(range, node)
20088 var nodeRange = node.ownerDocument.createRange();
20090 nodeRange.selectNode(node);
20092 nodeRange.selectNodeContents(node);
20095 var rangeStartRange = range.cloneRange();
20096 rangeStartRange.collapse(true);
20098 var rangeEndRange = range.cloneRange();
20099 rangeEndRange.collapse(false);
20101 var nodeStartRange = nodeRange.cloneRange();
20102 nodeStartRange.collapse(true);
20104 var nodeEndRange = nodeRange.cloneRange();
20105 nodeEndRange.collapse(false);
20107 return rangeStartRange.compareBoundaryPoints(
20108 Range.START_TO_START, nodeEndRange) == -1 &&
20109 rangeEndRange.compareBoundaryPoints(
20110 Range.START_TO_START, nodeStartRange) == 1;
20114 rangeCompareNode : function(range, node)
20116 var nodeRange = node.ownerDocument.createRange();
20118 nodeRange.selectNode(node);
20120 nodeRange.selectNodeContents(node);
20124 range.collapse(true);
20126 nodeRange.collapse(true);
20128 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20129 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20131 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20133 var nodeIsBefore = ss == 1;
20134 var nodeIsAfter = ee == -1;
20136 if (nodeIsBefore && nodeIsAfter) {
20139 if (!nodeIsBefore && nodeIsAfter) {
20140 return 1; //right trailed.
20143 if (nodeIsBefore && !nodeIsAfter) {
20144 return 2; // left trailed.
20150 // private? - in a new class?
20151 cleanUpPaste : function()
20153 // cleans up the whole document..
20154 Roo.log('cleanuppaste');
20156 this.cleanUpChildren(this.doc.body);
20157 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20158 if (clean != this.doc.body.innerHTML) {
20159 this.doc.body.innerHTML = clean;
20164 cleanWordChars : function(input) {// change the chars to hex code
20165 var he = Roo.HtmlEditorCore;
20167 var output = input;
20168 Roo.each(he.swapCodes, function(sw) {
20169 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20171 output = output.replace(swapper, sw[1]);
20178 cleanUpChildren : function (n)
20180 if (!n.childNodes.length) {
20183 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20184 this.cleanUpChild(n.childNodes[i]);
20191 cleanUpChild : function (node)
20194 //console.log(node);
20195 if (node.nodeName == "#text") {
20196 // clean up silly Windows -- stuff?
20199 if (node.nodeName == "#comment") {
20200 node.parentNode.removeChild(node);
20201 // clean up silly Windows -- stuff?
20204 var lcname = node.tagName.toLowerCase();
20205 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20206 // whitelist of tags..
20208 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20210 node.parentNode.removeChild(node);
20215 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20217 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20218 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20220 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20221 // remove_keep_children = true;
20224 if (remove_keep_children) {
20225 this.cleanUpChildren(node);
20226 // inserts everything just before this node...
20227 while (node.childNodes.length) {
20228 var cn = node.childNodes[0];
20229 node.removeChild(cn);
20230 node.parentNode.insertBefore(cn, node);
20232 node.parentNode.removeChild(node);
20236 if (!node.attributes || !node.attributes.length) {
20237 this.cleanUpChildren(node);
20241 function cleanAttr(n,v)
20244 if (v.match(/^\./) || v.match(/^\//)) {
20247 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20250 if (v.match(/^#/)) {
20253 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20254 node.removeAttribute(n);
20258 var cwhite = this.cwhite;
20259 var cblack = this.cblack;
20261 function cleanStyle(n,v)
20263 if (v.match(/expression/)) { //XSS?? should we even bother..
20264 node.removeAttribute(n);
20268 var parts = v.split(/;/);
20271 Roo.each(parts, function(p) {
20272 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20276 var l = p.split(':').shift().replace(/\s+/g,'');
20277 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20279 if ( cwhite.length && cblack.indexOf(l) > -1) {
20280 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20281 //node.removeAttribute(n);
20285 // only allow 'c whitelisted system attributes'
20286 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20287 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20288 //node.removeAttribute(n);
20298 if (clean.length) {
20299 node.setAttribute(n, clean.join(';'));
20301 node.removeAttribute(n);
20307 for (var i = node.attributes.length-1; i > -1 ; i--) {
20308 var a = node.attributes[i];
20311 if (a.name.toLowerCase().substr(0,2)=='on') {
20312 node.removeAttribute(a.name);
20315 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20316 node.removeAttribute(a.name);
20319 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20320 cleanAttr(a.name,a.value); // fixme..
20323 if (a.name == 'style') {
20324 cleanStyle(a.name,a.value);
20327 /// clean up MS crap..
20328 // tecnically this should be a list of valid class'es..
20331 if (a.name == 'class') {
20332 if (a.value.match(/^Mso/)) {
20333 node.className = '';
20336 if (a.value.match(/body/)) {
20337 node.className = '';
20348 this.cleanUpChildren(node);
20354 * Clean up MS wordisms...
20356 cleanWord : function(node)
20361 this.cleanWord(this.doc.body);
20364 if (node.nodeName == "#text") {
20365 // clean up silly Windows -- stuff?
20368 if (node.nodeName == "#comment") {
20369 node.parentNode.removeChild(node);
20370 // clean up silly Windows -- stuff?
20374 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20375 node.parentNode.removeChild(node);
20379 // remove - but keep children..
20380 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20381 while (node.childNodes.length) {
20382 var cn = node.childNodes[0];
20383 node.removeChild(cn);
20384 node.parentNode.insertBefore(cn, node);
20386 node.parentNode.removeChild(node);
20387 this.iterateChildren(node, this.cleanWord);
20391 if (node.className.length) {
20393 var cn = node.className.split(/\W+/);
20395 Roo.each(cn, function(cls) {
20396 if (cls.match(/Mso[a-zA-Z]+/)) {
20401 node.className = cna.length ? cna.join(' ') : '';
20403 node.removeAttribute("class");
20407 if (node.hasAttribute("lang")) {
20408 node.removeAttribute("lang");
20411 if (node.hasAttribute("style")) {
20413 var styles = node.getAttribute("style").split(";");
20415 Roo.each(styles, function(s) {
20416 if (!s.match(/:/)) {
20419 var kv = s.split(":");
20420 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20423 // what ever is left... we allow.
20426 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20427 if (!nstyle.length) {
20428 node.removeAttribute('style');
20431 this.iterateChildren(node, this.cleanWord);
20437 * iterateChildren of a Node, calling fn each time, using this as the scole..
20438 * @param {DomNode} node node to iterate children of.
20439 * @param {Function} fn method of this class to call on each item.
20441 iterateChildren : function(node, fn)
20443 if (!node.childNodes.length) {
20446 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20447 fn.call(this, node.childNodes[i])
20453 * cleanTableWidths.
20455 * Quite often pasting from word etc.. results in tables with column and widths.
20456 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20459 cleanTableWidths : function(node)
20464 this.cleanTableWidths(this.doc.body);
20469 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20472 Roo.log(node.tagName);
20473 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20474 this.iterateChildren(node, this.cleanTableWidths);
20477 if (node.hasAttribute('width')) {
20478 node.removeAttribute('width');
20482 if (node.hasAttribute("style")) {
20485 var styles = node.getAttribute("style").split(";");
20487 Roo.each(styles, function(s) {
20488 if (!s.match(/:/)) {
20491 var kv = s.split(":");
20492 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20495 // what ever is left... we allow.
20498 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20499 if (!nstyle.length) {
20500 node.removeAttribute('style');
20504 this.iterateChildren(node, this.cleanTableWidths);
20512 domToHTML : function(currentElement, depth, nopadtext) {
20514 depth = depth || 0;
20515 nopadtext = nopadtext || false;
20517 if (!currentElement) {
20518 return this.domToHTML(this.doc.body);
20521 //Roo.log(currentElement);
20523 var allText = false;
20524 var nodeName = currentElement.nodeName;
20525 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20527 if (nodeName == '#text') {
20529 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20534 if (nodeName != 'BODY') {
20537 // Prints the node tagName, such as <A>, <IMG>, etc
20540 for(i = 0; i < currentElement.attributes.length;i++) {
20542 var aname = currentElement.attributes.item(i).name;
20543 if (!currentElement.attributes.item(i).value.length) {
20546 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20549 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20558 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20561 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20566 // Traverse the tree
20568 var currentElementChild = currentElement.childNodes.item(i);
20569 var allText = true;
20570 var innerHTML = '';
20572 while (currentElementChild) {
20573 // Formatting code (indent the tree so it looks nice on the screen)
20574 var nopad = nopadtext;
20575 if (lastnode == 'SPAN') {
20579 if (currentElementChild.nodeName == '#text') {
20580 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20581 toadd = nopadtext ? toadd : toadd.trim();
20582 if (!nopad && toadd.length > 80) {
20583 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20585 innerHTML += toadd;
20588 currentElementChild = currentElement.childNodes.item(i);
20594 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20596 // Recursively traverse the tree structure of the child node
20597 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20598 lastnode = currentElementChild.nodeName;
20600 currentElementChild=currentElement.childNodes.item(i);
20606 // The remaining code is mostly for formatting the tree
20607 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20612 ret+= "</"+tagName+">";
20618 applyBlacklists : function()
20620 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20621 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20625 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20626 if (b.indexOf(tag) > -1) {
20629 this.white.push(tag);
20633 Roo.each(w, function(tag) {
20634 if (b.indexOf(tag) > -1) {
20637 if (this.white.indexOf(tag) > -1) {
20640 this.white.push(tag);
20645 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20646 if (w.indexOf(tag) > -1) {
20649 this.black.push(tag);
20653 Roo.each(b, function(tag) {
20654 if (w.indexOf(tag) > -1) {
20657 if (this.black.indexOf(tag) > -1) {
20660 this.black.push(tag);
20665 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20666 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20670 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20671 if (b.indexOf(tag) > -1) {
20674 this.cwhite.push(tag);
20678 Roo.each(w, function(tag) {
20679 if (b.indexOf(tag) > -1) {
20682 if (this.cwhite.indexOf(tag) > -1) {
20685 this.cwhite.push(tag);
20690 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20691 if (w.indexOf(tag) > -1) {
20694 this.cblack.push(tag);
20698 Roo.each(b, function(tag) {
20699 if (w.indexOf(tag) > -1) {
20702 if (this.cblack.indexOf(tag) > -1) {
20705 this.cblack.push(tag);
20710 setStylesheets : function(stylesheets)
20712 if(typeof(stylesheets) == 'string'){
20713 Roo.get(this.iframe.contentDocument.head).createChild({
20715 rel : 'stylesheet',
20724 Roo.each(stylesheets, function(s) {
20729 Roo.get(_this.iframe.contentDocument.head).createChild({
20731 rel : 'stylesheet',
20740 removeStylesheets : function()
20744 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20749 // hide stuff that is not compatible
20763 * @event specialkey
20767 * @cfg {String} fieldClass @hide
20770 * @cfg {String} focusClass @hide
20773 * @cfg {String} autoCreate @hide
20776 * @cfg {String} inputType @hide
20779 * @cfg {String} invalidClass @hide
20782 * @cfg {String} invalidText @hide
20785 * @cfg {String} msgFx @hide
20788 * @cfg {String} validateOnBlur @hide
20792 Roo.HtmlEditorCore.white = [
20793 'area', 'br', 'img', 'input', 'hr', 'wbr',
20795 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20796 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20797 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20798 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20799 'table', 'ul', 'xmp',
20801 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20804 'dir', 'menu', 'ol', 'ul', 'dl',
20810 Roo.HtmlEditorCore.black = [
20811 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20813 'base', 'basefont', 'bgsound', 'blink', 'body',
20814 'frame', 'frameset', 'head', 'html', 'ilayer',
20815 'iframe', 'layer', 'link', 'meta', 'object',
20816 'script', 'style' ,'title', 'xml' // clean later..
20818 Roo.HtmlEditorCore.clean = [
20819 'script', 'style', 'title', 'xml'
20821 Roo.HtmlEditorCore.remove = [
20826 Roo.HtmlEditorCore.ablack = [
20830 Roo.HtmlEditorCore.aclean = [
20831 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20835 Roo.HtmlEditorCore.pwhite= [
20836 'http', 'https', 'mailto'
20839 // white listed style attributes.
20840 Roo.HtmlEditorCore.cwhite= [
20841 // 'text-align', /// default is to allow most things..
20847 // black listed style attributes.
20848 Roo.HtmlEditorCore.cblack= [
20849 // 'font-size' -- this can be set by the project
20853 Roo.HtmlEditorCore.swapCodes =[
20872 * @class Roo.bootstrap.HtmlEditor
20873 * @extends Roo.bootstrap.TextArea
20874 * Bootstrap HtmlEditor class
20877 * Create a new HtmlEditor
20878 * @param {Object} config The config object
20881 Roo.bootstrap.HtmlEditor = function(config){
20882 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20883 if (!this.toolbars) {
20884 this.toolbars = [];
20886 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20889 * @event initialize
20890 * Fires when the editor is fully initialized (including the iframe)
20891 * @param {HtmlEditor} this
20896 * Fires when the editor is first receives the focus. Any insertion must wait
20897 * until after this event.
20898 * @param {HtmlEditor} this
20902 * @event beforesync
20903 * Fires before the textarea is updated with content from the editor iframe. Return false
20904 * to cancel the sync.
20905 * @param {HtmlEditor} this
20906 * @param {String} html
20910 * @event beforepush
20911 * Fires before the iframe editor is updated with content from the textarea. Return false
20912 * to cancel the push.
20913 * @param {HtmlEditor} this
20914 * @param {String} html
20919 * Fires when the textarea is updated with content from the editor iframe.
20920 * @param {HtmlEditor} this
20921 * @param {String} html
20926 * Fires when the iframe editor is updated with content from the textarea.
20927 * @param {HtmlEditor} this
20928 * @param {String} html
20932 * @event editmodechange
20933 * Fires when the editor switches edit modes
20934 * @param {HtmlEditor} this
20935 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20937 editmodechange: true,
20939 * @event editorevent
20940 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20941 * @param {HtmlEditor} this
20945 * @event firstfocus
20946 * Fires when on first focus - needed by toolbars..
20947 * @param {HtmlEditor} this
20952 * Auto save the htmlEditor value as a file into Events
20953 * @param {HtmlEditor} this
20957 * @event savedpreview
20958 * preview the saved version of htmlEditor
20959 * @param {HtmlEditor} this
20966 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20970 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20975 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20980 * @cfg {Number} height (in pixels)
20984 * @cfg {Number} width (in pixels)
20989 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20992 stylesheets: false,
20997 // private properties
20998 validationEvent : false,
21000 initialized : false,
21003 onFocus : Roo.emptyFn,
21005 hideMode:'offsets',
21008 tbContainer : false,
21010 toolbarContainer :function() {
21011 return this.wrap.select('.x-html-editor-tb',true).first();
21015 * Protected method that will not generally be called directly. It
21016 * is called when the editor creates its toolbar. Override this method if you need to
21017 * add custom toolbar buttons.
21018 * @param {HtmlEditor} editor
21020 createToolbar : function(){
21022 Roo.log("create toolbars");
21024 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21025 this.toolbars[0].render(this.toolbarContainer());
21029 // if (!editor.toolbars || !editor.toolbars.length) {
21030 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21033 // for (var i =0 ; i < editor.toolbars.length;i++) {
21034 // editor.toolbars[i] = Roo.factory(
21035 // typeof(editor.toolbars[i]) == 'string' ?
21036 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21037 // Roo.bootstrap.HtmlEditor);
21038 // editor.toolbars[i].init(editor);
21044 onRender : function(ct, position)
21046 // Roo.log("Call onRender: " + this.xtype);
21048 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21050 this.wrap = this.inputEl().wrap({
21051 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21054 this.editorcore.onRender(ct, position);
21056 if (this.resizable) {
21057 this.resizeEl = new Roo.Resizable(this.wrap, {
21061 minHeight : this.height,
21062 height: this.height,
21063 handles : this.resizable,
21066 resize : function(r, w, h) {
21067 _t.onResize(w,h); // -something
21073 this.createToolbar(this);
21076 if(!this.width && this.resizable){
21077 this.setSize(this.wrap.getSize());
21079 if (this.resizeEl) {
21080 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21081 // should trigger onReize..
21087 onResize : function(w, h)
21089 Roo.log('resize: ' +w + ',' + h );
21090 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21094 if(this.inputEl() ){
21095 if(typeof w == 'number'){
21096 var aw = w - this.wrap.getFrameWidth('lr');
21097 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21100 if(typeof h == 'number'){
21101 var tbh = -11; // fixme it needs to tool bar size!
21102 for (var i =0; i < this.toolbars.length;i++) {
21103 // fixme - ask toolbars for heights?
21104 tbh += this.toolbars[i].el.getHeight();
21105 //if (this.toolbars[i].footer) {
21106 // tbh += this.toolbars[i].footer.el.getHeight();
21114 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21115 ah -= 5; // knock a few pixes off for look..
21116 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21120 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21121 this.editorcore.onResize(ew,eh);
21126 * Toggles the editor between standard and source edit mode.
21127 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21129 toggleSourceEdit : function(sourceEditMode)
21131 this.editorcore.toggleSourceEdit(sourceEditMode);
21133 if(this.editorcore.sourceEditMode){
21134 Roo.log('editor - showing textarea');
21137 // Roo.log(this.syncValue());
21139 this.inputEl().removeClass(['hide', 'x-hidden']);
21140 this.inputEl().dom.removeAttribute('tabIndex');
21141 this.inputEl().focus();
21143 Roo.log('editor - hiding textarea');
21145 // Roo.log(this.pushValue());
21148 this.inputEl().addClass(['hide', 'x-hidden']);
21149 this.inputEl().dom.setAttribute('tabIndex', -1);
21150 //this.deferFocus();
21153 if(this.resizable){
21154 this.setSize(this.wrap.getSize());
21157 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21160 // private (for BoxComponent)
21161 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21163 // private (for BoxComponent)
21164 getResizeEl : function(){
21168 // private (for BoxComponent)
21169 getPositionEl : function(){
21174 initEvents : function(){
21175 this.originalValue = this.getValue();
21179 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21182 // markInvalid : Roo.emptyFn,
21184 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21187 // clearInvalid : Roo.emptyFn,
21189 setValue : function(v){
21190 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21191 this.editorcore.pushValue();
21196 deferFocus : function(){
21197 this.focus.defer(10, this);
21201 focus : function(){
21202 this.editorcore.focus();
21208 onDestroy : function(){
21214 for (var i =0; i < this.toolbars.length;i++) {
21215 // fixme - ask toolbars for heights?
21216 this.toolbars[i].onDestroy();
21219 this.wrap.dom.innerHTML = '';
21220 this.wrap.remove();
21225 onFirstFocus : function(){
21226 //Roo.log("onFirstFocus");
21227 this.editorcore.onFirstFocus();
21228 for (var i =0; i < this.toolbars.length;i++) {
21229 this.toolbars[i].onFirstFocus();
21235 syncValue : function()
21237 this.editorcore.syncValue();
21240 pushValue : function()
21242 this.editorcore.pushValue();
21246 // hide stuff that is not compatible
21260 * @event specialkey
21264 * @cfg {String} fieldClass @hide
21267 * @cfg {String} focusClass @hide
21270 * @cfg {String} autoCreate @hide
21273 * @cfg {String} inputType @hide
21276 * @cfg {String} invalidClass @hide
21279 * @cfg {String} invalidText @hide
21282 * @cfg {String} msgFx @hide
21285 * @cfg {String} validateOnBlur @hide
21294 Roo.namespace('Roo.bootstrap.htmleditor');
21296 * @class Roo.bootstrap.HtmlEditorToolbar1
21301 new Roo.bootstrap.HtmlEditor({
21304 new Roo.bootstrap.HtmlEditorToolbar1({
21305 disable : { fonts: 1 , format: 1, ..., ... , ...],
21311 * @cfg {Object} disable List of elements to disable..
21312 * @cfg {Array} btns List of additional buttons.
21316 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21319 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21322 Roo.apply(this, config);
21324 // default disabled, based on 'good practice'..
21325 this.disable = this.disable || {};
21326 Roo.applyIf(this.disable, {
21329 specialElements : true
21331 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21333 this.editor = config.editor;
21334 this.editorcore = config.editor.editorcore;
21336 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21338 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21339 // dont call parent... till later.
21341 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21346 editorcore : false,
21351 "h1","h2","h3","h4","h5","h6",
21353 "abbr", "acronym", "address", "cite", "samp", "var",
21357 onRender : function(ct, position)
21359 // Roo.log("Call onRender: " + this.xtype);
21361 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21363 this.el.dom.style.marginBottom = '0';
21365 var editorcore = this.editorcore;
21366 var editor= this.editor;
21369 var btn = function(id,cmd , toggle, handler){
21371 var event = toggle ? 'toggle' : 'click';
21376 xns: Roo.bootstrap,
21379 enableToggle:toggle !== false,
21381 pressed : toggle ? false : null,
21384 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21385 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21394 xns: Roo.bootstrap,
21395 glyphicon : 'font',
21399 xns: Roo.bootstrap,
21403 Roo.each(this.formats, function(f) {
21404 style.menu.items.push({
21406 xns: Roo.bootstrap,
21407 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21412 editorcore.insertTag(this.tagname);
21419 children.push(style);
21422 btn('bold',false,true);
21423 btn('italic',false,true);
21424 btn('align-left', 'justifyleft',true);
21425 btn('align-center', 'justifycenter',true);
21426 btn('align-right' , 'justifyright',true);
21427 btn('link', false, false, function(btn) {
21428 //Roo.log("create link?");
21429 var url = prompt(this.createLinkText, this.defaultLinkValue);
21430 if(url && url != 'http:/'+'/'){
21431 this.editorcore.relayCmd('createlink', url);
21434 btn('list','insertunorderedlist',true);
21435 btn('pencil', false,true, function(btn){
21438 this.toggleSourceEdit(btn.pressed);
21444 xns: Roo.bootstrap,
21449 xns: Roo.bootstrap,
21454 cog.menu.items.push({
21456 xns: Roo.bootstrap,
21457 html : Clean styles,
21462 editorcore.insertTag(this.tagname);
21471 this.xtype = 'NavSimplebar';
21473 for(var i=0;i< children.length;i++) {
21475 this.buttons.add(this.addxtypeChild(children[i]));
21479 editor.on('editorevent', this.updateToolbar, this);
21481 onBtnClick : function(id)
21483 this.editorcore.relayCmd(id);
21484 this.editorcore.focus();
21488 * Protected method that will not generally be called directly. It triggers
21489 * a toolbar update by reading the markup state of the current selection in the editor.
21491 updateToolbar: function(){
21493 if(!this.editorcore.activated){
21494 this.editor.onFirstFocus(); // is this neeed?
21498 var btns = this.buttons;
21499 var doc = this.editorcore.doc;
21500 btns.get('bold').setActive(doc.queryCommandState('bold'));
21501 btns.get('italic').setActive(doc.queryCommandState('italic'));
21502 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21504 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21505 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21506 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21508 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21509 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21512 var ans = this.editorcore.getAllAncestors();
21513 if (this.formatCombo) {
21516 var store = this.formatCombo.store;
21517 this.formatCombo.setValue("");
21518 for (var i =0; i < ans.length;i++) {
21519 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21521 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21529 // hides menus... - so this cant be on a menu...
21530 Roo.bootstrap.MenuMgr.hideAll();
21532 Roo.bootstrap.MenuMgr.hideAll();
21533 //this.editorsyncValue();
21535 onFirstFocus: function() {
21536 this.buttons.each(function(item){
21540 toggleSourceEdit : function(sourceEditMode){
21543 if(sourceEditMode){
21544 Roo.log("disabling buttons");
21545 this.buttons.each( function(item){
21546 if(item.cmd != 'pencil'){
21552 Roo.log("enabling buttons");
21553 if(this.editorcore.initialized){
21554 this.buttons.each( function(item){
21560 Roo.log("calling toggole on editor");
21561 // tell the editor that it's been pressed..
21562 this.editor.toggleSourceEdit(sourceEditMode);
21572 * @class Roo.bootstrap.Table.AbstractSelectionModel
21573 * @extends Roo.util.Observable
21574 * Abstract base class for grid SelectionModels. It provides the interface that should be
21575 * implemented by descendant classes. This class should not be directly instantiated.
21578 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21579 this.locked = false;
21580 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21584 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21585 /** @ignore Called by the grid automatically. Do not call directly. */
21586 init : function(grid){
21592 * Locks the selections.
21595 this.locked = true;
21599 * Unlocks the selections.
21601 unlock : function(){
21602 this.locked = false;
21606 * Returns true if the selections are locked.
21607 * @return {Boolean}
21609 isLocked : function(){
21610 return this.locked;
21614 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21615 * @class Roo.bootstrap.Table.RowSelectionModel
21616 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21617 * It supports multiple selections and keyboard selection/navigation.
21619 * @param {Object} config
21622 Roo.bootstrap.Table.RowSelectionModel = function(config){
21623 Roo.apply(this, config);
21624 this.selections = new Roo.util.MixedCollection(false, function(o){
21629 this.lastActive = false;
21633 * @event selectionchange
21634 * Fires when the selection changes
21635 * @param {SelectionModel} this
21637 "selectionchange" : true,
21639 * @event afterselectionchange
21640 * Fires after the selection changes (eg. by key press or clicking)
21641 * @param {SelectionModel} this
21643 "afterselectionchange" : true,
21645 * @event beforerowselect
21646 * Fires when a row is selected being selected, return false to cancel.
21647 * @param {SelectionModel} this
21648 * @param {Number} rowIndex The selected index
21649 * @param {Boolean} keepExisting False if other selections will be cleared
21651 "beforerowselect" : true,
21654 * Fires when a row is selected.
21655 * @param {SelectionModel} this
21656 * @param {Number} rowIndex The selected index
21657 * @param {Roo.data.Record} r The record
21659 "rowselect" : true,
21661 * @event rowdeselect
21662 * Fires when a row is deselected.
21663 * @param {SelectionModel} this
21664 * @param {Number} rowIndex The selected index
21666 "rowdeselect" : true
21668 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21669 this.locked = false;
21672 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21674 * @cfg {Boolean} singleSelect
21675 * True to allow selection of only one row at a time (defaults to false)
21677 singleSelect : false,
21680 initEvents : function(){
21682 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21683 this.grid.on("mousedown", this.handleMouseDown, this);
21684 }else{ // allow click to work like normal
21685 this.grid.on("rowclick", this.handleDragableRowClick, this);
21688 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21689 "up" : function(e){
21691 this.selectPrevious(e.shiftKey);
21692 }else if(this.last !== false && this.lastActive !== false){
21693 var last = this.last;
21694 this.selectRange(this.last, this.lastActive-1);
21695 this.grid.getView().focusRow(this.lastActive);
21696 if(last !== false){
21700 this.selectFirstRow();
21702 this.fireEvent("afterselectionchange", this);
21704 "down" : function(e){
21706 this.selectNext(e.shiftKey);
21707 }else if(this.last !== false && this.lastActive !== false){
21708 var last = this.last;
21709 this.selectRange(this.last, this.lastActive+1);
21710 this.grid.getView().focusRow(this.lastActive);
21711 if(last !== false){
21715 this.selectFirstRow();
21717 this.fireEvent("afterselectionchange", this);
21722 var view = this.grid.view;
21723 view.on("refresh", this.onRefresh, this);
21724 view.on("rowupdated", this.onRowUpdated, this);
21725 view.on("rowremoved", this.onRemove, this);
21729 onRefresh : function(){
21730 var ds = this.grid.dataSource, i, v = this.grid.view;
21731 var s = this.selections;
21732 s.each(function(r){
21733 if((i = ds.indexOfId(r.id)) != -1){
21742 onRemove : function(v, index, r){
21743 this.selections.remove(r);
21747 onRowUpdated : function(v, index, r){
21748 if(this.isSelected(r)){
21749 v.onRowSelect(index);
21755 * @param {Array} records The records to select
21756 * @param {Boolean} keepExisting (optional) True to keep existing selections
21758 selectRecords : function(records, keepExisting){
21760 this.clearSelections();
21762 var ds = this.grid.dataSource;
21763 for(var i = 0, len = records.length; i < len; i++){
21764 this.selectRow(ds.indexOf(records[i]), true);
21769 * Gets the number of selected rows.
21772 getCount : function(){
21773 return this.selections.length;
21777 * Selects the first row in the grid.
21779 selectFirstRow : function(){
21784 * Select the last row.
21785 * @param {Boolean} keepExisting (optional) True to keep existing selections
21787 selectLastRow : function(keepExisting){
21788 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21792 * Selects the row immediately following the last selected row.
21793 * @param {Boolean} keepExisting (optional) True to keep existing selections
21795 selectNext : function(keepExisting){
21796 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21797 this.selectRow(this.last+1, keepExisting);
21798 this.grid.getView().focusRow(this.last);
21803 * Selects the row that precedes the last selected row.
21804 * @param {Boolean} keepExisting (optional) True to keep existing selections
21806 selectPrevious : function(keepExisting){
21808 this.selectRow(this.last-1, keepExisting);
21809 this.grid.getView().focusRow(this.last);
21814 * Returns the selected records
21815 * @return {Array} Array of selected records
21817 getSelections : function(){
21818 return [].concat(this.selections.items);
21822 * Returns the first selected record.
21825 getSelected : function(){
21826 return this.selections.itemAt(0);
21831 * Clears all selections.
21833 clearSelections : function(fast){
21838 var ds = this.grid.dataSource;
21839 var s = this.selections;
21840 s.each(function(r){
21841 this.deselectRow(ds.indexOfId(r.id));
21845 this.selections.clear();
21852 * Selects all rows.
21854 selectAll : function(){
21858 this.selections.clear();
21859 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21860 this.selectRow(i, true);
21865 * Returns True if there is a selection.
21866 * @return {Boolean}
21868 hasSelection : function(){
21869 return this.selections.length > 0;
21873 * Returns True if the specified row is selected.
21874 * @param {Number/Record} record The record or index of the record to check
21875 * @return {Boolean}
21877 isSelected : function(index){
21878 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21879 return (r && this.selections.key(r.id) ? true : false);
21883 * Returns True if the specified record id is selected.
21884 * @param {String} id The id of record to check
21885 * @return {Boolean}
21887 isIdSelected : function(id){
21888 return (this.selections.key(id) ? true : false);
21892 handleMouseDown : function(e, t){
21893 var view = this.grid.getView(), rowIndex;
21894 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21897 if(e.shiftKey && this.last !== false){
21898 var last = this.last;
21899 this.selectRange(last, rowIndex, e.ctrlKey);
21900 this.last = last; // reset the last
21901 view.focusRow(rowIndex);
21903 var isSelected = this.isSelected(rowIndex);
21904 if(e.button !== 0 && isSelected){
21905 view.focusRow(rowIndex);
21906 }else if(e.ctrlKey && isSelected){
21907 this.deselectRow(rowIndex);
21908 }else if(!isSelected){
21909 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21910 view.focusRow(rowIndex);
21913 this.fireEvent("afterselectionchange", this);
21916 handleDragableRowClick : function(grid, rowIndex, e)
21918 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21919 this.selectRow(rowIndex, false);
21920 grid.view.focusRow(rowIndex);
21921 this.fireEvent("afterselectionchange", this);
21926 * Selects multiple rows.
21927 * @param {Array} rows Array of the indexes of the row to select
21928 * @param {Boolean} keepExisting (optional) True to keep existing selections
21930 selectRows : function(rows, keepExisting){
21932 this.clearSelections();
21934 for(var i = 0, len = rows.length; i < len; i++){
21935 this.selectRow(rows[i], true);
21940 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21941 * @param {Number} startRow The index of the first row in the range
21942 * @param {Number} endRow The index of the last row in the range
21943 * @param {Boolean} keepExisting (optional) True to retain existing selections
21945 selectRange : function(startRow, endRow, keepExisting){
21950 this.clearSelections();
21952 if(startRow <= endRow){
21953 for(var i = startRow; i <= endRow; i++){
21954 this.selectRow(i, true);
21957 for(var i = startRow; i >= endRow; i--){
21958 this.selectRow(i, true);
21964 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21965 * @param {Number} startRow The index of the first row in the range
21966 * @param {Number} endRow The index of the last row in the range
21968 deselectRange : function(startRow, endRow, preventViewNotify){
21972 for(var i = startRow; i <= endRow; i++){
21973 this.deselectRow(i, preventViewNotify);
21979 * @param {Number} row The index of the row to select
21980 * @param {Boolean} keepExisting (optional) True to keep existing selections
21982 selectRow : function(index, keepExisting, preventViewNotify){
21983 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
21986 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21987 if(!keepExisting || this.singleSelect){
21988 this.clearSelections();
21990 var r = this.grid.dataSource.getAt(index);
21991 this.selections.add(r);
21992 this.last = this.lastActive = index;
21993 if(!preventViewNotify){
21994 this.grid.getView().onRowSelect(index);
21996 this.fireEvent("rowselect", this, index, r);
21997 this.fireEvent("selectionchange", this);
22003 * @param {Number} row The index of the row to deselect
22005 deselectRow : function(index, preventViewNotify){
22009 if(this.last == index){
22012 if(this.lastActive == index){
22013 this.lastActive = false;
22015 var r = this.grid.dataSource.getAt(index);
22016 this.selections.remove(r);
22017 if(!preventViewNotify){
22018 this.grid.getView().onRowDeselect(index);
22020 this.fireEvent("rowdeselect", this, index);
22021 this.fireEvent("selectionchange", this);
22025 restoreLast : function(){
22027 this.last = this._last;
22032 acceptsNav : function(row, col, cm){
22033 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22037 onEditorKey : function(field, e){
22038 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22043 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22045 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22047 }else if(k == e.ENTER && !e.ctrlKey){
22051 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22053 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22055 }else if(k == e.ESC){
22059 g.startEditing(newCell[0], newCell[1]);
22064 * Ext JS Library 1.1.1
22065 * Copyright(c) 2006-2007, Ext JS, LLC.
22067 * Originally Released Under LGPL - original licence link has changed is not relivant.
22070 * <script type="text/javascript">
22074 * @class Roo.bootstrap.PagingToolbar
22075 * @extends Roo.bootstrap.NavSimplebar
22076 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22078 * Create a new PagingToolbar
22079 * @param {Object} config The config object
22080 * @param {Roo.data.Store} store
22082 Roo.bootstrap.PagingToolbar = function(config)
22084 // old args format still supported... - xtype is prefered..
22085 // created from xtype...
22087 this.ds = config.dataSource;
22089 if (config.store && !this.ds) {
22090 this.store= Roo.factory(config.store, Roo.data);
22091 this.ds = this.store;
22092 this.ds.xmodule = this.xmodule || false;
22095 this.toolbarItems = [];
22096 if (config.items) {
22097 this.toolbarItems = config.items;
22100 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22105 this.bind(this.ds);
22108 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22112 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22114 * @cfg {Roo.data.Store} dataSource
22115 * The underlying data store providing the paged data
22118 * @cfg {String/HTMLElement/Element} container
22119 * container The id or element that will contain the toolbar
22122 * @cfg {Boolean} displayInfo
22123 * True to display the displayMsg (defaults to false)
22126 * @cfg {Number} pageSize
22127 * The number of records to display per page (defaults to 20)
22131 * @cfg {String} displayMsg
22132 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22134 displayMsg : 'Displaying {0} - {1} of {2}',
22136 * @cfg {String} emptyMsg
22137 * The message to display when no records are found (defaults to "No data to display")
22139 emptyMsg : 'No data to display',
22141 * Customizable piece of the default paging text (defaults to "Page")
22144 beforePageText : "Page",
22146 * Customizable piece of the default paging text (defaults to "of %0")
22149 afterPageText : "of {0}",
22151 * Customizable piece of the default paging text (defaults to "First Page")
22154 firstText : "First Page",
22156 * Customizable piece of the default paging text (defaults to "Previous Page")
22159 prevText : "Previous Page",
22161 * Customizable piece of the default paging text (defaults to "Next Page")
22164 nextText : "Next Page",
22166 * Customizable piece of the default paging text (defaults to "Last Page")
22169 lastText : "Last Page",
22171 * Customizable piece of the default paging text (defaults to "Refresh")
22174 refreshText : "Refresh",
22178 onRender : function(ct, position)
22180 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22181 this.navgroup.parentId = this.id;
22182 this.navgroup.onRender(this.el, null);
22183 // add the buttons to the navgroup
22185 if(this.displayInfo){
22186 Roo.log(this.el.select('ul.navbar-nav',true).first());
22187 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22188 this.displayEl = this.el.select('.x-paging-info', true).first();
22189 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22190 // this.displayEl = navel.el.select('span',true).first();
22196 Roo.each(_this.buttons, function(e){ // this might need to use render????
22197 Roo.factory(e).onRender(_this.el, null);
22201 Roo.each(_this.toolbarItems, function(e) {
22202 _this.navgroup.addItem(e);
22206 this.first = this.navgroup.addItem({
22207 tooltip: this.firstText,
22209 icon : 'fa fa-backward',
22211 preventDefault: true,
22212 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22215 this.prev = this.navgroup.addItem({
22216 tooltip: this.prevText,
22218 icon : 'fa fa-step-backward',
22220 preventDefault: true,
22221 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22223 //this.addSeparator();
22226 var field = this.navgroup.addItem( {
22228 cls : 'x-paging-position',
22230 html : this.beforePageText +
22231 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22232 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22235 this.field = field.el.select('input', true).first();
22236 this.field.on("keydown", this.onPagingKeydown, this);
22237 this.field.on("focus", function(){this.dom.select();});
22240 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22241 //this.field.setHeight(18);
22242 //this.addSeparator();
22243 this.next = this.navgroup.addItem({
22244 tooltip: this.nextText,
22246 html : ' <i class="fa fa-step-forward">',
22248 preventDefault: true,
22249 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22251 this.last = this.navgroup.addItem({
22252 tooltip: this.lastText,
22253 icon : 'fa fa-forward',
22256 preventDefault: true,
22257 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22259 //this.addSeparator();
22260 this.loading = this.navgroup.addItem({
22261 tooltip: this.refreshText,
22262 icon: 'fa fa-refresh',
22263 preventDefault: true,
22264 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22270 updateInfo : function(){
22271 if(this.displayEl){
22272 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22273 var msg = count == 0 ?
22277 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22279 this.displayEl.update(msg);
22284 onLoad : function(ds, r, o){
22285 this.cursor = o.params ? o.params.start : 0;
22286 var d = this.getPageData(),
22290 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22291 this.field.dom.value = ap;
22292 this.first.setDisabled(ap == 1);
22293 this.prev.setDisabled(ap == 1);
22294 this.next.setDisabled(ap == ps);
22295 this.last.setDisabled(ap == ps);
22296 this.loading.enable();
22301 getPageData : function(){
22302 var total = this.ds.getTotalCount();
22305 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22306 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22311 onLoadError : function(){
22312 this.loading.enable();
22316 onPagingKeydown : function(e){
22317 var k = e.getKey();
22318 var d = this.getPageData();
22320 var v = this.field.dom.value, pageNum;
22321 if(!v || isNaN(pageNum = parseInt(v, 10))){
22322 this.field.dom.value = d.activePage;
22325 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22326 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22329 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))
22331 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22332 this.field.dom.value = pageNum;
22333 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22336 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22338 var v = this.field.dom.value, pageNum;
22339 var increment = (e.shiftKey) ? 10 : 1;
22340 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22343 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22344 this.field.dom.value = d.activePage;
22347 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22349 this.field.dom.value = parseInt(v, 10) + increment;
22350 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22351 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22358 beforeLoad : function(){
22360 this.loading.disable();
22365 onClick : function(which){
22374 ds.load({params:{start: 0, limit: this.pageSize}});
22377 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22380 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22383 var total = ds.getTotalCount();
22384 var extra = total % this.pageSize;
22385 var lastStart = extra ? (total - extra) : total-this.pageSize;
22386 ds.load({params:{start: lastStart, limit: this.pageSize}});
22389 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22395 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22396 * @param {Roo.data.Store} store The data store to unbind
22398 unbind : function(ds){
22399 ds.un("beforeload", this.beforeLoad, this);
22400 ds.un("load", this.onLoad, this);
22401 ds.un("loadexception", this.onLoadError, this);
22402 ds.un("remove", this.updateInfo, this);
22403 ds.un("add", this.updateInfo, this);
22404 this.ds = undefined;
22408 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22409 * @param {Roo.data.Store} store The data store to bind
22411 bind : function(ds){
22412 ds.on("beforeload", this.beforeLoad, this);
22413 ds.on("load", this.onLoad, this);
22414 ds.on("loadexception", this.onLoadError, this);
22415 ds.on("remove", this.updateInfo, this);
22416 ds.on("add", this.updateInfo, this);
22427 * @class Roo.bootstrap.MessageBar
22428 * @extends Roo.bootstrap.Component
22429 * Bootstrap MessageBar class
22430 * @cfg {String} html contents of the MessageBar
22431 * @cfg {String} weight (info | success | warning | danger) default info
22432 * @cfg {String} beforeClass insert the bar before the given class
22433 * @cfg {Boolean} closable (true | false) default false
22434 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22437 * Create a new Element
22438 * @param {Object} config The config object
22441 Roo.bootstrap.MessageBar = function(config){
22442 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22445 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22451 beforeClass: 'bootstrap-sticky-wrap',
22453 getAutoCreate : function(){
22457 cls: 'alert alert-dismissable alert-' + this.weight,
22462 html: this.html || ''
22468 cfg.cls += ' alert-messages-fixed';
22482 onRender : function(ct, position)
22484 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22487 var cfg = Roo.apply({}, this.getAutoCreate());
22491 cfg.cls += ' ' + this.cls;
22494 cfg.style = this.style;
22496 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22498 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22501 this.el.select('>button.close').on('click', this.hide, this);
22507 if (!this.rendered) {
22513 this.fireEvent('show', this);
22519 if (!this.rendered) {
22525 this.fireEvent('hide', this);
22528 update : function()
22530 // var e = this.el.dom.firstChild;
22532 // if(this.closable){
22533 // e = e.nextSibling;
22536 // e.data = this.html || '';
22538 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22554 * @class Roo.bootstrap.Graph
22555 * @extends Roo.bootstrap.Component
22556 * Bootstrap Graph class
22560 @cfg {String} graphtype bar | vbar | pie
22561 @cfg {number} g_x coodinator | centre x (pie)
22562 @cfg {number} g_y coodinator | centre y (pie)
22563 @cfg {number} g_r radius (pie)
22564 @cfg {number} g_height height of the chart (respected by all elements in the set)
22565 @cfg {number} g_width width of the chart (respected by all elements in the set)
22566 @cfg {Object} title The title of the chart
22569 -opts (object) options for the chart
22571 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22572 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22574 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.
22575 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22577 o stretch (boolean)
22579 -opts (object) options for the pie
22582 o startAngle (number)
22583 o endAngle (number)
22587 * Create a new Input
22588 * @param {Object} config The config object
22591 Roo.bootstrap.Graph = function(config){
22592 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22598 * The img click event for the img.
22599 * @param {Roo.EventObject} e
22605 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22616 //g_colors: this.colors,
22623 getAutoCreate : function(){
22634 onRender : function(ct,position){
22635 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22636 this.raphael = Raphael(this.el.dom);
22638 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22639 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22640 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22641 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22643 r.text(160, 10, "Single Series Chart").attr(txtattr);
22644 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22645 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22646 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22648 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22649 r.barchart(330, 10, 300, 220, data1);
22650 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22651 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22654 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22655 // r.barchart(30, 30, 560, 250, xdata, {
22656 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22657 // axis : "0 0 1 1",
22658 // axisxlabels : xdata
22659 // //yvalues : cols,
22662 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22664 // this.load(null,xdata,{
22665 // axis : "0 0 1 1",
22666 // axisxlabels : xdata
22671 load : function(graphtype,xdata,opts){
22672 this.raphael.clear();
22674 graphtype = this.graphtype;
22679 var r = this.raphael,
22680 fin = function () {
22681 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22683 fout = function () {
22684 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22686 pfin = function() {
22687 this.sector.stop();
22688 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22691 this.label[0].stop();
22692 this.label[0].attr({ r: 7.5 });
22693 this.label[1].attr({ "font-weight": 800 });
22696 pfout = function() {
22697 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22700 this.label[0].animate({ r: 5 }, 500, "bounce");
22701 this.label[1].attr({ "font-weight": 400 });
22707 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22710 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22713 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22714 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22716 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22723 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22728 setTitle: function(o)
22733 initEvents: function() {
22736 this.el.on('click', this.onClick, this);
22740 onClick : function(e)
22742 Roo.log('img onclick');
22743 this.fireEvent('click', this, e);
22755 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22758 * @class Roo.bootstrap.dash.NumberBox
22759 * @extends Roo.bootstrap.Component
22760 * Bootstrap NumberBox class
22761 * @cfg {String} headline Box headline
22762 * @cfg {String} content Box content
22763 * @cfg {String} icon Box icon
22764 * @cfg {String} footer Footer text
22765 * @cfg {String} fhref Footer href
22768 * Create a new NumberBox
22769 * @param {Object} config The config object
22773 Roo.bootstrap.dash.NumberBox = function(config){
22774 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22778 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22787 getAutoCreate : function(){
22791 cls : 'small-box ',
22799 cls : 'roo-headline',
22800 html : this.headline
22804 cls : 'roo-content',
22805 html : this.content
22819 cls : 'ion ' + this.icon
22828 cls : 'small-box-footer',
22829 href : this.fhref || '#',
22833 cfg.cn.push(footer);
22840 onRender : function(ct,position){
22841 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22848 setHeadline: function (value)
22850 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22853 setFooter: function (value, href)
22855 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22858 this.el.select('a.small-box-footer',true).first().attr('href', href);
22863 setContent: function (value)
22865 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22868 initEvents: function()
22882 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22885 * @class Roo.bootstrap.dash.TabBox
22886 * @extends Roo.bootstrap.Component
22887 * Bootstrap TabBox class
22888 * @cfg {String} title Title of the TabBox
22889 * @cfg {String} icon Icon of the TabBox
22890 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22891 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22894 * Create a new TabBox
22895 * @param {Object} config The config object
22899 Roo.bootstrap.dash.TabBox = function(config){
22900 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22905 * When a pane is added
22906 * @param {Roo.bootstrap.dash.TabPane} pane
22910 * @event activatepane
22911 * When a pane is activated
22912 * @param {Roo.bootstrap.dash.TabPane} pane
22914 "activatepane" : true
22922 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22927 tabScrollable : false,
22929 getChildContainer : function()
22931 return this.el.select('.tab-content', true).first();
22934 getAutoCreate : function(){
22938 cls: 'pull-left header',
22946 cls: 'fa ' + this.icon
22952 cls: 'nav nav-tabs pull-right',
22958 if(this.tabScrollable){
22965 cls: 'nav nav-tabs pull-right',
22976 cls: 'nav-tabs-custom',
22981 cls: 'tab-content no-padding',
22989 initEvents : function()
22991 //Roo.log('add add pane handler');
22992 this.on('addpane', this.onAddPane, this);
22995 * Updates the box title
22996 * @param {String} html to set the title to.
22998 setTitle : function(value)
23000 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23002 onAddPane : function(pane)
23004 this.panes.push(pane);
23005 //Roo.log('addpane');
23007 // tabs are rendere left to right..
23008 if(!this.showtabs){
23012 var ctr = this.el.select('.nav-tabs', true).first();
23015 var existing = ctr.select('.nav-tab',true);
23016 var qty = existing.getCount();;
23019 var tab = ctr.createChild({
23021 cls : 'nav-tab' + (qty ? '' : ' active'),
23029 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23032 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23034 pane.el.addClass('active');
23039 onTabClick : function(ev,un,ob,pane)
23041 //Roo.log('tab - prev default');
23042 ev.preventDefault();
23045 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23046 pane.tab.addClass('active');
23047 //Roo.log(pane.title);
23048 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23049 // technically we should have a deactivate event.. but maybe add later.
23050 // and it should not de-activate the selected tab...
23051 this.fireEvent('activatepane', pane);
23052 pane.el.addClass('active');
23053 pane.fireEvent('activate');
23058 getActivePane : function()
23061 Roo.each(this.panes, function(p) {
23062 if(p.el.hasClass('active')){
23083 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23085 * @class Roo.bootstrap.TabPane
23086 * @extends Roo.bootstrap.Component
23087 * Bootstrap TabPane class
23088 * @cfg {Boolean} active (false | true) Default false
23089 * @cfg {String} title title of panel
23093 * Create a new TabPane
23094 * @param {Object} config The config object
23097 Roo.bootstrap.dash.TabPane = function(config){
23098 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23104 * When a pane is activated
23105 * @param {Roo.bootstrap.dash.TabPane} pane
23112 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23117 // the tabBox that this is attached to.
23120 getAutoCreate : function()
23128 cfg.cls += ' active';
23133 initEvents : function()
23135 //Roo.log('trigger add pane handler');
23136 this.parent().fireEvent('addpane', this)
23140 * Updates the tab title
23141 * @param {String} html to set the title to.
23143 setTitle: function(str)
23149 this.tab.select('a', true).first().dom.innerHTML = str;
23166 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23169 * @class Roo.bootstrap.menu.Menu
23170 * @extends Roo.bootstrap.Component
23171 * Bootstrap Menu class - container for Menu
23172 * @cfg {String} html Text of the menu
23173 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23174 * @cfg {String} icon Font awesome icon
23175 * @cfg {String} pos Menu align to (top | bottom) default bottom
23179 * Create a new Menu
23180 * @param {Object} config The config object
23184 Roo.bootstrap.menu.Menu = function(config){
23185 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23189 * @event beforeshow
23190 * Fires before this menu is displayed
23191 * @param {Roo.bootstrap.menu.Menu} this
23195 * @event beforehide
23196 * Fires before this menu is hidden
23197 * @param {Roo.bootstrap.menu.Menu} this
23202 * Fires after this menu is displayed
23203 * @param {Roo.bootstrap.menu.Menu} this
23208 * Fires after this menu is hidden
23209 * @param {Roo.bootstrap.menu.Menu} this
23214 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23215 * @param {Roo.bootstrap.menu.Menu} this
23216 * @param {Roo.EventObject} e
23223 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23227 weight : 'default',
23232 getChildContainer : function() {
23233 if(this.isSubMenu){
23237 return this.el.select('ul.dropdown-menu', true).first();
23240 getAutoCreate : function()
23245 cls : 'roo-menu-text',
23253 cls : 'fa ' + this.icon
23264 cls : 'dropdown-button btn btn-' + this.weight,
23269 cls : 'dropdown-toggle btn btn-' + this.weight,
23279 cls : 'dropdown-menu'
23285 if(this.pos == 'top'){
23286 cfg.cls += ' dropup';
23289 if(this.isSubMenu){
23292 cls : 'dropdown-menu'
23299 onRender : function(ct, position)
23301 this.isSubMenu = ct.hasClass('dropdown-submenu');
23303 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23306 initEvents : function()
23308 if(this.isSubMenu){
23312 this.hidden = true;
23314 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23315 this.triggerEl.on('click', this.onTriggerPress, this);
23317 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23318 this.buttonEl.on('click', this.onClick, this);
23324 if(this.isSubMenu){
23328 return this.el.select('ul.dropdown-menu', true).first();
23331 onClick : function(e)
23333 this.fireEvent("click", this, e);
23336 onTriggerPress : function(e)
23338 if (this.isVisible()) {
23345 isVisible : function(){
23346 return !this.hidden;
23351 this.fireEvent("beforeshow", this);
23353 this.hidden = false;
23354 this.el.addClass('open');
23356 Roo.get(document).on("mouseup", this.onMouseUp, this);
23358 this.fireEvent("show", this);
23365 this.fireEvent("beforehide", this);
23367 this.hidden = true;
23368 this.el.removeClass('open');
23370 Roo.get(document).un("mouseup", this.onMouseUp);
23372 this.fireEvent("hide", this);
23375 onMouseUp : function()
23389 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23392 * @class Roo.bootstrap.menu.Item
23393 * @extends Roo.bootstrap.Component
23394 * Bootstrap MenuItem class
23395 * @cfg {Boolean} submenu (true | false) default false
23396 * @cfg {String} html text of the item
23397 * @cfg {String} href the link
23398 * @cfg {Boolean} disable (true | false) default false
23399 * @cfg {Boolean} preventDefault (true | false) default true
23400 * @cfg {String} icon Font awesome icon
23401 * @cfg {String} pos Submenu align to (left | right) default right
23405 * Create a new Item
23406 * @param {Object} config The config object
23410 Roo.bootstrap.menu.Item = function(config){
23411 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23415 * Fires when the mouse is hovering over this menu
23416 * @param {Roo.bootstrap.menu.Item} this
23417 * @param {Roo.EventObject} e
23422 * Fires when the mouse exits this menu
23423 * @param {Roo.bootstrap.menu.Item} this
23424 * @param {Roo.EventObject} e
23430 * The raw click event for the entire grid.
23431 * @param {Roo.EventObject} e
23437 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23442 preventDefault: true,
23447 getAutoCreate : function()
23452 cls : 'roo-menu-item-text',
23460 cls : 'fa ' + this.icon
23469 href : this.href || '#',
23476 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23480 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23482 if(this.pos == 'left'){
23483 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23490 initEvents : function()
23492 this.el.on('mouseover', this.onMouseOver, this);
23493 this.el.on('mouseout', this.onMouseOut, this);
23495 this.el.select('a', true).first().on('click', this.onClick, this);
23499 onClick : function(e)
23501 if(this.preventDefault){
23502 e.preventDefault();
23505 this.fireEvent("click", this, e);
23508 onMouseOver : function(e)
23510 if(this.submenu && this.pos == 'left'){
23511 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23514 this.fireEvent("mouseover", this, e);
23517 onMouseOut : function(e)
23519 this.fireEvent("mouseout", this, e);
23531 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23534 * @class Roo.bootstrap.menu.Separator
23535 * @extends Roo.bootstrap.Component
23536 * Bootstrap Separator class
23539 * Create a new Separator
23540 * @param {Object} config The config object
23544 Roo.bootstrap.menu.Separator = function(config){
23545 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23548 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23550 getAutoCreate : function(){
23571 * @class Roo.bootstrap.Tooltip
23572 * Bootstrap Tooltip class
23573 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23574 * to determine which dom element triggers the tooltip.
23576 * It needs to add support for additional attributes like tooltip-position
23579 * Create a new Toolti
23580 * @param {Object} config The config object
23583 Roo.bootstrap.Tooltip = function(config){
23584 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23587 Roo.apply(Roo.bootstrap.Tooltip, {
23589 * @function init initialize tooltip monitoring.
23593 currentTip : false,
23594 currentRegion : false,
23600 Roo.get(document).on('mouseover', this.enter ,this);
23601 Roo.get(document).on('mouseout', this.leave, this);
23604 this.currentTip = new Roo.bootstrap.Tooltip();
23607 enter : function(ev)
23609 var dom = ev.getTarget();
23611 //Roo.log(['enter',dom]);
23612 var el = Roo.fly(dom);
23613 if (this.currentEl) {
23615 //Roo.log(this.currentEl);
23616 //Roo.log(this.currentEl.contains(dom));
23617 if (this.currentEl == el) {
23620 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23626 if (this.currentTip.el) {
23627 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23632 // you can not look for children, as if el is the body.. then everythign is the child..
23633 if (!el.attr('tooltip')) { //
23634 if (!el.select("[tooltip]").elements.length) {
23637 // is the mouse over this child...?
23638 bindEl = el.select("[tooltip]").first();
23639 var xy = ev.getXY();
23640 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23641 //Roo.log("not in region.");
23644 //Roo.log("child element over..");
23647 this.currentEl = bindEl;
23648 this.currentTip.bind(bindEl);
23649 this.currentRegion = Roo.lib.Region.getRegion(dom);
23650 this.currentTip.enter();
23653 leave : function(ev)
23655 var dom = ev.getTarget();
23656 //Roo.log(['leave',dom]);
23657 if (!this.currentEl) {
23662 if (dom != this.currentEl.dom) {
23665 var xy = ev.getXY();
23666 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23669 // only activate leave if mouse cursor is outside... bounding box..
23674 if (this.currentTip) {
23675 this.currentTip.leave();
23677 //Roo.log('clear currentEl');
23678 this.currentEl = false;
23683 'left' : ['r-l', [-2,0], 'right'],
23684 'right' : ['l-r', [2,0], 'left'],
23685 'bottom' : ['t-b', [0,2], 'top'],
23686 'top' : [ 'b-t', [0,-2], 'bottom']
23692 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23697 delay : null, // can be { show : 300 , hide: 500}
23701 hoverState : null, //???
23703 placement : 'bottom',
23705 getAutoCreate : function(){
23712 cls : 'tooltip-arrow'
23715 cls : 'tooltip-inner'
23722 bind : function(el)
23728 enter : function () {
23730 if (this.timeout != null) {
23731 clearTimeout(this.timeout);
23734 this.hoverState = 'in';
23735 //Roo.log("enter - show");
23736 if (!this.delay || !this.delay.show) {
23741 this.timeout = setTimeout(function () {
23742 if (_t.hoverState == 'in') {
23745 }, this.delay.show);
23749 clearTimeout(this.timeout);
23751 this.hoverState = 'out';
23752 if (!this.delay || !this.delay.hide) {
23758 this.timeout = setTimeout(function () {
23759 //Roo.log("leave - timeout");
23761 if (_t.hoverState == 'out') {
23763 Roo.bootstrap.Tooltip.currentEl = false;
23771 this.render(document.body);
23774 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23776 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23778 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23780 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23782 var placement = typeof this.placement == 'function' ?
23783 this.placement.call(this, this.el, on_el) :
23786 var autoToken = /\s?auto?\s?/i;
23787 var autoPlace = autoToken.test(placement);
23789 placement = placement.replace(autoToken, '') || 'top';
23793 //this.el.setXY([0,0]);
23795 //this.el.dom.style.display='block';
23797 //this.el.appendTo(on_el);
23799 var p = this.getPosition();
23800 var box = this.el.getBox();
23806 var align = Roo.bootstrap.Tooltip.alignment[placement];
23808 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23810 if(placement == 'top' || placement == 'bottom'){
23812 placement = 'right';
23815 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23816 placement = 'left';
23820 align = Roo.bootstrap.Tooltip.alignment[placement];
23822 this.el.alignTo(this.bindEl, align[0],align[1]);
23823 //var arrow = this.el.select('.arrow',true).first();
23824 //arrow.set(align[2],
23826 this.el.addClass(placement);
23828 this.el.addClass('in fade');
23830 this.hoverState = null;
23832 if (this.el.hasClass('fade')) {
23843 //this.el.setXY([0,0]);
23844 this.el.removeClass('in');
23860 * @class Roo.bootstrap.LocationPicker
23861 * @extends Roo.bootstrap.Component
23862 * Bootstrap LocationPicker class
23863 * @cfg {Number} latitude Position when init default 0
23864 * @cfg {Number} longitude Position when init default 0
23865 * @cfg {Number} zoom default 15
23866 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23867 * @cfg {Boolean} mapTypeControl default false
23868 * @cfg {Boolean} disableDoubleClickZoom default false
23869 * @cfg {Boolean} scrollwheel default true
23870 * @cfg {Boolean} streetViewControl default false
23871 * @cfg {Number} radius default 0
23872 * @cfg {String} locationName
23873 * @cfg {Boolean} draggable default true
23874 * @cfg {Boolean} enableAutocomplete default false
23875 * @cfg {Boolean} enableReverseGeocode default true
23876 * @cfg {String} markerTitle
23879 * Create a new LocationPicker
23880 * @param {Object} config The config object
23884 Roo.bootstrap.LocationPicker = function(config){
23886 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23891 * Fires when the picker initialized.
23892 * @param {Roo.bootstrap.LocationPicker} this
23893 * @param {Google Location} location
23897 * @event positionchanged
23898 * Fires when the picker position changed.
23899 * @param {Roo.bootstrap.LocationPicker} this
23900 * @param {Google Location} location
23902 positionchanged : true,
23905 * Fires when the map resize.
23906 * @param {Roo.bootstrap.LocationPicker} this
23911 * Fires when the map show.
23912 * @param {Roo.bootstrap.LocationPicker} this
23917 * Fires when the map hide.
23918 * @param {Roo.bootstrap.LocationPicker} this
23923 * Fires when click the map.
23924 * @param {Roo.bootstrap.LocationPicker} this
23925 * @param {Map event} e
23929 * @event mapRightClick
23930 * Fires when right click the map.
23931 * @param {Roo.bootstrap.LocationPicker} this
23932 * @param {Map event} e
23934 mapRightClick : true,
23936 * @event markerClick
23937 * Fires when click the marker.
23938 * @param {Roo.bootstrap.LocationPicker} this
23939 * @param {Map event} e
23941 markerClick : true,
23943 * @event markerRightClick
23944 * Fires when right click the marker.
23945 * @param {Roo.bootstrap.LocationPicker} this
23946 * @param {Map event} e
23948 markerRightClick : true,
23950 * @event OverlayViewDraw
23951 * Fires when OverlayView Draw
23952 * @param {Roo.bootstrap.LocationPicker} this
23954 OverlayViewDraw : true,
23956 * @event OverlayViewOnAdd
23957 * Fires when OverlayView Draw
23958 * @param {Roo.bootstrap.LocationPicker} this
23960 OverlayViewOnAdd : true,
23962 * @event OverlayViewOnRemove
23963 * Fires when OverlayView Draw
23964 * @param {Roo.bootstrap.LocationPicker} this
23966 OverlayViewOnRemove : true,
23968 * @event OverlayViewShow
23969 * Fires when OverlayView Draw
23970 * @param {Roo.bootstrap.LocationPicker} this
23971 * @param {Pixel} cpx
23973 OverlayViewShow : true,
23975 * @event OverlayViewHide
23976 * Fires when OverlayView Draw
23977 * @param {Roo.bootstrap.LocationPicker} this
23979 OverlayViewHide : true,
23981 * @event loadexception
23982 * Fires when load google lib failed.
23983 * @param {Roo.bootstrap.LocationPicker} this
23985 loadexception : true
23990 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23992 gMapContext: false,
23998 mapTypeControl: false,
23999 disableDoubleClickZoom: false,
24001 streetViewControl: false,
24005 enableAutocomplete: false,
24006 enableReverseGeocode: true,
24009 getAutoCreate: function()
24014 cls: 'roo-location-picker'
24020 initEvents: function(ct, position)
24022 if(!this.el.getWidth() || this.isApplied()){
24026 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24031 initial: function()
24033 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24034 this.fireEvent('loadexception', this);
24038 if(!this.mapTypeId){
24039 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24042 this.gMapContext = this.GMapContext();
24044 this.initOverlayView();
24046 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24050 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24051 _this.setPosition(_this.gMapContext.marker.position);
24054 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24055 _this.fireEvent('mapClick', this, event);
24059 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24060 _this.fireEvent('mapRightClick', this, event);
24064 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24065 _this.fireEvent('markerClick', this, event);
24069 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24070 _this.fireEvent('markerRightClick', this, event);
24074 this.setPosition(this.gMapContext.location);
24076 this.fireEvent('initial', this, this.gMapContext.location);
24079 initOverlayView: function()
24083 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24087 _this.fireEvent('OverlayViewDraw', _this);
24092 _this.fireEvent('OverlayViewOnAdd', _this);
24095 onRemove: function()
24097 _this.fireEvent('OverlayViewOnRemove', _this);
24100 show: function(cpx)
24102 _this.fireEvent('OverlayViewShow', _this, cpx);
24107 _this.fireEvent('OverlayViewHide', _this);
24113 fromLatLngToContainerPixel: function(event)
24115 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24118 isApplied: function()
24120 return this.getGmapContext() == false ? false : true;
24123 getGmapContext: function()
24125 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24128 GMapContext: function()
24130 var position = new google.maps.LatLng(this.latitude, this.longitude);
24132 var _map = new google.maps.Map(this.el.dom, {
24135 mapTypeId: this.mapTypeId,
24136 mapTypeControl: this.mapTypeControl,
24137 disableDoubleClickZoom: this.disableDoubleClickZoom,
24138 scrollwheel: this.scrollwheel,
24139 streetViewControl: this.streetViewControl,
24140 locationName: this.locationName,
24141 draggable: this.draggable,
24142 enableAutocomplete: this.enableAutocomplete,
24143 enableReverseGeocode: this.enableReverseGeocode
24146 var _marker = new google.maps.Marker({
24147 position: position,
24149 title: this.markerTitle,
24150 draggable: this.draggable
24157 location: position,
24158 radius: this.radius,
24159 locationName: this.locationName,
24160 addressComponents: {
24161 formatted_address: null,
24162 addressLine1: null,
24163 addressLine2: null,
24165 streetNumber: null,
24169 stateOrProvince: null
24172 domContainer: this.el.dom,
24173 geodecoder: new google.maps.Geocoder()
24177 drawCircle: function(center, radius, options)
24179 if (this.gMapContext.circle != null) {
24180 this.gMapContext.circle.setMap(null);
24184 options = Roo.apply({}, options, {
24185 strokeColor: "#0000FF",
24186 strokeOpacity: .35,
24188 fillColor: "#0000FF",
24192 options.map = this.gMapContext.map;
24193 options.radius = radius;
24194 options.center = center;
24195 this.gMapContext.circle = new google.maps.Circle(options);
24196 return this.gMapContext.circle;
24202 setPosition: function(location)
24204 this.gMapContext.location = location;
24205 this.gMapContext.marker.setPosition(location);
24206 this.gMapContext.map.panTo(location);
24207 this.drawCircle(location, this.gMapContext.radius, {});
24211 if (this.gMapContext.settings.enableReverseGeocode) {
24212 this.gMapContext.geodecoder.geocode({
24213 latLng: this.gMapContext.location
24214 }, function(results, status) {
24216 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24217 _this.gMapContext.locationName = results[0].formatted_address;
24218 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24220 _this.fireEvent('positionchanged', this, location);
24227 this.fireEvent('positionchanged', this, location);
24232 google.maps.event.trigger(this.gMapContext.map, "resize");
24234 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24236 this.fireEvent('resize', this);
24239 setPositionByLatLng: function(latitude, longitude)
24241 this.setPosition(new google.maps.LatLng(latitude, longitude));
24244 getCurrentPosition: function()
24247 latitude: this.gMapContext.location.lat(),
24248 longitude: this.gMapContext.location.lng()
24252 getAddressName: function()
24254 return this.gMapContext.locationName;
24257 getAddressComponents: function()
24259 return this.gMapContext.addressComponents;
24262 address_component_from_google_geocode: function(address_components)
24266 for (var i = 0; i < address_components.length; i++) {
24267 var component = address_components[i];
24268 if (component.types.indexOf("postal_code") >= 0) {
24269 result.postalCode = component.short_name;
24270 } else if (component.types.indexOf("street_number") >= 0) {
24271 result.streetNumber = component.short_name;
24272 } else if (component.types.indexOf("route") >= 0) {
24273 result.streetName = component.short_name;
24274 } else if (component.types.indexOf("neighborhood") >= 0) {
24275 result.city = component.short_name;
24276 } else if (component.types.indexOf("locality") >= 0) {
24277 result.city = component.short_name;
24278 } else if (component.types.indexOf("sublocality") >= 0) {
24279 result.district = component.short_name;
24280 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24281 result.stateOrProvince = component.short_name;
24282 } else if (component.types.indexOf("country") >= 0) {
24283 result.country = component.short_name;
24287 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24288 result.addressLine2 = "";
24292 setZoomLevel: function(zoom)
24294 this.gMapContext.map.setZoom(zoom);
24307 this.fireEvent('show', this);
24318 this.fireEvent('hide', this);
24323 Roo.apply(Roo.bootstrap.LocationPicker, {
24325 OverlayView : function(map, options)
24327 options = options || {};
24341 * @class Roo.bootstrap.Alert
24342 * @extends Roo.bootstrap.Component
24343 * Bootstrap Alert class
24344 * @cfg {String} title The title of alert
24345 * @cfg {String} html The content of alert
24346 * @cfg {String} weight ( success | info | warning | danger )
24347 * @cfg {String} faicon font-awesomeicon
24350 * Create a new alert
24351 * @param {Object} config The config object
24355 Roo.bootstrap.Alert = function(config){
24356 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24360 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24367 getAutoCreate : function()
24376 cls : 'roo-alert-icon'
24381 cls : 'roo-alert-title',
24386 cls : 'roo-alert-text',
24393 cfg.cn[0].cls += ' fa ' + this.faicon;
24397 cfg.cls += ' alert-' + this.weight;
24403 initEvents: function()
24405 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24408 setTitle : function(str)
24410 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24413 setText : function(str)
24415 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24418 setWeight : function(weight)
24421 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24424 this.weight = weight;
24426 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24429 setIcon : function(icon)
24432 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24435 this.faicon = icon;
24437 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24458 * @class Roo.bootstrap.UploadCropbox
24459 * @extends Roo.bootstrap.Component
24460 * Bootstrap UploadCropbox class
24461 * @cfg {String} emptyText show when image has been loaded
24462 * @cfg {String} rotateNotify show when image too small to rotate
24463 * @cfg {Number} errorTimeout default 3000
24464 * @cfg {Number} minWidth default 300
24465 * @cfg {Number} minHeight default 300
24466 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24467 * @cfg {Boolean} isDocument (true|false) default false
24468 * @cfg {String} url action url
24469 * @cfg {String} paramName default 'imageUpload'
24470 * @cfg {String} method default POST
24471 * @cfg {Boolean} loadMask (true|false) default true
24472 * @cfg {Boolean} loadingText default 'Loading...'
24475 * Create a new UploadCropbox
24476 * @param {Object} config The config object
24479 Roo.bootstrap.UploadCropbox = function(config){
24480 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24484 * @event beforeselectfile
24485 * Fire before select file
24486 * @param {Roo.bootstrap.UploadCropbox} this
24488 "beforeselectfile" : true,
24491 * Fire after initEvent
24492 * @param {Roo.bootstrap.UploadCropbox} this
24497 * Fire after initEvent
24498 * @param {Roo.bootstrap.UploadCropbox} this
24499 * @param {String} data
24504 * Fire when preparing the file data
24505 * @param {Roo.bootstrap.UploadCropbox} this
24506 * @param {Object} file
24511 * Fire when get exception
24512 * @param {Roo.bootstrap.UploadCropbox} this
24513 * @param {XMLHttpRequest} xhr
24515 "exception" : true,
24517 * @event beforeloadcanvas
24518 * Fire before load the canvas
24519 * @param {Roo.bootstrap.UploadCropbox} this
24520 * @param {String} src
24522 "beforeloadcanvas" : true,
24525 * Fire when trash image
24526 * @param {Roo.bootstrap.UploadCropbox} this
24531 * Fire when download the image
24532 * @param {Roo.bootstrap.UploadCropbox} this
24536 * @event footerbuttonclick
24537 * Fire when footerbuttonclick
24538 * @param {Roo.bootstrap.UploadCropbox} this
24539 * @param {String} type
24541 "footerbuttonclick" : true,
24545 * @param {Roo.bootstrap.UploadCropbox} this
24550 * Fire when rotate the image
24551 * @param {Roo.bootstrap.UploadCropbox} this
24552 * @param {String} pos
24557 * Fire when inspect the file
24558 * @param {Roo.bootstrap.UploadCropbox} this
24559 * @param {Object} file
24564 * Fire when xhr upload the file
24565 * @param {Roo.bootstrap.UploadCropbox} this
24566 * @param {Object} data
24571 * Fire when arrange the file data
24572 * @param {Roo.bootstrap.UploadCropbox} this
24573 * @param {Object} formData
24578 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24581 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24583 emptyText : 'Click to upload image',
24584 rotateNotify : 'Image is too small to rotate',
24585 errorTimeout : 3000,
24599 cropType : 'image/jpeg',
24601 canvasLoaded : false,
24602 isDocument : false,
24604 paramName : 'imageUpload',
24606 loadingText : 'Loading...',
24609 getAutoCreate : function()
24613 cls : 'roo-upload-cropbox',
24617 cls : 'roo-upload-cropbox-selector',
24622 cls : 'roo-upload-cropbox-body',
24623 style : 'cursor:pointer',
24627 cls : 'roo-upload-cropbox-preview'
24631 cls : 'roo-upload-cropbox-thumb'
24635 cls : 'roo-upload-cropbox-empty-notify',
24636 html : this.emptyText
24640 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24641 html : this.rotateNotify
24647 cls : 'roo-upload-cropbox-footer',
24650 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24660 onRender : function(ct, position)
24662 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24664 if (this.buttons.length) {
24666 Roo.each(this.buttons, function(bb) {
24668 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24670 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24676 this.maskEl = this.el;
24680 initEvents : function()
24682 this.urlAPI = (window.createObjectURL && window) ||
24683 (window.URL && URL.revokeObjectURL && URL) ||
24684 (window.webkitURL && webkitURL);
24686 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24687 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24689 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24690 this.selectorEl.hide();
24692 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24693 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24695 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24696 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24697 this.thumbEl.hide();
24699 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24700 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24702 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24703 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24704 this.errorEl.hide();
24706 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24707 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24708 this.footerEl.hide();
24710 this.setThumbBoxSize();
24716 this.fireEvent('initial', this);
24723 window.addEventListener("resize", function() { _this.resize(); } );
24725 this.bodyEl.on('click', this.beforeSelectFile, this);
24728 this.bodyEl.on('touchstart', this.onTouchStart, this);
24729 this.bodyEl.on('touchmove', this.onTouchMove, this);
24730 this.bodyEl.on('touchend', this.onTouchEnd, this);
24734 this.bodyEl.on('mousedown', this.onMouseDown, this);
24735 this.bodyEl.on('mousemove', this.onMouseMove, this);
24736 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24737 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24738 Roo.get(document).on('mouseup', this.onMouseUp, this);
24741 this.selectorEl.on('change', this.onFileSelected, this);
24747 this.baseScale = 1;
24749 this.baseRotate = 1;
24750 this.dragable = false;
24751 this.pinching = false;
24754 this.cropData = false;
24755 this.notifyEl.dom.innerHTML = this.emptyText;
24757 this.selectorEl.dom.value = '';
24761 resize : function()
24763 if(this.fireEvent('resize', this) != false){
24764 this.setThumbBoxPosition();
24765 this.setCanvasPosition();
24769 onFooterButtonClick : function(e, el, o, type)
24772 case 'rotate-left' :
24773 this.onRotateLeft(e);
24775 case 'rotate-right' :
24776 this.onRotateRight(e);
24779 this.beforeSelectFile(e);
24794 this.fireEvent('footerbuttonclick', this, type);
24797 beforeSelectFile : function(e)
24799 e.preventDefault();
24801 if(this.fireEvent('beforeselectfile', this) != false){
24802 this.selectorEl.dom.click();
24806 onFileSelected : function(e)
24808 e.preventDefault();
24810 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24814 var file = this.selectorEl.dom.files[0];
24816 if(this.fireEvent('inspect', this, file) != false){
24817 this.prepare(file);
24822 trash : function(e)
24824 this.fireEvent('trash', this);
24827 download : function(e)
24829 this.fireEvent('download', this);
24832 loadCanvas : function(src)
24834 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24838 this.imageEl = document.createElement('img');
24842 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24844 this.imageEl.src = src;
24848 onLoadCanvas : function()
24850 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24851 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24853 this.bodyEl.un('click', this.beforeSelectFile, this);
24855 this.notifyEl.hide();
24856 this.thumbEl.show();
24857 this.footerEl.show();
24859 this.baseRotateLevel();
24861 if(this.isDocument){
24862 this.setThumbBoxSize();
24865 this.setThumbBoxPosition();
24867 this.baseScaleLevel();
24873 this.canvasLoaded = true;
24876 this.maskEl.unmask();
24881 setCanvasPosition : function()
24883 if(!this.canvasEl){
24887 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24888 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24890 this.previewEl.setLeft(pw);
24891 this.previewEl.setTop(ph);
24895 onMouseDown : function(e)
24899 this.dragable = true;
24900 this.pinching = false;
24902 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24903 this.dragable = false;
24907 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24908 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24912 onMouseMove : function(e)
24916 if(!this.canvasLoaded){
24920 if (!this.dragable){
24924 var minX = Math.ceil(this.thumbEl.getLeft(true));
24925 var minY = Math.ceil(this.thumbEl.getTop(true));
24927 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24928 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24930 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24931 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24933 x = x - this.mouseX;
24934 y = y - this.mouseY;
24936 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24937 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24939 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24940 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24942 this.previewEl.setLeft(bgX);
24943 this.previewEl.setTop(bgY);
24945 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24946 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24949 onMouseUp : function(e)
24953 this.dragable = false;
24956 onMouseWheel : function(e)
24960 this.startScale = this.scale;
24962 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24964 if(!this.zoomable()){
24965 this.scale = this.startScale;
24974 zoomable : function()
24976 var minScale = this.thumbEl.getWidth() / this.minWidth;
24978 if(this.minWidth < this.minHeight){
24979 minScale = this.thumbEl.getHeight() / this.minHeight;
24982 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24983 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24987 (this.rotate == 0 || this.rotate == 180) &&
24989 width > this.imageEl.OriginWidth ||
24990 height > this.imageEl.OriginHeight ||
24991 (width < this.minWidth && height < this.minHeight)
24999 (this.rotate == 90 || this.rotate == 270) &&
25001 width > this.imageEl.OriginWidth ||
25002 height > this.imageEl.OriginHeight ||
25003 (width < this.minHeight && height < this.minWidth)
25010 !this.isDocument &&
25011 (this.rotate == 0 || this.rotate == 180) &&
25013 width < this.minWidth ||
25014 width > this.imageEl.OriginWidth ||
25015 height < this.minHeight ||
25016 height > this.imageEl.OriginHeight
25023 !this.isDocument &&
25024 (this.rotate == 90 || this.rotate == 270) &&
25026 width < this.minHeight ||
25027 width > this.imageEl.OriginWidth ||
25028 height < this.minWidth ||
25029 height > this.imageEl.OriginHeight
25039 onRotateLeft : function(e)
25041 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25043 var minScale = this.thumbEl.getWidth() / this.minWidth;
25045 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25046 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25048 this.startScale = this.scale;
25050 while (this.getScaleLevel() < minScale){
25052 this.scale = this.scale + 1;
25054 if(!this.zoomable()){
25059 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25060 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25065 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25072 this.scale = this.startScale;
25074 this.onRotateFail();
25079 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25081 if(this.isDocument){
25082 this.setThumbBoxSize();
25083 this.setThumbBoxPosition();
25084 this.setCanvasPosition();
25089 this.fireEvent('rotate', this, 'left');
25093 onRotateRight : function(e)
25095 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25097 var minScale = this.thumbEl.getWidth() / this.minWidth;
25099 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25100 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25102 this.startScale = this.scale;
25104 while (this.getScaleLevel() < minScale){
25106 this.scale = this.scale + 1;
25108 if(!this.zoomable()){
25113 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25114 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25119 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25126 this.scale = this.startScale;
25128 this.onRotateFail();
25133 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25135 if(this.isDocument){
25136 this.setThumbBoxSize();
25137 this.setThumbBoxPosition();
25138 this.setCanvasPosition();
25143 this.fireEvent('rotate', this, 'right');
25146 onRotateFail : function()
25148 this.errorEl.show(true);
25152 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25157 this.previewEl.dom.innerHTML = '';
25159 var canvasEl = document.createElement("canvas");
25161 var contextEl = canvasEl.getContext("2d");
25163 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25164 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25165 var center = this.imageEl.OriginWidth / 2;
25167 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25168 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25169 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25170 center = this.imageEl.OriginHeight / 2;
25173 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25175 contextEl.translate(center, center);
25176 contextEl.rotate(this.rotate * Math.PI / 180);
25178 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25180 this.canvasEl = document.createElement("canvas");
25182 this.contextEl = this.canvasEl.getContext("2d");
25184 switch (this.rotate) {
25187 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25188 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25190 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25195 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25196 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25198 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25199 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);
25203 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25208 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25209 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25211 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25212 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);
25216 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25221 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25222 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25224 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25225 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25229 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);
25236 this.previewEl.appendChild(this.canvasEl);
25238 this.setCanvasPosition();
25243 if(!this.canvasLoaded){
25247 var imageCanvas = document.createElement("canvas");
25249 var imageContext = imageCanvas.getContext("2d");
25251 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25252 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25254 var center = imageCanvas.width / 2;
25256 imageContext.translate(center, center);
25258 imageContext.rotate(this.rotate * Math.PI / 180);
25260 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25262 var canvas = document.createElement("canvas");
25264 var context = canvas.getContext("2d");
25266 canvas.width = this.minWidth;
25267 canvas.height = this.minHeight;
25269 switch (this.rotate) {
25272 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25273 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25275 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25276 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25278 var targetWidth = this.minWidth - 2 * x;
25279 var targetHeight = this.minHeight - 2 * y;
25283 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25284 scale = targetWidth / width;
25287 if(x > 0 && y == 0){
25288 scale = targetHeight / height;
25291 if(x > 0 && y > 0){
25292 scale = targetWidth / width;
25294 if(width < height){
25295 scale = targetHeight / height;
25299 context.scale(scale, scale);
25301 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25302 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25304 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25305 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25307 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25312 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25313 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25315 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25316 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25318 var targetWidth = this.minWidth - 2 * x;
25319 var targetHeight = this.minHeight - 2 * y;
25323 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25324 scale = targetWidth / width;
25327 if(x > 0 && y == 0){
25328 scale = targetHeight / height;
25331 if(x > 0 && y > 0){
25332 scale = targetWidth / width;
25334 if(width < height){
25335 scale = targetHeight / height;
25339 context.scale(scale, scale);
25341 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25342 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25344 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25345 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25347 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25349 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25354 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25355 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25357 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25358 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25360 var targetWidth = this.minWidth - 2 * x;
25361 var targetHeight = this.minHeight - 2 * y;
25365 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25366 scale = targetWidth / width;
25369 if(x > 0 && y == 0){
25370 scale = targetHeight / height;
25373 if(x > 0 && y > 0){
25374 scale = targetWidth / width;
25376 if(width < height){
25377 scale = targetHeight / height;
25381 context.scale(scale, scale);
25383 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25384 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25386 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25387 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25389 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25390 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25392 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25397 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25398 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25400 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25401 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25403 var targetWidth = this.minWidth - 2 * x;
25404 var targetHeight = this.minHeight - 2 * y;
25408 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25409 scale = targetWidth / width;
25412 if(x > 0 && y == 0){
25413 scale = targetHeight / height;
25416 if(x > 0 && y > 0){
25417 scale = targetWidth / width;
25419 if(width < height){
25420 scale = targetHeight / height;
25424 context.scale(scale, scale);
25426 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25427 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25429 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25430 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25432 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25434 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25441 this.cropData = canvas.toDataURL(this.cropType);
25443 if(this.fireEvent('crop', this, this.cropData) !== false){
25444 this.process(this.file, this.cropData);
25451 setThumbBoxSize : function()
25455 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25456 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25457 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25459 this.minWidth = width;
25460 this.minHeight = height;
25462 if(this.rotate == 90 || this.rotate == 270){
25463 this.minWidth = height;
25464 this.minHeight = width;
25469 width = Math.ceil(this.minWidth * height / this.minHeight);
25471 if(this.minWidth > this.minHeight){
25473 height = Math.ceil(this.minHeight * width / this.minWidth);
25476 this.thumbEl.setStyle({
25477 width : width + 'px',
25478 height : height + 'px'
25485 setThumbBoxPosition : function()
25487 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25488 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25490 this.thumbEl.setLeft(x);
25491 this.thumbEl.setTop(y);
25495 baseRotateLevel : function()
25497 this.baseRotate = 1;
25500 typeof(this.exif) != 'undefined' &&
25501 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25502 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25504 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25507 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25511 baseScaleLevel : function()
25515 if(this.isDocument){
25517 if(this.baseRotate == 6 || this.baseRotate == 8){
25519 height = this.thumbEl.getHeight();
25520 this.baseScale = height / this.imageEl.OriginWidth;
25522 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25523 width = this.thumbEl.getWidth();
25524 this.baseScale = width / this.imageEl.OriginHeight;
25530 height = this.thumbEl.getHeight();
25531 this.baseScale = height / this.imageEl.OriginHeight;
25533 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25534 width = this.thumbEl.getWidth();
25535 this.baseScale = width / this.imageEl.OriginWidth;
25541 if(this.baseRotate == 6 || this.baseRotate == 8){
25543 width = this.thumbEl.getHeight();
25544 this.baseScale = width / this.imageEl.OriginHeight;
25546 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25547 height = this.thumbEl.getWidth();
25548 this.baseScale = height / this.imageEl.OriginHeight;
25551 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25552 height = this.thumbEl.getWidth();
25553 this.baseScale = height / this.imageEl.OriginHeight;
25555 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25556 width = this.thumbEl.getHeight();
25557 this.baseScale = width / this.imageEl.OriginWidth;
25564 width = this.thumbEl.getWidth();
25565 this.baseScale = width / this.imageEl.OriginWidth;
25567 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25568 height = this.thumbEl.getHeight();
25569 this.baseScale = height / this.imageEl.OriginHeight;
25572 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25574 height = this.thumbEl.getHeight();
25575 this.baseScale = height / this.imageEl.OriginHeight;
25577 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25578 width = this.thumbEl.getWidth();
25579 this.baseScale = width / this.imageEl.OriginWidth;
25587 getScaleLevel : function()
25589 return this.baseScale * Math.pow(1.1, this.scale);
25592 onTouchStart : function(e)
25594 if(!this.canvasLoaded){
25595 this.beforeSelectFile(e);
25599 var touches = e.browserEvent.touches;
25605 if(touches.length == 1){
25606 this.onMouseDown(e);
25610 if(touches.length != 2){
25616 for(var i = 0, finger; finger = touches[i]; i++){
25617 coords.push(finger.pageX, finger.pageY);
25620 var x = Math.pow(coords[0] - coords[2], 2);
25621 var y = Math.pow(coords[1] - coords[3], 2);
25623 this.startDistance = Math.sqrt(x + y);
25625 this.startScale = this.scale;
25627 this.pinching = true;
25628 this.dragable = false;
25632 onTouchMove : function(e)
25634 if(!this.pinching && !this.dragable){
25638 var touches = e.browserEvent.touches;
25645 this.onMouseMove(e);
25651 for(var i = 0, finger; finger = touches[i]; i++){
25652 coords.push(finger.pageX, finger.pageY);
25655 var x = Math.pow(coords[0] - coords[2], 2);
25656 var y = Math.pow(coords[1] - coords[3], 2);
25658 this.endDistance = Math.sqrt(x + y);
25660 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25662 if(!this.zoomable()){
25663 this.scale = this.startScale;
25671 onTouchEnd : function(e)
25673 this.pinching = false;
25674 this.dragable = false;
25678 process : function(file, crop)
25681 this.maskEl.mask(this.loadingText);
25684 this.xhr = new XMLHttpRequest();
25686 file.xhr = this.xhr;
25688 this.xhr.open(this.method, this.url, true);
25691 "Accept": "application/json",
25692 "Cache-Control": "no-cache",
25693 "X-Requested-With": "XMLHttpRequest"
25696 for (var headerName in headers) {
25697 var headerValue = headers[headerName];
25699 this.xhr.setRequestHeader(headerName, headerValue);
25705 this.xhr.onload = function()
25707 _this.xhrOnLoad(_this.xhr);
25710 this.xhr.onerror = function()
25712 _this.xhrOnError(_this.xhr);
25715 var formData = new FormData();
25717 formData.append('returnHTML', 'NO');
25720 formData.append('crop', crop);
25723 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25724 formData.append(this.paramName, file, file.name);
25727 if(typeof(file.filename) != 'undefined'){
25728 formData.append('filename', file.filename);
25731 if(typeof(file.mimetype) != 'undefined'){
25732 formData.append('mimetype', file.mimetype);
25735 if(this.fireEvent('arrange', this, formData) != false){
25736 this.xhr.send(formData);
25740 xhrOnLoad : function(xhr)
25743 this.maskEl.unmask();
25746 if (xhr.readyState !== 4) {
25747 this.fireEvent('exception', this, xhr);
25751 var response = Roo.decode(xhr.responseText);
25753 if(!response.success){
25754 this.fireEvent('exception', this, xhr);
25758 var response = Roo.decode(xhr.responseText);
25760 this.fireEvent('upload', this, response);
25764 xhrOnError : function()
25767 this.maskEl.unmask();
25770 Roo.log('xhr on error');
25772 var response = Roo.decode(xhr.responseText);
25778 prepare : function(file)
25781 this.maskEl.mask(this.loadingText);
25787 if(typeof(file) === 'string'){
25788 this.loadCanvas(file);
25792 if(!file || !this.urlAPI){
25797 this.cropType = file.type;
25801 if(this.fireEvent('prepare', this, this.file) != false){
25803 var reader = new FileReader();
25805 reader.onload = function (e) {
25806 if (e.target.error) {
25807 Roo.log(e.target.error);
25811 var buffer = e.target.result,
25812 dataView = new DataView(buffer),
25814 maxOffset = dataView.byteLength - 4,
25818 if (dataView.getUint16(0) === 0xffd8) {
25819 while (offset < maxOffset) {
25820 markerBytes = dataView.getUint16(offset);
25822 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25823 markerLength = dataView.getUint16(offset + 2) + 2;
25824 if (offset + markerLength > dataView.byteLength) {
25825 Roo.log('Invalid meta data: Invalid segment size.');
25829 if(markerBytes == 0xffe1){
25830 _this.parseExifData(
25837 offset += markerLength;
25847 var url = _this.urlAPI.createObjectURL(_this.file);
25849 _this.loadCanvas(url);
25854 reader.readAsArrayBuffer(this.file);
25860 parseExifData : function(dataView, offset, length)
25862 var tiffOffset = offset + 10,
25866 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25867 // No Exif data, might be XMP data instead
25871 // Check for the ASCII code for "Exif" (0x45786966):
25872 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25873 // No Exif data, might be XMP data instead
25876 if (tiffOffset + 8 > dataView.byteLength) {
25877 Roo.log('Invalid Exif data: Invalid segment size.');
25880 // Check for the two null bytes:
25881 if (dataView.getUint16(offset + 8) !== 0x0000) {
25882 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25885 // Check the byte alignment:
25886 switch (dataView.getUint16(tiffOffset)) {
25888 littleEndian = true;
25891 littleEndian = false;
25894 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25897 // Check for the TIFF tag marker (0x002A):
25898 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25899 Roo.log('Invalid Exif data: Missing TIFF marker.');
25902 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25903 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25905 this.parseExifTags(
25908 tiffOffset + dirOffset,
25913 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25918 if (dirOffset + 6 > dataView.byteLength) {
25919 Roo.log('Invalid Exif data: Invalid directory offset.');
25922 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25923 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25924 if (dirEndOffset + 4 > dataView.byteLength) {
25925 Roo.log('Invalid Exif data: Invalid directory size.');
25928 for (i = 0; i < tagsNumber; i += 1) {
25932 dirOffset + 2 + 12 * i, // tag offset
25936 // Return the offset to the next directory:
25937 return dataView.getUint32(dirEndOffset, littleEndian);
25940 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25942 var tag = dataView.getUint16(offset, littleEndian);
25944 this.exif[tag] = this.getExifValue(
25948 dataView.getUint16(offset + 2, littleEndian), // tag type
25949 dataView.getUint32(offset + 4, littleEndian), // tag length
25954 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25956 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25965 Roo.log('Invalid Exif data: Invalid tag type.');
25969 tagSize = tagType.size * length;
25970 // Determine if the value is contained in the dataOffset bytes,
25971 // or if the value at the dataOffset is a pointer to the actual data:
25972 dataOffset = tagSize > 4 ?
25973 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25974 if (dataOffset + tagSize > dataView.byteLength) {
25975 Roo.log('Invalid Exif data: Invalid data offset.');
25978 if (length === 1) {
25979 return tagType.getValue(dataView, dataOffset, littleEndian);
25982 for (i = 0; i < length; i += 1) {
25983 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25986 if (tagType.ascii) {
25988 // Concatenate the chars:
25989 for (i = 0; i < values.length; i += 1) {
25991 // Ignore the terminating NULL byte(s):
25992 if (c === '\u0000') {
26004 Roo.apply(Roo.bootstrap.UploadCropbox, {
26006 'Orientation': 0x0112
26010 1: 0, //'top-left',
26012 3: 180, //'bottom-right',
26013 // 4: 'bottom-left',
26015 6: 90, //'right-top',
26016 // 7: 'right-bottom',
26017 8: 270 //'left-bottom'
26021 // byte, 8-bit unsigned int:
26023 getValue: function (dataView, dataOffset) {
26024 return dataView.getUint8(dataOffset);
26028 // ascii, 8-bit byte:
26030 getValue: function (dataView, dataOffset) {
26031 return String.fromCharCode(dataView.getUint8(dataOffset));
26036 // short, 16 bit int:
26038 getValue: function (dataView, dataOffset, littleEndian) {
26039 return dataView.getUint16(dataOffset, littleEndian);
26043 // long, 32 bit int:
26045 getValue: function (dataView, dataOffset, littleEndian) {
26046 return dataView.getUint32(dataOffset, littleEndian);
26050 // rational = two long values, first is numerator, second is denominator:
26052 getValue: function (dataView, dataOffset, littleEndian) {
26053 return dataView.getUint32(dataOffset, littleEndian) /
26054 dataView.getUint32(dataOffset + 4, littleEndian);
26058 // slong, 32 bit signed int:
26060 getValue: function (dataView, dataOffset, littleEndian) {
26061 return dataView.getInt32(dataOffset, littleEndian);
26065 // srational, two slongs, first is numerator, second is denominator:
26067 getValue: function (dataView, dataOffset, littleEndian) {
26068 return dataView.getInt32(dataOffset, littleEndian) /
26069 dataView.getInt32(dataOffset + 4, littleEndian);
26079 cls : 'btn-group roo-upload-cropbox-rotate-left',
26080 action : 'rotate-left',
26084 cls : 'btn btn-default',
26085 html : '<i class="fa fa-undo"></i>'
26091 cls : 'btn-group roo-upload-cropbox-picture',
26092 action : 'picture',
26096 cls : 'btn btn-default',
26097 html : '<i class="fa fa-picture-o"></i>'
26103 cls : 'btn-group roo-upload-cropbox-rotate-right',
26104 action : 'rotate-right',
26108 cls : 'btn btn-default',
26109 html : '<i class="fa fa-repeat"></i>'
26117 cls : 'btn-group roo-upload-cropbox-rotate-left',
26118 action : 'rotate-left',
26122 cls : 'btn btn-default',
26123 html : '<i class="fa fa-undo"></i>'
26129 cls : 'btn-group roo-upload-cropbox-download',
26130 action : 'download',
26134 cls : 'btn btn-default',
26135 html : '<i class="fa fa-download"></i>'
26141 cls : 'btn-group roo-upload-cropbox-crop',
26146 cls : 'btn btn-default',
26147 html : '<i class="fa fa-crop"></i>'
26153 cls : 'btn-group roo-upload-cropbox-trash',
26158 cls : 'btn btn-default',
26159 html : '<i class="fa fa-trash"></i>'
26165 cls : 'btn-group roo-upload-cropbox-rotate-right',
26166 action : 'rotate-right',
26170 cls : 'btn btn-default',
26171 html : '<i class="fa fa-repeat"></i>'
26179 cls : 'btn-group roo-upload-cropbox-rotate-left',
26180 action : 'rotate-left',
26184 cls : 'btn btn-default',
26185 html : '<i class="fa fa-undo"></i>'
26191 cls : 'btn-group roo-upload-cropbox-rotate-right',
26192 action : 'rotate-right',
26196 cls : 'btn btn-default',
26197 html : '<i class="fa fa-repeat"></i>'
26210 * @class Roo.bootstrap.DocumentManager
26211 * @extends Roo.bootstrap.Component
26212 * Bootstrap DocumentManager class
26213 * @cfg {String} paramName default 'imageUpload'
26214 * @cfg {String} method default POST
26215 * @cfg {String} url action url
26216 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26217 * @cfg {Boolean} multiple multiple upload default true
26218 * @cfg {Number} thumbSize default 300
26219 * @cfg {String} fieldLabel
26220 * @cfg {Number} labelWidth default 4
26221 * @cfg {String} labelAlign (left|top) default left
26222 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26225 * Create a new DocumentManager
26226 * @param {Object} config The config object
26229 Roo.bootstrap.DocumentManager = function(config){
26230 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26235 * Fire when initial the DocumentManager
26236 * @param {Roo.bootstrap.DocumentManager} this
26241 * inspect selected file
26242 * @param {Roo.bootstrap.DocumentManager} this
26243 * @param {File} file
26248 * Fire when xhr load exception
26249 * @param {Roo.bootstrap.DocumentManager} this
26250 * @param {XMLHttpRequest} xhr
26252 "exception" : true,
26255 * prepare the form data
26256 * @param {Roo.bootstrap.DocumentManager} this
26257 * @param {Object} formData
26262 * Fire when remove the file
26263 * @param {Roo.bootstrap.DocumentManager} this
26264 * @param {Object} file
26269 * Fire after refresh the file
26270 * @param {Roo.bootstrap.DocumentManager} this
26275 * Fire after click the image
26276 * @param {Roo.bootstrap.DocumentManager} this
26277 * @param {Object} file
26282 * Fire when upload a image and editable set to true
26283 * @param {Roo.bootstrap.DocumentManager} this
26284 * @param {Object} file
26288 * @event beforeselectfile
26289 * Fire before select file
26290 * @param {Roo.bootstrap.DocumentManager} this
26292 "beforeselectfile" : true,
26295 * Fire before process file
26296 * @param {Roo.bootstrap.DocumentManager} this
26297 * @param {Object} file
26304 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26313 paramName : 'imageUpload',
26316 labelAlign : 'left',
26323 getAutoCreate : function()
26325 var managerWidget = {
26327 cls : 'roo-document-manager',
26331 cls : 'roo-document-manager-selector',
26336 cls : 'roo-document-manager-uploader',
26340 cls : 'roo-document-manager-upload-btn',
26341 html : '<i class="fa fa-plus"></i>'
26352 cls : 'column col-md-12',
26357 if(this.fieldLabel.length){
26362 cls : 'column col-md-12',
26363 html : this.fieldLabel
26367 cls : 'column col-md-12',
26372 if(this.labelAlign == 'left'){
26376 cls : 'column col-md-' + this.labelWidth,
26377 html : this.fieldLabel
26381 cls : 'column col-md-' + (12 - this.labelWidth),
26391 cls : 'row clearfix',
26399 initEvents : function()
26401 this.managerEl = this.el.select('.roo-document-manager', true).first();
26402 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26404 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26405 this.selectorEl.hide();
26408 this.selectorEl.attr('multiple', 'multiple');
26411 this.selectorEl.on('change', this.onFileSelected, this);
26413 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26414 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26416 this.uploader.on('click', this.onUploaderClick, this);
26418 this.renderProgressDialog();
26422 window.addEventListener("resize", function() { _this.refresh(); } );
26424 this.fireEvent('initial', this);
26427 renderProgressDialog : function()
26431 this.progressDialog = new Roo.bootstrap.Modal({
26432 cls : 'roo-document-manager-progress-dialog',
26433 allow_close : false,
26443 btnclick : function() {
26444 _this.uploadCancel();
26450 this.progressDialog.render(Roo.get(document.body));
26452 this.progress = new Roo.bootstrap.Progress({
26453 cls : 'roo-document-manager-progress',
26458 this.progress.render(this.progressDialog.getChildContainer());
26460 this.progressBar = new Roo.bootstrap.ProgressBar({
26461 cls : 'roo-document-manager-progress-bar',
26464 aria_valuemax : 12,
26468 this.progressBar.render(this.progress.getChildContainer());
26471 onUploaderClick : function(e)
26473 e.preventDefault();
26475 if(this.fireEvent('beforeselectfile', this) != false){
26476 this.selectorEl.dom.click();
26481 onFileSelected : function(e)
26483 e.preventDefault();
26485 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26489 Roo.each(this.selectorEl.dom.files, function(file){
26490 if(this.fireEvent('inspect', this, file) != false){
26491 this.files.push(file);
26501 this.selectorEl.dom.value = '';
26503 if(!this.files.length){
26507 if(this.boxes > 0 && this.files.length > this.boxes){
26508 this.files = this.files.slice(0, this.boxes);
26511 this.uploader.show();
26513 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26514 this.uploader.hide();
26523 Roo.each(this.files, function(file){
26525 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26526 var f = this.renderPreview(file);
26531 if(file.type.indexOf('image') != -1){
26532 this.delegates.push(
26534 _this.process(file);
26535 }).createDelegate(this)
26543 _this.process(file);
26544 }).createDelegate(this)
26549 this.files = files;
26551 this.delegates = this.delegates.concat(docs);
26553 if(!this.delegates.length){
26558 this.progressBar.aria_valuemax = this.delegates.length;
26565 arrange : function()
26567 if(!this.delegates.length){
26568 this.progressDialog.hide();
26573 var delegate = this.delegates.shift();
26575 this.progressDialog.show();
26577 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26579 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26584 refresh : function()
26586 this.uploader.show();
26588 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26589 this.uploader.hide();
26592 Roo.isTouch ? this.closable(false) : this.closable(true);
26594 this.fireEvent('refresh', this);
26597 onRemove : function(e, el, o)
26599 e.preventDefault();
26601 this.fireEvent('remove', this, o);
26605 remove : function(o)
26609 Roo.each(this.files, function(file){
26610 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26619 this.files = files;
26626 Roo.each(this.files, function(file){
26631 file.target.remove();
26640 onClick : function(e, el, o)
26642 e.preventDefault();
26644 this.fireEvent('click', this, o);
26648 closable : function(closable)
26650 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26652 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26664 xhrOnLoad : function(xhr)
26666 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26670 if (xhr.readyState !== 4) {
26672 this.fireEvent('exception', this, xhr);
26676 var response = Roo.decode(xhr.responseText);
26678 if(!response.success){
26680 this.fireEvent('exception', this, xhr);
26684 var file = this.renderPreview(response.data);
26686 this.files.push(file);
26692 xhrOnError : function()
26694 Roo.log('xhr on error');
26696 var response = Roo.decode(xhr.responseText);
26703 process : function(file)
26705 if(this.fireEvent('process', this, file) !== false){
26706 if(this.editable && file.type.indexOf('image') != -1){
26707 this.fireEvent('edit', this, file);
26711 this.uploadStart(file, false);
26718 uploadStart : function(file, crop)
26720 this.xhr = new XMLHttpRequest();
26722 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26727 file.xhr = this.xhr;
26729 this.managerEl.createChild({
26731 cls : 'roo-document-manager-loading',
26735 tooltip : file.name,
26736 cls : 'roo-document-manager-thumb',
26737 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26743 this.xhr.open(this.method, this.url, true);
26746 "Accept": "application/json",
26747 "Cache-Control": "no-cache",
26748 "X-Requested-With": "XMLHttpRequest"
26751 for (var headerName in headers) {
26752 var headerValue = headers[headerName];
26754 this.xhr.setRequestHeader(headerName, headerValue);
26760 this.xhr.onload = function()
26762 _this.xhrOnLoad(_this.xhr);
26765 this.xhr.onerror = function()
26767 _this.xhrOnError(_this.xhr);
26770 var formData = new FormData();
26772 formData.append('returnHTML', 'NO');
26775 formData.append('crop', crop);
26778 formData.append(this.paramName, file, file.name);
26780 if(this.fireEvent('prepare', this, formData) != false){
26781 this.xhr.send(formData);
26785 uploadCancel : function()
26792 this.delegates = [];
26794 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26801 renderPreview : function(file)
26803 if(typeof(file.target) != 'undefined' && file.target){
26807 var previewEl = this.managerEl.createChild({
26809 cls : 'roo-document-manager-preview',
26813 tooltip : file.filename,
26814 cls : 'roo-document-manager-thumb',
26815 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26820 html : '<i class="fa fa-times-circle"></i>'
26825 var close = previewEl.select('button.close', true).first();
26827 close.on('click', this.onRemove, this, file);
26829 file.target = previewEl;
26831 var image = previewEl.select('img', true).first();
26835 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26837 image.on('click', this.onClick, this, file);
26843 onPreviewLoad : function(file, image)
26845 if(typeof(file.target) == 'undefined' || !file.target){
26849 var width = image.dom.naturalWidth || image.dom.width;
26850 var height = image.dom.naturalHeight || image.dom.height;
26852 if(width > height){
26853 file.target.addClass('wide');
26857 file.target.addClass('tall');
26862 uploadFromSource : function(file, crop)
26864 this.xhr = new XMLHttpRequest();
26866 this.managerEl.createChild({
26868 cls : 'roo-document-manager-loading',
26872 tooltip : file.name,
26873 cls : 'roo-document-manager-thumb',
26874 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26880 this.xhr.open(this.method, this.url, true);
26883 "Accept": "application/json",
26884 "Cache-Control": "no-cache",
26885 "X-Requested-With": "XMLHttpRequest"
26888 for (var headerName in headers) {
26889 var headerValue = headers[headerName];
26891 this.xhr.setRequestHeader(headerName, headerValue);
26897 this.xhr.onload = function()
26899 _this.xhrOnLoad(_this.xhr);
26902 this.xhr.onerror = function()
26904 _this.xhrOnError(_this.xhr);
26907 var formData = new FormData();
26909 formData.append('returnHTML', 'NO');
26911 formData.append('crop', crop);
26913 if(typeof(file.filename) != 'undefined'){
26914 formData.append('filename', file.filename);
26917 if(typeof(file.mimetype) != 'undefined'){
26918 formData.append('mimetype', file.mimetype);
26921 if(this.fireEvent('prepare', this, formData) != false){
26922 this.xhr.send(formData);
26932 * @class Roo.bootstrap.DocumentViewer
26933 * @extends Roo.bootstrap.Component
26934 * Bootstrap DocumentViewer class
26937 * Create a new DocumentViewer
26938 * @param {Object} config The config object
26941 Roo.bootstrap.DocumentViewer = function(config){
26942 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26947 * Fire after initEvent
26948 * @param {Roo.bootstrap.DocumentViewer} this
26954 * @param {Roo.bootstrap.DocumentViewer} this
26959 * Fire after trash button
26960 * @param {Roo.bootstrap.DocumentViewer} this
26967 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26969 getAutoCreate : function()
26973 cls : 'roo-document-viewer',
26977 cls : 'roo-document-viewer-body',
26981 cls : 'roo-document-viewer-thumb',
26985 cls : 'roo-document-viewer-image'
26993 cls : 'roo-document-viewer-footer',
26996 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27004 cls : 'btn btn-default roo-document-viewer-trash',
27005 html : '<i class="fa fa-trash"></i>'
27018 initEvents : function()
27021 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27022 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27024 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27025 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27027 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27028 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27030 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27031 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27033 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27034 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27036 this.bodyEl.on('click', this.onClick, this);
27038 this.trashBtn.on('click', this.onTrash, this);
27042 initial : function()
27044 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27047 this.fireEvent('initial', this);
27051 onClick : function(e)
27053 e.preventDefault();
27055 this.fireEvent('click', this);
27058 onTrash : function(e)
27060 e.preventDefault();
27062 this.fireEvent('trash', this);
27074 * @class Roo.bootstrap.NavProgressBar
27075 * @extends Roo.bootstrap.Component
27076 * Bootstrap NavProgressBar class
27079 * Create a new nav progress bar
27080 * @param {Object} config The config object
27083 Roo.bootstrap.NavProgressBar = function(config){
27084 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27086 this.bullets = this.bullets || [];
27088 // Roo.bootstrap.NavProgressBar.register(this);
27092 * Fires when the active item changes
27093 * @param {Roo.bootstrap.NavProgressBar} this
27094 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27095 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27102 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27107 getAutoCreate : function()
27109 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27113 cls : 'roo-navigation-bar-group',
27117 cls : 'roo-navigation-top-bar'
27121 cls : 'roo-navigation-bullets-bar',
27125 cls : 'roo-navigation-bar'
27132 cls : 'roo-navigation-bottom-bar'
27142 initEvents: function()
27147 onRender : function(ct, position)
27149 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27151 if(this.bullets.length){
27152 Roo.each(this.bullets, function(b){
27161 addItem : function(cfg)
27163 var item = new Roo.bootstrap.NavProgressItem(cfg);
27165 item.parentId = this.id;
27166 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27169 var top = new Roo.bootstrap.Element({
27171 cls : 'roo-navigation-bar-text'
27174 var bottom = new Roo.bootstrap.Element({
27176 cls : 'roo-navigation-bar-text'
27179 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27180 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27182 var topText = new Roo.bootstrap.Element({
27184 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27187 var bottomText = new Roo.bootstrap.Element({
27189 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27192 topText.onRender(top.el, null);
27193 bottomText.onRender(bottom.el, null);
27196 item.bottomEl = bottom;
27199 this.barItems.push(item);
27204 getActive : function()
27206 var active = false;
27208 Roo.each(this.barItems, function(v){
27210 if (!v.isActive()) {
27222 setActiveItem : function(item)
27226 Roo.each(this.barItems, function(v){
27227 if (v.rid == item.rid) {
27231 if (v.isActive()) {
27232 v.setActive(false);
27237 item.setActive(true);
27239 this.fireEvent('changed', this, item, prev);
27242 getBarItem: function(rid)
27246 Roo.each(this.barItems, function(e) {
27247 if (e.rid != rid) {
27258 indexOfItem : function(item)
27262 Roo.each(this.barItems, function(v, i){
27264 if (v.rid != item.rid) {
27275 setActiveNext : function()
27277 var i = this.indexOfItem(this.getActive());
27279 if (i > this.barItems.length) {
27283 this.setActiveItem(this.barItems[i+1]);
27286 setActivePrev : function()
27288 var i = this.indexOfItem(this.getActive());
27294 this.setActiveItem(this.barItems[i-1]);
27297 format : function()
27299 if(!this.barItems.length){
27303 var width = 100 / this.barItems.length;
27305 Roo.each(this.barItems, function(i){
27306 i.el.setStyle('width', width + '%');
27307 i.topEl.el.setStyle('width', width + '%');
27308 i.bottomEl.el.setStyle('width', width + '%');
27317 * Nav Progress Item
27322 * @class Roo.bootstrap.NavProgressItem
27323 * @extends Roo.bootstrap.Component
27324 * Bootstrap NavProgressItem class
27325 * @cfg {String} rid the reference id
27326 * @cfg {Boolean} active (true|false) Is item active default false
27327 * @cfg {Boolean} disabled (true|false) Is item active default false
27328 * @cfg {String} html
27329 * @cfg {String} position (top|bottom) text position default bottom
27330 * @cfg {String} icon show icon instead of number
27333 * Create a new NavProgressItem
27334 * @param {Object} config The config object
27336 Roo.bootstrap.NavProgressItem = function(config){
27337 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27342 * The raw click event for the entire grid.
27343 * @param {Roo.bootstrap.NavProgressItem} this
27344 * @param {Roo.EventObject} e
27351 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27357 position : 'bottom',
27360 getAutoCreate : function()
27362 var iconCls = 'roo-navigation-bar-item-icon';
27364 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27368 cls: 'roo-navigation-bar-item',
27378 cfg.cls += ' active';
27381 cfg.cls += ' disabled';
27387 disable : function()
27389 this.setDisabled(true);
27392 enable : function()
27394 this.setDisabled(false);
27397 initEvents: function()
27399 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27401 this.iconEl.on('click', this.onClick, this);
27404 onClick : function(e)
27406 e.preventDefault();
27412 if(this.fireEvent('click', this, e) === false){
27416 this.parent().setActiveItem(this);
27419 isActive: function ()
27421 return this.active;
27424 setActive : function(state)
27426 if(this.active == state){
27430 this.active = state;
27433 this.el.addClass('active');
27437 this.el.removeClass('active');
27442 setDisabled : function(state)
27444 if(this.disabled == state){
27448 this.disabled = state;
27451 this.el.addClass('disabled');
27455 this.el.removeClass('disabled');
27458 tooltipEl : function()
27460 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27473 * @class Roo.bootstrap.FieldLabel
27474 * @extends Roo.bootstrap.Component
27475 * Bootstrap FieldLabel class
27476 * @cfg {String} html contents of the element
27477 * @cfg {String} tag tag of the element default label
27478 * @cfg {String} cls class of the element
27479 * @cfg {String} target label target
27480 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27481 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27482 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27483 * @cfg {String} iconTooltip default "This field is required"
27486 * Create a new FieldLabel
27487 * @param {Object} config The config object
27490 Roo.bootstrap.FieldLabel = function(config){
27491 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27496 * Fires after the field has been marked as invalid.
27497 * @param {Roo.form.FieldLabel} this
27498 * @param {String} msg The validation message
27503 * Fires after the field has been validated with no errors.
27504 * @param {Roo.form.FieldLabel} this
27510 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27517 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27518 validClass : 'text-success fa fa-lg fa-check',
27519 iconTooltip : 'This field is required',
27521 getAutoCreate : function(){
27525 cls : 'roo-bootstrap-field-label ' + this.cls,
27531 tooltip : this.iconTooltip
27543 initEvents: function()
27545 Roo.bootstrap.Element.superclass.initEvents.call(this);
27547 this.iconEl = this.el.select('i', true).first();
27549 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27551 Roo.bootstrap.FieldLabel.register(this);
27555 * Mark this field as valid
27557 markValid : function()
27559 this.iconEl.show();
27561 this.iconEl.removeClass(this.invalidClass);
27563 this.iconEl.addClass(this.validClass);
27565 this.fireEvent('valid', this);
27569 * Mark this field as invalid
27570 * @param {String} msg The validation message
27572 markInvalid : function(msg)
27574 this.iconEl.show();
27576 this.iconEl.removeClass(this.validClass);
27578 this.iconEl.addClass(this.invalidClass);
27580 this.fireEvent('invalid', this, msg);
27586 Roo.apply(Roo.bootstrap.FieldLabel, {
27591 * register a FieldLabel Group
27592 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27594 register : function(label)
27596 if(this.groups.hasOwnProperty(label.target)){
27600 this.groups[label.target] = label;
27604 * fetch a FieldLabel Group based on the target
27605 * @param {string} target
27606 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27608 get: function(target) {
27609 if (typeof(this.groups[target]) == 'undefined') {
27613 return this.groups[target] ;
27622 * page DateSplitField.
27628 * @class Roo.bootstrap.DateSplitField
27629 * @extends Roo.bootstrap.Component
27630 * Bootstrap DateSplitField class
27631 * @cfg {string} fieldLabel - the label associated
27632 * @cfg {Number} labelWidth set the width of label (0-12)
27633 * @cfg {String} labelAlign (top|left)
27634 * @cfg {Boolean} dayAllowBlank (true|false) default false
27635 * @cfg {Boolean} monthAllowBlank (true|false) default false
27636 * @cfg {Boolean} yearAllowBlank (true|false) default false
27637 * @cfg {string} dayPlaceholder
27638 * @cfg {string} monthPlaceholder
27639 * @cfg {string} yearPlaceholder
27640 * @cfg {string} dayFormat default 'd'
27641 * @cfg {string} monthFormat default 'm'
27642 * @cfg {string} yearFormat default 'Y'
27646 * Create a new DateSplitField
27647 * @param {Object} config The config object
27650 Roo.bootstrap.DateSplitField = function(config){
27651 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27657 * getting the data of years
27658 * @param {Roo.bootstrap.DateSplitField} this
27659 * @param {Object} years
27664 * getting the data of days
27665 * @param {Roo.bootstrap.DateSplitField} this
27666 * @param {Object} days
27671 * Fires after the field has been marked as invalid.
27672 * @param {Roo.form.Field} this
27673 * @param {String} msg The validation message
27678 * Fires after the field has been validated with no errors.
27679 * @param {Roo.form.Field} this
27685 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27688 labelAlign : 'top',
27690 dayAllowBlank : false,
27691 monthAllowBlank : false,
27692 yearAllowBlank : false,
27693 dayPlaceholder : '',
27694 monthPlaceholder : '',
27695 yearPlaceholder : '',
27699 isFormField : true,
27701 getAutoCreate : function()
27705 cls : 'row roo-date-split-field-group',
27710 cls : 'form-hidden-field roo-date-split-field-group-value',
27716 if(this.fieldLabel){
27719 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27723 html : this.fieldLabel
27729 Roo.each(['day', 'month', 'year'], function(t){
27732 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27739 inputEl: function ()
27741 return this.el.select('.roo-date-split-field-group-value', true).first();
27744 onRender : function(ct, position)
27748 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27750 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27752 this.dayField = new Roo.bootstrap.ComboBox({
27753 allowBlank : this.dayAllowBlank,
27754 alwaysQuery : true,
27755 displayField : 'value',
27758 forceSelection : true,
27760 placeholder : this.dayPlaceholder,
27761 selectOnFocus : true,
27762 tpl : '<div class="select2-result"><b>{value}</b></div>',
27763 triggerAction : 'all',
27765 valueField : 'value',
27766 store : new Roo.data.SimpleStore({
27767 data : (function() {
27769 _this.fireEvent('days', _this, days);
27772 fields : [ 'value' ]
27775 select : function (_self, record, index)
27777 _this.setValue(_this.getValue());
27782 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27784 this.monthField = new Roo.bootstrap.MonthField({
27785 after : '<i class=\"fa fa-calendar\"></i>',
27786 allowBlank : this.monthAllowBlank,
27787 placeholder : this.monthPlaceholder,
27790 render : function (_self)
27792 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27793 e.preventDefault();
27797 select : function (_self, oldvalue, newvalue)
27799 _this.setValue(_this.getValue());
27804 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27806 this.yearField = new Roo.bootstrap.ComboBox({
27807 allowBlank : this.yearAllowBlank,
27808 alwaysQuery : true,
27809 displayField : 'value',
27812 forceSelection : true,
27814 placeholder : this.yearPlaceholder,
27815 selectOnFocus : true,
27816 tpl : '<div class="select2-result"><b>{value}</b></div>',
27817 triggerAction : 'all',
27819 valueField : 'value',
27820 store : new Roo.data.SimpleStore({
27821 data : (function() {
27823 _this.fireEvent('years', _this, years);
27826 fields : [ 'value' ]
27829 select : function (_self, record, index)
27831 _this.setValue(_this.getValue());
27836 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27839 setValue : function(v, format)
27841 this.inputEl.dom.value = v;
27843 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27845 var d = Date.parseDate(v, f);
27852 this.setDay(d.format(this.dayFormat));
27853 this.setMonth(d.format(this.monthFormat));
27854 this.setYear(d.format(this.yearFormat));
27861 setDay : function(v)
27863 this.dayField.setValue(v);
27864 this.inputEl.dom.value = this.getValue();
27869 setMonth : function(v)
27871 this.monthField.setValue(v, true);
27872 this.inputEl.dom.value = this.getValue();
27877 setYear : function(v)
27879 this.yearField.setValue(v);
27880 this.inputEl.dom.value = this.getValue();
27885 getDay : function()
27887 return this.dayField.getValue();
27890 getMonth : function()
27892 return this.monthField.getValue();
27895 getYear : function()
27897 return this.yearField.getValue();
27900 getValue : function()
27902 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27904 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27914 this.inputEl.dom.value = '';
27919 validate : function()
27921 var d = this.dayField.validate();
27922 var m = this.monthField.validate();
27923 var y = this.yearField.validate();
27928 (!this.dayAllowBlank && !d) ||
27929 (!this.monthAllowBlank && !m) ||
27930 (!this.yearAllowBlank && !y)
27935 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27944 this.markInvalid();
27949 markValid : function()
27952 var label = this.el.select('label', true).first();
27953 var icon = this.el.select('i.fa-star', true).first();
27959 this.fireEvent('valid', this);
27963 * Mark this field as invalid
27964 * @param {String} msg The validation message
27966 markInvalid : function(msg)
27969 var label = this.el.select('label', true).first();
27970 var icon = this.el.select('i.fa-star', true).first();
27972 if(label && !icon){
27973 this.el.select('.roo-date-split-field-label', true).createChild({
27975 cls : 'text-danger fa fa-lg fa-star',
27976 tooltip : 'This field is required',
27977 style : 'margin-right:5px;'
27981 this.fireEvent('invalid', this, msg);
27984 clearInvalid : function()
27986 var label = this.el.select('label', true).first();
27987 var icon = this.el.select('i.fa-star', true).first();
27993 this.fireEvent('valid', this);
27996 getName: function()