4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa (ban|check|...) font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1968 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1970 this.el.on("mouseover", this.onMouseOver, this);
1971 this.el.on("mouseout", this.onMouseOut, this);
1975 findTargetItem : function(e){
1976 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1980 //Roo.log(t); Roo.log(t.id);
1982 //Roo.log(this.menuitems);
1983 return this.menuitems.get(t.id);
1985 //return this.items.get(t.menuItemId);
1990 onClick : function(e){
1991 Roo.log("menu.onClick");
1992 var t = this.findTargetItem(e);
1993 if(!t || t.isContainer){
1998 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1999 if(t == this.activeItem && t.shouldDeactivate(e)){
2000 this.activeItem.deactivate();
2001 delete this.activeItem;
2005 this.setActiveItem(t, true);
2013 Roo.log('pass click event');
2017 this.fireEvent("click", this, t, e);
2021 onMouseOver : function(e){
2022 var t = this.findTargetItem(e);
2025 // if(t.canActivate && !t.disabled){
2026 // this.setActiveItem(t, true);
2030 this.fireEvent("mouseover", this, e, t);
2032 isVisible : function(){
2033 return !this.hidden;
2035 onMouseOut : function(e){
2036 var t = this.findTargetItem(e);
2039 // if(t == this.activeItem && t.shouldDeactivate(e)){
2040 // this.activeItem.deactivate();
2041 // delete this.activeItem;
2044 this.fireEvent("mouseout", this, e, t);
2049 * Displays this menu relative to another element
2050 * @param {String/HTMLElement/Roo.Element} element The element to align to
2051 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052 * the element (defaults to this.defaultAlign)
2053 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2055 show : function(el, pos, parentMenu){
2056 this.parentMenu = parentMenu;
2060 this.fireEvent("beforeshow", this);
2061 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2064 * Displays this menu at a specific xy position
2065 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 showAt : function(xy, parentMenu, /* private: */_e){
2069 this.parentMenu = parentMenu;
2074 this.fireEvent("beforeshow", this);
2075 //xy = this.el.adjustForConstraints(xy);
2079 this.hideMenuItems();
2080 this.hidden = false;
2081 this.triggerEl.addClass('open');
2083 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2089 this.fireEvent("show", this);
2095 this.doFocus.defer(50, this);
2099 doFocus : function(){
2101 this.focusEl.focus();
2106 * Hides this menu and optionally all parent menus
2107 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2109 hide : function(deep){
2111 this.hideMenuItems();
2112 if(this.el && this.isVisible()){
2113 this.fireEvent("beforehide", this);
2114 if(this.activeItem){
2115 this.activeItem.deactivate();
2116 this.activeItem = null;
2118 this.triggerEl.removeClass('open');;
2120 this.fireEvent("hide", this);
2122 if(deep === true && this.parentMenu){
2123 this.parentMenu.hide(true);
2127 onTriggerPress : function(e)
2130 Roo.log('trigger press');
2131 //Roo.log(e.getTarget());
2132 // Roo.log(this.triggerEl.dom);
2133 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2137 if (this.isVisible()) {
2142 this.show(this.triggerEl, false, false);
2151 hideMenuItems : function()
2153 //$(backdrop).remove()
2154 Roo.select('.open',true).each(function(aa) {
2156 aa.removeClass('open');
2157 //var parent = getParent($(this))
2158 //var relatedTarget = { relatedTarget: this }
2160 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161 //if (e.isDefaultPrevented()) return
2162 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2165 addxtypeChild : function (tree, cntr) {
2166 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2168 this.menuitems.add(comp);
2189 * @class Roo.bootstrap.MenuItem
2190 * @extends Roo.bootstrap.Component
2191 * Bootstrap MenuItem class
2192 * @cfg {String} html the menu label
2193 * @cfg {String} href the link
2194 * @cfg {Boolean} preventDefault (true | false) default true
2195 * @cfg {Boolean} isContainer (true | false) default false
2199 * Create a new MenuItem
2200 * @param {Object} config The config object
2204 Roo.bootstrap.MenuItem = function(config){
2205 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2210 * The raw click event for the entire grid.
2211 * @param {Roo.bootstrap.MenuItem} this
2212 * @param {Roo.EventObject} e
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2222 preventDefault: true,
2223 isContainer : false,
2225 getAutoCreate : function(){
2227 if(this.isContainer){
2230 cls: 'dropdown-menu-item'
2236 cls: 'dropdown-menu-item',
2245 if (this.parent().type == 'treeview') {
2246 cfg.cls = 'treeview-menu';
2249 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2254 initEvents: function() {
2256 //this.el.select('a').on('click', this.onClick, this);
2259 onClick : function(e)
2261 Roo.log('item on click ');
2262 //if(this.preventDefault){
2263 // e.preventDefault();
2265 //this.parent().hideMenuItems();
2267 this.fireEvent('click', this, e);
2286 * @class Roo.bootstrap.MenuSeparator
2287 * @extends Roo.bootstrap.Component
2288 * Bootstrap MenuSeparator class
2291 * Create a new MenuItem
2292 * @param {Object} config The config object
2296 Roo.bootstrap.MenuSeparator = function(config){
2297 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2302 getAutoCreate : function(){
2321 * @class Roo.bootstrap.Modal
2322 * @extends Roo.bootstrap.Component
2323 * Bootstrap Modal class
2324 * @cfg {String} title Title of dialog
2325 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2327 * @cfg {Boolean} specificTitle default false
2328 * @cfg {Array} buttons Array of buttons or standard button set..
2329 * @cfg {String} buttonPosition (left|right|center) default right
2330 * @cfg {Boolean} animate default true
2331 * @cfg {Boolean} allow_close default true
2334 * Create a new Modal Dialog
2335 * @param {Object} config The config object
2338 Roo.bootstrap.Modal = function(config){
2339 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2344 * The raw btnclick event for the button
2345 * @param {Roo.EventObject} e
2349 this.buttons = this.buttons || [];
2352 this.tmpl = Roo.factory(this.tmpl);
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2359 title : 'test dialog',
2369 specificTitle: false,
2371 buttonPosition: 'right',
2385 onRender : function(ct, position)
2387 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2390 var cfg = Roo.apply({}, this.getAutoCreate());
2393 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2395 //if (!cfg.name.length) {
2399 cfg.cls += ' ' + this.cls;
2402 cfg.style = this.style;
2404 this.el = Roo.get(document.body).createChild(cfg, position);
2406 //var type = this.el.dom.type;
2411 if(this.tabIndex !== undefined){
2412 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2416 this.bodyEl = this.el.select('.modal-body',true).first();
2417 this.closeEl = this.el.select('.modal-header .close', true).first();
2418 this.footerEl = this.el.select('.modal-footer',true).first();
2419 this.titleEl = this.el.select('.modal-title',true).first();
2423 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424 this.maskEl.enableDisplayMode("block");
2426 //this.el.addClass("x-dlg-modal");
2428 if (this.buttons.length) {
2429 Roo.each(this.buttons, function(bb) {
2430 var b = Roo.apply({}, bb);
2431 b.xns = b.xns || Roo.bootstrap;
2432 b.xtype = b.xtype || 'Button';
2433 if (typeof(b.listeners) == 'undefined') {
2434 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2437 var btn = Roo.factory(b);
2439 btn.onRender(this.el.select('.modal-footer div').first());
2443 // render the children.
2446 if(typeof(this.items) != 'undefined'){
2447 var items = this.items;
2450 for(var i =0;i < items.length;i++) {
2451 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2455 this.items = nitems;
2457 // where are these used - they used to be body/close/footer
2461 //this.el.addClass([this.fieldClass, this.cls]);
2465 getAutoCreate : function(){
2470 html : this.html || ''
2475 cls : 'modal-title',
2479 if(this.specificTitle){
2485 if (this.allow_close) {
2496 style : 'display: none',
2499 cls: "modal-dialog",
2502 cls : "modal-content",
2505 cls : 'modal-header',
2510 cls : 'modal-footer',
2514 cls: 'btn-' + this.buttonPosition
2531 modal.cls += ' fade';
2537 getChildContainer : function() {
2542 getButtonContainer : function() {
2543 return this.el.select('.modal-footer div',true).first();
2546 initEvents : function()
2548 if (this.allow_close) {
2549 this.closeEl.on('click', this.hide, this);
2554 window.addEventListener("resize", function() { _this.resize(); } );
2560 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2565 if (!this.rendered) {
2569 this.el.setStyle('display', 'block');
2573 (function(){ _this.el.addClass('in'); }).defer(50);
2575 this.el.addClass('in');
2578 // not sure how we can show data in here..
2580 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2583 Roo.get(document.body).addClass("x-body-masked");
2584 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2586 this.el.setStyle('zIndex', '10001');
2588 this.fireEvent('show', this);
2595 Roo.get(document.body).removeClass("x-body-masked");
2596 this.el.removeClass('in');
2600 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2602 this.el.setStyle('display', 'none');
2605 this.fireEvent('hide', this);
2608 addButton : function(str, cb)
2612 var b = Roo.apply({}, { html : str } );
2613 b.xns = b.xns || Roo.bootstrap;
2614 b.xtype = b.xtype || 'Button';
2615 if (typeof(b.listeners) == 'undefined') {
2616 b.listeners = { click : cb.createDelegate(this) };
2619 var btn = Roo.factory(b);
2621 btn.onRender(this.el.select('.modal-footer div').first());
2627 setDefaultButton : function(btn)
2629 //this.el.select('.modal-footer').()
2631 resizeTo: function(w,h)
2635 setContentSize : function(w, h)
2639 onButtonClick: function(btn,e)
2642 this.fireEvent('btnclick', btn.name, e);
2645 * Set the title of the Dialog
2646 * @param {String} str new Title
2648 setTitle: function(str) {
2649 this.titleEl.dom.innerHTML = str;
2652 * Set the body of the Dialog
2653 * @param {String} str new Title
2655 setBody: function(str) {
2656 this.bodyEl.dom.innerHTML = str;
2659 * Set the body of the Dialog using the template
2660 * @param {Obj} data - apply this data to the template and replace the body contents.
2662 applyBody: function(obj)
2665 Roo.log("Error - using apply Body without a template");
2668 this.tmpl.overwrite(this.bodyEl, obj);
2674 Roo.apply(Roo.bootstrap.Modal, {
2676 * Button config that displays a single OK button
2685 * Button config that displays Yes and No buttons
2701 * Button config that displays OK and Cancel buttons
2716 * Button config that displays Yes, No and Cancel buttons
2739 * messagebox - can be used as a replace
2743 * @class Roo.MessageBox
2744 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2753 // process text value...
2757 // Show a dialog using config options:
2759 title:'Save Changes?',
2760 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761 buttons: Roo.Msg.YESNOCANCEL,
2768 Roo.bootstrap.MessageBox = function(){
2769 var dlg, opt, mask, waitTimer;
2770 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771 var buttons, activeTextEl, bwidth;
2775 var handleButton = function(button){
2777 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2781 var handleHide = function(){
2783 dlg.el.removeClass(opt.cls);
2786 // Roo.TaskMgr.stop(waitTimer);
2787 // waitTimer = null;
2792 var updateButtons = function(b){
2795 buttons["ok"].hide();
2796 buttons["cancel"].hide();
2797 buttons["yes"].hide();
2798 buttons["no"].hide();
2799 //dlg.footer.dom.style.display = 'none';
2802 dlg.footerEl.dom.style.display = '';
2803 for(var k in buttons){
2804 if(typeof buttons[k] != "function"){
2807 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808 width += buttons[k].el.getWidth()+15;
2818 var handleEsc = function(d, k, e){
2819 if(opt && opt.closable !== false){
2829 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830 * @return {Roo.BasicDialog} The BasicDialog element
2832 getDialog : function(){
2834 dlg = new Roo.bootstrap.Modal( {
2837 //constraintoviewport:false,
2839 //collapsible : false,
2844 //buttonAlign:"center",
2845 closeClick : function(){
2846 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2849 handleButton("cancel");
2854 dlg.on("hide", handleHide);
2856 //dlg.addKeyListener(27, handleEsc);
2858 this.buttons = buttons;
2859 var bt = this.buttonText;
2860 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2865 bodyEl = dlg.bodyEl.createChild({
2867 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868 '<textarea class="roo-mb-textarea"></textarea>' +
2869 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2871 msgEl = bodyEl.dom.firstChild;
2872 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873 textboxEl.enableDisplayMode();
2874 textboxEl.addKeyListener([10,13], function(){
2875 if(dlg.isVisible() && opt && opt.buttons){
2878 }else if(opt.buttons.yes){
2879 handleButton("yes");
2883 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884 textareaEl.enableDisplayMode();
2885 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886 progressEl.enableDisplayMode();
2887 var pf = progressEl.dom.firstChild;
2889 pp = Roo.get(pf.firstChild);
2890 pp.setHeight(pf.offsetHeight);
2898 * Updates the message box body text
2899 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900 * the XHTML-compliant non-breaking space character '&#160;')
2901 * @return {Roo.MessageBox} This message box
2903 updateText : function(text){
2904 if(!dlg.isVisible() && !opt.width){
2905 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2907 msgEl.innerHTML = text || ' ';
2909 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2912 Math.min(opt.width || cw , this.maxWidth),
2913 Math.max(opt.minWidth || this.minWidth, bwidth)
2916 activeTextEl.setWidth(w);
2918 if(dlg.isVisible()){
2919 dlg.fixedcenter = false;
2921 // to big, make it scroll. = But as usual stupid IE does not support
2924 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2928 bodyEl.dom.style.height = '';
2929 bodyEl.dom.style.overflowY = '';
2932 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2934 bodyEl.dom.style.overflowX = '';
2937 dlg.setContentSize(w, bodyEl.getHeight());
2938 if(dlg.isVisible()){
2939 dlg.fixedcenter = true;
2945 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2946 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949 * @return {Roo.MessageBox} This message box
2951 updateProgress : function(value, text){
2953 this.updateText(text);
2955 if (pp) { // weird bug on my firefox - for some reason this is not defined
2956 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2962 * Returns true if the message box is currently displayed
2963 * @return {Boolean} True if the message box is visible, else false
2965 isVisible : function(){
2966 return dlg && dlg.isVisible();
2970 * Hides the message box if it is displayed
2973 if(this.isVisible()){
2979 * Displays a new message box, or reinitializes an existing message box, based on the config options
2980 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981 * The following config object properties are supported:
2983 Property Type Description
2984 ---------- --------------- ------------------------------------------------------------------------------------
2985 animEl String/Element An id or Element from which the message box should animate as it opens and
2986 closes (defaults to undefined)
2987 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable Boolean False to hide the top-right close button (defaults to true). Note that
2990 progress and wait dialogs will ignore this property and always hide the
2991 close button as they can only be closed programmatically.
2992 cls String A custom CSS class to apply to the message box element
2993 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2994 displayed (defaults to 75)
2995 fn Function A callback function to execute after closing the dialog. The arguments to the
2996 function will be btn (the name of the button that was clicked, if applicable,
2997 e.g. "ok"), and text (the value of the active text field, if applicable).
2998 Progress and wait dialogs will ignore this option since they do not respond to
2999 user actions and can only be closed programmatically, so any required function
3000 should be called by the same code after it closes the dialog.
3001 icon String A CSS class that provides a background image to be used as an icon for
3002 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3004 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3005 modal Boolean False to allow user interaction with the page while the message box is
3006 displayed (defaults to true)
3007 msg String A string that will replace the existing message box body text (defaults
3008 to the XHTML-compliant non-breaking space character ' ')
3009 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3010 progress Boolean True to display a progress bar (defaults to false)
3011 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3014 title String The title text
3015 value String The string value to set into the active textbox element if displayed
3016 wait Boolean True to display a progress bar (defaults to false)
3017 width Number The width of the dialog in pixels
3024 msg: 'Please enter your address:',
3026 buttons: Roo.MessageBox.OKCANCEL,
3029 animEl: 'addAddressBtn'
3032 * @param {Object} config Configuration options
3033 * @return {Roo.MessageBox} This message box
3035 show : function(options)
3038 // this causes nightmares if you show one dialog after another
3039 // especially on callbacks..
3041 if(this.isVisible()){
3044 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3046 Roo.log("New Dialog Message:" + options.msg )
3047 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3051 var d = this.getDialog();
3053 d.setTitle(opt.title || " ");
3054 d.closeEl.setDisplayed(opt.closable !== false);
3055 activeTextEl = textboxEl;
3056 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3061 textareaEl.setHeight(typeof opt.multiline == "number" ?
3062 opt.multiline : this.defaultTextHeight);
3063 activeTextEl = textareaEl;
3072 progressEl.setDisplayed(opt.progress === true);
3073 this.updateProgress(0);
3074 activeTextEl.dom.value = opt.value || "";
3076 dlg.setDefaultButton(activeTextEl);
3078 var bs = opt.buttons;
3082 }else if(bs && bs.yes){
3083 db = buttons["yes"];
3085 dlg.setDefaultButton(db);
3087 bwidth = updateButtons(opt.buttons);
3088 this.updateText(opt.msg);
3090 d.el.addClass(opt.cls);
3092 d.proxyDrag = opt.proxyDrag === true;
3093 d.modal = opt.modal !== false;
3094 d.mask = opt.modal !== false ? mask : false;
3096 // force it to the end of the z-index stack so it gets a cursor in FF
3097 document.body.appendChild(dlg.el.dom);
3098 d.animateTarget = null;
3099 d.show(options.animEl);
3105 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3106 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107 * and closing the message box when the process is complete.
3108 * @param {String} title The title bar text
3109 * @param {String} msg The message box body text
3110 * @return {Roo.MessageBox} This message box
3112 progress : function(title, msg){
3119 minWidth: this.minProgressWidth,
3126 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127 * If a callback function is passed it will be called after the user clicks the button, and the
3128 * id of the button that was clicked will be passed as the only parameter to the callback
3129 * (could also be the top-right close button).
3130 * @param {String} title The title bar text
3131 * @param {String} msg The message box body text
3132 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133 * @param {Object} scope (optional) The scope of the callback function
3134 * @return {Roo.MessageBox} This message box
3136 alert : function(title, msg, fn, scope){
3149 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3150 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151 * You are responsible for closing the message box when the process is complete.
3152 * @param {String} msg The message box body text
3153 * @param {String} title (optional) The title bar text
3154 * @return {Roo.MessageBox} This message box
3156 wait : function(msg, title){
3167 waitTimer = Roo.TaskMgr.start({
3169 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3177 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180 * @param {String} title The title bar text
3181 * @param {String} msg The message box body text
3182 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183 * @param {Object} scope (optional) The scope of the callback function
3184 * @return {Roo.MessageBox} This message box
3186 confirm : function(title, msg, fn, scope){
3190 buttons: this.YESNO,
3199 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3201 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202 * (could also be the top-right close button) and the text that was entered will be passed as the two
3203 * parameters to the callback.
3204 * @param {String} title The title bar text
3205 * @param {String} msg The message box body text
3206 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207 * @param {Object} scope (optional) The scope of the callback function
3208 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210 * @return {Roo.MessageBox} This message box
3212 prompt : function(title, msg, fn, scope, multiline){
3216 buttons: this.OKCANCEL,
3221 multiline: multiline,
3228 * Button config that displays a single OK button
3233 * Button config that displays Yes and No buttons
3236 YESNO : {yes:true, no:true},
3238 * Button config that displays OK and Cancel buttons
3241 OKCANCEL : {ok:true, cancel:true},
3243 * Button config that displays Yes, No and Cancel buttons
3246 YESNOCANCEL : {yes:true, no:true, cancel:true},
3249 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3252 defaultTextHeight : 75,
3254 * The maximum width in pixels of the message box (defaults to 600)
3259 * The minimum width in pixels of the message box (defaults to 100)
3264 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3265 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3268 minProgressWidth : 250,
3270 * An object containing the default button text strings that can be overriden for localized language support.
3271 * Supported properties are: ok, cancel, yes and no.
3272 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3285 * Shorthand for {@link Roo.MessageBox}
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3297 * @class Roo.bootstrap.Navbar
3298 * @extends Roo.bootstrap.Component
3299 * Bootstrap Navbar class
3302 * Create a new Navbar
3303 * @param {Object} config The config object
3307 Roo.bootstrap.Navbar = function(config){
3308 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3321 getAutoCreate : function(){
3324 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3328 initEvents :function ()
3330 //Roo.log(this.el.select('.navbar-toggle',true));
3331 this.el.select('.navbar-toggle',true).on('click', function() {
3332 // Roo.log('click');
3333 this.el.select('.navbar-collapse',true).toggleClass('in');
3341 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3343 var size = this.el.getSize();
3344 this.maskEl.setSize(size.width, size.height);
3345 this.maskEl.enableDisplayMode("block");
3354 getChildContainer : function()
3356 if (this.el.select('.collapse').getCount()) {
3357 return this.el.select('.collapse',true).first();
3390 * @class Roo.bootstrap.NavSimplebar
3391 * @extends Roo.bootstrap.Navbar
3392 * Bootstrap Sidebar class
3394 * @cfg {Boolean} inverse is inverted color
3396 * @cfg {String} type (nav | pills | tabs)
3397 * @cfg {Boolean} arrangement stacked | justified
3398 * @cfg {String} align (left | right) alignment
3400 * @cfg {Boolean} main (true|false) main nav bar? default false
3401 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3403 * @cfg {String} tag (header|footer|nav|div) default is nav
3409 * Create a new Sidebar
3410 * @param {Object} config The config object
3414 Roo.bootstrap.NavSimplebar = function(config){
3415 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3434 getAutoCreate : function(){
3438 tag : this.tag || 'div',
3451 this.type = this.type || 'nav';
3452 if (['tabs','pills'].indexOf(this.type)!==-1) {
3453 cfg.cn[0].cls += ' nav-' + this.type
3457 if (this.type!=='nav') {
3458 Roo.log('nav type must be nav/tabs/pills')
3460 cfg.cn[0].cls += ' navbar-nav'
3466 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467 cfg.cn[0].cls += ' nav-' + this.arrangement;
3471 if (this.align === 'right') {
3472 cfg.cn[0].cls += ' navbar-right';
3476 cfg.cls += ' navbar-inverse';
3503 * @class Roo.bootstrap.NavHeaderbar
3504 * @extends Roo.bootstrap.NavSimplebar
3505 * Bootstrap Sidebar class
3507 * @cfg {String} brand what is brand
3508 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509 * @cfg {String} brand_href href of the brand
3510 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3511 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3516 * Create a new Sidebar
3517 * @param {Object} config The config object
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3533 desktopCenter : false,
3536 getAutoCreate : function(){
3539 tag: this.nav || 'nav',
3546 if (this.desktopCenter) {
3547 cn.push({cls : 'container', cn : []});
3554 cls: 'navbar-header',
3559 cls: 'navbar-toggle',
3560 'data-toggle': 'collapse',
3565 html: 'Toggle navigation'
3587 cls: 'collapse navbar-collapse',
3591 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3593 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594 cfg.cls += ' navbar-' + this.position;
3596 // tag can override this..
3598 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3601 if (this.brand !== '') {
3604 href: this.brand_href ? this.brand_href : '#',
3605 cls: 'navbar-brand',
3613 cfg.cls += ' main-nav';
3621 getHeaderChildContainer : function()
3623 if (this.el.select('.navbar-header').getCount()) {
3624 return this.el.select('.navbar-header',true).first();
3627 return this.getChildContainer();
3631 initEvents : function()
3633 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3635 if (this.autohide) {
3640 Roo.get(document).on('scroll',function(e) {
3641 var ns = Roo.get(document).getScroll().top;
3642 var os = prevScroll;
3646 ft.removeClass('slideDown');
3647 ft.addClass('slideUp');
3650 ft.removeClass('slideUp');
3651 ft.addClass('slideDown');
3672 * @class Roo.bootstrap.NavSidebar
3673 * @extends Roo.bootstrap.Navbar
3674 * Bootstrap Sidebar class
3677 * Create a new Sidebar
3678 * @param {Object} config The config object
3682 Roo.bootstrap.NavSidebar = function(config){
3683 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3688 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3690 getAutoCreate : function(){
3695 cls: 'sidebar sidebar-nav'
3717 * @class Roo.bootstrap.NavGroup
3718 * @extends Roo.bootstrap.Component
3719 * Bootstrap NavGroup class
3720 * @cfg {String} align (left|right)
3721 * @cfg {Boolean} inverse
3722 * @cfg {String} type (nav|pills|tab) default nav
3723 * @cfg {String} navId - reference Id for navbar.
3727 * Create a new nav group
3728 * @param {Object} config The config object
3731 Roo.bootstrap.NavGroup = function(config){
3732 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3735 Roo.bootstrap.NavGroup.register(this);
3739 * Fires when the active item changes
3740 * @param {Roo.bootstrap.NavGroup} this
3741 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3760 getAutoCreate : function()
3762 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3769 if (['tabs','pills'].indexOf(this.type)!==-1) {
3770 cfg.cls += ' nav-' + this.type
3772 if (this.type!=='nav') {
3773 Roo.log('nav type must be nav/tabs/pills')
3775 cfg.cls += ' navbar-nav'
3778 if (this.parent().sidebar) {
3781 cls: 'dashboard-menu sidebar-menu'
3787 if (this.form === true) {
3793 if (this.align === 'right') {
3794 cfg.cls += ' navbar-right';
3796 cfg.cls += ' navbar-left';
3800 if (this.align === 'right') {
3801 cfg.cls += ' navbar-right';
3805 cfg.cls += ' navbar-inverse';
3813 * sets the active Navigation item
3814 * @param {Roo.bootstrap.NavItem} the new current navitem
3816 setActiveItem : function(item)
3819 Roo.each(this.navItems, function(v){
3824 v.setActive(false, true);
3831 item.setActive(true, true);
3832 this.fireEvent('changed', this, item, prev);
3837 * gets the active Navigation item
3838 * @return {Roo.bootstrap.NavItem} the current navitem
3840 getActive : function()
3844 Roo.each(this.navItems, function(v){
3855 indexOfNav : function()
3859 Roo.each(this.navItems, function(v,i){
3870 * adds a Navigation item
3871 * @param {Roo.bootstrap.NavItem} the navitem to add
3873 addItem : function(cfg)
3875 var cn = new Roo.bootstrap.NavItem(cfg);
3877 cn.parentId = this.id;
3878 cn.onRender(this.el, null);
3882 * register a Navigation item
3883 * @param {Roo.bootstrap.NavItem} the navitem to add
3885 register : function(item)
3887 this.navItems.push( item);
3888 item.navId = this.navId;
3893 * clear all the Navigation item
3896 clearAll : function()
3899 this.el.dom.innerHTML = '';
3902 getNavItem: function(tabId)
3905 Roo.each(this.navItems, function(e) {
3906 if (e.tabId == tabId) {
3916 setActiveNext : function()
3918 var i = this.indexOfNav(this.getActive());
3919 if (i > this.navItems.length) {
3922 this.setActiveItem(this.navItems[i+1]);
3924 setActivePrev : function()
3926 var i = this.indexOfNav(this.getActive());
3930 this.setActiveItem(this.navItems[i-1]);
3932 clearWasActive : function(except) {
3933 Roo.each(this.navItems, function(e) {
3934 if (e.tabId != except.tabId && e.was_active) {
3935 e.was_active = false;
3942 getWasActive : function ()
3945 Roo.each(this.navItems, function(e) {
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3964 * register a Navigation Group
3965 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3967 register : function(navgrp)
3969 this.groups[navgrp.navId] = navgrp;
3973 * fetch a Navigation Group based on the navigation ID
3974 * @param {string} the navgroup to add
3975 * @returns {Roo.bootstrap.NavGroup} the navgroup
3977 get: function(navId) {
3978 if (typeof(this.groups[navId]) == 'undefined') {
3980 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3982 return this.groups[navId] ;
3997 * @class Roo.bootstrap.NavItem
3998 * @extends Roo.bootstrap.Component
3999 * Bootstrap Navbar.NavItem class
4000 * @cfg {String} href link to
4001 * @cfg {String} html content of button
4002 * @cfg {String} badge text inside badge
4003 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004 * @cfg {String} glyphicon name of glyphicon
4005 * @cfg {String} icon name of font awesome icon
4006 * @cfg {Boolean} active Is item active
4007 * @cfg {Boolean} disabled Is item disabled
4009 * @cfg {Boolean} preventDefault (true | false) default false
4010 * @cfg {String} tabId the tab that this item activates.
4011 * @cfg {String} tagtype (a|span) render as a href or span?
4012 * @cfg {Boolean} animateRef (true|false) link to element default false
4015 * Create a new Navbar Item
4016 * @param {Object} config The config object
4018 Roo.bootstrap.NavItem = function(config){
4019 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4024 * The raw click event for the entire grid.
4025 * @param {Roo.EventObject} e
4030 * Fires when the active item active state changes
4031 * @param {Roo.bootstrap.NavItem} this
4032 * @param {boolean} state the new state
4038 * Fires when scroll to element
4039 * @param {Roo.bootstrap.NavItem} this
4040 * @param {Object} options
4041 * @param {Roo.EventObject} e
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4057 preventDefault : false,
4064 getAutoCreate : function(){
4072 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4074 if (this.disabled) {
4075 cfg.cls += ' disabled';
4078 if (this.href || this.html || this.glyphicon || this.icon) {
4082 href : this.href || "#",
4083 html: this.html || ''
4088 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4091 if(this.glyphicon) {
4092 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4097 cfg.cn[0].html += " <span class='caret'></span>";
4101 if (this.badge !== '') {
4103 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4111 initEvents: function()
4113 if (typeof (this.menu) != 'undefined') {
4114 this.menu.parentType = this.xtype;
4115 this.menu.triggerEl = this.el;
4116 this.menu = this.addxtype(Roo.apply({}, this.menu));
4119 this.el.select('a',true).on('click', this.onClick, this);
4121 if(this.tagtype == 'span'){
4122 this.el.select('span',true).on('click', this.onClick, this);
4125 // at this point parent should be available..
4126 this.parent().register(this);
4129 onClick : function(e)
4132 this.preventDefault ||
4139 if (this.disabled) {
4143 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144 if (tg && tg.transition) {
4145 Roo.log("waiting for the transitionend");
4151 //Roo.log("fire event clicked");
4152 if(this.fireEvent('click', this, e) === false){
4156 if(this.tagtype == 'span'){
4160 //Roo.log(this.href);
4161 var ael = this.el.select('a',true).first();
4164 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167 return; // ignore... - it's a 'hash' to another page.
4171 this.scrollToElement(e);
4175 var p = this.parent();
4177 if (['tabs','pills'].indexOf(p.type)!==-1) {
4178 if (typeof(p.setActiveItem) !== 'undefined') {
4179 p.setActiveItem(this);
4183 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185 // remove the collapsed menu expand...
4186 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4190 isActive: function () {
4193 setActive : function(state, fire, is_was_active)
4195 if (this.active && !state && this.navId) {
4196 this.was_active = true;
4197 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4199 nv.clearWasActive(this);
4203 this.active = state;
4206 this.el.removeClass('active');
4207 } else if (!this.el.hasClass('active')) {
4208 this.el.addClass('active');
4211 this.fireEvent('changed', this, state);
4214 // show a panel if it's registered and related..
4216 if (!this.navId || !this.tabId || !state || is_was_active) {
4220 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4224 var pan = tg.getPanelByName(this.tabId);
4228 // if we can not flip to new panel - go back to old nav highlight..
4229 if (false == tg.showPanel(pan)) {
4230 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4232 var onav = nv.getWasActive();
4234 onav.setActive(true, false, true);
4243 // this should not be here...
4244 setDisabled : function(state)
4246 this.disabled = state;
4248 this.el.removeClass('disabled');
4249 } else if (!this.el.hasClass('disabled')) {
4250 this.el.addClass('disabled');
4256 * Fetch the element to display the tooltip on.
4257 * @return {Roo.Element} defaults to this.el
4259 tooltipEl : function()
4261 return this.el.select('' + this.tagtype + '', true).first();
4264 scrollToElement : function(e)
4266 var c = document.body;
4269 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4271 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272 c = document.documentElement;
4275 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4281 var o = target.calcOffsetsTo(c);
4288 this.fireEvent('scrollto', this, options, e);
4290 Roo.get(c).scrollTo('top', options.value, true);
4303 * <span> icon </span>
4304 * <span> text </span>
4305 * <span>badge </span>
4309 * @class Roo.bootstrap.NavSidebarItem
4310 * @extends Roo.bootstrap.NavItem
4311 * Bootstrap Navbar.NavSidebarItem class
4312 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4314 * Create a new Navbar Button
4315 * @param {Object} config The config object
4317 Roo.bootstrap.NavSidebarItem = function(config){
4318 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4323 * The raw click event for the entire grid.
4324 * @param {Roo.EventObject} e
4329 * Fires when the active item active state changes
4330 * @param {Roo.bootstrap.NavSidebarItem} this
4331 * @param {boolean} state the new state
4339 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4341 badgeWeight : 'default',
4343 getAutoCreate : function(){
4348 href : this.href || '#',
4360 html : this.html || ''
4365 cfg.cls += ' active';
4368 if (this.disabled) {
4369 cfg.cls += ' disabled';
4373 if (this.glyphicon || this.icon) {
4374 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4375 a.cn.push({ tag : 'i', cls : c }) ;
4380 if (this.badge !== '') {
4382 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4386 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4387 a.cls += 'dropdown-toggle treeview' ;
4398 initEvents : function()
4400 this.el.on('click', this.onClick, this);
4403 if(this.badge !== ''){
4405 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4410 onClick : function(e)
4417 if(this.preventDefault){
4421 this.fireEvent('click', this);
4424 disable : function()
4426 this.setDisabled(true);
4431 this.setDisabled(false);
4434 setDisabled : function(state)
4436 if(this.disabled == state){
4440 this.disabled = state;
4443 this.el.addClass('disabled');
4447 this.el.removeClass('disabled');
4452 setActive : function(state)
4454 if(this.active == state){
4458 this.active = state;
4461 this.el.addClass('active');
4465 this.el.removeClass('active');
4470 isActive: function ()
4475 setBadge : function(str)
4481 this.badgeEl.dom.innerHTML = str;
4498 * @class Roo.bootstrap.Row
4499 * @extends Roo.bootstrap.Component
4500 * Bootstrap Row class (contains columns...)
4504 * @param {Object} config The config object
4507 Roo.bootstrap.Row = function(config){
4508 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4511 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4513 getAutoCreate : function(){
4532 * @class Roo.bootstrap.Element
4533 * @extends Roo.bootstrap.Component
4534 * Bootstrap Element class
4535 * @cfg {String} html contents of the element
4536 * @cfg {String} tag tag of the element
4537 * @cfg {String} cls class of the element
4538 * @cfg {Boolean} preventDefault (true|false) default false
4539 * @cfg {Boolean} clickable (true|false) default false
4542 * Create a new Element
4543 * @param {Object} config The config object
4546 Roo.bootstrap.Element = function(config){
4547 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4553 * When a element is chick
4554 * @param {Roo.bootstrap.Element} this
4555 * @param {Roo.EventObject} e
4561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4566 preventDefault: false,
4569 getAutoCreate : function(){
4580 initEvents: function()
4582 Roo.bootstrap.Element.superclass.initEvents.call(this);
4585 this.el.on('click', this.onClick, this);
4590 onClick : function(e)
4592 if(this.preventDefault){
4596 this.fireEvent('click', this, e);
4599 getValue : function()
4601 return this.el.dom.innerHTML;
4604 setValue : function(value)
4606 this.el.dom.innerHTML = value;
4621 * @class Roo.bootstrap.Pagination
4622 * @extends Roo.bootstrap.Component
4623 * Bootstrap Pagination class
4624 * @cfg {String} size xs | sm | md | lg
4625 * @cfg {Boolean} inverse false | true
4628 * Create a new Pagination
4629 * @param {Object} config The config object
4632 Roo.bootstrap.Pagination = function(config){
4633 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4636 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4642 getAutoCreate : function(){
4648 cfg.cls += ' inverse';
4654 cfg.cls += " " + this.cls;
4672 * @class Roo.bootstrap.PaginationItem
4673 * @extends Roo.bootstrap.Component
4674 * Bootstrap PaginationItem class
4675 * @cfg {String} html text
4676 * @cfg {String} href the link
4677 * @cfg {Boolean} preventDefault (true | false) default true
4678 * @cfg {Boolean} active (true | false) default false
4679 * @cfg {Boolean} disabled default false
4683 * Create a new PaginationItem
4684 * @param {Object} config The config object
4688 Roo.bootstrap.PaginationItem = function(config){
4689 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4694 * The raw click event for the entire grid.
4695 * @param {Roo.EventObject} e
4701 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4705 preventDefault: true,
4710 getAutoCreate : function(){
4716 href : this.href ? this.href : '#',
4717 html : this.html ? this.html : ''
4727 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4731 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4737 initEvents: function() {
4739 this.el.on('click', this.onClick, this);
4742 onClick : function(e)
4744 Roo.log('PaginationItem on click ');
4745 if(this.preventDefault){
4753 this.fireEvent('click', this, e);
4769 * @class Roo.bootstrap.Slider
4770 * @extends Roo.bootstrap.Component
4771 * Bootstrap Slider class
4774 * Create a new Slider
4775 * @param {Object} config The config object
4778 Roo.bootstrap.Slider = function(config){
4779 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4782 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4784 getAutoCreate : function(){
4788 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4792 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4804 * Ext JS Library 1.1.1
4805 * Copyright(c) 2006-2007, Ext JS, LLC.
4807 * Originally Released Under LGPL - original licence link has changed is not relivant.
4810 * <script type="text/javascript">
4815 * @class Roo.grid.ColumnModel
4816 * @extends Roo.util.Observable
4817 * This is the default implementation of a ColumnModel used by the Grid. It defines
4818 * the columns in the grid.
4821 var colModel = new Roo.grid.ColumnModel([
4822 {header: "Ticker", width: 60, sortable: true, locked: true},
4823 {header: "Company Name", width: 150, sortable: true},
4824 {header: "Market Cap.", width: 100, sortable: true},
4825 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4826 {header: "Employees", width: 100, sortable: true, resizable: false}
4831 * The config options listed for this class are options which may appear in each
4832 * individual column definition.
4833 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4835 * @param {Object} config An Array of column config objects. See this class's
4836 * config objects for details.
4838 Roo.grid.ColumnModel = function(config){
4840 * The config passed into the constructor
4842 this.config = config;
4845 // if no id, create one
4846 // if the column does not have a dataIndex mapping,
4847 // map it to the order it is in the config
4848 for(var i = 0, len = config.length; i < len; i++){
4850 if(typeof c.dataIndex == "undefined"){
4853 if(typeof c.renderer == "string"){
4854 c.renderer = Roo.util.Format[c.renderer];
4856 if(typeof c.id == "undefined"){
4859 if(c.editor && c.editor.xtype){
4860 c.editor = Roo.factory(c.editor, Roo.grid);
4862 if(c.editor && c.editor.isFormField){
4863 c.editor = new Roo.grid.GridEditor(c.editor);
4865 this.lookup[c.id] = c;
4869 * The width of columns which have no width specified (defaults to 100)
4872 this.defaultWidth = 100;
4875 * Default sortable of columns which have no sortable specified (defaults to false)
4878 this.defaultSortable = false;
4882 * @event widthchange
4883 * Fires when the width of a column changes.
4884 * @param {ColumnModel} this
4885 * @param {Number} columnIndex The column index
4886 * @param {Number} newWidth The new width
4888 "widthchange": true,
4890 * @event headerchange
4891 * Fires when the text of a header changes.
4892 * @param {ColumnModel} this
4893 * @param {Number} columnIndex The column index
4894 * @param {Number} newText The new header text
4896 "headerchange": true,
4898 * @event hiddenchange
4899 * Fires when a column is hidden or "unhidden".
4900 * @param {ColumnModel} this
4901 * @param {Number} columnIndex The column index
4902 * @param {Boolean} hidden true if hidden, false otherwise
4904 "hiddenchange": true,
4906 * @event columnmoved
4907 * Fires when a column is moved.
4908 * @param {ColumnModel} this
4909 * @param {Number} oldIndex
4910 * @param {Number} newIndex
4912 "columnmoved" : true,
4914 * @event columlockchange
4915 * Fires when a column's locked state is changed
4916 * @param {ColumnModel} this
4917 * @param {Number} colIndex
4918 * @param {Boolean} locked true if locked
4920 "columnlockchange" : true
4922 Roo.grid.ColumnModel.superclass.constructor.call(this);
4924 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4926 * @cfg {String} header The header text to display in the Grid view.
4929 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4930 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4931 * specified, the column's index is used as an index into the Record's data Array.
4934 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4935 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4938 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4939 * Defaults to the value of the {@link #defaultSortable} property.
4940 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4943 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4946 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4949 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4952 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4955 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4956 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4957 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4958 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4961 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4964 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4967 * @cfg {String} cursor (Optional)
4970 * @cfg {String} tooltip (Optional)
4973 * Returns the id of the column at the specified index.
4974 * @param {Number} index The column index
4975 * @return {String} the id
4977 getColumnId : function(index){
4978 return this.config[index].id;
4982 * Returns the column for a specified id.
4983 * @param {String} id The column id
4984 * @return {Object} the column
4986 getColumnById : function(id){
4987 return this.lookup[id];
4992 * Returns the column for a specified dataIndex.
4993 * @param {String} dataIndex The column dataIndex
4994 * @return {Object|Boolean} the column or false if not found
4996 getColumnByDataIndex: function(dataIndex){
4997 var index = this.findColumnIndex(dataIndex);
4998 return index > -1 ? this.config[index] : false;
5002 * Returns the index for a specified column id.
5003 * @param {String} id The column id
5004 * @return {Number} the index, or -1 if not found
5006 getIndexById : function(id){
5007 for(var i = 0, len = this.config.length; i < len; i++){
5008 if(this.config[i].id == id){
5016 * Returns the index for a specified column dataIndex.
5017 * @param {String} dataIndex The column dataIndex
5018 * @return {Number} the index, or -1 if not found
5021 findColumnIndex : function(dataIndex){
5022 for(var i = 0, len = this.config.length; i < len; i++){
5023 if(this.config[i].dataIndex == dataIndex){
5031 moveColumn : function(oldIndex, newIndex){
5032 var c = this.config[oldIndex];
5033 this.config.splice(oldIndex, 1);
5034 this.config.splice(newIndex, 0, c);
5035 this.dataMap = null;
5036 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5039 isLocked : function(colIndex){
5040 return this.config[colIndex].locked === true;
5043 setLocked : function(colIndex, value, suppressEvent){
5044 if(this.isLocked(colIndex) == value){
5047 this.config[colIndex].locked = value;
5049 this.fireEvent("columnlockchange", this, colIndex, value);
5053 getTotalLockedWidth : function(){
5055 for(var i = 0; i < this.config.length; i++){
5056 if(this.isLocked(i) && !this.isHidden(i)){
5057 this.totalWidth += this.getColumnWidth(i);
5063 getLockedCount : function(){
5064 for(var i = 0, len = this.config.length; i < len; i++){
5065 if(!this.isLocked(i)){
5072 * Returns the number of columns.
5075 getColumnCount : function(visibleOnly){
5076 if(visibleOnly === true){
5078 for(var i = 0, len = this.config.length; i < len; i++){
5079 if(!this.isHidden(i)){
5085 return this.config.length;
5089 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5090 * @param {Function} fn
5091 * @param {Object} scope (optional)
5092 * @return {Array} result
5094 getColumnsBy : function(fn, scope){
5096 for(var i = 0, len = this.config.length; i < len; i++){
5097 var c = this.config[i];
5098 if(fn.call(scope||this, c, i) === true){
5106 * Returns true if the specified column is sortable.
5107 * @param {Number} col The column index
5110 isSortable : function(col){
5111 if(typeof this.config[col].sortable == "undefined"){
5112 return this.defaultSortable;
5114 return this.config[col].sortable;
5118 * Returns the rendering (formatting) function defined for the column.
5119 * @param {Number} col The column index.
5120 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5122 getRenderer : function(col){
5123 if(!this.config[col].renderer){
5124 return Roo.grid.ColumnModel.defaultRenderer;
5126 return this.config[col].renderer;
5130 * Sets the rendering (formatting) function for a column.
5131 * @param {Number} col The column index
5132 * @param {Function} fn The function to use to process the cell's raw data
5133 * to return HTML markup for the grid view. The render function is called with
5134 * the following parameters:<ul>
5135 * <li>Data value.</li>
5136 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5137 * <li>css A CSS style string to apply to the table cell.</li>
5138 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5139 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5140 * <li>Row index</li>
5141 * <li>Column index</li>
5142 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5144 setRenderer : function(col, fn){
5145 this.config[col].renderer = fn;
5149 * Returns the width for the specified column.
5150 * @param {Number} col The column index
5153 getColumnWidth : function(col){
5154 return this.config[col].width * 1 || this.defaultWidth;
5158 * Sets the width for a column.
5159 * @param {Number} col The column index
5160 * @param {Number} width The new width
5162 setColumnWidth : function(col, width, suppressEvent){
5163 this.config[col].width = width;
5164 this.totalWidth = null;
5166 this.fireEvent("widthchange", this, col, width);
5171 * Returns the total width of all columns.
5172 * @param {Boolean} includeHidden True to include hidden column widths
5175 getTotalWidth : function(includeHidden){
5176 if(!this.totalWidth){
5177 this.totalWidth = 0;
5178 for(var i = 0, len = this.config.length; i < len; i++){
5179 if(includeHidden || !this.isHidden(i)){
5180 this.totalWidth += this.getColumnWidth(i);
5184 return this.totalWidth;
5188 * Returns the header for the specified column.
5189 * @param {Number} col The column index
5192 getColumnHeader : function(col){
5193 return this.config[col].header;
5197 * Sets the header for a column.
5198 * @param {Number} col The column index
5199 * @param {String} header The new header
5201 setColumnHeader : function(col, header){
5202 this.config[col].header = header;
5203 this.fireEvent("headerchange", this, col, header);
5207 * Returns the tooltip for the specified column.
5208 * @param {Number} col The column index
5211 getColumnTooltip : function(col){
5212 return this.config[col].tooltip;
5215 * Sets the tooltip for a column.
5216 * @param {Number} col The column index
5217 * @param {String} tooltip The new tooltip
5219 setColumnTooltip : function(col, tooltip){
5220 this.config[col].tooltip = tooltip;
5224 * Returns the dataIndex for the specified column.
5225 * @param {Number} col The column index
5228 getDataIndex : function(col){
5229 return this.config[col].dataIndex;
5233 * Sets the dataIndex for a column.
5234 * @param {Number} col The column index
5235 * @param {Number} dataIndex The new dataIndex
5237 setDataIndex : function(col, dataIndex){
5238 this.config[col].dataIndex = dataIndex;
5244 * Returns true if the cell is editable.
5245 * @param {Number} colIndex The column index
5246 * @param {Number} rowIndex The row index
5249 isCellEditable : function(colIndex, rowIndex){
5250 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5254 * Returns the editor defined for the cell/column.
5255 * return false or null to disable editing.
5256 * @param {Number} colIndex The column index
5257 * @param {Number} rowIndex The row index
5260 getCellEditor : function(colIndex, rowIndex){
5261 return this.config[colIndex].editor;
5265 * Sets if a column is editable.
5266 * @param {Number} col The column index
5267 * @param {Boolean} editable True if the column is editable
5269 setEditable : function(col, editable){
5270 this.config[col].editable = editable;
5275 * Returns true if the column is hidden.
5276 * @param {Number} colIndex The column index
5279 isHidden : function(colIndex){
5280 return this.config[colIndex].hidden;
5285 * Returns true if the column width cannot be changed
5287 isFixed : function(colIndex){
5288 return this.config[colIndex].fixed;
5292 * Returns true if the column can be resized
5295 isResizable : function(colIndex){
5296 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5299 * Sets if a column is hidden.
5300 * @param {Number} colIndex The column index
5301 * @param {Boolean} hidden True if the column is hidden
5303 setHidden : function(colIndex, hidden){
5304 this.config[colIndex].hidden = hidden;
5305 this.totalWidth = null;
5306 this.fireEvent("hiddenchange", this, colIndex, hidden);
5310 * Sets the editor for a column.
5311 * @param {Number} col The column index
5312 * @param {Object} editor The editor object
5314 setEditor : function(col, editor){
5315 this.config[col].editor = editor;
5319 Roo.grid.ColumnModel.defaultRenderer = function(value){
5320 if(typeof value == "string" && value.length < 1){
5326 // Alias for backwards compatibility
5327 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5330 * Ext JS Library 1.1.1
5331 * Copyright(c) 2006-2007, Ext JS, LLC.
5333 * Originally Released Under LGPL - original licence link has changed is not relivant.
5336 * <script type="text/javascript">
5340 * @class Roo.LoadMask
5341 * A simple utility class for generically masking elements while loading data. If the element being masked has
5342 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5343 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5344 * element's UpdateManager load indicator and will be destroyed after the initial load.
5346 * Create a new LoadMask
5347 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5348 * @param {Object} config The config object
5350 Roo.LoadMask = function(el, config){
5351 this.el = Roo.get(el);
5352 Roo.apply(this, config);
5354 this.store.on('beforeload', this.onBeforeLoad, this);
5355 this.store.on('load', this.onLoad, this);
5356 this.store.on('loadexception', this.onLoadException, this);
5357 this.removeMask = false;
5359 var um = this.el.getUpdateManager();
5360 um.showLoadIndicator = false; // disable the default indicator
5361 um.on('beforeupdate', this.onBeforeLoad, this);
5362 um.on('update', this.onLoad, this);
5363 um.on('failure', this.onLoad, this);
5364 this.removeMask = true;
5368 Roo.LoadMask.prototype = {
5370 * @cfg {Boolean} removeMask
5371 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5372 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5376 * The text to display in a centered loading message box (defaults to 'Loading...')
5380 * @cfg {String} msgCls
5381 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5383 msgCls : 'x-mask-loading',
5386 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5392 * Disables the mask to prevent it from being displayed
5394 disable : function(){
5395 this.disabled = true;
5399 * Enables the mask so that it can be displayed
5401 enable : function(){
5402 this.disabled = false;
5405 onLoadException : function()
5409 if (typeof(arguments[3]) != 'undefined') {
5410 Roo.MessageBox.alert("Error loading",arguments[3]);
5414 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5415 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5424 this.el.unmask(this.removeMask);
5429 this.el.unmask(this.removeMask);
5433 onBeforeLoad : function(){
5435 this.el.mask(this.msg, this.msgCls);
5440 destroy : function(){
5442 this.store.un('beforeload', this.onBeforeLoad, this);
5443 this.store.un('load', this.onLoad, this);
5444 this.store.un('loadexception', this.onLoadException, this);
5446 var um = this.el.getUpdateManager();
5447 um.un('beforeupdate', this.onBeforeLoad, this);
5448 um.un('update', this.onLoad, this);
5449 um.un('failure', this.onLoad, this);
5460 * @class Roo.bootstrap.Table
5461 * @extends Roo.bootstrap.Component
5462 * Bootstrap Table class
5463 * @cfg {String} cls table class
5464 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5465 * @cfg {String} bgcolor Specifies the background color for a table
5466 * @cfg {Number} border Specifies whether the table cells should have borders or not
5467 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5468 * @cfg {Number} cellspacing Specifies the space between cells
5469 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5470 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5471 * @cfg {String} sortable Specifies that the table should be sortable
5472 * @cfg {String} summary Specifies a summary of the content of a table
5473 * @cfg {Number} width Specifies the width of a table
5474 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5476 * @cfg {boolean} striped Should the rows be alternative striped
5477 * @cfg {boolean} bordered Add borders to the table
5478 * @cfg {boolean} hover Add hover highlighting
5479 * @cfg {boolean} condensed Format condensed
5480 * @cfg {boolean} responsive Format condensed
5481 * @cfg {Boolean} loadMask (true|false) default false
5482 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5483 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5484 * @cfg {Boolean} rowSelection (true|false) default false
5485 * @cfg {Boolean} cellSelection (true|false) default false
5486 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5490 * Create a new Table
5491 * @param {Object} config The config object
5494 Roo.bootstrap.Table = function(config){
5495 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5498 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5499 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5500 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5501 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5505 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5506 this.sm = this.selModel;
5507 this.sm.xmodule = this.xmodule || false;
5509 if (this.cm && typeof(this.cm.config) == 'undefined') {
5510 this.colModel = new Roo.grid.ColumnModel(this.cm);
5511 this.cm = this.colModel;
5512 this.cm.xmodule = this.xmodule || false;
5515 this.store= Roo.factory(this.store, Roo.data);
5516 this.ds = this.store;
5517 this.ds.xmodule = this.xmodule || false;
5520 if (this.footer && this.store) {
5521 this.footer.dataSource = this.ds;
5522 this.footer = Roo.factory(this.footer);
5529 * Fires when a cell is clicked
5530 * @param {Roo.bootstrap.Table} this
5531 * @param {Roo.Element} el
5532 * @param {Number} rowIndex
5533 * @param {Number} columnIndex
5534 * @param {Roo.EventObject} e
5538 * @event celldblclick
5539 * Fires when a cell is double clicked
5540 * @param {Roo.bootstrap.Table} this
5541 * @param {Roo.Element} el
5542 * @param {Number} rowIndex
5543 * @param {Number} columnIndex
5544 * @param {Roo.EventObject} e
5546 "celldblclick" : true,
5549 * Fires when a row is clicked
5550 * @param {Roo.bootstrap.Table} this
5551 * @param {Roo.Element} el
5552 * @param {Number} rowIndex
5553 * @param {Roo.EventObject} e
5557 * @event rowdblclick
5558 * Fires when a row is double clicked
5559 * @param {Roo.bootstrap.Table} this
5560 * @param {Roo.Element} el
5561 * @param {Number} rowIndex
5562 * @param {Roo.EventObject} e
5564 "rowdblclick" : true,
5567 * Fires when a mouseover occur
5568 * @param {Roo.bootstrap.Table} this
5569 * @param {Roo.Element} el
5570 * @param {Number} rowIndex
5571 * @param {Number} columnIndex
5572 * @param {Roo.EventObject} e
5577 * Fires when a mouseout occur
5578 * @param {Roo.bootstrap.Table} this
5579 * @param {Roo.Element} el
5580 * @param {Number} rowIndex
5581 * @param {Number} columnIndex
5582 * @param {Roo.EventObject} e
5587 * Fires when a row is rendered, so you can change add a style to it.
5588 * @param {Roo.bootstrap.Table} this
5589 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5593 * @event rowsrendered
5594 * Fires when all the rows have been rendered
5595 * @param {Roo.bootstrap.Table} this
5597 'rowsrendered' : true
5602 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5627 rowSelection : false,
5628 cellSelection : false,
5631 // Roo.Element - the tbody
5634 getAutoCreate : function(){
5635 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5644 cfg.cls += ' table-striped';
5648 cfg.cls += ' table-hover';
5650 if (this.bordered) {
5651 cfg.cls += ' table-bordered';
5653 if (this.condensed) {
5654 cfg.cls += ' table-condensed';
5656 if (this.responsive) {
5657 cfg.cls += ' table-responsive';
5661 cfg.cls+= ' ' +this.cls;
5664 // this lot should be simplifed...
5667 cfg.align=this.align;
5670 cfg.bgcolor=this.bgcolor;
5673 cfg.border=this.border;
5675 if (this.cellpadding) {
5676 cfg.cellpadding=this.cellpadding;
5678 if (this.cellspacing) {
5679 cfg.cellspacing=this.cellspacing;
5682 cfg.frame=this.frame;
5685 cfg.rules=this.rules;
5687 if (this.sortable) {
5688 cfg.sortable=this.sortable;
5691 cfg.summary=this.summary;
5694 cfg.width=this.width;
5697 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5700 if(this.store || this.cm){
5701 if(this.headerShow){
5702 cfg.cn.push(this.renderHeader());
5705 cfg.cn.push(this.renderBody());
5707 if(this.footerShow){
5708 cfg.cn.push(this.renderFooter());
5711 cfg.cls+= ' TableGrid';
5714 return { cn : [ cfg ] };
5717 initEvents : function()
5719 if(!this.store || !this.cm){
5723 //Roo.log('initEvents with ds!!!!');
5725 this.mainBody = this.el.select('tbody', true).first();
5730 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5731 e.on('click', _this.sort, _this);
5734 this.el.on("click", this.onClick, this);
5735 this.el.on("dblclick", this.onDblClick, this);
5737 // why is this done????? = it breaks dialogs??
5738 //this.parent().el.setStyle('position', 'relative');
5742 this.footer.parentId = this.id;
5743 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5746 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5748 this.store.on('load', this.onLoad, this);
5749 this.store.on('beforeload', this.onBeforeLoad, this);
5750 this.store.on('update', this.onUpdate, this);
5751 this.store.on('add', this.onAdd, this);
5755 onMouseover : function(e, el)
5757 var cell = Roo.get(el);
5763 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5764 cell = cell.findParent('td', false, true);
5767 var row = cell.findParent('tr', false, true);
5768 var cellIndex = cell.dom.cellIndex;
5769 var rowIndex = row.dom.rowIndex - 1; // start from 0
5771 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5775 onMouseout : function(e, el)
5777 var cell = Roo.get(el);
5783 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5784 cell = cell.findParent('td', false, true);
5787 var row = cell.findParent('tr', false, true);
5788 var cellIndex = cell.dom.cellIndex;
5789 var rowIndex = row.dom.rowIndex - 1; // start from 0
5791 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5795 onClick : function(e, el)
5797 var cell = Roo.get(el);
5799 if(!cell || (!this.cellSelection && !this.rowSelection)){
5803 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5804 cell = cell.findParent('td', false, true);
5807 if(!cell || typeof(cell) == 'undefined'){
5811 var row = cell.findParent('tr', false, true);
5813 if(!row || typeof(row) == 'undefined'){
5817 var cellIndex = cell.dom.cellIndex;
5818 var rowIndex = this.getRowIndex(row);
5820 // why??? - should these not be based on SelectionModel?
5821 if(this.cellSelection){
5822 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5825 if(this.rowSelection){
5826 this.fireEvent('rowclick', this, row, rowIndex, e);
5832 onDblClick : function(e,el)
5834 var cell = Roo.get(el);
5836 if(!cell || (!this.CellSelection && !this.RowSelection)){
5840 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5841 cell = cell.findParent('td', false, true);
5844 if(!cell || typeof(cell) == 'undefined'){
5848 var row = cell.findParent('tr', false, true);
5850 if(!row || typeof(row) == 'undefined'){
5854 var cellIndex = cell.dom.cellIndex;
5855 var rowIndex = this.getRowIndex(row);
5857 if(this.CellSelection){
5858 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5861 if(this.RowSelection){
5862 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5866 sort : function(e,el)
5868 var col = Roo.get(el);
5870 if(!col.hasClass('sortable')){
5874 var sort = col.attr('sort');
5877 if(col.hasClass('glyphicon-arrow-up')){
5881 this.store.sortInfo = {field : sort, direction : dir};
5884 Roo.log("calling footer first");
5885 this.footer.onClick('first');
5888 this.store.load({ params : { start : 0 } });
5892 renderHeader : function()
5901 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5903 var config = cm.config[i];
5908 html: cm.getColumnHeader(i)
5913 if(typeof(config.lgHeader) != 'undefined'){
5914 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5917 if(typeof(config.mdHeader) != 'undefined'){
5918 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5921 if(typeof(config.smHeader) != 'undefined'){
5922 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5925 if(typeof(config.xsHeader) != 'undefined'){
5926 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5933 if(typeof(config.tooltip) != 'undefined'){
5934 c.tooltip = config.tooltip;
5937 if(typeof(config.colspan) != 'undefined'){
5938 c.colspan = config.colspan;
5941 if(typeof(config.hidden) != 'undefined' && config.hidden){
5942 c.style += ' display:none;';
5945 if(typeof(config.dataIndex) != 'undefined'){
5946 c.sort = config.dataIndex;
5949 if(typeof(config.sortable) != 'undefined' && config.sortable){
5953 if(typeof(config.align) != 'undefined' && config.align.length){
5954 c.style += ' text-align:' + config.align + ';';
5957 if(typeof(config.width) != 'undefined'){
5958 c.style += ' width:' + config.width + 'px;';
5961 if(typeof(config.cls) != 'undefined'){
5962 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5971 renderBody : function()
5981 colspan : this.cm.getColumnCount()
5991 renderFooter : function()
6001 colspan : this.cm.getColumnCount()
6015 Roo.log('ds onload');
6020 var ds = this.store;
6022 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6023 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6025 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6026 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6029 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6030 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6034 var tbody = this.mainBody;
6036 if(ds.getCount() > 0){
6037 ds.data.each(function(d,rowIndex){
6038 var row = this.renderRow(cm, ds, rowIndex);
6040 tbody.createChild(row);
6044 if(row.cellObjects.length){
6045 Roo.each(row.cellObjects, function(r){
6046 _this.renderCellObject(r);
6053 Roo.each(this.el.select('tbody td', true).elements, function(e){
6054 e.on('mouseover', _this.onMouseover, _this);
6057 Roo.each(this.el.select('tbody td', true).elements, function(e){
6058 e.on('mouseout', _this.onMouseout, _this);
6060 this.fireEvent('rowsrendered', this);
6061 //if(this.loadMask){
6062 // this.maskEl.hide();
6067 onUpdate : function(ds,record)
6069 this.refreshRow(record);
6072 onRemove : function(ds, record, index, isUpdate){
6073 if(isUpdate !== true){
6074 this.fireEvent("beforerowremoved", this, index, record);
6076 var bt = this.mainBody.dom;
6078 var rows = this.el.select('tbody > tr', true).elements;
6080 if(typeof(rows[index]) != 'undefined'){
6081 bt.removeChild(rows[index].dom);
6084 // if(bt.rows[index]){
6085 // bt.removeChild(bt.rows[index]);
6088 if(isUpdate !== true){
6089 //this.stripeRows(index);
6090 //this.syncRowHeights(index, index);
6092 this.fireEvent("rowremoved", this, index, record);
6096 onAdd : function(ds, records, rowIndex)
6098 //Roo.log('on Add called');
6099 // - note this does not handle multiple adding very well..
6100 var bt = this.mainBody.dom;
6101 for (var i =0 ; i < records.length;i++) {
6102 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6103 //Roo.log(records[i]);
6104 //Roo.log(this.store.getAt(rowIndex+i));
6105 this.insertRow(this.store, rowIndex + i, false);
6112 refreshRow : function(record){
6113 var ds = this.store, index;
6114 if(typeof record == 'number'){
6116 record = ds.getAt(index);
6118 index = ds.indexOf(record);
6120 this.insertRow(ds, index, true);
6121 this.onRemove(ds, record, index+1, true);
6122 //this.syncRowHeights(index, index);
6124 this.fireEvent("rowupdated", this, index, record);
6127 insertRow : function(dm, rowIndex, isUpdate){
6130 this.fireEvent("beforerowsinserted", this, rowIndex);
6132 //var s = this.getScrollState();
6133 var row = this.renderRow(this.cm, this.store, rowIndex);
6134 // insert before rowIndex..
6135 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6139 if(row.cellObjects.length){
6140 Roo.each(row.cellObjects, function(r){
6141 _this.renderCellObject(r);
6146 this.fireEvent("rowsinserted", this, rowIndex);
6147 //this.syncRowHeights(firstRow, lastRow);
6148 //this.stripeRows(firstRow);
6155 getRowDom : function(rowIndex)
6157 var rows = this.el.select('tbody > tr', true).elements;
6159 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6162 // returns the object tree for a tr..
6165 renderRow : function(cm, ds, rowIndex)
6168 var d = ds.getAt(rowIndex);
6175 var cellObjects = [];
6177 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6178 var config = cm.config[i];
6180 var renderer = cm.getRenderer(i);
6184 if(typeof(renderer) !== 'undefined'){
6185 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6187 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6188 // and are rendered into the cells after the row is rendered - using the id for the element.
6190 if(typeof(value) === 'object'){
6200 rowIndex : rowIndex,
6205 this.fireEvent('rowclass', this, rowcfg);
6209 cls : rowcfg.rowClass,
6211 html: (typeof(value) === 'object') ? '' : value
6218 if(typeof(config.colspan) != 'undefined'){
6219 td.colspan = config.colspan;
6222 if(typeof(config.hidden) != 'undefined' && config.hidden){
6223 td.style += ' display:none;';
6226 if(typeof(config.align) != 'undefined' && config.align.length){
6227 td.style += ' text-align:' + config.align + ';';
6230 if(typeof(config.width) != 'undefined'){
6231 td.style += ' width:' + config.width + 'px;';
6234 if(typeof(config.cursor) != 'undefined'){
6235 td.style += ' cursor:' + config.cursor + ';';
6238 if(typeof(config.cls) != 'undefined'){
6239 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6246 row.cellObjects = cellObjects;
6254 onBeforeLoad : function()
6256 //Roo.log('ds onBeforeLoad');
6260 //if(this.loadMask){
6261 // this.maskEl.show();
6269 this.el.select('tbody', true).first().dom.innerHTML = '';
6272 * Show or hide a row.
6273 * @param {Number} rowIndex to show or hide
6274 * @param {Boolean} state hide
6276 setRowVisibility : function(rowIndex, state)
6278 var bt = this.mainBody.dom;
6280 var rows = this.el.select('tbody > tr', true).elements;
6282 if(typeof(rows[rowIndex]) == 'undefined'){
6285 rows[rowIndex].dom.style.display = state ? '' : 'none';
6289 getSelectionModel : function(){
6291 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6293 return this.selModel;
6296 * Render the Roo.bootstrap object from renderder
6298 renderCellObject : function(r)
6302 var t = r.cfg.render(r.container);
6305 Roo.each(r.cfg.cn, function(c){
6307 container: t.getChildContainer(),
6310 _this.renderCellObject(child);
6315 getRowIndex : function(row)
6319 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6342 * @class Roo.bootstrap.TableCell
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap TableCell class
6345 * @cfg {String} html cell contain text
6346 * @cfg {String} cls cell class
6347 * @cfg {String} tag cell tag (td|th) default td
6348 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6349 * @cfg {String} align Aligns the content in a cell
6350 * @cfg {String} axis Categorizes cells
6351 * @cfg {String} bgcolor Specifies the background color of a cell
6352 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6353 * @cfg {Number} colspan Specifies the number of columns a cell should span
6354 * @cfg {String} headers Specifies one or more header cells a cell is related to
6355 * @cfg {Number} height Sets the height of a cell
6356 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6357 * @cfg {Number} rowspan Sets the number of rows a cell should span
6358 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6359 * @cfg {String} valign Vertical aligns the content in a cell
6360 * @cfg {Number} width Specifies the width of a cell
6363 * Create a new TableCell
6364 * @param {Object} config The config object
6367 Roo.bootstrap.TableCell = function(config){
6368 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6371 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6391 getAutoCreate : function(){
6392 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6412 cfg.align=this.align
6418 cfg.bgcolor=this.bgcolor
6421 cfg.charoff=this.charoff
6424 cfg.colspan=this.colspan
6427 cfg.headers=this.headers
6430 cfg.height=this.height
6433 cfg.nowrap=this.nowrap
6436 cfg.rowspan=this.rowspan
6439 cfg.scope=this.scope
6442 cfg.valign=this.valign
6445 cfg.width=this.width
6464 * @class Roo.bootstrap.TableRow
6465 * @extends Roo.bootstrap.Component
6466 * Bootstrap TableRow class
6467 * @cfg {String} cls row class
6468 * @cfg {String} align Aligns the content in a table row
6469 * @cfg {String} bgcolor Specifies a background color for a table row
6470 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6471 * @cfg {String} valign Vertical aligns the content in a table row
6474 * Create a new TableRow
6475 * @param {Object} config The config object
6478 Roo.bootstrap.TableRow = function(config){
6479 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6482 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6490 getAutoCreate : function(){
6491 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6501 cfg.align = this.align;
6504 cfg.bgcolor = this.bgcolor;
6507 cfg.charoff = this.charoff;
6510 cfg.valign = this.valign;
6528 * @class Roo.bootstrap.TableBody
6529 * @extends Roo.bootstrap.Component
6530 * Bootstrap TableBody class
6531 * @cfg {String} cls element class
6532 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6533 * @cfg {String} align Aligns the content inside the element
6534 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6535 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6538 * Create a new TableBody
6539 * @param {Object} config The config object
6542 Roo.bootstrap.TableBody = function(config){
6543 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6546 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6554 getAutoCreate : function(){
6555 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6569 cfg.align = this.align;
6572 cfg.charoff = this.charoff;
6575 cfg.valign = this.valign;
6582 // initEvents : function()
6589 // this.store = Roo.factory(this.store, Roo.data);
6590 // this.store.on('load', this.onLoad, this);
6592 // this.store.load();
6596 // onLoad: function ()
6598 // this.fireEvent('load', this);
6608 * Ext JS Library 1.1.1
6609 * Copyright(c) 2006-2007, Ext JS, LLC.
6611 * Originally Released Under LGPL - original licence link has changed is not relivant.
6614 * <script type="text/javascript">
6617 // as we use this in bootstrap.
6618 Roo.namespace('Roo.form');
6620 * @class Roo.form.Action
6621 * Internal Class used to handle form actions
6623 * @param {Roo.form.BasicForm} el The form element or its id
6624 * @param {Object} config Configuration options
6629 // define the action interface
6630 Roo.form.Action = function(form, options){
6632 this.options = options || {};
6635 * Client Validation Failed
6638 Roo.form.Action.CLIENT_INVALID = 'client';
6640 * Server Validation Failed
6643 Roo.form.Action.SERVER_INVALID = 'server';
6645 * Connect to Server Failed
6648 Roo.form.Action.CONNECT_FAILURE = 'connect';
6650 * Reading Data from Server Failed
6653 Roo.form.Action.LOAD_FAILURE = 'load';
6655 Roo.form.Action.prototype = {
6657 failureType : undefined,
6658 response : undefined,
6662 run : function(options){
6667 success : function(response){
6672 handleResponse : function(response){
6676 // default connection failure
6677 failure : function(response){
6679 this.response = response;
6680 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6681 this.form.afterAction(this, false);
6684 processResponse : function(response){
6685 this.response = response;
6686 if(!response.responseText){
6689 this.result = this.handleResponse(response);
6693 // utility functions used internally
6694 getUrl : function(appendParams){
6695 var url = this.options.url || this.form.url || this.form.el.dom.action;
6697 var p = this.getParams();
6699 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6705 getMethod : function(){
6706 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6709 getParams : function(){
6710 var bp = this.form.baseParams;
6711 var p = this.options.params;
6713 if(typeof p == "object"){
6714 p = Roo.urlEncode(Roo.applyIf(p, bp));
6715 }else if(typeof p == 'string' && bp){
6716 p += '&' + Roo.urlEncode(bp);
6719 p = Roo.urlEncode(bp);
6724 createCallback : function(){
6726 success: this.success,
6727 failure: this.failure,
6729 timeout: (this.form.timeout*1000),
6730 upload: this.form.fileUpload ? this.success : undefined
6735 Roo.form.Action.Submit = function(form, options){
6736 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6739 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6742 haveProgress : false,
6743 uploadComplete : false,
6745 // uploadProgress indicator.
6746 uploadProgress : function()
6748 if (!this.form.progressUrl) {
6752 if (!this.haveProgress) {
6753 Roo.MessageBox.progress("Uploading", "Uploading");
6755 if (this.uploadComplete) {
6756 Roo.MessageBox.hide();
6760 this.haveProgress = true;
6762 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6764 var c = new Roo.data.Connection();
6766 url : this.form.progressUrl,
6771 success : function(req){
6772 //console.log(data);
6776 rdata = Roo.decode(req.responseText)
6778 Roo.log("Invalid data from server..");
6782 if (!rdata || !rdata.success) {
6784 Roo.MessageBox.alert(Roo.encode(rdata));
6787 var data = rdata.data;
6789 if (this.uploadComplete) {
6790 Roo.MessageBox.hide();
6795 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6796 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6799 this.uploadProgress.defer(2000,this);
6802 failure: function(data) {
6803 Roo.log('progress url failed ');
6814 // run get Values on the form, so it syncs any secondary forms.
6815 this.form.getValues();
6817 var o = this.options;
6818 var method = this.getMethod();
6819 var isPost = method == 'POST';
6820 if(o.clientValidation === false || this.form.isValid()){
6822 if (this.form.progressUrl) {
6823 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6824 (new Date() * 1) + '' + Math.random());
6829 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6830 form:this.form.el.dom,
6831 url:this.getUrl(!isPost),
6833 params:isPost ? this.getParams() : null,
6834 isUpload: this.form.fileUpload
6837 this.uploadProgress();
6839 }else if (o.clientValidation !== false){ // client validation failed
6840 this.failureType = Roo.form.Action.CLIENT_INVALID;
6841 this.form.afterAction(this, false);
6845 success : function(response)
6847 this.uploadComplete= true;
6848 if (this.haveProgress) {
6849 Roo.MessageBox.hide();
6853 var result = this.processResponse(response);
6854 if(result === true || result.success){
6855 this.form.afterAction(this, true);
6859 this.form.markInvalid(result.errors);
6860 this.failureType = Roo.form.Action.SERVER_INVALID;
6862 this.form.afterAction(this, false);
6864 failure : function(response)
6866 this.uploadComplete= true;
6867 if (this.haveProgress) {
6868 Roo.MessageBox.hide();
6871 this.response = response;
6872 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6873 this.form.afterAction(this, false);
6876 handleResponse : function(response){
6877 if(this.form.errorReader){
6878 var rs = this.form.errorReader.read(response);
6881 for(var i = 0, len = rs.records.length; i < len; i++) {
6882 var r = rs.records[i];
6886 if(errors.length < 1){
6890 success : rs.success,
6896 ret = Roo.decode(response.responseText);
6900 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6910 Roo.form.Action.Load = function(form, options){
6911 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6912 this.reader = this.form.reader;
6915 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6920 Roo.Ajax.request(Roo.apply(
6921 this.createCallback(), {
6922 method:this.getMethod(),
6923 url:this.getUrl(false),
6924 params:this.getParams()
6928 success : function(response){
6930 var result = this.processResponse(response);
6931 if(result === true || !result.success || !result.data){
6932 this.failureType = Roo.form.Action.LOAD_FAILURE;
6933 this.form.afterAction(this, false);
6936 this.form.clearInvalid();
6937 this.form.setValues(result.data);
6938 this.form.afterAction(this, true);
6941 handleResponse : function(response){
6942 if(this.form.reader){
6943 var rs = this.form.reader.read(response);
6944 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6946 success : rs.success,
6950 return Roo.decode(response.responseText);
6954 Roo.form.Action.ACTION_TYPES = {
6955 'load' : Roo.form.Action.Load,
6956 'submit' : Roo.form.Action.Submit
6965 * @class Roo.bootstrap.Form
6966 * @extends Roo.bootstrap.Component
6967 * Bootstrap Form class
6968 * @cfg {String} method GET | POST (default POST)
6969 * @cfg {String} labelAlign top | left (default top)
6970 * @cfg {String} align left | right - for navbars
6971 * @cfg {Boolean} loadMask load mask when submit (default true)
6976 * @param {Object} config The config object
6980 Roo.bootstrap.Form = function(config){
6981 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6984 * @event clientvalidation
6985 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6986 * @param {Form} this
6987 * @param {Boolean} valid true if the form has passed client-side validation
6989 clientvalidation: true,
6991 * @event beforeaction
6992 * Fires before any action is performed. Return false to cancel the action.
6993 * @param {Form} this
6994 * @param {Action} action The action to be performed
6998 * @event actionfailed
6999 * Fires when an action fails.
7000 * @param {Form} this
7001 * @param {Action} action The action that failed
7003 actionfailed : true,
7005 * @event actioncomplete
7006 * Fires when an action is completed.
7007 * @param {Form} this
7008 * @param {Action} action The action that completed
7010 actioncomplete : true
7015 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7018 * @cfg {String} method
7019 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7024 * The URL to use for form actions if one isn't supplied in the action options.
7027 * @cfg {Boolean} fileUpload
7028 * Set to true if this form is a file upload.
7032 * @cfg {Object} baseParams
7033 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7037 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7041 * @cfg {Sting} align (left|right) for navbar forms
7046 activeAction : null,
7049 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7050 * element by passing it or its id or mask the form itself by passing in true.
7053 waitMsgTarget : false,
7057 getAutoCreate : function(){
7061 method : this.method || 'POST',
7062 id : this.id || Roo.id(),
7065 if (this.parent().xtype.match(/^Nav/)) {
7066 cfg.cls = 'navbar-form navbar-' + this.align;
7070 if (this.labelAlign == 'left' ) {
7071 cfg.cls += ' form-horizontal';
7077 initEvents : function()
7079 this.el.on('submit', this.onSubmit, this);
7080 // this was added as random key presses on the form where triggering form submit.
7081 this.el.on('keypress', function(e) {
7082 if (e.getCharCode() != 13) {
7085 // we might need to allow it for textareas.. and some other items.
7086 // check e.getTarget().
7088 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7092 Roo.log("keypress blocked");
7100 onSubmit : function(e){
7105 * Returns true if client-side validation on the form is successful.
7108 isValid : function(){
7109 var items = this.getItems();
7111 items.each(function(f){
7120 * Returns true if any fields in this form have changed since their original load.
7123 isDirty : function(){
7125 var items = this.getItems();
7126 items.each(function(f){
7136 * Performs a predefined action (submit or load) or custom actions you define on this form.
7137 * @param {String} actionName The name of the action type
7138 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7139 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7140 * accept other config options):
7142 Property Type Description
7143 ---------------- --------------- ----------------------------------------------------------------------------------
7144 url String The url for the action (defaults to the form's url)
7145 method String The form method to use (defaults to the form's method, or POST if not defined)
7146 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7147 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7148 validate the form on the client (defaults to false)
7150 * @return {BasicForm} this
7152 doAction : function(action, options){
7153 if(typeof action == 'string'){
7154 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7156 if(this.fireEvent('beforeaction', this, action) !== false){
7157 this.beforeAction(action);
7158 action.run.defer(100, action);
7164 beforeAction : function(action){
7165 var o = action.options;
7168 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7170 // not really supported yet.. ??
7172 //if(this.waitMsgTarget === true){
7173 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7174 //}else if(this.waitMsgTarget){
7175 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7176 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7178 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7184 afterAction : function(action, success){
7185 this.activeAction = null;
7186 var o = action.options;
7188 //if(this.waitMsgTarget === true){
7190 //}else if(this.waitMsgTarget){
7191 // this.waitMsgTarget.unmask();
7193 // Roo.MessageBox.updateProgress(1);
7194 // Roo.MessageBox.hide();
7201 Roo.callback(o.success, o.scope, [this, action]);
7202 this.fireEvent('actioncomplete', this, action);
7206 // failure condition..
7207 // we have a scenario where updates need confirming.
7208 // eg. if a locking scenario exists..
7209 // we look for { errors : { needs_confirm : true }} in the response.
7211 (typeof(action.result) != 'undefined') &&
7212 (typeof(action.result.errors) != 'undefined') &&
7213 (typeof(action.result.errors.needs_confirm) != 'undefined')
7216 Roo.log("not supported yet");
7219 Roo.MessageBox.confirm(
7220 "Change requires confirmation",
7221 action.result.errorMsg,
7226 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7236 Roo.callback(o.failure, o.scope, [this, action]);
7237 // show an error message if no failed handler is set..
7238 if (!this.hasListener('actionfailed')) {
7239 Roo.log("need to add dialog support");
7241 Roo.MessageBox.alert("Error",
7242 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7243 action.result.errorMsg :
7244 "Saving Failed, please check your entries or try again"
7249 this.fireEvent('actionfailed', this, action);
7254 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7255 * @param {String} id The value to search for
7258 findField : function(id){
7259 var items = this.getItems();
7260 var field = items.get(id);
7262 items.each(function(f){
7263 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7270 return field || null;
7273 * Mark fields in this form invalid in bulk.
7274 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7275 * @return {BasicForm} this
7277 markInvalid : function(errors){
7278 if(errors instanceof Array){
7279 for(var i = 0, len = errors.length; i < len; i++){
7280 var fieldError = errors[i];
7281 var f = this.findField(fieldError.id);
7283 f.markInvalid(fieldError.msg);
7289 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7290 field.markInvalid(errors[id]);
7294 //Roo.each(this.childForms || [], function (f) {
7295 // f.markInvalid(errors);
7302 * Set values for fields in this form in bulk.
7303 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7304 * @return {BasicForm} this
7306 setValues : function(values){
7307 if(values instanceof Array){ // array of objects
7308 for(var i = 0, len = values.length; i < len; i++){
7310 var f = this.findField(v.id);
7312 f.setValue(v.value);
7313 if(this.trackResetOnLoad){
7314 f.originalValue = f.getValue();
7318 }else{ // object hash
7321 if(typeof values[id] != 'function' && (field = this.findField(id))){
7323 if (field.setFromData &&
7325 field.displayField &&
7326 // combos' with local stores can
7327 // be queried via setValue()
7328 // to set their value..
7329 (field.store && !field.store.isLocal)
7333 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7334 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7335 field.setFromData(sd);
7338 field.setValue(values[id]);
7342 if(this.trackResetOnLoad){
7343 field.originalValue = field.getValue();
7349 //Roo.each(this.childForms || [], function (f) {
7350 // f.setValues(values);
7357 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7358 * they are returned as an array.
7359 * @param {Boolean} asString
7362 getValues : function(asString){
7363 //if (this.childForms) {
7364 // copy values from the child forms
7365 // Roo.each(this.childForms, function (f) {
7366 // this.setValues(f.getValues());
7372 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7373 if(asString === true){
7376 return Roo.urlDecode(fs);
7380 * Returns the fields in this form as an object with key/value pairs.
7381 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7384 getFieldValues : function(with_hidden)
7386 var items = this.getItems();
7388 items.each(function(f){
7392 var v = f.getValue();
7393 if (f.inputType =='radio') {
7394 if (typeof(ret[f.getName()]) == 'undefined') {
7395 ret[f.getName()] = ''; // empty..
7398 if (!f.el.dom.checked) {
7406 // not sure if this supported any more..
7407 if ((typeof(v) == 'object') && f.getRawValue) {
7408 v = f.getRawValue() ; // dates..
7410 // combo boxes where name != hiddenName...
7411 if (f.name != f.getName()) {
7412 ret[f.name] = f.getRawValue();
7414 ret[f.getName()] = v;
7421 * Clears all invalid messages in this form.
7422 * @return {BasicForm} this
7424 clearInvalid : function(){
7425 var items = this.getItems();
7427 items.each(function(f){
7438 * @return {BasicForm} this
7441 var items = this.getItems();
7442 items.each(function(f){
7446 Roo.each(this.childForms || [], function (f) {
7453 getItems : function()
7455 var r=new Roo.util.MixedCollection(false, function(o){
7456 return o.id || (o.id = Roo.id());
7458 var iter = function(el) {
7465 Roo.each(el.items,function(e) {
7485 * Ext JS Library 1.1.1
7486 * Copyright(c) 2006-2007, Ext JS, LLC.
7488 * Originally Released Under LGPL - original licence link has changed is not relivant.
7491 * <script type="text/javascript">
7494 * @class Roo.form.VTypes
7495 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7498 Roo.form.VTypes = function(){
7499 // closure these in so they are only created once.
7500 var alpha = /^[a-zA-Z_]+$/;
7501 var alphanum = /^[a-zA-Z0-9_]+$/;
7502 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7503 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7505 // All these messages and functions are configurable
7508 * The function used to validate email addresses
7509 * @param {String} value The email address
7511 'email' : function(v){
7512 return email.test(v);
7515 * The error text to display when the email validation function returns false
7518 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7520 * The keystroke filter mask to be applied on email input
7523 'emailMask' : /[a-z0-9_\.\-@]/i,
7526 * The function used to validate URLs
7527 * @param {String} value The URL
7529 'url' : function(v){
7533 * The error text to display when the url validation function returns false
7536 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7539 * The function used to validate alpha values
7540 * @param {String} value The value
7542 'alpha' : function(v){
7543 return alpha.test(v);
7546 * The error text to display when the alpha validation function returns false
7549 'alphaText' : 'This field should only contain letters and _',
7551 * The keystroke filter mask to be applied on alpha input
7554 'alphaMask' : /[a-z_]/i,
7557 * The function used to validate alphanumeric values
7558 * @param {String} value The value
7560 'alphanum' : function(v){
7561 return alphanum.test(v);
7564 * The error text to display when the alphanumeric validation function returns false
7567 'alphanumText' : 'This field should only contain letters, numbers and _',
7569 * The keystroke filter mask to be applied on alphanumeric input
7572 'alphanumMask' : /[a-z0-9_]/i
7582 * @class Roo.bootstrap.Input
7583 * @extends Roo.bootstrap.Component
7584 * Bootstrap Input class
7585 * @cfg {Boolean} disabled is it disabled
7586 * @cfg {String} fieldLabel - the label associated
7587 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7588 * @cfg {String} name name of the input
7589 * @cfg {string} fieldLabel - the label associated
7590 * @cfg {string} inputType - input / file submit ...
7591 * @cfg {string} placeholder - placeholder to put in text.
7592 * @cfg {string} before - input group add on before
7593 * @cfg {string} after - input group add on after
7594 * @cfg {string} size - (lg|sm) or leave empty..
7595 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7596 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7597 * @cfg {Number} md colspan out of 12 for computer-sized screens
7598 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7599 * @cfg {string} value default value of the input
7600 * @cfg {Number} labelWidth set the width of label (0-12)
7601 * @cfg {String} labelAlign (top|left)
7602 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7603 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7605 * @cfg {String} align (left|center|right) Default left
7606 * @cfg {Boolean} forceFeedback (true|false) Default false
7612 * Create a new Input
7613 * @param {Object} config The config object
7616 Roo.bootstrap.Input = function(config){
7617 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7622 * Fires when this field receives input focus.
7623 * @param {Roo.form.Field} this
7628 * Fires when this field loses input focus.
7629 * @param {Roo.form.Field} this
7634 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7635 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7636 * @param {Roo.form.Field} this
7637 * @param {Roo.EventObject} e The event object
7642 * Fires just before the field blurs if the field value has changed.
7643 * @param {Roo.form.Field} this
7644 * @param {Mixed} newValue The new value
7645 * @param {Mixed} oldValue The original value
7650 * Fires after the field has been marked as invalid.
7651 * @param {Roo.form.Field} this
7652 * @param {String} msg The validation message
7657 * Fires after the field has been validated with no errors.
7658 * @param {Roo.form.Field} this
7663 * Fires after the key up
7664 * @param {Roo.form.Field} this
7665 * @param {Roo.EventObject} e The event Object
7671 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7673 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7674 automatic validation (defaults to "keyup").
7676 validationEvent : "keyup",
7678 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7680 validateOnBlur : true,
7682 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7684 validationDelay : 250,
7686 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7688 focusClass : "x-form-focus", // not needed???
7692 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7694 invalidClass : "has-warning",
7697 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7699 validClass : "has-success",
7702 * @cfg {Boolean} hasFeedback (true|false) default true
7707 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7709 invalidFeedbackClass : "glyphicon-warning-sign",
7712 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7714 validFeedbackClass : "glyphicon-ok",
7717 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7719 selectOnFocus : false,
7722 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7726 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7731 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7733 disableKeyFilter : false,
7736 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7740 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7744 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7746 blankText : "This field is required",
7749 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7753 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7755 maxLength : Number.MAX_VALUE,
7757 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7759 minLengthText : "The minimum length for this field is {0}",
7761 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7763 maxLengthText : "The maximum length for this field is {0}",
7767 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7768 * If available, this function will be called only after the basic validators all return true, and will be passed the
7769 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7773 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7774 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7775 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7779 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7783 autocomplete: false,
7802 formatedValue : false,
7803 forceFeedback : false,
7805 parentLabelAlign : function()
7808 while (parent.parent()) {
7809 parent = parent.parent();
7810 if (typeof(parent.labelAlign) !='undefined') {
7811 return parent.labelAlign;
7818 getAutoCreate : function(){
7820 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7826 if(this.inputType != 'hidden'){
7827 cfg.cls = 'form-group' //input-group
7833 type : this.inputType,
7835 cls : 'form-control',
7836 placeholder : this.placeholder || '',
7837 autocomplete : this.autocomplete || 'new-password'
7842 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7845 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7846 input.maxLength = this.maxLength;
7849 if (this.disabled) {
7850 input.disabled=true;
7853 if (this.readOnly) {
7854 input.readonly=true;
7858 input.name = this.name;
7861 input.cls += ' input-' + this.size;
7864 ['xs','sm','md','lg'].map(function(size){
7865 if (settings[size]) {
7866 cfg.cls += ' col-' + size + '-' + settings[size];
7870 var inputblock = input;
7874 cls: 'glyphicon form-control-feedback'
7877 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7880 cls : 'has-feedback',
7888 if (this.before || this.after) {
7891 cls : 'input-group',
7895 if (this.before && typeof(this.before) == 'string') {
7897 inputblock.cn.push({
7899 cls : 'roo-input-before input-group-addon',
7903 if (this.before && typeof(this.before) == 'object') {
7904 this.before = Roo.factory(this.before);
7905 Roo.log(this.before);
7906 inputblock.cn.push({
7908 cls : 'roo-input-before input-group-' +
7909 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7913 inputblock.cn.push(input);
7915 if (this.after && typeof(this.after) == 'string') {
7916 inputblock.cn.push({
7918 cls : 'roo-input-after input-group-addon',
7922 if (this.after && typeof(this.after) == 'object') {
7923 this.after = Roo.factory(this.after);
7924 Roo.log(this.after);
7925 inputblock.cn.push({
7927 cls : 'roo-input-after input-group-' +
7928 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7932 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7933 inputblock.cls += ' has-feedback';
7934 inputblock.cn.push(feedback);
7938 if (align ==='left' && this.fieldLabel.length) {
7939 Roo.log("left and has label");
7945 cls : 'control-label col-sm-' + this.labelWidth,
7946 html : this.fieldLabel
7950 cls : "col-sm-" + (12 - this.labelWidth),
7957 } else if ( this.fieldLabel.length) {
7963 //cls : 'input-group-addon',
7964 html : this.fieldLabel
7974 Roo.log(" no label && no align");
7983 Roo.log('input-parentType: ' + this.parentType);
7985 if (this.parentType === 'Navbar' && this.parent().bar) {
7986 cfg.cls += ' navbar-form';
7994 * return the real input element.
7996 inputEl: function ()
7998 return this.el.select('input.form-control',true).first();
8001 tooltipEl : function()
8003 return this.inputEl();
8006 setDisabled : function(v)
8008 var i = this.inputEl().dom;
8010 i.removeAttribute('disabled');
8014 i.setAttribute('disabled','true');
8016 initEvents : function()
8019 this.inputEl().on("keydown" , this.fireKey, this);
8020 this.inputEl().on("focus", this.onFocus, this);
8021 this.inputEl().on("blur", this.onBlur, this);
8023 this.inputEl().relayEvent('keyup', this);
8025 // reference to original value for reset
8026 this.originalValue = this.getValue();
8027 //Roo.form.TextField.superclass.initEvents.call(this);
8028 if(this.validationEvent == 'keyup'){
8029 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8030 this.inputEl().on('keyup', this.filterValidation, this);
8032 else if(this.validationEvent !== false){
8033 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8036 if(this.selectOnFocus){
8037 this.on("focus", this.preFocus, this);
8040 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8041 this.inputEl().on("keypress", this.filterKeys, this);
8044 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8045 this.el.on("click", this.autoSize, this);
8048 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8049 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8052 if (typeof(this.before) == 'object') {
8053 this.before.render(this.el.select('.roo-input-before',true).first());
8055 if (typeof(this.after) == 'object') {
8056 this.after.render(this.el.select('.roo-input-after',true).first());
8061 filterValidation : function(e){
8062 if(!e.isNavKeyPress()){
8063 this.validationTask.delay(this.validationDelay);
8067 * Validates the field value
8068 * @return {Boolean} True if the value is valid, else false
8070 validate : function(){
8071 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8072 if(this.disabled || this.validateValue(this.getRawValue())){
8083 * Validates a value according to the field's validation rules and marks the field as invalid
8084 * if the validation fails
8085 * @param {Mixed} value The value to validate
8086 * @return {Boolean} True if the value is valid, else false
8088 validateValue : function(value){
8089 if(value.length < 1) { // if it's blank
8090 if(this.allowBlank){
8096 if(value.length < this.minLength){
8099 if(value.length > this.maxLength){
8103 var vt = Roo.form.VTypes;
8104 if(!vt[this.vtype](value, this)){
8108 if(typeof this.validator == "function"){
8109 var msg = this.validator(value);
8115 if(this.regex && !this.regex.test(value)){
8125 fireKey : function(e){
8126 //Roo.log('field ' + e.getKey());
8127 if(e.isNavKeyPress()){
8128 this.fireEvent("specialkey", this, e);
8131 focus : function (selectText){
8133 this.inputEl().focus();
8134 if(selectText === true){
8135 this.inputEl().dom.select();
8141 onFocus : function(){
8142 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8143 // this.el.addClass(this.focusClass);
8146 this.hasFocus = true;
8147 this.startValue = this.getValue();
8148 this.fireEvent("focus", this);
8152 beforeBlur : Roo.emptyFn,
8156 onBlur : function(){
8158 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8159 //this.el.removeClass(this.focusClass);
8161 this.hasFocus = false;
8162 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8165 var v = this.getValue();
8166 if(String(v) !== String(this.startValue)){
8167 this.fireEvent('change', this, v, this.startValue);
8169 this.fireEvent("blur", this);
8173 * Resets the current field value to the originally loaded value and clears any validation messages
8176 this.setValue(this.originalValue);
8180 * Returns the name of the field
8181 * @return {Mixed} name The name field
8183 getName: function(){
8187 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8188 * @return {Mixed} value The field value
8190 getValue : function(){
8192 var v = this.inputEl().getValue();
8197 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8198 * @return {Mixed} value The field value
8200 getRawValue : function(){
8201 var v = this.inputEl().getValue();
8207 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8208 * @param {Mixed} value The value to set
8210 setRawValue : function(v){
8211 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8214 selectText : function(start, end){
8215 var v = this.getRawValue();
8217 start = start === undefined ? 0 : start;
8218 end = end === undefined ? v.length : end;
8219 var d = this.inputEl().dom;
8220 if(d.setSelectionRange){
8221 d.setSelectionRange(start, end);
8222 }else if(d.createTextRange){
8223 var range = d.createTextRange();
8224 range.moveStart("character", start);
8225 range.moveEnd("character", v.length-end);
8232 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8233 * @param {Mixed} value The value to set
8235 setValue : function(v){
8238 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8244 processValue : function(value){
8245 if(this.stripCharsRe){
8246 var newValue = value.replace(this.stripCharsRe, '');
8247 if(newValue !== value){
8248 this.setRawValue(newValue);
8255 preFocus : function(){
8257 if(this.selectOnFocus){
8258 this.inputEl().dom.select();
8261 filterKeys : function(e){
8263 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8266 var c = e.getCharCode(), cc = String.fromCharCode(c);
8267 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8270 if(!this.maskRe.test(cc)){
8275 * Clear any invalid styles/messages for this field
8277 clearInvalid : function(){
8279 if(!this.el || this.preventMark){ // not rendered
8282 this.el.removeClass(this.invalidClass);
8284 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8286 var feedback = this.el.select('.form-control-feedback', true).first();
8289 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8294 this.fireEvent('valid', this);
8298 * Mark this field as valid
8300 markValid : function(){
8301 if(!this.el || this.preventMark){ // not rendered
8305 this.el.removeClass([this.invalidClass, this.validClass]);
8307 var feedback = this.el.select('.form-control-feedback', true).first();
8310 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8313 if(this.disabled || this.allowBlank){
8317 this.el.addClass(this.validClass);
8319 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8321 var feedback = this.el.select('.form-control-feedback', true).first();
8324 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8325 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8330 this.fireEvent('valid', this);
8334 * Mark this field as invalid
8335 * @param {String} msg The validation message
8337 markInvalid : function(msg)
8339 if(!this.el || this.preventMark){ // not rendered
8343 this.el.removeClass([this.invalidClass, this.validClass]);
8345 var feedback = this.el.select('.form-control-feedback', true).first();
8348 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8351 if(this.disabled || this.allowBlank){
8355 this.el.addClass(this.invalidClass);
8357 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8359 var feedback = this.el.select('.form-control-feedback', true).first();
8362 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8364 if(this.getValue().length || this.forceFeedback){
8365 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8372 this.fireEvent('invalid', this, msg);
8375 SafariOnKeyDown : function(event)
8377 // this is a workaround for a password hang bug on chrome/ webkit.
8379 var isSelectAll = false;
8381 if(this.inputEl().dom.selectionEnd > 0){
8382 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8384 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8385 event.preventDefault();
8390 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8392 event.preventDefault();
8393 // this is very hacky as keydown always get's upper case.
8395 var cc = String.fromCharCode(event.getCharCode());
8396 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8400 adjustWidth : function(tag, w){
8401 tag = tag.toLowerCase();
8402 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8403 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8407 if(tag == 'textarea'){
8410 }else if(Roo.isOpera){
8414 if(tag == 'textarea'){
8433 * @class Roo.bootstrap.TextArea
8434 * @extends Roo.bootstrap.Input
8435 * Bootstrap TextArea class
8436 * @cfg {Number} cols Specifies the visible width of a text area
8437 * @cfg {Number} rows Specifies the visible number of lines in a text area
8438 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8439 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8440 * @cfg {string} html text
8443 * Create a new TextArea
8444 * @param {Object} config The config object
8447 Roo.bootstrap.TextArea = function(config){
8448 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8452 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8462 getAutoCreate : function(){
8464 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8475 value : this.value || '',
8476 html: this.html || '',
8477 cls : 'form-control',
8478 placeholder : this.placeholder || ''
8482 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8483 input.maxLength = this.maxLength;
8487 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8491 input.cols = this.cols;
8494 if (this.readOnly) {
8495 input.readonly = true;
8499 input.name = this.name;
8503 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8507 ['xs','sm','md','lg'].map(function(size){
8508 if (settings[size]) {
8509 cfg.cls += ' col-' + size + '-' + settings[size];
8513 var inputblock = input;
8515 if(this.hasFeedback && !this.allowBlank){
8519 cls: 'glyphicon form-control-feedback'
8523 cls : 'has-feedback',
8532 if (this.before || this.after) {
8535 cls : 'input-group',
8539 inputblock.cn.push({
8541 cls : 'input-group-addon',
8546 inputblock.cn.push(input);
8548 if(this.hasFeedback && !this.allowBlank){
8549 inputblock.cls += ' has-feedback';
8550 inputblock.cn.push(feedback);
8554 inputblock.cn.push({
8556 cls : 'input-group-addon',
8563 if (align ==='left' && this.fieldLabel.length) {
8564 Roo.log("left and has label");
8570 cls : 'control-label col-sm-' + this.labelWidth,
8571 html : this.fieldLabel
8575 cls : "col-sm-" + (12 - this.labelWidth),
8582 } else if ( this.fieldLabel.length) {
8588 //cls : 'input-group-addon',
8589 html : this.fieldLabel
8599 Roo.log(" no label && no align");
8609 if (this.disabled) {
8610 input.disabled=true;
8617 * return the real textarea element.
8619 inputEl: function ()
8621 return this.el.select('textarea.form-control',true).first();
8629 * trigger field - base class for combo..
8634 * @class Roo.bootstrap.TriggerField
8635 * @extends Roo.bootstrap.Input
8636 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8637 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8638 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8639 * for which you can provide a custom implementation. For example:
8641 var trigger = new Roo.bootstrap.TriggerField();
8642 trigger.onTriggerClick = myTriggerFn;
8643 trigger.applyTo('my-field');
8646 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8647 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8648 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8649 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8650 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8653 * Create a new TriggerField.
8654 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8655 * to the base TextField)
8657 Roo.bootstrap.TriggerField = function(config){
8658 this.mimicing = false;
8659 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8662 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8664 * @cfg {String} triggerClass A CSS class to apply to the trigger
8667 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8672 * @cfg {Boolean} removable (true|false) special filter default false
8676 /** @cfg {Boolean} grow @hide */
8677 /** @cfg {Number} growMin @hide */
8678 /** @cfg {Number} growMax @hide */
8684 autoSize: Roo.emptyFn,
8691 actionMode : 'wrap',
8696 getAutoCreate : function(){
8698 var align = this.labelAlign || this.parentLabelAlign();
8703 cls: 'form-group' //input-group
8710 type : this.inputType,
8711 cls : 'form-control',
8712 autocomplete: 'new-password',
8713 placeholder : this.placeholder || ''
8717 input.name = this.name;
8720 input.cls += ' input-' + this.size;
8723 if (this.disabled) {
8724 input.disabled=true;
8727 var inputblock = input;
8729 if(this.hasFeedback && !this.allowBlank){
8733 cls: 'glyphicon form-control-feedback'
8736 if(this.removable && !this.editable && !this.tickable){
8738 cls : 'has-feedback',
8744 cls : 'roo-combo-removable-btn close'
8751 cls : 'has-feedback',
8760 if(this.removable && !this.editable && !this.tickable){
8762 cls : 'roo-removable',
8768 cls : 'roo-combo-removable-btn close'
8775 if (this.before || this.after) {
8778 cls : 'input-group',
8782 inputblock.cn.push({
8784 cls : 'input-group-addon',
8789 inputblock.cn.push(input);
8791 if(this.hasFeedback && !this.allowBlank){
8792 inputblock.cls += ' has-feedback';
8793 inputblock.cn.push(feedback);
8797 inputblock.cn.push({
8799 cls : 'input-group-addon',
8812 cls: 'form-hidden-field'
8820 Roo.log('multiple');
8828 cls: 'form-hidden-field'
8832 cls: 'select2-choices',
8836 cls: 'select2-search-field',
8849 cls: 'select2-container input-group',
8854 // cls: 'typeahead typeahead-long dropdown-menu',
8855 // style: 'display:none'
8860 if(!this.multiple && this.showToggleBtn){
8866 if (this.caret != false) {
8869 cls: 'fa fa-' + this.caret
8876 cls : 'input-group-addon btn dropdown-toggle',
8881 cls: 'combobox-clear',
8895 combobox.cls += ' select2-container-multi';
8898 if (align ==='left' && this.fieldLabel.length) {
8900 Roo.log("left and has label");
8906 cls : 'control-label col-sm-' + this.labelWidth,
8907 html : this.fieldLabel
8911 cls : "col-sm-" + (12 - this.labelWidth),
8918 } else if ( this.fieldLabel.length) {
8924 //cls : 'input-group-addon',
8925 html : this.fieldLabel
8935 Roo.log(" no label && no align");
8942 ['xs','sm','md','lg'].map(function(size){
8943 if (settings[size]) {
8944 cfg.cls += ' col-' + size + '-' + settings[size];
8955 onResize : function(w, h){
8956 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8957 // if(typeof w == 'number'){
8958 // var x = w - this.trigger.getWidth();
8959 // this.inputEl().setWidth(this.adjustWidth('input', x));
8960 // this.trigger.setStyle('left', x+'px');
8965 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8968 getResizeEl : function(){
8969 return this.inputEl();
8973 getPositionEl : function(){
8974 return this.inputEl();
8978 alignErrorIcon : function(){
8979 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8983 initEvents : function(){
8987 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8988 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8989 if(!this.multiple && this.showToggleBtn){
8990 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8991 if(this.hideTrigger){
8992 this.trigger.setDisplayed(false);
8994 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8998 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9001 if(this.removable && !this.editable && !this.tickable){
9002 var close = this.closeTriggerEl();
9005 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9006 close.on('click', this.removeBtnClick, this, close);
9010 //this.trigger.addClassOnOver('x-form-trigger-over');
9011 //this.trigger.addClassOnClick('x-form-trigger-click');
9014 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9018 closeTriggerEl : function()
9020 var close = this.el.select('.roo-combo-removable-btn', true).first();
9021 return close ? close : false;
9024 removeBtnClick : function(e, h, el)
9028 if(this.fireEvent("remove", this) !== false){
9033 createList : function()
9035 this.list = Roo.get(document.body).createChild({
9037 cls: 'typeahead typeahead-long dropdown-menu',
9038 style: 'display:none'
9041 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9046 initTrigger : function(){
9051 onDestroy : function(){
9053 this.trigger.removeAllListeners();
9054 // this.trigger.remove();
9057 // this.wrap.remove();
9059 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9063 onFocus : function(){
9064 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9067 this.wrap.addClass('x-trigger-wrap-focus');
9068 this.mimicing = true;
9069 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9070 if(this.monitorTab){
9071 this.el.on("keydown", this.checkTab, this);
9078 checkTab : function(e){
9079 if(e.getKey() == e.TAB){
9085 onBlur : function(){
9090 mimicBlur : function(e, t){
9092 if(!this.wrap.contains(t) && this.validateBlur()){
9099 triggerBlur : function(){
9100 this.mimicing = false;
9101 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9102 if(this.monitorTab){
9103 this.el.un("keydown", this.checkTab, this);
9105 //this.wrap.removeClass('x-trigger-wrap-focus');
9106 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9110 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9111 validateBlur : function(e, t){
9116 onDisable : function(){
9117 this.inputEl().dom.disabled = true;
9118 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9120 // this.wrap.addClass('x-item-disabled');
9125 onEnable : function(){
9126 this.inputEl().dom.disabled = false;
9127 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9129 // this.el.removeClass('x-item-disabled');
9134 onShow : function(){
9135 var ae = this.getActionEl();
9138 ae.dom.style.display = '';
9139 ae.dom.style.visibility = 'visible';
9145 onHide : function(){
9146 var ae = this.getActionEl();
9147 ae.dom.style.display = 'none';
9151 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9152 * by an implementing function.
9154 * @param {EventObject} e
9156 onTriggerClick : Roo.emptyFn
9160 * Ext JS Library 1.1.1
9161 * Copyright(c) 2006-2007, Ext JS, LLC.
9163 * Originally Released Under LGPL - original licence link has changed is not relivant.
9166 * <script type="text/javascript">
9171 * @class Roo.data.SortTypes
9173 * Defines the default sorting (casting?) comparison functions used when sorting data.
9175 Roo.data.SortTypes = {
9177 * Default sort that does nothing
9178 * @param {Mixed} s The value being converted
9179 * @return {Mixed} The comparison value
9186 * The regular expression used to strip tags
9190 stripTagsRE : /<\/?[^>]+>/gi,
9193 * Strips all HTML tags to sort on text only
9194 * @param {Mixed} s The value being converted
9195 * @return {String} The comparison value
9197 asText : function(s){
9198 return String(s).replace(this.stripTagsRE, "");
9202 * Strips all HTML tags to sort on text only - Case insensitive
9203 * @param {Mixed} s The value being converted
9204 * @return {String} The comparison value
9206 asUCText : function(s){
9207 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9211 * Case insensitive string
9212 * @param {Mixed} s The value being converted
9213 * @return {String} The comparison value
9215 asUCString : function(s) {
9216 return String(s).toUpperCase();
9221 * @param {Mixed} s The value being converted
9222 * @return {Number} The comparison value
9224 asDate : function(s) {
9228 if(s instanceof Date){
9231 return Date.parse(String(s));
9236 * @param {Mixed} s The value being converted
9237 * @return {Float} The comparison value
9239 asFloat : function(s) {
9240 var val = parseFloat(String(s).replace(/,/g, ""));
9241 if(isNaN(val)) val = 0;
9247 * @param {Mixed} s The value being converted
9248 * @return {Number} The comparison value
9250 asInt : function(s) {
9251 var val = parseInt(String(s).replace(/,/g, ""));
9252 if(isNaN(val)) val = 0;
9257 * Ext JS Library 1.1.1
9258 * Copyright(c) 2006-2007, Ext JS, LLC.
9260 * Originally Released Under LGPL - original licence link has changed is not relivant.
9263 * <script type="text/javascript">
9267 * @class Roo.data.Record
9268 * Instances of this class encapsulate both record <em>definition</em> information, and record
9269 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9270 * to access Records cached in an {@link Roo.data.Store} object.<br>
9272 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9273 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9276 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9278 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9279 * {@link #create}. The parameters are the same.
9280 * @param {Array} data An associative Array of data values keyed by the field name.
9281 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9282 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9283 * not specified an integer id is generated.
9285 Roo.data.Record = function(data, id){
9286 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9291 * Generate a constructor for a specific record layout.
9292 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9293 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9294 * Each field definition object may contain the following properties: <ul>
9295 * <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,
9296 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9297 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9298 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9299 * is being used, then this is a string containing the javascript expression to reference the data relative to
9300 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9301 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9302 * this may be omitted.</p></li>
9303 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9304 * <ul><li>auto (Default, implies no conversion)</li>
9309 * <li>date</li></ul></p></li>
9310 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9311 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9312 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9313 * by the Reader into an object that will be stored in the Record. It is passed the
9314 * following parameters:<ul>
9315 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9317 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9319 * <br>usage:<br><pre><code>
9320 var TopicRecord = Roo.data.Record.create(
9321 {name: 'title', mapping: 'topic_title'},
9322 {name: 'author', mapping: 'username'},
9323 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9324 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9325 {name: 'lastPoster', mapping: 'user2'},
9326 {name: 'excerpt', mapping: 'post_text'}
9329 var myNewRecord = new TopicRecord({
9330 title: 'Do my job please',
9333 lastPost: new Date(),
9334 lastPoster: 'Animal',
9335 excerpt: 'No way dude!'
9337 myStore.add(myNewRecord);
9342 Roo.data.Record.create = function(o){
9344 f.superclass.constructor.apply(this, arguments);
9346 Roo.extend(f, Roo.data.Record);
9347 var p = f.prototype;
9348 p.fields = new Roo.util.MixedCollection(false, function(field){
9351 for(var i = 0, len = o.length; i < len; i++){
9352 p.fields.add(new Roo.data.Field(o[i]));
9354 f.getField = function(name){
9355 return p.fields.get(name);
9360 Roo.data.Record.AUTO_ID = 1000;
9361 Roo.data.Record.EDIT = 'edit';
9362 Roo.data.Record.REJECT = 'reject';
9363 Roo.data.Record.COMMIT = 'commit';
9365 Roo.data.Record.prototype = {
9367 * Readonly flag - true if this record has been modified.
9376 join : function(store){
9381 * Set the named field to the specified value.
9382 * @param {String} name The name of the field to set.
9383 * @param {Object} value The value to set the field to.
9385 set : function(name, value){
9386 if(this.data[name] == value){
9393 if(typeof this.modified[name] == 'undefined'){
9394 this.modified[name] = this.data[name];
9396 this.data[name] = value;
9397 if(!this.editing && this.store){
9398 this.store.afterEdit(this);
9403 * Get the value of the named field.
9404 * @param {String} name The name of the field to get the value of.
9405 * @return {Object} The value of the field.
9407 get : function(name){
9408 return this.data[name];
9412 beginEdit : function(){
9413 this.editing = true;
9418 cancelEdit : function(){
9419 this.editing = false;
9420 delete this.modified;
9424 endEdit : function(){
9425 this.editing = false;
9426 if(this.dirty && this.store){
9427 this.store.afterEdit(this);
9432 * Usually called by the {@link Roo.data.Store} which owns the Record.
9433 * Rejects all changes made to the Record since either creation, or the last commit operation.
9434 * Modified fields are reverted to their original values.
9436 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9437 * of reject operations.
9439 reject : function(){
9440 var m = this.modified;
9442 if(typeof m[n] != "function"){
9443 this.data[n] = m[n];
9447 delete this.modified;
9448 this.editing = false;
9450 this.store.afterReject(this);
9455 * Usually called by the {@link Roo.data.Store} which owns the Record.
9456 * Commits all changes made to the Record since either creation, or the last commit operation.
9458 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9459 * of commit operations.
9461 commit : function(){
9463 delete this.modified;
9464 this.editing = false;
9466 this.store.afterCommit(this);
9471 hasError : function(){
9472 return this.error != null;
9476 clearError : function(){
9481 * Creates a copy of this record.
9482 * @param {String} id (optional) A new record id if you don't want to use this record's id
9485 copy : function(newId) {
9486 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9490 * Ext JS Library 1.1.1
9491 * Copyright(c) 2006-2007, Ext JS, LLC.
9493 * Originally Released Under LGPL - original licence link has changed is not relivant.
9496 * <script type="text/javascript">
9502 * @class Roo.data.Store
9503 * @extends Roo.util.Observable
9504 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9505 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9507 * 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
9508 * has no knowledge of the format of the data returned by the Proxy.<br>
9510 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9511 * instances from the data object. These records are cached and made available through accessor functions.
9513 * Creates a new Store.
9514 * @param {Object} config A config object containing the objects needed for the Store to access data,
9515 * and read the data into Records.
9517 Roo.data.Store = function(config){
9518 this.data = new Roo.util.MixedCollection(false);
9519 this.data.getKey = function(o){
9522 this.baseParams = {};
9529 "multisort" : "_multisort"
9532 if(config && config.data){
9533 this.inlineData = config.data;
9537 Roo.apply(this, config);
9539 if(this.reader){ // reader passed
9540 this.reader = Roo.factory(this.reader, Roo.data);
9541 this.reader.xmodule = this.xmodule || false;
9542 if(!this.recordType){
9543 this.recordType = this.reader.recordType;
9545 if(this.reader.onMetaChange){
9546 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9550 if(this.recordType){
9551 this.fields = this.recordType.prototype.fields;
9557 * @event datachanged
9558 * Fires when the data cache has changed, and a widget which is using this Store
9559 * as a Record cache should refresh its view.
9560 * @param {Store} this
9565 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9566 * @param {Store} this
9567 * @param {Object} meta The JSON metadata
9572 * Fires when Records have been added to the Store
9573 * @param {Store} this
9574 * @param {Roo.data.Record[]} records The array of Records added
9575 * @param {Number} index The index at which the record(s) were added
9580 * Fires when a Record has been removed from the Store
9581 * @param {Store} this
9582 * @param {Roo.data.Record} record The Record that was removed
9583 * @param {Number} index The index at which the record was removed
9588 * Fires when a Record has been updated
9589 * @param {Store} this
9590 * @param {Roo.data.Record} record The Record that was updated
9591 * @param {String} operation The update operation being performed. Value may be one of:
9593 Roo.data.Record.EDIT
9594 Roo.data.Record.REJECT
9595 Roo.data.Record.COMMIT
9601 * Fires when the data cache has been cleared.
9602 * @param {Store} this
9607 * Fires before a request is made for a new data object. If the beforeload handler returns false
9608 * the load action will be canceled.
9609 * @param {Store} this
9610 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9614 * @event beforeloadadd
9615 * Fires after a new set of Records has been loaded.
9616 * @param {Store} this
9617 * @param {Roo.data.Record[]} records The Records that were loaded
9618 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9620 beforeloadadd : true,
9623 * Fires after a new set of Records has been loaded, before they are added to the store.
9624 * @param {Store} this
9625 * @param {Roo.data.Record[]} records The Records that were loaded
9626 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9627 * @params {Object} return from reader
9631 * @event loadexception
9632 * Fires if an exception occurs in the Proxy during loading.
9633 * Called with the signature of the Proxy's "loadexception" event.
9634 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9637 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9638 * @param {Object} load options
9639 * @param {Object} jsonData from your request (normally this contains the Exception)
9641 loadexception : true
9645 this.proxy = Roo.factory(this.proxy, Roo.data);
9646 this.proxy.xmodule = this.xmodule || false;
9647 this.relayEvents(this.proxy, ["loadexception"]);
9649 this.sortToggle = {};
9650 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9652 Roo.data.Store.superclass.constructor.call(this);
9654 if(this.inlineData){
9655 this.loadData(this.inlineData);
9656 delete this.inlineData;
9660 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9662 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9663 * without a remote query - used by combo/forms at present.
9667 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9670 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9673 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9674 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9677 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9678 * on any HTTP request
9681 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9684 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9688 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9689 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9694 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9695 * loaded or when a record is removed. (defaults to false).
9697 pruneModifiedRecords : false,
9703 * Add Records to the Store and fires the add event.
9704 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9706 add : function(records){
9707 records = [].concat(records);
9708 for(var i = 0, len = records.length; i < len; i++){
9709 records[i].join(this);
9711 var index = this.data.length;
9712 this.data.addAll(records);
9713 this.fireEvent("add", this, records, index);
9717 * Remove a Record from the Store and fires the remove event.
9718 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9720 remove : function(record){
9721 var index = this.data.indexOf(record);
9722 this.data.removeAt(index);
9723 if(this.pruneModifiedRecords){
9724 this.modified.remove(record);
9726 this.fireEvent("remove", this, record, index);
9730 * Remove all Records from the Store and fires the clear event.
9732 removeAll : function(){
9734 if(this.pruneModifiedRecords){
9737 this.fireEvent("clear", this);
9741 * Inserts Records to the Store at the given index and fires the add event.
9742 * @param {Number} index The start index at which to insert the passed Records.
9743 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9745 insert : function(index, records){
9746 records = [].concat(records);
9747 for(var i = 0, len = records.length; i < len; i++){
9748 this.data.insert(index, records[i]);
9749 records[i].join(this);
9751 this.fireEvent("add", this, records, index);
9755 * Get the index within the cache of the passed Record.
9756 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9757 * @return {Number} The index of the passed Record. Returns -1 if not found.
9759 indexOf : function(record){
9760 return this.data.indexOf(record);
9764 * Get the index within the cache of the Record with the passed id.
9765 * @param {String} id The id of the Record to find.
9766 * @return {Number} The index of the Record. Returns -1 if not found.
9768 indexOfId : function(id){
9769 return this.data.indexOfKey(id);
9773 * Get the Record with the specified id.
9774 * @param {String} id The id of the Record to find.
9775 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9777 getById : function(id){
9778 return this.data.key(id);
9782 * Get the Record at the specified index.
9783 * @param {Number} index The index of the Record to find.
9784 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9786 getAt : function(index){
9787 return this.data.itemAt(index);
9791 * Returns a range of Records between specified indices.
9792 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9793 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9794 * @return {Roo.data.Record[]} An array of Records
9796 getRange : function(start, end){
9797 return this.data.getRange(start, end);
9801 storeOptions : function(o){
9802 o = Roo.apply({}, o);
9805 this.lastOptions = o;
9809 * Loads the Record cache from the configured Proxy using the configured Reader.
9811 * If using remote paging, then the first load call must specify the <em>start</em>
9812 * and <em>limit</em> properties in the options.params property to establish the initial
9813 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9815 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9816 * and this call will return before the new data has been loaded. Perform any post-processing
9817 * in a callback function, or in a "load" event handler.</strong>
9819 * @param {Object} options An object containing properties which control loading options:<ul>
9820 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9821 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9822 * passed the following arguments:<ul>
9823 * <li>r : Roo.data.Record[]</li>
9824 * <li>options: Options object from the load call</li>
9825 * <li>success: Boolean success indicator</li></ul></li>
9826 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9827 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9830 load : function(options){
9831 options = options || {};
9832 if(this.fireEvent("beforeload", this, options) !== false){
9833 this.storeOptions(options);
9834 var p = Roo.apply(options.params || {}, this.baseParams);
9835 // if meta was not loaded from remote source.. try requesting it.
9836 if (!this.reader.metaFromRemote) {
9839 if(this.sortInfo && this.remoteSort){
9840 var pn = this.paramNames;
9841 p[pn["sort"]] = this.sortInfo.field;
9842 p[pn["dir"]] = this.sortInfo.direction;
9844 if (this.multiSort) {
9845 var pn = this.paramNames;
9846 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9849 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9854 * Reloads the Record cache from the configured Proxy using the configured Reader and
9855 * the options from the last load operation performed.
9856 * @param {Object} options (optional) An object containing properties which may override the options
9857 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9858 * the most recently used options are reused).
9860 reload : function(options){
9861 this.load(Roo.applyIf(options||{}, this.lastOptions));
9865 // Called as a callback by the Reader during a load operation.
9866 loadRecords : function(o, options, success){
9867 if(!o || success === false){
9868 if(success !== false){
9869 this.fireEvent("load", this, [], options, o);
9871 if(options.callback){
9872 options.callback.call(options.scope || this, [], options, false);
9876 // if data returned failure - throw an exception.
9877 if (o.success === false) {
9878 // show a message if no listener is registered.
9879 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9880 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9882 // loadmask wil be hooked into this..
9883 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9886 var r = o.records, t = o.totalRecords || r.length;
9888 this.fireEvent("beforeloadadd", this, r, options, o);
9890 if(!options || options.add !== true){
9891 if(this.pruneModifiedRecords){
9894 for(var i = 0, len = r.length; i < len; i++){
9898 this.data = this.snapshot;
9899 delete this.snapshot;
9902 this.data.addAll(r);
9903 this.totalLength = t;
9905 this.fireEvent("datachanged", this);
9907 this.totalLength = Math.max(t, this.data.length+r.length);
9910 this.fireEvent("load", this, r, options, o);
9911 if(options.callback){
9912 options.callback.call(options.scope || this, r, options, true);
9918 * Loads data from a passed data block. A Reader which understands the format of the data
9919 * must have been configured in the constructor.
9920 * @param {Object} data The data block from which to read the Records. The format of the data expected
9921 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9922 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9924 loadData : function(o, append){
9925 var r = this.reader.readRecords(o);
9926 this.loadRecords(r, {add: append}, true);
9930 * Gets the number of cached records.
9932 * <em>If using paging, this may not be the total size of the dataset. If the data object
9933 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9934 * the data set size</em>
9936 getCount : function(){
9937 return this.data.length || 0;
9941 * Gets the total number of records in the dataset as returned by the server.
9943 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9944 * the dataset size</em>
9946 getTotalCount : function(){
9947 return this.totalLength || 0;
9951 * Returns the sort state of the Store as an object with two properties:
9953 field {String} The name of the field by which the Records are sorted
9954 direction {String} The sort order, "ASC" or "DESC"
9957 getSortState : function(){
9958 return this.sortInfo;
9962 applySort : function(){
9963 if(this.sortInfo && !this.remoteSort){
9964 var s = this.sortInfo, f = s.field;
9965 var st = this.fields.get(f).sortType;
9966 var fn = function(r1, r2){
9967 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9968 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9970 this.data.sort(s.direction, fn);
9971 if(this.snapshot && this.snapshot != this.data){
9972 this.snapshot.sort(s.direction, fn);
9978 * Sets the default sort column and order to be used by the next load operation.
9979 * @param {String} fieldName The name of the field to sort by.
9980 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9982 setDefaultSort : function(field, dir){
9983 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9988 * If remote sorting is used, the sort is performed on the server, and the cache is
9989 * reloaded. If local sorting is used, the cache is sorted internally.
9990 * @param {String} fieldName The name of the field to sort by.
9991 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9993 sort : function(fieldName, dir){
9994 var f = this.fields.get(fieldName);
9996 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9998 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9999 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10004 this.sortToggle[f.name] = dir;
10005 this.sortInfo = {field: f.name, direction: dir};
10006 if(!this.remoteSort){
10008 this.fireEvent("datachanged", this);
10010 this.load(this.lastOptions);
10015 * Calls the specified function for each of the Records in the cache.
10016 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10017 * Returning <em>false</em> aborts and exits the iteration.
10018 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10020 each : function(fn, scope){
10021 this.data.each(fn, scope);
10025 * Gets all records modified since the last commit. Modified records are persisted across load operations
10026 * (e.g., during paging).
10027 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10029 getModifiedRecords : function(){
10030 return this.modified;
10034 createFilterFn : function(property, value, anyMatch){
10035 if(!value.exec){ // not a regex
10036 value = String(value);
10037 if(value.length == 0){
10040 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10042 return function(r){
10043 return value.test(r.data[property]);
10048 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10049 * @param {String} property A field on your records
10050 * @param {Number} start The record index to start at (defaults to 0)
10051 * @param {Number} end The last record index to include (defaults to length - 1)
10052 * @return {Number} The sum
10054 sum : function(property, start, end){
10055 var rs = this.data.items, v = 0;
10056 start = start || 0;
10057 end = (end || end === 0) ? end : rs.length-1;
10059 for(var i = start; i <= end; i++){
10060 v += (rs[i].data[property] || 0);
10066 * Filter the records by a specified property.
10067 * @param {String} field A field on your records
10068 * @param {String/RegExp} value Either a string that the field
10069 * should start with or a RegExp to test against the field
10070 * @param {Boolean} anyMatch True to match any part not just the beginning
10072 filter : function(property, value, anyMatch){
10073 var fn = this.createFilterFn(property, value, anyMatch);
10074 return fn ? this.filterBy(fn) : this.clearFilter();
10078 * Filter by a function. The specified function will be called with each
10079 * record in this data source. If the function returns true the record is included,
10080 * otherwise it is filtered.
10081 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10082 * @param {Object} scope (optional) The scope of the function (defaults to this)
10084 filterBy : function(fn, scope){
10085 this.snapshot = this.snapshot || this.data;
10086 this.data = this.queryBy(fn, scope||this);
10087 this.fireEvent("datachanged", this);
10091 * Query the records by a specified property.
10092 * @param {String} field A field on your records
10093 * @param {String/RegExp} value Either a string that the field
10094 * should start with or a RegExp to test against the field
10095 * @param {Boolean} anyMatch True to match any part not just the beginning
10096 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10098 query : function(property, value, anyMatch){
10099 var fn = this.createFilterFn(property, value, anyMatch);
10100 return fn ? this.queryBy(fn) : this.data.clone();
10104 * Query by a function. The specified function will be called with each
10105 * record in this data source. If the function returns true the record is included
10107 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10108 * @param {Object} scope (optional) The scope of the function (defaults to this)
10109 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10111 queryBy : function(fn, scope){
10112 var data = this.snapshot || this.data;
10113 return data.filterBy(fn, scope||this);
10117 * Collects unique values for a particular dataIndex from this store.
10118 * @param {String} dataIndex The property to collect
10119 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10120 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10121 * @return {Array} An array of the unique values
10123 collect : function(dataIndex, allowNull, bypassFilter){
10124 var d = (bypassFilter === true && this.snapshot) ?
10125 this.snapshot.items : this.data.items;
10126 var v, sv, r = [], l = {};
10127 for(var i = 0, len = d.length; i < len; i++){
10128 v = d[i].data[dataIndex];
10130 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10139 * Revert to a view of the Record cache with no filtering applied.
10140 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10142 clearFilter : function(suppressEvent){
10143 if(this.snapshot && this.snapshot != this.data){
10144 this.data = this.snapshot;
10145 delete this.snapshot;
10146 if(suppressEvent !== true){
10147 this.fireEvent("datachanged", this);
10153 afterEdit : function(record){
10154 if(this.modified.indexOf(record) == -1){
10155 this.modified.push(record);
10157 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10161 afterReject : function(record){
10162 this.modified.remove(record);
10163 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10167 afterCommit : function(record){
10168 this.modified.remove(record);
10169 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10173 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10174 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10176 commitChanges : function(){
10177 var m = this.modified.slice(0);
10178 this.modified = [];
10179 for(var i = 0, len = m.length; i < len; i++){
10185 * Cancel outstanding changes on all changed records.
10187 rejectChanges : function(){
10188 var m = this.modified.slice(0);
10189 this.modified = [];
10190 for(var i = 0, len = m.length; i < len; i++){
10195 onMetaChange : function(meta, rtype, o){
10196 this.recordType = rtype;
10197 this.fields = rtype.prototype.fields;
10198 delete this.snapshot;
10199 this.sortInfo = meta.sortInfo || this.sortInfo;
10200 this.modified = [];
10201 this.fireEvent('metachange', this, this.reader.meta);
10204 moveIndex : function(data, type)
10206 var index = this.indexOf(data);
10208 var newIndex = index + type;
10212 this.insert(newIndex, data);
10217 * Ext JS Library 1.1.1
10218 * Copyright(c) 2006-2007, Ext JS, LLC.
10220 * Originally Released Under LGPL - original licence link has changed is not relivant.
10223 * <script type="text/javascript">
10227 * @class Roo.data.SimpleStore
10228 * @extends Roo.data.Store
10229 * Small helper class to make creating Stores from Array data easier.
10230 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10231 * @cfg {Array} fields An array of field definition objects, or field name strings.
10232 * @cfg {Array} data The multi-dimensional array of data
10234 * @param {Object} config
10236 Roo.data.SimpleStore = function(config){
10237 Roo.data.SimpleStore.superclass.constructor.call(this, {
10239 reader: new Roo.data.ArrayReader({
10242 Roo.data.Record.create(config.fields)
10244 proxy : new Roo.data.MemoryProxy(config.data)
10248 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10250 * Ext JS Library 1.1.1
10251 * Copyright(c) 2006-2007, Ext JS, LLC.
10253 * Originally Released Under LGPL - original licence link has changed is not relivant.
10256 * <script type="text/javascript">
10261 * @extends Roo.data.Store
10262 * @class Roo.data.JsonStore
10263 * Small helper class to make creating Stores for JSON data easier. <br/>
10265 var store = new Roo.data.JsonStore({
10266 url: 'get-images.php',
10268 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10271 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10272 * JsonReader and HttpProxy (unless inline data is provided).</b>
10273 * @cfg {Array} fields An array of field definition objects, or field name strings.
10275 * @param {Object} config
10277 Roo.data.JsonStore = function(c){
10278 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10279 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10280 reader: new Roo.data.JsonReader(c, c.fields)
10283 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10285 * Ext JS Library 1.1.1
10286 * Copyright(c) 2006-2007, Ext JS, LLC.
10288 * Originally Released Under LGPL - original licence link has changed is not relivant.
10291 * <script type="text/javascript">
10295 Roo.data.Field = function(config){
10296 if(typeof config == "string"){
10297 config = {name: config};
10299 Roo.apply(this, config);
10302 this.type = "auto";
10305 var st = Roo.data.SortTypes;
10306 // named sortTypes are supported, here we look them up
10307 if(typeof this.sortType == "string"){
10308 this.sortType = st[this.sortType];
10311 // set default sortType for strings and dates
10312 if(!this.sortType){
10315 this.sortType = st.asUCString;
10318 this.sortType = st.asDate;
10321 this.sortType = st.none;
10326 var stripRe = /[\$,%]/g;
10328 // prebuilt conversion function for this field, instead of
10329 // switching every time we're reading a value
10331 var cv, dateFormat = this.dateFormat;
10336 cv = function(v){ return v; };
10339 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10343 return v !== undefined && v !== null && v !== '' ?
10344 parseInt(String(v).replace(stripRe, ""), 10) : '';
10349 return v !== undefined && v !== null && v !== '' ?
10350 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10355 cv = function(v){ return v === true || v === "true" || v == 1; };
10362 if(v instanceof Date){
10366 if(dateFormat == "timestamp"){
10367 return new Date(v*1000);
10369 return Date.parseDate(v, dateFormat);
10371 var parsed = Date.parse(v);
10372 return parsed ? new Date(parsed) : null;
10381 Roo.data.Field.prototype = {
10389 * Ext JS Library 1.1.1
10390 * Copyright(c) 2006-2007, Ext JS, LLC.
10392 * Originally Released Under LGPL - original licence link has changed is not relivant.
10395 * <script type="text/javascript">
10398 // Base class for reading structured data from a data source. This class is intended to be
10399 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10402 * @class Roo.data.DataReader
10403 * Base class for reading structured data from a data source. This class is intended to be
10404 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10407 Roo.data.DataReader = function(meta, recordType){
10411 this.recordType = recordType instanceof Array ?
10412 Roo.data.Record.create(recordType) : recordType;
10415 Roo.data.DataReader.prototype = {
10417 * Create an empty record
10418 * @param {Object} data (optional) - overlay some values
10419 * @return {Roo.data.Record} record created.
10421 newRow : function(d) {
10423 this.recordType.prototype.fields.each(function(c) {
10425 case 'int' : da[c.name] = 0; break;
10426 case 'date' : da[c.name] = new Date(); break;
10427 case 'float' : da[c.name] = 0.0; break;
10428 case 'boolean' : da[c.name] = false; break;
10429 default : da[c.name] = ""; break;
10433 return new this.recordType(Roo.apply(da, d));
10438 * Ext JS Library 1.1.1
10439 * Copyright(c) 2006-2007, Ext JS, LLC.
10441 * Originally Released Under LGPL - original licence link has changed is not relivant.
10444 * <script type="text/javascript">
10448 * @class Roo.data.DataProxy
10449 * @extends Roo.data.Observable
10450 * This class is an abstract base class for implementations which provide retrieval of
10451 * unformatted data objects.<br>
10453 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10454 * (of the appropriate type which knows how to parse the data object) to provide a block of
10455 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10457 * Custom implementations must implement the load method as described in
10458 * {@link Roo.data.HttpProxy#load}.
10460 Roo.data.DataProxy = function(){
10463 * @event beforeload
10464 * Fires before a network request is made to retrieve a data object.
10465 * @param {Object} This DataProxy object.
10466 * @param {Object} params The params parameter to the load function.
10471 * Fires before the load method's callback is called.
10472 * @param {Object} This DataProxy object.
10473 * @param {Object} o The data object.
10474 * @param {Object} arg The callback argument object passed to the load function.
10478 * @event loadexception
10479 * Fires if an Exception occurs during data retrieval.
10480 * @param {Object} This DataProxy object.
10481 * @param {Object} o The data object.
10482 * @param {Object} arg The callback argument object passed to the load function.
10483 * @param {Object} e The Exception.
10485 loadexception : true
10487 Roo.data.DataProxy.superclass.constructor.call(this);
10490 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10493 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10497 * Ext JS Library 1.1.1
10498 * Copyright(c) 2006-2007, Ext JS, LLC.
10500 * Originally Released Under LGPL - original licence link has changed is not relivant.
10503 * <script type="text/javascript">
10506 * @class Roo.data.MemoryProxy
10507 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10508 * to the Reader when its load method is called.
10510 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10512 Roo.data.MemoryProxy = function(data){
10516 Roo.data.MemoryProxy.superclass.constructor.call(this);
10520 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10522 * Load data from the requested source (in this case an in-memory
10523 * data object passed to the constructor), read the data object into
10524 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10525 * process that block using the passed callback.
10526 * @param {Object} params This parameter is not used by the MemoryProxy class.
10527 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10528 * object into a block of Roo.data.Records.
10529 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10530 * The function must be passed <ul>
10531 * <li>The Record block object</li>
10532 * <li>The "arg" argument from the load function</li>
10533 * <li>A boolean success indicator</li>
10535 * @param {Object} scope The scope in which to call the callback
10536 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10538 load : function(params, reader, callback, scope, arg){
10539 params = params || {};
10542 result = reader.readRecords(this.data);
10544 this.fireEvent("loadexception", this, arg, null, e);
10545 callback.call(scope, null, arg, false);
10548 callback.call(scope, result, arg, true);
10552 update : function(params, records){
10557 * Ext JS Library 1.1.1
10558 * Copyright(c) 2006-2007, Ext JS, LLC.
10560 * Originally Released Under LGPL - original licence link has changed is not relivant.
10563 * <script type="text/javascript">
10566 * @class Roo.data.HttpProxy
10567 * @extends Roo.data.DataProxy
10568 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10569 * configured to reference a certain URL.<br><br>
10571 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10572 * from which the running page was served.<br><br>
10574 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10576 * Be aware that to enable the browser to parse an XML document, the server must set
10577 * the Content-Type header in the HTTP response to "text/xml".
10579 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10580 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10581 * will be used to make the request.
10583 Roo.data.HttpProxy = function(conn){
10584 Roo.data.HttpProxy.superclass.constructor.call(this);
10585 // is conn a conn config or a real conn?
10587 this.useAjax = !conn || !conn.events;
10591 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10592 // thse are take from connection...
10595 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10598 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10599 * extra parameters to each request made by this object. (defaults to undefined)
10602 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10603 * to each request made by this object. (defaults to undefined)
10606 * @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)
10609 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10612 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10618 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10622 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10623 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10624 * a finer-grained basis than the DataProxy events.
10626 getConnection : function(){
10627 return this.useAjax ? Roo.Ajax : this.conn;
10631 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10632 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10633 * process that block using the passed callback.
10634 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10635 * for the request to the remote server.
10636 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10637 * object into a block of Roo.data.Records.
10638 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10639 * The function must be passed <ul>
10640 * <li>The Record block object</li>
10641 * <li>The "arg" argument from the load function</li>
10642 * <li>A boolean success indicator</li>
10644 * @param {Object} scope The scope in which to call the callback
10645 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10647 load : function(params, reader, callback, scope, arg){
10648 if(this.fireEvent("beforeload", this, params) !== false){
10650 params : params || {},
10652 callback : callback,
10657 callback : this.loadResponse,
10661 Roo.applyIf(o, this.conn);
10662 if(this.activeRequest){
10663 Roo.Ajax.abort(this.activeRequest);
10665 this.activeRequest = Roo.Ajax.request(o);
10667 this.conn.request(o);
10670 callback.call(scope||this, null, arg, false);
10675 loadResponse : function(o, success, response){
10676 delete this.activeRequest;
10678 this.fireEvent("loadexception", this, o, response);
10679 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10684 result = o.reader.read(response);
10686 this.fireEvent("loadexception", this, o, response, e);
10687 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10691 this.fireEvent("load", this, o, o.request.arg);
10692 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10696 update : function(dataSet){
10701 updateResponse : function(dataSet){
10706 * Ext JS Library 1.1.1
10707 * Copyright(c) 2006-2007, Ext JS, LLC.
10709 * Originally Released Under LGPL - original licence link has changed is not relivant.
10712 * <script type="text/javascript">
10716 * @class Roo.data.ScriptTagProxy
10717 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10718 * other than the originating domain of the running page.<br><br>
10720 * <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
10721 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10723 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10724 * source code that is used as the source inside a <script> tag.<br><br>
10726 * In order for the browser to process the returned data, the server must wrap the data object
10727 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10728 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10729 * depending on whether the callback name was passed:
10732 boolean scriptTag = false;
10733 String cb = request.getParameter("callback");
10736 response.setContentType("text/javascript");
10738 response.setContentType("application/x-json");
10740 Writer out = response.getWriter();
10742 out.write(cb + "(");
10744 out.print(dataBlock.toJsonString());
10751 * @param {Object} config A configuration object.
10753 Roo.data.ScriptTagProxy = function(config){
10754 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10755 Roo.apply(this, config);
10756 this.head = document.getElementsByTagName("head")[0];
10759 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10761 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10763 * @cfg {String} url The URL from which to request the data object.
10766 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10770 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10771 * the server the name of the callback function set up by the load call to process the returned data object.
10772 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10773 * javascript output which calls this named function passing the data object as its only parameter.
10775 callbackParam : "callback",
10777 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10778 * name to the request.
10783 * Load data from the configured URL, read the data object into
10784 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10785 * process that block using the passed callback.
10786 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10787 * for the request to the remote server.
10788 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10789 * object into a block of Roo.data.Records.
10790 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10791 * The function must be passed <ul>
10792 * <li>The Record block object</li>
10793 * <li>The "arg" argument from the load function</li>
10794 * <li>A boolean success indicator</li>
10796 * @param {Object} scope The scope in which to call the callback
10797 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10799 load : function(params, reader, callback, scope, arg){
10800 if(this.fireEvent("beforeload", this, params) !== false){
10802 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10804 var url = this.url;
10805 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10807 url += "&_dc=" + (new Date().getTime());
10809 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10812 cb : "stcCallback"+transId,
10813 scriptId : "stcScript"+transId,
10817 callback : callback,
10823 window[trans.cb] = function(o){
10824 conn.handleResponse(o, trans);
10827 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10829 if(this.autoAbort !== false){
10833 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10835 var script = document.createElement("script");
10836 script.setAttribute("src", url);
10837 script.setAttribute("type", "text/javascript");
10838 script.setAttribute("id", trans.scriptId);
10839 this.head.appendChild(script);
10841 this.trans = trans;
10843 callback.call(scope||this, null, arg, false);
10848 isLoading : function(){
10849 return this.trans ? true : false;
10853 * Abort the current server request.
10855 abort : function(){
10856 if(this.isLoading()){
10857 this.destroyTrans(this.trans);
10862 destroyTrans : function(trans, isLoaded){
10863 this.head.removeChild(document.getElementById(trans.scriptId));
10864 clearTimeout(trans.timeoutId);
10866 window[trans.cb] = undefined;
10868 delete window[trans.cb];
10871 // if hasn't been loaded, wait for load to remove it to prevent script error
10872 window[trans.cb] = function(){
10873 window[trans.cb] = undefined;
10875 delete window[trans.cb];
10882 handleResponse : function(o, trans){
10883 this.trans = false;
10884 this.destroyTrans(trans, true);
10887 result = trans.reader.readRecords(o);
10889 this.fireEvent("loadexception", this, o, trans.arg, e);
10890 trans.callback.call(trans.scope||window, null, trans.arg, false);
10893 this.fireEvent("load", this, o, trans.arg);
10894 trans.callback.call(trans.scope||window, result, trans.arg, true);
10898 handleFailure : function(trans){
10899 this.trans = false;
10900 this.destroyTrans(trans, false);
10901 this.fireEvent("loadexception", this, null, trans.arg);
10902 trans.callback.call(trans.scope||window, null, trans.arg, false);
10906 * Ext JS Library 1.1.1
10907 * Copyright(c) 2006-2007, Ext JS, LLC.
10909 * Originally Released Under LGPL - original licence link has changed is not relivant.
10912 * <script type="text/javascript">
10916 * @class Roo.data.JsonReader
10917 * @extends Roo.data.DataReader
10918 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10919 * based on mappings in a provided Roo.data.Record constructor.
10921 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10922 * in the reply previously.
10927 var RecordDef = Roo.data.Record.create([
10928 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10929 {name: 'occupation'} // This field will use "occupation" as the mapping.
10931 var myReader = new Roo.data.JsonReader({
10932 totalProperty: "results", // The property which contains the total dataset size (optional)
10933 root: "rows", // The property which contains an Array of row objects
10934 id: "id" // The property within each row object that provides an ID for the record (optional)
10938 * This would consume a JSON file like this:
10940 { 'results': 2, 'rows': [
10941 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10942 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10945 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10946 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10947 * paged from the remote server.
10948 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10949 * @cfg {String} root name of the property which contains the Array of row objects.
10950 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10951 * @cfg {Array} fields Array of field definition objects
10953 * Create a new JsonReader
10954 * @param {Object} meta Metadata configuration options
10955 * @param {Object} recordType Either an Array of field definition objects,
10956 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10958 Roo.data.JsonReader = function(meta, recordType){
10961 // set some defaults:
10962 Roo.applyIf(meta, {
10963 totalProperty: 'total',
10964 successProperty : 'success',
10969 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10971 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10974 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10975 * Used by Store query builder to append _requestMeta to params.
10978 metaFromRemote : false,
10980 * This method is only used by a DataProxy which has retrieved data from a remote server.
10981 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10982 * @return {Object} data A data block which is used by an Roo.data.Store object as
10983 * a cache of Roo.data.Records.
10985 read : function(response){
10986 var json = response.responseText;
10988 var o = /* eval:var:o */ eval("("+json+")");
10990 throw {message: "JsonReader.read: Json object not found"};
10996 this.metaFromRemote = true;
10997 this.meta = o.metaData;
10998 this.recordType = Roo.data.Record.create(o.metaData.fields);
10999 this.onMetaChange(this.meta, this.recordType, o);
11001 return this.readRecords(o);
11004 // private function a store will implement
11005 onMetaChange : function(meta, recordType, o){
11012 simpleAccess: function(obj, subsc) {
11019 getJsonAccessor: function(){
11021 return function(expr) {
11023 return(re.test(expr))
11024 ? new Function("obj", "return obj." + expr)
11029 return Roo.emptyFn;
11034 * Create a data block containing Roo.data.Records from an XML document.
11035 * @param {Object} o An object which contains an Array of row objects in the property specified
11036 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11037 * which contains the total size of the dataset.
11038 * @return {Object} data A data block which is used by an Roo.data.Store object as
11039 * a cache of Roo.data.Records.
11041 readRecords : function(o){
11043 * After any data loads, the raw JSON data is available for further custom processing.
11047 var s = this.meta, Record = this.recordType,
11048 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11050 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11052 if(s.totalProperty) {
11053 this.getTotal = this.getJsonAccessor(s.totalProperty);
11055 if(s.successProperty) {
11056 this.getSuccess = this.getJsonAccessor(s.successProperty);
11058 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11060 var g = this.getJsonAccessor(s.id);
11061 this.getId = function(rec) {
11063 return (r === undefined || r === "") ? null : r;
11066 this.getId = function(){return null;};
11069 for(var jj = 0; jj < fl; jj++){
11071 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11072 this.ef[jj] = this.getJsonAccessor(map);
11076 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11077 if(s.totalProperty){
11078 var vt = parseInt(this.getTotal(o), 10);
11083 if(s.successProperty){
11084 var vs = this.getSuccess(o);
11085 if(vs === false || vs === 'false'){
11090 for(var i = 0; i < c; i++){
11093 var id = this.getId(n);
11094 for(var j = 0; j < fl; j++){
11096 var v = this.ef[j](n);
11098 Roo.log('missing convert for ' + f.name);
11102 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11104 var record = new Record(values, id);
11106 records[i] = record;
11112 totalRecords : totalRecords
11117 * Ext JS Library 1.1.1
11118 * Copyright(c) 2006-2007, Ext JS, LLC.
11120 * Originally Released Under LGPL - original licence link has changed is not relivant.
11123 * <script type="text/javascript">
11127 * @class Roo.data.ArrayReader
11128 * @extends Roo.data.DataReader
11129 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11130 * Each element of that Array represents a row of data fields. The
11131 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11132 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11136 var RecordDef = Roo.data.Record.create([
11137 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11138 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11140 var myReader = new Roo.data.ArrayReader({
11141 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11145 * This would consume an Array like this:
11147 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11149 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11151 * Create a new JsonReader
11152 * @param {Object} meta Metadata configuration options.
11153 * @param {Object} recordType Either an Array of field definition objects
11154 * as specified to {@link Roo.data.Record#create},
11155 * or an {@link Roo.data.Record} object
11156 * created using {@link Roo.data.Record#create}.
11158 Roo.data.ArrayReader = function(meta, recordType){
11159 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11162 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11164 * Create a data block containing Roo.data.Records from an XML document.
11165 * @param {Object} o An Array of row objects which represents the dataset.
11166 * @return {Object} data A data block which is used by an Roo.data.Store object as
11167 * a cache of Roo.data.Records.
11169 readRecords : function(o){
11170 var sid = this.meta ? this.meta.id : null;
11171 var recordType = this.recordType, fields = recordType.prototype.fields;
11174 for(var i = 0; i < root.length; i++){
11177 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11178 for(var j = 0, jlen = fields.length; j < jlen; j++){
11179 var f = fields.items[j];
11180 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11181 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11183 values[f.name] = v;
11185 var record = new recordType(values, id);
11187 records[records.length] = record;
11191 totalRecords : records.length
11200 * @class Roo.bootstrap.ComboBox
11201 * @extends Roo.bootstrap.TriggerField
11202 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11203 * @cfg {Boolean} append (true|false) default false
11204 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11205 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11206 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11207 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11208 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11209 * @cfg {Boolean} animate default true
11210 * @cfg {Boolean} emptyResultText only for touch device
11211 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11213 * Create a new ComboBox.
11214 * @param {Object} config Configuration options
11216 Roo.bootstrap.ComboBox = function(config){
11217 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11221 * Fires when the dropdown list is expanded
11222 * @param {Roo.bootstrap.ComboBox} combo This combo box
11227 * Fires when the dropdown list is collapsed
11228 * @param {Roo.bootstrap.ComboBox} combo This combo box
11232 * @event beforeselect
11233 * Fires before a list item is selected. Return false to cancel the selection.
11234 * @param {Roo.bootstrap.ComboBox} combo This combo box
11235 * @param {Roo.data.Record} record The data record returned from the underlying store
11236 * @param {Number} index The index of the selected item in the dropdown list
11238 'beforeselect' : true,
11241 * Fires when a list item is selected
11242 * @param {Roo.bootstrap.ComboBox} combo This combo box
11243 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11244 * @param {Number} index The index of the selected item in the dropdown list
11248 * @event beforequery
11249 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11250 * The event object passed has these properties:
11251 * @param {Roo.bootstrap.ComboBox} combo This combo box
11252 * @param {String} query The query
11253 * @param {Boolean} forceAll true to force "all" query
11254 * @param {Boolean} cancel true to cancel the query
11255 * @param {Object} e The query event object
11257 'beforequery': true,
11260 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11261 * @param {Roo.bootstrap.ComboBox} combo This combo box
11266 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11267 * @param {Roo.bootstrap.ComboBox} combo This combo box
11268 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11273 * Fires when the remove value from the combobox array
11274 * @param {Roo.bootstrap.ComboBox} combo This combo box
11278 * @event specialfilter
11279 * Fires when specialfilter
11280 * @param {Roo.bootstrap.ComboBox} combo This combo box
11282 'specialfilter' : true
11287 this.tickItems = [];
11289 this.selectedIndex = -1;
11290 if(this.mode == 'local'){
11291 if(config.queryDelay === undefined){
11292 this.queryDelay = 10;
11294 if(config.minChars === undefined){
11300 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11303 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11304 * rendering into an Roo.Editor, defaults to false)
11307 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11308 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11311 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11314 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11315 * the dropdown list (defaults to undefined, with no header element)
11319 * @cfg {String/Roo.Template} tpl The template to use to render the output
11323 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11325 listWidth: undefined,
11327 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11328 * mode = 'remote' or 'text' if mode = 'local')
11330 displayField: undefined,
11333 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11334 * mode = 'remote' or 'value' if mode = 'local').
11335 * Note: use of a valueField requires the user make a selection
11336 * in order for a value to be mapped.
11338 valueField: undefined,
11342 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11343 * field's data value (defaults to the underlying DOM element's name)
11345 hiddenName: undefined,
11347 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11351 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11353 selectedClass: 'active',
11356 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11360 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11361 * anchor positions (defaults to 'tl-bl')
11363 listAlign: 'tl-bl?',
11365 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11369 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11370 * query specified by the allQuery config option (defaults to 'query')
11372 triggerAction: 'query',
11374 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11375 * (defaults to 4, does not apply if editable = false)
11379 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11380 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11384 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11385 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11389 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11390 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11394 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11395 * when editable = true (defaults to false)
11397 selectOnFocus:false,
11399 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11401 queryParam: 'query',
11403 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11404 * when mode = 'remote' (defaults to 'Loading...')
11406 loadingText: 'Loading...',
11408 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11412 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11416 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11417 * traditional select (defaults to true)
11421 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11425 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11429 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11430 * listWidth has a higher value)
11434 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11435 * allow the user to set arbitrary text into the field (defaults to false)
11437 forceSelection:false,
11439 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11440 * if typeAhead = true (defaults to 250)
11442 typeAheadDelay : 250,
11444 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11445 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11447 valueNotFoundText : undefined,
11449 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11451 blockFocus : false,
11454 * @cfg {Boolean} disableClear Disable showing of clear button.
11456 disableClear : false,
11458 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11460 alwaysQuery : false,
11463 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11468 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11470 invalidClass : "has-warning",
11473 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11475 validClass : "has-success",
11478 * @cfg {Boolean} specialFilter (true|false) special filter default false
11480 specialFilter : false,
11483 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11485 mobileTouchView : true,
11497 btnPosition : 'right',
11498 triggerList : true,
11499 showToggleBtn : true,
11501 emptyResultText: 'Empty',
11502 triggerText : 'Select',
11504 // element that contains real text value.. (when hidden is used..)
11506 getAutoCreate : function()
11514 if(Roo.isTouch && this.mobileTouchView){
11515 cfg = this.getAutoCreateTouchView();
11522 if(!this.tickable){
11523 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11528 * ComboBox with tickable selections
11531 var align = this.labelAlign || this.parentLabelAlign();
11534 cls : 'form-group roo-combobox-tickable' //input-group
11539 cls : 'tickable-buttons',
11544 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11545 html : this.triggerText
11551 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11558 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11565 buttons.cn.unshift({
11567 cls: 'select2-search-field-input'
11573 Roo.each(buttons.cn, function(c){
11575 c.cls += ' btn-' + _this.size;
11578 if (_this.disabled) {
11589 cls: 'form-hidden-field'
11593 cls: 'select2-choices',
11597 cls: 'select2-search-field',
11609 cls: 'select2-container input-group select2-container-multi',
11614 // cls: 'typeahead typeahead-long dropdown-menu',
11615 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11620 if(this.hasFeedback && !this.allowBlank){
11624 cls: 'glyphicon form-control-feedback'
11627 combobox.cn.push(feedback);
11630 if (align ==='left' && this.fieldLabel.length) {
11632 Roo.log("left and has label");
11638 cls : 'control-label col-sm-' + this.labelWidth,
11639 html : this.fieldLabel
11643 cls : "col-sm-" + (12 - this.labelWidth),
11650 } else if ( this.fieldLabel.length) {
11656 //cls : 'input-group-addon',
11657 html : this.fieldLabel
11667 Roo.log(" no label && no align");
11674 ['xs','sm','md','lg'].map(function(size){
11675 if (settings[size]) {
11676 cfg.cls += ' col-' + size + '-' + settings[size];
11684 _initEventsCalled : false,
11687 initEvents: function()
11690 if (this._initEventsCalled) { // as we call render... prevent looping...
11693 this._initEventsCalled = true;
11696 throw "can not find store for combo";
11699 this.store = Roo.factory(this.store, Roo.data);
11701 // if we are building from html. then this element is so complex, that we can not really
11702 // use the rendered HTML.
11703 // so we have to trash and replace the previous code.
11704 if (Roo.XComponent.build_from_html) {
11706 // remove this element....
11707 var e = this.el.dom, k=0;
11708 while (e ) { e = e.previousSibling; ++k;}
11713 this.rendered = false;
11715 this.render(this.parent().getChildContainer(true), k);
11726 if(Roo.isTouch && this.mobileTouchView){
11727 this.initTouchView();
11732 this.initTickableEvents();
11736 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11738 if(this.hiddenName){
11740 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11742 this.hiddenField.dom.value =
11743 this.hiddenValue !== undefined ? this.hiddenValue :
11744 this.value !== undefined ? this.value : '';
11746 // prevent input submission
11747 this.el.dom.removeAttribute('name');
11748 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11753 // this.el.dom.setAttribute('autocomplete', 'off');
11756 var cls = 'x-combo-list';
11758 //this.list = new Roo.Layer({
11759 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11765 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11766 _this.list.setWidth(lw);
11769 this.list.on('mouseover', this.onViewOver, this);
11770 this.list.on('mousemove', this.onViewMove, this);
11772 this.list.on('scroll', this.onViewScroll, this);
11775 this.list.swallowEvent('mousewheel');
11776 this.assetHeight = 0;
11779 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11780 this.assetHeight += this.header.getHeight();
11783 this.innerList = this.list.createChild({cls:cls+'-inner'});
11784 this.innerList.on('mouseover', this.onViewOver, this);
11785 this.innerList.on('mousemove', this.onViewMove, this);
11786 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11788 if(this.allowBlank && !this.pageSize && !this.disableClear){
11789 this.footer = this.list.createChild({cls:cls+'-ft'});
11790 this.pageTb = new Roo.Toolbar(this.footer);
11794 this.footer = this.list.createChild({cls:cls+'-ft'});
11795 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11796 {pageSize: this.pageSize});
11800 if (this.pageTb && this.allowBlank && !this.disableClear) {
11802 this.pageTb.add(new Roo.Toolbar.Fill(), {
11803 cls: 'x-btn-icon x-btn-clear',
11805 handler: function()
11808 _this.clearValue();
11809 _this.onSelect(false, -1);
11814 this.assetHeight += this.footer.getHeight();
11819 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11822 this.view = new Roo.View(this.list, this.tpl, {
11823 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11825 //this.view.wrapEl.setDisplayed(false);
11826 this.view.on('click', this.onViewClick, this);
11830 this.store.on('beforeload', this.onBeforeLoad, this);
11831 this.store.on('load', this.onLoad, this);
11832 this.store.on('loadexception', this.onLoadException, this);
11834 if(this.resizable){
11835 this.resizer = new Roo.Resizable(this.list, {
11836 pinned:true, handles:'se'
11838 this.resizer.on('resize', function(r, w, h){
11839 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11840 this.listWidth = w;
11841 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11842 this.restrictHeight();
11844 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11847 if(!this.editable){
11848 this.editable = true;
11849 this.setEditable(false);
11854 if (typeof(this.events.add.listeners) != 'undefined') {
11856 this.addicon = this.wrap.createChild(
11857 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11859 this.addicon.on('click', function(e) {
11860 this.fireEvent('add', this);
11863 if (typeof(this.events.edit.listeners) != 'undefined') {
11865 this.editicon = this.wrap.createChild(
11866 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11867 if (this.addicon) {
11868 this.editicon.setStyle('margin-left', '40px');
11870 this.editicon.on('click', function(e) {
11872 // we fire even if inothing is selected..
11873 this.fireEvent('edit', this, this.lastData );
11879 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11880 "up" : function(e){
11881 this.inKeyMode = true;
11885 "down" : function(e){
11886 if(!this.isExpanded()){
11887 this.onTriggerClick();
11889 this.inKeyMode = true;
11894 "enter" : function(e){
11895 // this.onViewClick();
11899 if(this.fireEvent("specialkey", this, e)){
11900 this.onViewClick(false);
11906 "esc" : function(e){
11910 "tab" : function(e){
11913 if(this.fireEvent("specialkey", this, e)){
11914 this.onViewClick(false);
11922 doRelay : function(foo, bar, hname){
11923 if(hname == 'down' || this.scope.isExpanded()){
11924 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11933 this.queryDelay = Math.max(this.queryDelay || 10,
11934 this.mode == 'local' ? 10 : 250);
11937 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11939 if(this.typeAhead){
11940 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11942 if(this.editable !== false){
11943 this.inputEl().on("keyup", this.onKeyUp, this);
11945 if(this.forceSelection){
11946 this.inputEl().on('blur', this.doForce, this);
11950 this.choices = this.el.select('ul.select2-choices', true).first();
11951 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11955 initTickableEvents: function()
11959 if(this.hiddenName){
11961 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11963 this.hiddenField.dom.value =
11964 this.hiddenValue !== undefined ? this.hiddenValue :
11965 this.value !== undefined ? this.value : '';
11967 // prevent input submission
11968 this.el.dom.removeAttribute('name');
11969 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11974 // this.list = this.el.select('ul.dropdown-menu',true).first();
11976 this.choices = this.el.select('ul.select2-choices', true).first();
11977 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11978 if(this.triggerList){
11979 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11982 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11983 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11985 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11986 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11988 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11989 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11991 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11992 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11993 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11996 this.cancelBtn.hide();
12001 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12002 _this.list.setWidth(lw);
12005 this.list.on('mouseover', this.onViewOver, this);
12006 this.list.on('mousemove', this.onViewMove, this);
12008 this.list.on('scroll', this.onViewScroll, this);
12011 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>';
12014 this.view = new Roo.View(this.list, this.tpl, {
12015 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12018 //this.view.wrapEl.setDisplayed(false);
12019 this.view.on('click', this.onViewClick, this);
12023 this.store.on('beforeload', this.onBeforeLoad, this);
12024 this.store.on('load', this.onLoad, this);
12025 this.store.on('loadexception', this.onLoadException, this);
12028 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12029 "up" : function(e){
12030 this.inKeyMode = true;
12034 "down" : function(e){
12035 this.inKeyMode = true;
12039 "enter" : function(e){
12040 if(this.fireEvent("specialkey", this, e)){
12041 this.onViewClick(false);
12047 "esc" : function(e){
12048 this.onTickableFooterButtonClick(e, false, false);
12051 "tab" : function(e){
12052 this.fireEvent("specialkey", this, e);
12054 this.onTickableFooterButtonClick(e, false, false);
12061 doRelay : function(e, fn, key){
12062 if(this.scope.isExpanded()){
12063 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12072 this.queryDelay = Math.max(this.queryDelay || 10,
12073 this.mode == 'local' ? 10 : 250);
12076 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12078 if(this.typeAhead){
12079 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12082 if(this.editable !== false){
12083 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12088 onDestroy : function(){
12090 this.view.setStore(null);
12091 this.view.el.removeAllListeners();
12092 this.view.el.remove();
12093 this.view.purgeListeners();
12096 this.list.dom.innerHTML = '';
12100 this.store.un('beforeload', this.onBeforeLoad, this);
12101 this.store.un('load', this.onLoad, this);
12102 this.store.un('loadexception', this.onLoadException, this);
12104 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12108 fireKey : function(e){
12109 if(e.isNavKeyPress() && !this.list.isVisible()){
12110 this.fireEvent("specialkey", this, e);
12115 onResize: function(w, h){
12116 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12118 // if(typeof w != 'number'){
12119 // // we do not handle it!?!?
12122 // var tw = this.trigger.getWidth();
12123 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12124 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12126 // this.inputEl().setWidth( this.adjustWidth('input', x));
12128 // //this.trigger.setStyle('left', x+'px');
12130 // if(this.list && this.listWidth === undefined){
12131 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12132 // this.list.setWidth(lw);
12133 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12141 * Allow or prevent the user from directly editing the field text. If false is passed,
12142 * the user will only be able to select from the items defined in the dropdown list. This method
12143 * is the runtime equivalent of setting the 'editable' config option at config time.
12144 * @param {Boolean} value True to allow the user to directly edit the field text
12146 setEditable : function(value){
12147 if(value == this.editable){
12150 this.editable = value;
12152 this.inputEl().dom.setAttribute('readOnly', true);
12153 this.inputEl().on('mousedown', this.onTriggerClick, this);
12154 this.inputEl().addClass('x-combo-noedit');
12156 this.inputEl().dom.setAttribute('readOnly', false);
12157 this.inputEl().un('mousedown', this.onTriggerClick, this);
12158 this.inputEl().removeClass('x-combo-noedit');
12164 onBeforeLoad : function(combo,opts){
12165 if(!this.hasFocus){
12169 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12171 this.restrictHeight();
12172 this.selectedIndex = -1;
12176 onLoad : function(){
12178 this.hasQuery = false;
12180 if(!this.hasFocus){
12184 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12185 this.loading.hide();
12188 if(this.store.getCount() > 0){
12190 this.restrictHeight();
12191 if(this.lastQuery == this.allQuery){
12192 if(this.editable && !this.tickable){
12193 this.inputEl().dom.select();
12197 !this.selectByValue(this.value, true) &&
12200 !this.store.lastOptions ||
12201 typeof(this.store.lastOptions.add) == 'undefined' ||
12202 this.store.lastOptions.add != true
12205 this.select(0, true);
12208 if(this.autoFocus){
12211 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12212 this.taTask.delay(this.typeAheadDelay);
12216 this.onEmptyResults();
12222 onLoadException : function()
12224 this.hasQuery = false;
12226 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12227 this.loading.hide();
12230 if(this.tickable && this.editable){
12236 Roo.log(this.store.reader.jsonData);
12237 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12239 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12245 onTypeAhead : function(){
12246 if(this.store.getCount() > 0){
12247 var r = this.store.getAt(0);
12248 var newValue = r.data[this.displayField];
12249 var len = newValue.length;
12250 var selStart = this.getRawValue().length;
12252 if(selStart != len){
12253 this.setRawValue(newValue);
12254 this.selectText(selStart, newValue.length);
12260 onSelect : function(record, index){
12262 if(this.fireEvent('beforeselect', this, record, index) !== false){
12264 this.setFromData(index > -1 ? record.data : false);
12267 this.fireEvent('select', this, record, index);
12272 * Returns the currently selected field value or empty string if no value is set.
12273 * @return {String} value The selected value
12275 getValue : function(){
12278 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12281 if(this.valueField){
12282 return typeof this.value != 'undefined' ? this.value : '';
12284 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12289 * Clears any text/value currently set in the field
12291 clearValue : function(){
12292 if(this.hiddenField){
12293 this.hiddenField.dom.value = '';
12296 this.setRawValue('');
12297 this.lastSelectionText = '';
12298 this.lastData = false;
12300 var close = this.closeTriggerEl();
12309 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12310 * will be displayed in the field. If the value does not match the data value of an existing item,
12311 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12312 * Otherwise the field will be blank (although the value will still be set).
12313 * @param {String} value The value to match
12315 setValue : function(v){
12322 if(this.valueField){
12323 var r = this.findRecord(this.valueField, v);
12325 text = r.data[this.displayField];
12326 }else if(this.valueNotFoundText !== undefined){
12327 text = this.valueNotFoundText;
12330 this.lastSelectionText = text;
12331 if(this.hiddenField){
12332 this.hiddenField.dom.value = v;
12334 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12337 var close = this.closeTriggerEl();
12340 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12344 * @property {Object} the last set data for the element
12349 * Sets the value of the field based on a object which is related to the record format for the store.
12350 * @param {Object} value the value to set as. or false on reset?
12352 setFromData : function(o){
12359 var dv = ''; // display value
12360 var vv = ''; // value value..
12362 if (this.displayField) {
12363 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12365 // this is an error condition!!!
12366 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12369 if(this.valueField){
12370 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12373 var close = this.closeTriggerEl();
12376 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12379 if(this.hiddenField){
12380 this.hiddenField.dom.value = vv;
12382 this.lastSelectionText = dv;
12383 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12387 // no hidden field.. - we store the value in 'value', but still display
12388 // display field!!!!
12389 this.lastSelectionText = dv;
12390 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12397 reset : function(){
12398 // overridden so that last data is reset..
12405 this.setValue(this.originalValue);
12406 this.clearInvalid();
12407 this.lastData = false;
12409 this.view.clearSelections();
12413 findRecord : function(prop, value){
12415 if(this.store.getCount() > 0){
12416 this.store.each(function(r){
12417 if(r.data[prop] == value){
12427 getName: function()
12429 // returns hidden if it's set..
12430 if (!this.rendered) {return ''};
12431 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12435 onViewMove : function(e, t){
12436 this.inKeyMode = false;
12440 onViewOver : function(e, t){
12441 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12444 var item = this.view.findItemFromChild(t);
12447 var index = this.view.indexOf(item);
12448 this.select(index, false);
12453 onViewClick : function(view, doFocus, el, e)
12455 var index = this.view.getSelectedIndexes()[0];
12457 var r = this.store.getAt(index);
12461 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12468 Roo.each(this.tickItems, function(v,k){
12470 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12471 _this.tickItems.splice(k, 1);
12473 if(typeof(e) == 'undefined' && view == false){
12474 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12486 this.tickItems.push(r.data);
12488 if(typeof(e) == 'undefined' && view == false){
12489 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12496 this.onSelect(r, index);
12498 if(doFocus !== false && !this.blockFocus){
12499 this.inputEl().focus();
12504 restrictHeight : function(){
12505 //this.innerList.dom.style.height = '';
12506 //var inner = this.innerList.dom;
12507 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12508 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12509 //this.list.beginUpdate();
12510 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12511 this.list.alignTo(this.inputEl(), this.listAlign);
12512 this.list.alignTo(this.inputEl(), this.listAlign);
12513 //this.list.endUpdate();
12517 onEmptyResults : function(){
12519 if(this.tickable && this.editable){
12520 this.restrictHeight();
12528 * Returns true if the dropdown list is expanded, else false.
12530 isExpanded : function(){
12531 return this.list.isVisible();
12535 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12536 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12537 * @param {String} value The data value of the item to select
12538 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12539 * selected item if it is not currently in view (defaults to true)
12540 * @return {Boolean} True if the value matched an item in the list, else false
12542 selectByValue : function(v, scrollIntoView){
12543 if(v !== undefined && v !== null){
12544 var r = this.findRecord(this.valueField || this.displayField, v);
12546 this.select(this.store.indexOf(r), scrollIntoView);
12554 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12555 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12556 * @param {Number} index The zero-based index of the list item to select
12557 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12558 * selected item if it is not currently in view (defaults to true)
12560 select : function(index, scrollIntoView){
12561 this.selectedIndex = index;
12562 this.view.select(index);
12563 if(scrollIntoView !== false){
12564 var el = this.view.getNode(index);
12566 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12569 this.list.scrollChildIntoView(el, false);
12575 selectNext : function(){
12576 var ct = this.store.getCount();
12578 if(this.selectedIndex == -1){
12580 }else if(this.selectedIndex < ct-1){
12581 this.select(this.selectedIndex+1);
12587 selectPrev : function(){
12588 var ct = this.store.getCount();
12590 if(this.selectedIndex == -1){
12592 }else if(this.selectedIndex != 0){
12593 this.select(this.selectedIndex-1);
12599 onKeyUp : function(e){
12600 if(this.editable !== false && !e.isSpecialKey()){
12601 this.lastKey = e.getKey();
12602 this.dqTask.delay(this.queryDelay);
12607 validateBlur : function(){
12608 return !this.list || !this.list.isVisible();
12612 initQuery : function(){
12614 var v = this.getRawValue();
12616 if(this.tickable && this.editable){
12617 v = this.tickableInputEl().getValue();
12624 doForce : function(){
12625 if(this.inputEl().dom.value.length > 0){
12626 this.inputEl().dom.value =
12627 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12633 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12634 * query allowing the query action to be canceled if needed.
12635 * @param {String} query The SQL query to execute
12636 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12637 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12638 * saved in the current store (defaults to false)
12640 doQuery : function(q, forceAll){
12642 if(q === undefined || q === null){
12647 forceAll: forceAll,
12651 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12656 forceAll = qe.forceAll;
12657 if(forceAll === true || (q.length >= this.minChars)){
12659 this.hasQuery = true;
12661 if(this.lastQuery != q || this.alwaysQuery){
12662 this.lastQuery = q;
12663 if(this.mode == 'local'){
12664 this.selectedIndex = -1;
12666 this.store.clearFilter();
12669 if(this.specialFilter){
12670 this.fireEvent('specialfilter', this);
12675 this.store.filter(this.displayField, q);
12678 this.store.fireEvent("datachanged", this.store);
12685 this.store.baseParams[this.queryParam] = q;
12687 var options = {params : this.getParams(q)};
12690 options.add = true;
12691 options.params.start = this.page * this.pageSize;
12694 this.store.load(options);
12697 * this code will make the page width larger, at the beginning, the list not align correctly,
12698 * we should expand the list on onLoad
12699 * so command out it
12704 this.selectedIndex = -1;
12709 this.loadNext = false;
12713 getParams : function(q){
12715 //p[this.queryParam] = q;
12719 p.limit = this.pageSize;
12725 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12727 collapse : function(){
12728 if(!this.isExpanded()){
12735 this.hasFocus = false;
12737 this.cancelBtn.hide();
12738 this.trigger.show();
12741 this.tickableInputEl().dom.value = '';
12742 this.tickableInputEl().blur();
12747 Roo.get(document).un('mousedown', this.collapseIf, this);
12748 Roo.get(document).un('mousewheel', this.collapseIf, this);
12749 if (!this.editable) {
12750 Roo.get(document).un('keydown', this.listKeyPress, this);
12752 this.fireEvent('collapse', this);
12756 collapseIf : function(e){
12757 var in_combo = e.within(this.el);
12758 var in_list = e.within(this.list);
12759 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12761 if (in_combo || in_list || is_list) {
12762 //e.stopPropagation();
12767 this.onTickableFooterButtonClick(e, false, false);
12775 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12777 expand : function(){
12779 if(this.isExpanded() || !this.hasFocus){
12783 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12784 this.list.setWidth(lw);
12791 this.restrictHeight();
12795 this.tickItems = Roo.apply([], this.item);
12798 this.cancelBtn.show();
12799 this.trigger.hide();
12802 this.tickableInputEl().focus();
12807 Roo.get(document).on('mousedown', this.collapseIf, this);
12808 Roo.get(document).on('mousewheel', this.collapseIf, this);
12809 if (!this.editable) {
12810 Roo.get(document).on('keydown', this.listKeyPress, this);
12813 this.fireEvent('expand', this);
12817 // Implements the default empty TriggerField.onTriggerClick function
12818 onTriggerClick : function(e)
12820 Roo.log('trigger click');
12822 if(this.disabled || !this.triggerList){
12827 this.loadNext = false;
12829 if(this.isExpanded()){
12831 if (!this.blockFocus) {
12832 this.inputEl().focus();
12836 this.hasFocus = true;
12837 if(this.triggerAction == 'all') {
12838 this.doQuery(this.allQuery, true);
12840 this.doQuery(this.getRawValue());
12842 if (!this.blockFocus) {
12843 this.inputEl().focus();
12848 onTickableTriggerClick : function(e)
12855 this.loadNext = false;
12856 this.hasFocus = true;
12858 if(this.triggerAction == 'all') {
12859 this.doQuery(this.allQuery, true);
12861 this.doQuery(this.getRawValue());
12865 onSearchFieldClick : function(e)
12867 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12868 this.onTickableFooterButtonClick(e, false, false);
12872 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12877 this.loadNext = false;
12878 this.hasFocus = true;
12880 if(this.triggerAction == 'all') {
12881 this.doQuery(this.allQuery, true);
12883 this.doQuery(this.getRawValue());
12887 listKeyPress : function(e)
12889 //Roo.log('listkeypress');
12890 // scroll to first matching element based on key pres..
12891 if (e.isSpecialKey()) {
12894 var k = String.fromCharCode(e.getKey()).toUpperCase();
12897 var csel = this.view.getSelectedNodes();
12898 var cselitem = false;
12900 var ix = this.view.indexOf(csel[0]);
12901 cselitem = this.store.getAt(ix);
12902 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12908 this.store.each(function(v) {
12910 // start at existing selection.
12911 if (cselitem.id == v.id) {
12917 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12918 match = this.store.indexOf(v);
12924 if (match === false) {
12925 return true; // no more action?
12928 this.view.select(match);
12929 var sn = Roo.get(this.view.getSelectedNodes()[0])
12930 sn.scrollIntoView(sn.dom.parentNode, false);
12933 onViewScroll : function(e, t){
12935 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){
12939 this.hasQuery = true;
12941 this.loading = this.list.select('.loading', true).first();
12943 if(this.loading === null){
12944 this.list.createChild({
12946 cls: 'loading select2-more-results select2-active',
12947 html: 'Loading more results...'
12950 this.loading = this.list.select('.loading', true).first();
12952 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12954 this.loading.hide();
12957 this.loading.show();
12962 this.loadNext = true;
12964 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12969 addItem : function(o)
12971 var dv = ''; // display value
12973 if (this.displayField) {
12974 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12976 // this is an error condition!!!
12977 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12984 var choice = this.choices.createChild({
12986 cls: 'select2-search-choice',
12995 cls: 'select2-search-choice-close',
13000 }, this.searchField);
13002 var close = choice.select('a.select2-search-choice-close', true).first()
13004 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13012 this.inputEl().dom.value = '';
13017 onRemoveItem : function(e, _self, o)
13019 e.preventDefault();
13021 this.lastItem = Roo.apply([], this.item);
13023 var index = this.item.indexOf(o.data) * 1;
13026 Roo.log('not this item?!');
13030 this.item.splice(index, 1);
13035 this.fireEvent('remove', this, e);
13041 syncValue : function()
13043 if(!this.item.length){
13050 Roo.each(this.item, function(i){
13051 if(_this.valueField){
13052 value.push(i[_this.valueField]);
13059 this.value = value.join(',');
13061 if(this.hiddenField){
13062 this.hiddenField.dom.value = this.value;
13065 this.store.fireEvent("datachanged", this.store);
13068 clearItem : function()
13070 if(!this.multiple){
13076 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13084 if(this.tickable && !Roo.isTouch){
13085 this.view.refresh();
13089 inputEl: function ()
13091 if(Roo.isTouch && this.mobileTouchView){
13092 return this.el.select('input.form-control',true).first();
13096 return this.searchField;
13099 return this.el.select('input.form-control',true).first();
13103 onTickableFooterButtonClick : function(e, btn, el)
13105 e.preventDefault();
13107 this.lastItem = Roo.apply([], this.item);
13109 if(btn && btn.name == 'cancel'){
13110 this.tickItems = Roo.apply([], this.item);
13119 Roo.each(this.tickItems, function(o){
13127 validate : function()
13129 var v = this.getRawValue();
13132 v = this.getValue();
13135 if(this.disabled || this.allowBlank || v.length){
13140 this.markInvalid();
13144 tickableInputEl : function()
13146 if(!this.tickable || !this.editable){
13147 return this.inputEl();
13150 return this.inputEl().select('.select2-search-field-input', true).first();
13154 getAutoCreateTouchView : function()
13159 cls: 'form-group' //input-group
13165 type : this.inputType,
13166 cls : 'form-control x-combo-noedit',
13167 autocomplete: 'new-password',
13168 placeholder : this.placeholder || '',
13173 input.name = this.name;
13177 input.cls += ' input-' + this.size;
13180 if (this.disabled) {
13181 input.disabled = true;
13192 inputblock.cls += ' input-group';
13194 inputblock.cn.unshift({
13196 cls : 'input-group-addon',
13201 if(this.removable && !this.multiple){
13202 inputblock.cls += ' roo-removable';
13204 inputblock.cn.push({
13207 cls : 'roo-combo-removable-btn close'
13211 if(this.hasFeedback && !this.allowBlank){
13213 inputblock.cls += ' has-feedback';
13215 inputblock.cn.push({
13217 cls: 'glyphicon form-control-feedback'
13224 inputblock.cls += (this.before) ? '' : ' input-group';
13226 inputblock.cn.push({
13228 cls : 'input-group-addon',
13239 cls: 'form-hidden-field'
13253 cls: 'form-hidden-field'
13257 cls: 'select2-choices',
13261 cls: 'select2-search-field',
13274 cls: 'select2-container input-group',
13281 combobox.cls += ' select2-container-multi';
13284 var align = this.labelAlign || this.parentLabelAlign();
13288 if(this.fieldLabel.length){
13290 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13291 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13296 cls : 'control-label ' + lw,
13297 html : this.fieldLabel
13309 var settings = this;
13311 ['xs','sm','md','lg'].map(function(size){
13312 if (settings[size]) {
13313 cfg.cls += ' col-' + size + '-' + settings[size];
13320 initTouchView : function()
13322 this.renderTouchView();
13324 this.touchViewEl.on('scroll', function(){
13325 this.el.dom.scrollTop = 0;
13328 this.inputEl().on("click", this.showTouchView, this);
13329 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13330 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13332 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13334 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13335 this.store.on('load', this.onTouchViewLoad, this);
13336 this.store.on('loadexception', this.onTouchViewLoadException, this);
13338 if(this.hiddenName){
13340 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13342 this.hiddenField.dom.value =
13343 this.hiddenValue !== undefined ? this.hiddenValue :
13344 this.value !== undefined ? this.value : '';
13346 this.el.dom.removeAttribute('name');
13347 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13351 this.choices = this.el.select('ul.select2-choices', true).first();
13352 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13355 if(this.removable && !this.multiple){
13356 var close = this.closeTriggerEl();
13358 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13359 close.on('click', this.removeBtnClick, this, close);
13368 renderTouchView : function()
13370 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13371 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13373 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13374 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13376 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13377 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13378 this.touchViewBodyEl.setStyle('overflow', 'auto');
13380 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13381 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13383 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13384 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13388 showTouchView : function()
13390 this.touchViewHeaderEl.hide();
13392 if(this.fieldLabel.length){
13393 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13394 this.touchViewHeaderEl.show();
13397 this.touchViewEl.show();
13399 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13400 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13402 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13404 if(this.fieldLabel.length){
13405 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13408 this.touchViewBodyEl.setHeight(bodyHeight);
13412 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13414 this.touchViewEl.addClass('in');
13417 this.doTouchViewQuery();
13421 hideTouchView : function()
13423 this.touchViewEl.removeClass('in');
13427 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13429 this.touchViewEl.setStyle('display', 'none');
13434 setTouchViewValue : function()
13441 Roo.each(this.tickItems, function(o){
13446 this.hideTouchView();
13449 doTouchViewQuery : function()
13458 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13462 if(!this.alwaysQuery || this.mode == 'local'){
13463 this.onTouchViewLoad();
13470 onTouchViewBeforeLoad : function(combo,opts)
13476 onTouchViewLoad : function()
13478 if(this.store.getCount() < 1){
13479 this.onTouchViewEmptyResults();
13483 this.clearTouchView();
13485 var rawValue = this.getRawValue();
13487 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13489 this.tickItems = [];
13491 this.store.data.each(function(d, rowIndex){
13492 var row = this.touchViewListGroup.createChild(template);
13494 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13495 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13498 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13499 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13502 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13503 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13504 this.tickItems.push(d.data);
13507 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13511 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13513 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13515 if(this.fieldLabel.length){
13516 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13519 var listHeight = this.touchViewListGroup.getHeight();
13523 if(firstChecked && listHeight > bodyHeight){
13524 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13529 onTouchViewLoadException : function()
13531 this.hideTouchView();
13534 onTouchViewEmptyResults : function()
13536 this.clearTouchView();
13538 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13540 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13544 clearTouchView : function()
13546 this.touchViewListGroup.dom.innerHTML = '';
13549 onTouchViewClick : function(e, el, o)
13551 e.preventDefault();
13554 var rowIndex = o.rowIndex;
13556 var r = this.store.getAt(rowIndex);
13558 if(!this.multiple){
13559 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13560 c.dom.removeAttribute('checked');
13563 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13565 this.setFromData(r.data);
13567 var close = this.closeTriggerEl();
13573 this.hideTouchView();
13575 this.fireEvent('select', this, r, rowIndex);
13580 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13581 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13582 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13586 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13587 this.addItem(r.data);
13588 this.tickItems.push(r.data);
13594 * @cfg {Boolean} grow
13598 * @cfg {Number} growMin
13602 * @cfg {Number} growMax
13611 Roo.apply(Roo.bootstrap.ComboBox, {
13615 cls: 'modal-header',
13637 cls: 'list-group-item',
13641 cls: 'roo-combobox-list-group-item-value'
13645 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13659 listItemCheckbox : {
13661 cls: 'list-group-item',
13665 cls: 'roo-combobox-list-group-item-value'
13669 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13685 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13690 cls: 'modal-footer',
13698 cls: 'col-xs-6 text-left',
13701 cls: 'btn btn-danger roo-touch-view-cancel',
13707 cls: 'col-xs-6 text-right',
13710 cls: 'btn btn-success roo-touch-view-ok',
13721 Roo.apply(Roo.bootstrap.ComboBox, {
13723 touchViewTemplate : {
13725 cls: 'modal fade roo-combobox-touch-view',
13729 cls: 'modal-dialog',
13733 cls: 'modal-content',
13735 Roo.bootstrap.ComboBox.header,
13736 Roo.bootstrap.ComboBox.body,
13737 Roo.bootstrap.ComboBox.footer
13746 * Ext JS Library 1.1.1
13747 * Copyright(c) 2006-2007, Ext JS, LLC.
13749 * Originally Released Under LGPL - original licence link has changed is not relivant.
13752 * <script type="text/javascript">
13757 * @extends Roo.util.Observable
13758 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13759 * This class also supports single and multi selection modes. <br>
13760 * Create a data model bound view:
13762 var store = new Roo.data.Store(...);
13764 var view = new Roo.View({
13766 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13768 singleSelect: true,
13769 selectedClass: "ydataview-selected",
13773 // listen for node click?
13774 view.on("click", function(vw, index, node, e){
13775 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13779 dataModel.load("foobar.xml");
13781 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13783 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13784 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13786 * Note: old style constructor is still suported (container, template, config)
13789 * Create a new View
13790 * @param {Object} config The config object
13793 Roo.View = function(config, depreciated_tpl, depreciated_config){
13795 this.parent = false;
13797 if (typeof(depreciated_tpl) == 'undefined') {
13798 // new way.. - universal constructor.
13799 Roo.apply(this, config);
13800 this.el = Roo.get(this.el);
13803 this.el = Roo.get(config);
13804 this.tpl = depreciated_tpl;
13805 Roo.apply(this, depreciated_config);
13807 this.wrapEl = this.el.wrap().wrap();
13808 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13811 if(typeof(this.tpl) == "string"){
13812 this.tpl = new Roo.Template(this.tpl);
13814 // support xtype ctors..
13815 this.tpl = new Roo.factory(this.tpl, Roo);
13819 this.tpl.compile();
13824 * @event beforeclick
13825 * Fires before a click is processed. Returns false to cancel the default action.
13826 * @param {Roo.View} this
13827 * @param {Number} index The index of the target node
13828 * @param {HTMLElement} node The target node
13829 * @param {Roo.EventObject} e The raw event object
13831 "beforeclick" : true,
13834 * Fires when a template node is clicked.
13835 * @param {Roo.View} this
13836 * @param {Number} index The index of the target node
13837 * @param {HTMLElement} node The target node
13838 * @param {Roo.EventObject} e The raw event object
13843 * Fires when a template node is double clicked.
13844 * @param {Roo.View} this
13845 * @param {Number} index The index of the target node
13846 * @param {HTMLElement} node The target node
13847 * @param {Roo.EventObject} e The raw event object
13851 * @event contextmenu
13852 * Fires when a template node is right clicked.
13853 * @param {Roo.View} this
13854 * @param {Number} index The index of the target node
13855 * @param {HTMLElement} node The target node
13856 * @param {Roo.EventObject} e The raw event object
13858 "contextmenu" : true,
13860 * @event selectionchange
13861 * Fires when the selected nodes change.
13862 * @param {Roo.View} this
13863 * @param {Array} selections Array of the selected nodes
13865 "selectionchange" : true,
13868 * @event beforeselect
13869 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13870 * @param {Roo.View} this
13871 * @param {HTMLElement} node The node to be selected
13872 * @param {Array} selections Array of currently selected nodes
13874 "beforeselect" : true,
13876 * @event preparedata
13877 * Fires on every row to render, to allow you to change the data.
13878 * @param {Roo.View} this
13879 * @param {Object} data to be rendered (change this)
13881 "preparedata" : true
13889 "click": this.onClick,
13890 "dblclick": this.onDblClick,
13891 "contextmenu": this.onContextMenu,
13895 this.selections = [];
13897 this.cmp = new Roo.CompositeElementLite([]);
13899 this.store = Roo.factory(this.store, Roo.data);
13900 this.setStore(this.store, true);
13903 if ( this.footer && this.footer.xtype) {
13905 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13907 this.footer.dataSource = this.store;
13908 this.footer.container = fctr;
13909 this.footer = Roo.factory(this.footer, Roo);
13910 fctr.insertFirst(this.el);
13912 // this is a bit insane - as the paging toolbar seems to detach the el..
13913 // dom.parentNode.parentNode.parentNode
13914 // they get detached?
13918 Roo.View.superclass.constructor.call(this);
13923 Roo.extend(Roo.View, Roo.util.Observable, {
13926 * @cfg {Roo.data.Store} store Data store to load data from.
13931 * @cfg {String|Roo.Element} el The container element.
13936 * @cfg {String|Roo.Template} tpl The template used by this View
13940 * @cfg {String} dataName the named area of the template to use as the data area
13941 * Works with domtemplates roo-name="name"
13945 * @cfg {String} selectedClass The css class to add to selected nodes
13947 selectedClass : "x-view-selected",
13949 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13954 * @cfg {String} text to display on mask (default Loading)
13958 * @cfg {Boolean} multiSelect Allow multiple selection
13960 multiSelect : false,
13962 * @cfg {Boolean} singleSelect Allow single selection
13964 singleSelect: false,
13967 * @cfg {Boolean} toggleSelect - selecting
13969 toggleSelect : false,
13972 * @cfg {Boolean} tickable - selecting
13977 * Returns the element this view is bound to.
13978 * @return {Roo.Element}
13980 getEl : function(){
13981 return this.wrapEl;
13987 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13989 refresh : function(){
13990 //Roo.log('refresh');
13993 // if we are using something like 'domtemplate', then
13994 // the what gets used is:
13995 // t.applySubtemplate(NAME, data, wrapping data..)
13996 // the outer template then get' applied with
13997 // the store 'extra data'
13998 // and the body get's added to the
13999 // roo-name="data" node?
14000 // <span class='roo-tpl-{name}'></span> ?????
14004 this.clearSelections();
14005 this.el.update("");
14007 var records = this.store.getRange();
14008 if(records.length < 1) {
14010 // is this valid?? = should it render a template??
14012 this.el.update(this.emptyText);
14016 if (this.dataName) {
14017 this.el.update(t.apply(this.store.meta)); //????
14018 el = this.el.child('.roo-tpl-' + this.dataName);
14021 for(var i = 0, len = records.length; i < len; i++){
14022 var data = this.prepareData(records[i].data, i, records[i]);
14023 this.fireEvent("preparedata", this, data, i, records[i]);
14025 var d = Roo.apply({}, data);
14028 Roo.apply(d, {'roo-id' : Roo.id()});
14032 Roo.each(this.parent.item, function(item){
14033 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14036 Roo.apply(d, {'roo-data-checked' : 'checked'});
14040 html[html.length] = Roo.util.Format.trim(
14042 t.applySubtemplate(this.dataName, d, this.store.meta) :
14049 el.update(html.join(""));
14050 this.nodes = el.dom.childNodes;
14051 this.updateIndexes(0);
14056 * Function to override to reformat the data that is sent to
14057 * the template for each node.
14058 * DEPRICATED - use the preparedata event handler.
14059 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14060 * a JSON object for an UpdateManager bound view).
14062 prepareData : function(data, index, record)
14064 this.fireEvent("preparedata", this, data, index, record);
14068 onUpdate : function(ds, record){
14069 // Roo.log('on update');
14070 this.clearSelections();
14071 var index = this.store.indexOf(record);
14072 var n = this.nodes[index];
14073 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14074 n.parentNode.removeChild(n);
14075 this.updateIndexes(index, index);
14081 onAdd : function(ds, records, index)
14083 //Roo.log(['on Add', ds, records, index] );
14084 this.clearSelections();
14085 if(this.nodes.length == 0){
14089 var n = this.nodes[index];
14090 for(var i = 0, len = records.length; i < len; i++){
14091 var d = this.prepareData(records[i].data, i, records[i]);
14093 this.tpl.insertBefore(n, d);
14096 this.tpl.append(this.el, d);
14099 this.updateIndexes(index);
14102 onRemove : function(ds, record, index){
14103 // Roo.log('onRemove');
14104 this.clearSelections();
14105 var el = this.dataName ?
14106 this.el.child('.roo-tpl-' + this.dataName) :
14109 el.dom.removeChild(this.nodes[index]);
14110 this.updateIndexes(index);
14114 * Refresh an individual node.
14115 * @param {Number} index
14117 refreshNode : function(index){
14118 this.onUpdate(this.store, this.store.getAt(index));
14121 updateIndexes : function(startIndex, endIndex){
14122 var ns = this.nodes;
14123 startIndex = startIndex || 0;
14124 endIndex = endIndex || ns.length - 1;
14125 for(var i = startIndex; i <= endIndex; i++){
14126 ns[i].nodeIndex = i;
14131 * Changes the data store this view uses and refresh the view.
14132 * @param {Store} store
14134 setStore : function(store, initial){
14135 if(!initial && this.store){
14136 this.store.un("datachanged", this.refresh);
14137 this.store.un("add", this.onAdd);
14138 this.store.un("remove", this.onRemove);
14139 this.store.un("update", this.onUpdate);
14140 this.store.un("clear", this.refresh);
14141 this.store.un("beforeload", this.onBeforeLoad);
14142 this.store.un("load", this.onLoad);
14143 this.store.un("loadexception", this.onLoad);
14147 store.on("datachanged", this.refresh, this);
14148 store.on("add", this.onAdd, this);
14149 store.on("remove", this.onRemove, this);
14150 store.on("update", this.onUpdate, this);
14151 store.on("clear", this.refresh, this);
14152 store.on("beforeload", this.onBeforeLoad, this);
14153 store.on("load", this.onLoad, this);
14154 store.on("loadexception", this.onLoad, this);
14162 * onbeforeLoad - masks the loading area.
14165 onBeforeLoad : function(store,opts)
14167 //Roo.log('onBeforeLoad');
14169 this.el.update("");
14171 this.el.mask(this.mask ? this.mask : "Loading" );
14173 onLoad : function ()
14180 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14181 * @param {HTMLElement} node
14182 * @return {HTMLElement} The template node
14184 findItemFromChild : function(node){
14185 var el = this.dataName ?
14186 this.el.child('.roo-tpl-' + this.dataName,true) :
14189 if(!node || node.parentNode == el){
14192 var p = node.parentNode;
14193 while(p && p != el){
14194 if(p.parentNode == el){
14203 onClick : function(e){
14204 var item = this.findItemFromChild(e.getTarget());
14206 var index = this.indexOf(item);
14207 if(this.onItemClick(item, index, e) !== false){
14208 this.fireEvent("click", this, index, item, e);
14211 this.clearSelections();
14216 onContextMenu : function(e){
14217 var item = this.findItemFromChild(e.getTarget());
14219 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14224 onDblClick : function(e){
14225 var item = this.findItemFromChild(e.getTarget());
14227 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14231 onItemClick : function(item, index, e)
14233 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14236 if (this.toggleSelect) {
14237 var m = this.isSelected(item) ? 'unselect' : 'select';
14240 _t[m](item, true, false);
14243 if(this.multiSelect || this.singleSelect){
14244 if(this.multiSelect && e.shiftKey && this.lastSelection){
14245 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14247 this.select(item, this.multiSelect && e.ctrlKey);
14248 this.lastSelection = item;
14251 if(!this.tickable){
14252 e.preventDefault();
14260 * Get the number of selected nodes.
14263 getSelectionCount : function(){
14264 return this.selections.length;
14268 * Get the currently selected nodes.
14269 * @return {Array} An array of HTMLElements
14271 getSelectedNodes : function(){
14272 return this.selections;
14276 * Get the indexes of the selected nodes.
14279 getSelectedIndexes : function(){
14280 var indexes = [], s = this.selections;
14281 for(var i = 0, len = s.length; i < len; i++){
14282 indexes.push(s[i].nodeIndex);
14288 * Clear all selections
14289 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14291 clearSelections : function(suppressEvent){
14292 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14293 this.cmp.elements = this.selections;
14294 this.cmp.removeClass(this.selectedClass);
14295 this.selections = [];
14296 if(!suppressEvent){
14297 this.fireEvent("selectionchange", this, this.selections);
14303 * Returns true if the passed node is selected
14304 * @param {HTMLElement/Number} node The node or node index
14305 * @return {Boolean}
14307 isSelected : function(node){
14308 var s = this.selections;
14312 node = this.getNode(node);
14313 return s.indexOf(node) !== -1;
14318 * @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
14319 * @param {Boolean} keepExisting (optional) true to keep existing selections
14320 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14322 select : function(nodeInfo, keepExisting, suppressEvent){
14323 if(nodeInfo instanceof Array){
14325 this.clearSelections(true);
14327 for(var i = 0, len = nodeInfo.length; i < len; i++){
14328 this.select(nodeInfo[i], true, true);
14332 var node = this.getNode(nodeInfo);
14333 if(!node || this.isSelected(node)){
14334 return; // already selected.
14337 this.clearSelections(true);
14340 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14341 Roo.fly(node).addClass(this.selectedClass);
14342 this.selections.push(node);
14343 if(!suppressEvent){
14344 this.fireEvent("selectionchange", this, this.selections);
14352 * @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
14353 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14354 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14356 unselect : function(nodeInfo, keepExisting, suppressEvent)
14358 if(nodeInfo instanceof Array){
14359 Roo.each(this.selections, function(s) {
14360 this.unselect(s, nodeInfo);
14364 var node = this.getNode(nodeInfo);
14365 if(!node || !this.isSelected(node)){
14366 //Roo.log("not selected");
14367 return; // not selected.
14371 Roo.each(this.selections, function(s) {
14373 Roo.fly(node).removeClass(this.selectedClass);
14380 this.selections= ns;
14381 this.fireEvent("selectionchange", this, this.selections);
14385 * Gets a template node.
14386 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14387 * @return {HTMLElement} The node or null if it wasn't found
14389 getNode : function(nodeInfo){
14390 if(typeof nodeInfo == "string"){
14391 return document.getElementById(nodeInfo);
14392 }else if(typeof nodeInfo == "number"){
14393 return this.nodes[nodeInfo];
14399 * Gets a range template nodes.
14400 * @param {Number} startIndex
14401 * @param {Number} endIndex
14402 * @return {Array} An array of nodes
14404 getNodes : function(start, end){
14405 var ns = this.nodes;
14406 start = start || 0;
14407 end = typeof end == "undefined" ? ns.length - 1 : end;
14410 for(var i = start; i <= end; i++){
14414 for(var i = start; i >= end; i--){
14422 * Finds the index of the passed node
14423 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14424 * @return {Number} The index of the node or -1
14426 indexOf : function(node){
14427 node = this.getNode(node);
14428 if(typeof node.nodeIndex == "number"){
14429 return node.nodeIndex;
14431 var ns = this.nodes;
14432 for(var i = 0, len = ns.length; i < len; i++){
14443 * based on jquery fullcalendar
14447 Roo.bootstrap = Roo.bootstrap || {};
14449 * @class Roo.bootstrap.Calendar
14450 * @extends Roo.bootstrap.Component
14451 * Bootstrap Calendar class
14452 * @cfg {Boolean} loadMask (true|false) default false
14453 * @cfg {Object} header generate the user specific header of the calendar, default false
14456 * Create a new Container
14457 * @param {Object} config The config object
14462 Roo.bootstrap.Calendar = function(config){
14463 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14467 * Fires when a date is selected
14468 * @param {DatePicker} this
14469 * @param {Date} date The selected date
14473 * @event monthchange
14474 * Fires when the displayed month changes
14475 * @param {DatePicker} this
14476 * @param {Date} date The selected month
14478 'monthchange': true,
14480 * @event evententer
14481 * Fires when mouse over an event
14482 * @param {Calendar} this
14483 * @param {event} Event
14485 'evententer': true,
14487 * @event eventleave
14488 * Fires when the mouse leaves an
14489 * @param {Calendar} this
14492 'eventleave': true,
14494 * @event eventclick
14495 * Fires when the mouse click an
14496 * @param {Calendar} this
14505 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14508 * @cfg {Number} startDay
14509 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14517 getAutoCreate : function(){
14520 var fc_button = function(name, corner, style, content ) {
14521 return Roo.apply({},{
14523 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14525 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14528 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14539 style : 'width:100%',
14546 cls : 'fc-header-left',
14548 fc_button('prev', 'left', 'arrow', '‹' ),
14549 fc_button('next', 'right', 'arrow', '›' ),
14550 { tag: 'span', cls: 'fc-header-space' },
14551 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14559 cls : 'fc-header-center',
14563 cls: 'fc-header-title',
14566 html : 'month / year'
14574 cls : 'fc-header-right',
14576 /* fc_button('month', 'left', '', 'month' ),
14577 fc_button('week', '', '', 'week' ),
14578 fc_button('day', 'right', '', 'day' )
14590 header = this.header;
14593 var cal_heads = function() {
14595 // fixme - handle this.
14597 for (var i =0; i < Date.dayNames.length; i++) {
14598 var d = Date.dayNames[i];
14601 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14602 html : d.substring(0,3)
14606 ret[0].cls += ' fc-first';
14607 ret[6].cls += ' fc-last';
14610 var cal_cell = function(n) {
14613 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14618 cls: 'fc-day-number',
14622 cls: 'fc-day-content',
14626 style: 'position: relative;' // height: 17px;
14638 var cal_rows = function() {
14641 for (var r = 0; r < 6; r++) {
14648 for (var i =0; i < Date.dayNames.length; i++) {
14649 var d = Date.dayNames[i];
14650 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14653 row.cn[0].cls+=' fc-first';
14654 row.cn[0].cn[0].style = 'min-height:90px';
14655 row.cn[6].cls+=' fc-last';
14659 ret[0].cls += ' fc-first';
14660 ret[4].cls += ' fc-prev-last';
14661 ret[5].cls += ' fc-last';
14668 cls: 'fc-border-separate',
14669 style : 'width:100%',
14677 cls : 'fc-first fc-last',
14695 cls : 'fc-content',
14696 style : "position: relative;",
14699 cls : 'fc-view fc-view-month fc-grid',
14700 style : 'position: relative',
14701 unselectable : 'on',
14704 cls : 'fc-event-container',
14705 style : 'position:absolute;z-index:8;top:0;left:0;'
14723 initEvents : function()
14726 throw "can not find store for calendar";
14732 style: "text-align:center",
14736 style: "background-color:white;width:50%;margin:250 auto",
14740 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14751 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14753 var size = this.el.select('.fc-content', true).first().getSize();
14754 this.maskEl.setSize(size.width, size.height);
14755 this.maskEl.enableDisplayMode("block");
14756 if(!this.loadMask){
14757 this.maskEl.hide();
14760 this.store = Roo.factory(this.store, Roo.data);
14761 this.store.on('load', this.onLoad, this);
14762 this.store.on('beforeload', this.onBeforeLoad, this);
14766 this.cells = this.el.select('.fc-day',true);
14767 //Roo.log(this.cells);
14768 this.textNodes = this.el.query('.fc-day-number');
14769 this.cells.addClassOnOver('fc-state-hover');
14771 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14772 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14773 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14774 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14776 this.on('monthchange', this.onMonthChange, this);
14778 this.update(new Date().clearTime());
14781 resize : function() {
14782 var sz = this.el.getSize();
14784 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14785 this.el.select('.fc-day-content div',true).setHeight(34);
14790 showPrevMonth : function(e){
14791 this.update(this.activeDate.add("mo", -1));
14793 showToday : function(e){
14794 this.update(new Date().clearTime());
14797 showNextMonth : function(e){
14798 this.update(this.activeDate.add("mo", 1));
14802 showPrevYear : function(){
14803 this.update(this.activeDate.add("y", -1));
14807 showNextYear : function(){
14808 this.update(this.activeDate.add("y", 1));
14813 update : function(date)
14815 var vd = this.activeDate;
14816 this.activeDate = date;
14817 // if(vd && this.el){
14818 // var t = date.getTime();
14819 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14820 // Roo.log('using add remove');
14822 // this.fireEvent('monthchange', this, date);
14824 // this.cells.removeClass("fc-state-highlight");
14825 // this.cells.each(function(c){
14826 // if(c.dateValue == t){
14827 // c.addClass("fc-state-highlight");
14828 // setTimeout(function(){
14829 // try{c.dom.firstChild.focus();}catch(e){}
14839 var days = date.getDaysInMonth();
14841 var firstOfMonth = date.getFirstDateOfMonth();
14842 var startingPos = firstOfMonth.getDay()-this.startDay;
14844 if(startingPos < this.startDay){
14848 var pm = date.add(Date.MONTH, -1);
14849 var prevStart = pm.getDaysInMonth()-startingPos;
14851 this.cells = this.el.select('.fc-day',true);
14852 this.textNodes = this.el.query('.fc-day-number');
14853 this.cells.addClassOnOver('fc-state-hover');
14855 var cells = this.cells.elements;
14856 var textEls = this.textNodes;
14858 Roo.each(cells, function(cell){
14859 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14862 days += startingPos;
14864 // convert everything to numbers so it's fast
14865 var day = 86400000;
14866 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14869 //Roo.log(prevStart);
14871 var today = new Date().clearTime().getTime();
14872 var sel = date.clearTime().getTime();
14873 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14874 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14875 var ddMatch = this.disabledDatesRE;
14876 var ddText = this.disabledDatesText;
14877 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14878 var ddaysText = this.disabledDaysText;
14879 var format = this.format;
14881 var setCellClass = function(cal, cell){
14885 //Roo.log('set Cell Class');
14887 var t = d.getTime();
14891 cell.dateValue = t;
14893 cell.className += " fc-today";
14894 cell.className += " fc-state-highlight";
14895 cell.title = cal.todayText;
14898 // disable highlight in other month..
14899 //cell.className += " fc-state-highlight";
14904 cell.className = " fc-state-disabled";
14905 cell.title = cal.minText;
14909 cell.className = " fc-state-disabled";
14910 cell.title = cal.maxText;
14914 if(ddays.indexOf(d.getDay()) != -1){
14915 cell.title = ddaysText;
14916 cell.className = " fc-state-disabled";
14919 if(ddMatch && format){
14920 var fvalue = d.dateFormat(format);
14921 if(ddMatch.test(fvalue)){
14922 cell.title = ddText.replace("%0", fvalue);
14923 cell.className = " fc-state-disabled";
14927 if (!cell.initialClassName) {
14928 cell.initialClassName = cell.dom.className;
14931 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14936 for(; i < startingPos; i++) {
14937 textEls[i].innerHTML = (++prevStart);
14938 d.setDate(d.getDate()+1);
14940 cells[i].className = "fc-past fc-other-month";
14941 setCellClass(this, cells[i]);
14946 for(; i < days; i++){
14947 intDay = i - startingPos + 1;
14948 textEls[i].innerHTML = (intDay);
14949 d.setDate(d.getDate()+1);
14951 cells[i].className = ''; // "x-date-active";
14952 setCellClass(this, cells[i]);
14956 for(; i < 42; i++) {
14957 textEls[i].innerHTML = (++extraDays);
14958 d.setDate(d.getDate()+1);
14960 cells[i].className = "fc-future fc-other-month";
14961 setCellClass(this, cells[i]);
14964 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14966 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14968 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14969 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14971 if(totalRows != 6){
14972 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14973 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14976 this.fireEvent('monthchange', this, date);
14980 if(!this.internalRender){
14981 var main = this.el.dom.firstChild;
14982 var w = main.offsetWidth;
14983 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14984 Roo.fly(main).setWidth(w);
14985 this.internalRender = true;
14986 // opera does not respect the auto grow header center column
14987 // then, after it gets a width opera refuses to recalculate
14988 // without a second pass
14989 if(Roo.isOpera && !this.secondPass){
14990 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14991 this.secondPass = true;
14992 this.update.defer(10, this, [date]);
14999 findCell : function(dt) {
15000 dt = dt.clearTime().getTime();
15002 this.cells.each(function(c){
15003 //Roo.log("check " +c.dateValue + '?=' + dt);
15004 if(c.dateValue == dt){
15014 findCells : function(ev) {
15015 var s = ev.start.clone().clearTime().getTime();
15017 var e= ev.end.clone().clearTime().getTime();
15020 this.cells.each(function(c){
15021 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15023 if(c.dateValue > e){
15026 if(c.dateValue < s){
15035 // findBestRow: function(cells)
15039 // for (var i =0 ; i < cells.length;i++) {
15040 // ret = Math.max(cells[i].rows || 0,ret);
15047 addItem : function(ev)
15049 // look for vertical location slot in
15050 var cells = this.findCells(ev);
15052 // ev.row = this.findBestRow(cells);
15054 // work out the location.
15058 for(var i =0; i < cells.length; i++) {
15060 cells[i].row = cells[0].row;
15063 cells[i].row = cells[i].row + 1;
15073 if (crow.start.getY() == cells[i].getY()) {
15075 crow.end = cells[i];
15092 cells[0].events.push(ev);
15094 this.calevents.push(ev);
15097 clearEvents: function() {
15099 if(!this.calevents){
15103 Roo.each(this.cells.elements, function(c){
15109 Roo.each(this.calevents, function(e) {
15110 Roo.each(e.els, function(el) {
15111 el.un('mouseenter' ,this.onEventEnter, this);
15112 el.un('mouseleave' ,this.onEventLeave, this);
15117 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15123 renderEvents: function()
15127 this.cells.each(function(c) {
15136 if(c.row != c.events.length){
15137 r = 4 - (4 - (c.row - c.events.length));
15140 c.events = ev.slice(0, r);
15141 c.more = ev.slice(r);
15143 if(c.more.length && c.more.length == 1){
15144 c.events.push(c.more.pop());
15147 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15151 this.cells.each(function(c) {
15153 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15156 for (var e = 0; e < c.events.length; e++){
15157 var ev = c.events[e];
15158 var rows = ev.rows;
15160 for(var i = 0; i < rows.length; i++) {
15162 // how many rows should it span..
15165 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15166 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15168 unselectable : "on",
15171 cls: 'fc-event-inner',
15175 // cls: 'fc-event-time',
15176 // html : cells.length > 1 ? '' : ev.time
15180 cls: 'fc-event-title',
15181 html : String.format('{0}', ev.title)
15188 cls: 'ui-resizable-handle ui-resizable-e',
15189 html : '  '
15196 cfg.cls += ' fc-event-start';
15198 if ((i+1) == rows.length) {
15199 cfg.cls += ' fc-event-end';
15202 var ctr = _this.el.select('.fc-event-container',true).first();
15203 var cg = ctr.createChild(cfg);
15205 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15206 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15208 var r = (c.more.length) ? 1 : 0;
15209 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15210 cg.setWidth(ebox.right - sbox.x -2);
15212 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15213 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15214 cg.on('click', _this.onEventClick, _this, ev);
15225 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15226 style : 'position: absolute',
15227 unselectable : "on",
15230 cls: 'fc-event-inner',
15234 cls: 'fc-event-title',
15242 cls: 'ui-resizable-handle ui-resizable-e',
15243 html : '  '
15249 var ctr = _this.el.select('.fc-event-container',true).first();
15250 var cg = ctr.createChild(cfg);
15252 var sbox = c.select('.fc-day-content',true).first().getBox();
15253 var ebox = c.select('.fc-day-content',true).first().getBox();
15255 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15256 cg.setWidth(ebox.right - sbox.x -2);
15258 cg.on('click', _this.onMoreEventClick, _this, c.more);
15268 onEventEnter: function (e, el,event,d) {
15269 this.fireEvent('evententer', this, el, event);
15272 onEventLeave: function (e, el,event,d) {
15273 this.fireEvent('eventleave', this, el, event);
15276 onEventClick: function (e, el,event,d) {
15277 this.fireEvent('eventclick', this, el, event);
15280 onMonthChange: function () {
15284 onMoreEventClick: function(e, el, more)
15288 this.calpopover.placement = 'right';
15289 this.calpopover.setTitle('More');
15291 this.calpopover.setContent('');
15293 var ctr = this.calpopover.el.select('.popover-content', true).first();
15295 Roo.each(more, function(m){
15297 cls : 'fc-event-hori fc-event-draggable',
15300 var cg = ctr.createChild(cfg);
15302 cg.on('click', _this.onEventClick, _this, m);
15305 this.calpopover.show(el);
15310 onLoad: function ()
15312 this.calevents = [];
15315 if(this.store.getCount() > 0){
15316 this.store.data.each(function(d){
15319 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15320 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15321 time : d.data.start_time,
15322 title : d.data.title,
15323 description : d.data.description,
15324 venue : d.data.venue
15329 this.renderEvents();
15331 if(this.calevents.length && this.loadMask){
15332 this.maskEl.hide();
15336 onBeforeLoad: function()
15338 this.clearEvents();
15340 this.maskEl.show();
15354 * @class Roo.bootstrap.Popover
15355 * @extends Roo.bootstrap.Component
15356 * Bootstrap Popover class
15357 * @cfg {String} html contents of the popover (or false to use children..)
15358 * @cfg {String} title of popover (or false to hide)
15359 * @cfg {String} placement how it is placed
15360 * @cfg {String} trigger click || hover (or false to trigger manually)
15361 * @cfg {String} over what (parent or false to trigger manually.)
15362 * @cfg {Number} delay - delay before showing
15365 * Create a new Popover
15366 * @param {Object} config The config object
15369 Roo.bootstrap.Popover = function(config){
15370 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15373 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15375 title: 'Fill in a title',
15378 placement : 'right',
15379 trigger : 'hover', // hover
15385 can_build_overlaid : false,
15387 getChildContainer : function()
15389 return this.el.select('.popover-content',true).first();
15392 getAutoCreate : function(){
15393 Roo.log('make popover?');
15395 cls : 'popover roo-dynamic',
15396 style: 'display:block',
15402 cls : 'popover-inner',
15406 cls: 'popover-title',
15410 cls : 'popover-content',
15421 setTitle: function(str)
15424 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15426 setContent: function(str)
15429 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15431 // as it get's added to the bottom of the page.
15432 onRender : function(ct, position)
15434 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15436 var cfg = Roo.apply({}, this.getAutoCreate());
15440 cfg.cls += ' ' + this.cls;
15443 cfg.style = this.style;
15445 Roo.log("adding to ")
15446 this.el = Roo.get(document.body).createChild(cfg, position);
15452 initEvents : function()
15454 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15455 this.el.enableDisplayMode('block');
15457 if (this.over === false) {
15460 if (this.triggers === false) {
15463 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15464 var triggers = this.trigger ? this.trigger.split(' ') : [];
15465 Roo.each(triggers, function(trigger) {
15467 if (trigger == 'click') {
15468 on_el.on('click', this.toggle, this);
15469 } else if (trigger != 'manual') {
15470 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15471 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15473 on_el.on(eventIn ,this.enter, this);
15474 on_el.on(eventOut, this.leave, this);
15485 toggle : function () {
15486 this.hoverState == 'in' ? this.leave() : this.enter();
15489 enter : function () {
15492 clearTimeout(this.timeout);
15494 this.hoverState = 'in';
15496 if (!this.delay || !this.delay.show) {
15501 this.timeout = setTimeout(function () {
15502 if (_t.hoverState == 'in') {
15505 }, this.delay.show)
15507 leave : function() {
15508 clearTimeout(this.timeout);
15510 this.hoverState = 'out';
15512 if (!this.delay || !this.delay.hide) {
15517 this.timeout = setTimeout(function () {
15518 if (_t.hoverState == 'out') {
15521 }, this.delay.hide)
15524 show : function (on_el)
15527 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15530 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15531 if (this.html !== false) {
15532 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15534 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15535 if (!this.title.length) {
15536 this.el.select('.popover-title',true).hide();
15539 var placement = typeof this.placement == 'function' ?
15540 this.placement.call(this, this.el, on_el) :
15543 var autoToken = /\s?auto?\s?/i;
15544 var autoPlace = autoToken.test(placement);
15546 placement = placement.replace(autoToken, '') || 'top';
15550 //this.el.setXY([0,0]);
15552 this.el.dom.style.display='block';
15553 this.el.addClass(placement);
15555 //this.el.appendTo(on_el);
15557 var p = this.getPosition();
15558 var box = this.el.getBox();
15563 var align = Roo.bootstrap.Popover.alignment[placement];
15564 this.el.alignTo(on_el, align[0],align[1]);
15565 //var arrow = this.el.select('.arrow',true).first();
15566 //arrow.set(align[2],
15568 this.el.addClass('in');
15571 if (this.el.hasClass('fade')) {
15578 this.el.setXY([0,0]);
15579 this.el.removeClass('in');
15581 this.hoverState = null;
15587 Roo.bootstrap.Popover.alignment = {
15588 'left' : ['r-l', [-10,0], 'right'],
15589 'right' : ['l-r', [10,0], 'left'],
15590 'bottom' : ['t-b', [0,10], 'top'],
15591 'top' : [ 'b-t', [0,-10], 'bottom']
15602 * @class Roo.bootstrap.Progress
15603 * @extends Roo.bootstrap.Component
15604 * Bootstrap Progress class
15605 * @cfg {Boolean} striped striped of the progress bar
15606 * @cfg {Boolean} active animated of the progress bar
15610 * Create a new Progress
15611 * @param {Object} config The config object
15614 Roo.bootstrap.Progress = function(config){
15615 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15618 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15623 getAutoCreate : function(){
15631 cfg.cls += ' progress-striped';
15635 cfg.cls += ' active';
15654 * @class Roo.bootstrap.ProgressBar
15655 * @extends Roo.bootstrap.Component
15656 * Bootstrap ProgressBar class
15657 * @cfg {Number} aria_valuenow aria-value now
15658 * @cfg {Number} aria_valuemin aria-value min
15659 * @cfg {Number} aria_valuemax aria-value max
15660 * @cfg {String} label label for the progress bar
15661 * @cfg {String} panel (success | info | warning | danger )
15662 * @cfg {String} role role of the progress bar
15663 * @cfg {String} sr_only text
15667 * Create a new ProgressBar
15668 * @param {Object} config The config object
15671 Roo.bootstrap.ProgressBar = function(config){
15672 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15675 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15679 aria_valuemax : 100,
15685 getAutoCreate : function()
15690 cls: 'progress-bar',
15691 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15703 cfg.role = this.role;
15706 if(this.aria_valuenow){
15707 cfg['aria-valuenow'] = this.aria_valuenow;
15710 if(this.aria_valuemin){
15711 cfg['aria-valuemin'] = this.aria_valuemin;
15714 if(this.aria_valuemax){
15715 cfg['aria-valuemax'] = this.aria_valuemax;
15718 if(this.label && !this.sr_only){
15719 cfg.html = this.label;
15723 cfg.cls += ' progress-bar-' + this.panel;
15729 update : function(aria_valuenow)
15731 this.aria_valuenow = aria_valuenow;
15733 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15748 * @class Roo.bootstrap.TabGroup
15749 * @extends Roo.bootstrap.Column
15750 * Bootstrap Column class
15751 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15752 * @cfg {Boolean} carousel true to make the group behave like a carousel
15753 * @cfg {Number} bullets show the panel pointer.. default 0
15754 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15755 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15756 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15759 * Create a new TabGroup
15760 * @param {Object} config The config object
15763 Roo.bootstrap.TabGroup = function(config){
15764 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15766 this.navId = Roo.id();
15769 Roo.bootstrap.TabGroup.register(this);
15773 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15776 transition : false,
15781 slideOnTouch : false,
15783 getAutoCreate : function()
15785 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15787 cfg.cls += ' tab-content';
15789 Roo.log('get auto create...............');
15791 if (this.carousel) {
15792 cfg.cls += ' carousel slide';
15795 cls : 'carousel-inner'
15798 if(this.bullets > 0 && !Roo.isTouch){
15801 cls : 'carousel-bullets',
15805 if(this.bullets_cls){
15806 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15809 for (var i = 0; i < this.bullets; i++){
15811 cls : 'bullet bullet-' + i
15819 cfg.cn[0].cn = bullets;
15826 initEvents: function()
15828 Roo.log('-------- init events on tab group ---------');
15830 if(this.bullets > 0 && !Roo.isTouch){
15836 if(Roo.isTouch && this.slideOnTouch){
15837 this.el.on("touchstart", this.onTouchStart, this);
15840 if(this.autoslide){
15843 this.slideFn = window.setInterval(function() {
15844 _this.showPanelNext();
15850 onTouchStart : function(e, el, o)
15852 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15856 this.showPanelNext();
15859 getChildContainer : function()
15861 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15865 * register a Navigation item
15866 * @param {Roo.bootstrap.NavItem} the navitem to add
15868 register : function(item)
15870 this.tabs.push( item);
15871 item.navId = this.navId; // not really needed..
15875 getActivePanel : function()
15878 Roo.each(this.tabs, function(t) {
15888 getPanelByName : function(n)
15891 Roo.each(this.tabs, function(t) {
15892 if (t.tabId == n) {
15900 indexOfPanel : function(p)
15903 Roo.each(this.tabs, function(t,i) {
15904 if (t.tabId == p.tabId) {
15913 * show a specific panel
15914 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15915 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15917 showPanel : function (pan)
15919 if(this.transition){
15920 Roo.log("waiting for the transitionend");
15924 if (typeof(pan) == 'number') {
15925 pan = this.tabs[pan];
15927 if (typeof(pan) == 'string') {
15928 pan = this.getPanelByName(pan);
15930 if (pan.tabId == this.getActivePanel().tabId) {
15933 var cur = this.getActivePanel();
15935 if (false === cur.fireEvent('beforedeactivate')) {
15939 if(this.bullets > 0 && !Roo.isTouch){
15940 this.setActiveBullet(this.indexOfPanel(pan));
15943 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15945 this.transition = true;
15946 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15947 var lr = dir == 'next' ? 'left' : 'right';
15948 pan.el.addClass(dir); // or prev
15949 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15950 cur.el.addClass(lr); // or right
15951 pan.el.addClass(lr);
15954 cur.el.on('transitionend', function() {
15955 Roo.log("trans end?");
15957 pan.el.removeClass([lr,dir]);
15958 pan.setActive(true);
15960 cur.el.removeClass([lr]);
15961 cur.setActive(false);
15963 _this.transition = false;
15965 }, this, { single: true } );
15970 cur.setActive(false);
15971 pan.setActive(true);
15976 showPanelNext : function()
15978 var i = this.indexOfPanel(this.getActivePanel());
15980 if (i >= this.tabs.length - 1 && !this.autoslide) {
15984 if (i >= this.tabs.length - 1 && this.autoslide) {
15988 this.showPanel(this.tabs[i+1]);
15991 showPanelPrev : function()
15993 var i = this.indexOfPanel(this.getActivePanel());
15995 if (i < 1 && !this.autoslide) {
15999 if (i < 1 && this.autoslide) {
16000 i = this.tabs.length;
16003 this.showPanel(this.tabs[i-1]);
16006 initBullet : function()
16014 for (var i = 0; i < this.bullets; i++){
16015 var bullet = this.el.select('.bullet-' + i, true).first();
16021 bullet.on('click', (function(e, el, o, ii, t){
16023 e.preventDefault();
16025 _this.showPanel(ii);
16027 if(_this.autoslide && _this.slideFn){
16028 clearInterval(_this.slideFn);
16029 _this.slideFn = window.setInterval(function() {
16030 _this.showPanelNext();
16034 }).createDelegate(this, [i, bullet], true));
16038 setActiveBullet : function(i)
16044 Roo.each(this.el.select('.bullet', true).elements, function(el){
16045 el.removeClass('selected');
16048 var bullet = this.el.select('.bullet-' + i, true).first();
16054 bullet.addClass('selected');
16065 Roo.apply(Roo.bootstrap.TabGroup, {
16069 * register a Navigation Group
16070 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16072 register : function(navgrp)
16074 this.groups[navgrp.navId] = navgrp;
16078 * fetch a Navigation Group based on the navigation ID
16079 * if one does not exist , it will get created.
16080 * @param {string} the navgroup to add
16081 * @returns {Roo.bootstrap.NavGroup} the navgroup
16083 get: function(navId) {
16084 if (typeof(this.groups[navId]) == 'undefined') {
16085 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16087 return this.groups[navId] ;
16102 * @class Roo.bootstrap.TabPanel
16103 * @extends Roo.bootstrap.Component
16104 * Bootstrap TabPanel class
16105 * @cfg {Boolean} active panel active
16106 * @cfg {String} html panel content
16107 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16108 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16112 * Create a new TabPanel
16113 * @param {Object} config The config object
16116 Roo.bootstrap.TabPanel = function(config){
16117 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16121 * Fires when the active status changes
16122 * @param {Roo.bootstrap.TabPanel} this
16123 * @param {Boolean} state the new state
16128 * @event beforedeactivate
16129 * Fires before a tab is de-activated - can be used to do validation on a form.
16130 * @param {Roo.bootstrap.TabPanel} this
16131 * @return {Boolean} false if there is an error
16134 'beforedeactivate': true
16137 this.tabId = this.tabId || Roo.id();
16141 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16148 getAutoCreate : function(){
16151 // item is needed for carousel - not sure if it has any effect otherwise
16152 cls: 'tab-pane item',
16153 html: this.html || ''
16157 cfg.cls += ' active';
16161 cfg.tabId = this.tabId;
16168 initEvents: function()
16170 Roo.log('-------- init events on tab panel ---------');
16172 var p = this.parent();
16173 this.navId = this.navId || p.navId;
16175 if (typeof(this.navId) != 'undefined') {
16176 // not really needed.. but just in case.. parent should be a NavGroup.
16177 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16178 Roo.log(['register', tg, this]);
16181 var i = tg.tabs.length - 1;
16183 if(this.active && tg.bullets > 0 && i < tg.bullets){
16184 tg.setActiveBullet(i);
16191 onRender : function(ct, position)
16193 // Roo.log("Call onRender: " + this.xtype);
16195 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16203 setActive: function(state)
16205 Roo.log("panel - set active " + this.tabId + "=" + state);
16207 this.active = state;
16209 this.el.removeClass('active');
16211 } else if (!this.el.hasClass('active')) {
16212 this.el.addClass('active');
16215 this.fireEvent('changed', this, state);
16232 * @class Roo.bootstrap.DateField
16233 * @extends Roo.bootstrap.Input
16234 * Bootstrap DateField class
16235 * @cfg {Number} weekStart default 0
16236 * @cfg {String} viewMode default empty, (months|years)
16237 * @cfg {String} minViewMode default empty, (months|years)
16238 * @cfg {Number} startDate default -Infinity
16239 * @cfg {Number} endDate default Infinity
16240 * @cfg {Boolean} todayHighlight default false
16241 * @cfg {Boolean} todayBtn default false
16242 * @cfg {Boolean} calendarWeeks default false
16243 * @cfg {Object} daysOfWeekDisabled default empty
16244 * @cfg {Boolean} singleMode default false (true | false)
16246 * @cfg {Boolean} keyboardNavigation default true
16247 * @cfg {String} language default en
16250 * Create a new DateField
16251 * @param {Object} config The config object
16254 Roo.bootstrap.DateField = function(config){
16255 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16259 * Fires when this field show.
16260 * @param {Roo.bootstrap.DateField} this
16261 * @param {Mixed} date The date value
16266 * Fires when this field hide.
16267 * @param {Roo.bootstrap.DateField} this
16268 * @param {Mixed} date The date value
16273 * Fires when select a date.
16274 * @param {Roo.bootstrap.DateField} this
16275 * @param {Mixed} date The date value
16281 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16284 * @cfg {String} format
16285 * The default date format string which can be overriden for localization support. The format must be
16286 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16290 * @cfg {String} altFormats
16291 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16292 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16294 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16302 todayHighlight : false,
16308 keyboardNavigation: true,
16310 calendarWeeks: false,
16312 startDate: -Infinity,
16316 daysOfWeekDisabled: [],
16320 singleMode : false,
16322 UTCDate: function()
16324 return new Date(Date.UTC.apply(Date, arguments));
16327 UTCToday: function()
16329 var today = new Date();
16330 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16333 getDate: function() {
16334 var d = this.getUTCDate();
16335 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16338 getUTCDate: function() {
16342 setDate: function(d) {
16343 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16346 setUTCDate: function(d) {
16348 this.setValue(this.formatDate(this.date));
16351 onRender: function(ct, position)
16354 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16356 this.language = this.language || 'en';
16357 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16358 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16360 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16361 this.format = this.format || 'm/d/y';
16362 this.isInline = false;
16363 this.isInput = true;
16364 this.component = this.el.select('.add-on', true).first() || false;
16365 this.component = (this.component && this.component.length === 0) ? false : this.component;
16366 this.hasInput = this.component && this.inputEL().length;
16368 if (typeof(this.minViewMode === 'string')) {
16369 switch (this.minViewMode) {
16371 this.minViewMode = 1;
16374 this.minViewMode = 2;
16377 this.minViewMode = 0;
16382 if (typeof(this.viewMode === 'string')) {
16383 switch (this.viewMode) {
16396 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16398 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16400 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16402 this.picker().on('mousedown', this.onMousedown, this);
16403 this.picker().on('click', this.onClick, this);
16405 this.picker().addClass('datepicker-dropdown');
16407 this.startViewMode = this.viewMode;
16409 if(this.singleMode){
16410 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16411 v.setVisibilityMode(Roo.Element.DISPLAY)
16415 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16416 v.setStyle('width', '189px');
16420 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16421 if(!this.calendarWeeks){
16426 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16427 v.attr('colspan', function(i, val){
16428 return parseInt(val) + 1;
16433 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16435 this.setStartDate(this.startDate);
16436 this.setEndDate(this.endDate);
16438 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16445 if(this.isInline) {
16450 picker : function()
16452 return this.pickerEl;
16453 // return this.el.select('.datepicker', true).first();
16456 fillDow: function()
16458 var dowCnt = this.weekStart;
16467 if(this.calendarWeeks){
16475 while (dowCnt < this.weekStart + 7) {
16479 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16483 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16486 fillMonths: function()
16489 var months = this.picker().select('>.datepicker-months td', true).first();
16491 months.dom.innerHTML = '';
16497 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16500 months.createChild(month);
16507 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;
16509 if (this.date < this.startDate) {
16510 this.viewDate = new Date(this.startDate);
16511 } else if (this.date > this.endDate) {
16512 this.viewDate = new Date(this.endDate);
16514 this.viewDate = new Date(this.date);
16522 var d = new Date(this.viewDate),
16523 year = d.getUTCFullYear(),
16524 month = d.getUTCMonth(),
16525 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16526 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16527 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16528 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16529 currentDate = this.date && this.date.valueOf(),
16530 today = this.UTCToday();
16532 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16534 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16536 // this.picker.select('>tfoot th.today').
16537 // .text(dates[this.language].today)
16538 // .toggle(this.todayBtn !== false);
16540 this.updateNavArrows();
16543 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16545 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16547 prevMonth.setUTCDate(day);
16549 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16551 var nextMonth = new Date(prevMonth);
16553 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16555 nextMonth = nextMonth.valueOf();
16557 var fillMonths = false;
16559 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16561 while(prevMonth.valueOf() < nextMonth) {
16564 if (prevMonth.getUTCDay() === this.weekStart) {
16566 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16574 if(this.calendarWeeks){
16575 // ISO 8601: First week contains first thursday.
16576 // ISO also states week starts on Monday, but we can be more abstract here.
16578 // Start of current week: based on weekstart/current date
16579 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16580 // Thursday of this week
16581 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16582 // First Thursday of year, year from thursday
16583 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16584 // Calendar week: ms between thursdays, div ms per day, div 7 days
16585 calWeek = (th - yth) / 864e5 / 7 + 1;
16587 fillMonths.cn.push({
16595 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16597 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16600 if (this.todayHighlight &&
16601 prevMonth.getUTCFullYear() == today.getFullYear() &&
16602 prevMonth.getUTCMonth() == today.getMonth() &&
16603 prevMonth.getUTCDate() == today.getDate()) {
16604 clsName += ' today';
16607 if (currentDate && prevMonth.valueOf() === currentDate) {
16608 clsName += ' active';
16611 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16612 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16613 clsName += ' disabled';
16616 fillMonths.cn.push({
16618 cls: 'day ' + clsName,
16619 html: prevMonth.getDate()
16622 prevMonth.setDate(prevMonth.getDate()+1);
16625 var currentYear = this.date && this.date.getUTCFullYear();
16626 var currentMonth = this.date && this.date.getUTCMonth();
16628 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16630 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16631 v.removeClass('active');
16633 if(currentYear === year && k === currentMonth){
16634 v.addClass('active');
16637 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16638 v.addClass('disabled');
16644 year = parseInt(year/10, 10) * 10;
16646 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16648 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16651 for (var i = -1; i < 11; i++) {
16652 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16654 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16662 showMode: function(dir)
16665 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16668 Roo.each(this.picker().select('>div',true).elements, function(v){
16669 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16672 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16677 if(this.isInline) return;
16679 this.picker().removeClass(['bottom', 'top']);
16681 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16683 * place to the top of element!
16687 this.picker().addClass('top');
16688 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16693 this.picker().addClass('bottom');
16695 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16698 parseDate : function(value)
16700 if(!value || value instanceof Date){
16703 var v = Date.parseDate(value, this.format);
16704 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16705 v = Date.parseDate(value, 'Y-m-d');
16707 if(!v && this.altFormats){
16708 if(!this.altFormatsArray){
16709 this.altFormatsArray = this.altFormats.split("|");
16711 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16712 v = Date.parseDate(value, this.altFormatsArray[i]);
16718 formatDate : function(date, fmt)
16720 return (!date || !(date instanceof Date)) ?
16721 date : date.dateFormat(fmt || this.format);
16724 onFocus : function()
16726 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16730 onBlur : function()
16732 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16734 var d = this.inputEl().getValue();
16743 this.picker().show();
16747 this.fireEvent('show', this, this.date);
16752 if(this.isInline) return;
16753 this.picker().hide();
16754 this.viewMode = this.startViewMode;
16757 this.fireEvent('hide', this, this.date);
16761 onMousedown: function(e)
16763 e.stopPropagation();
16764 e.preventDefault();
16769 Roo.bootstrap.DateField.superclass.keyup.call(this);
16773 setValue: function(v)
16776 // v can be a string or a date..
16779 var d = new Date(this.parseDate(v) ).clearTime();
16781 if(isNaN(d.getTime())){
16782 this.date = this.viewDate = '';
16783 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16787 v = this.formatDate(d);
16789 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16791 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16795 this.fireEvent('select', this, this.date);
16799 getValue: function()
16801 return this.formatDate(this.date);
16804 fireKey: function(e)
16806 if (!this.picker().isVisible()){
16807 if (e.keyCode == 27) // allow escape to hide and re-show picker
16812 var dateChanged = false,
16814 newDate, newViewDate;
16819 e.preventDefault();
16823 if (!this.keyboardNavigation) break;
16824 dir = e.keyCode == 37 ? -1 : 1;
16827 newDate = this.moveYear(this.date, dir);
16828 newViewDate = this.moveYear(this.viewDate, dir);
16829 } else if (e.shiftKey){
16830 newDate = this.moveMonth(this.date, dir);
16831 newViewDate = this.moveMonth(this.viewDate, dir);
16833 newDate = new Date(this.date);
16834 newDate.setUTCDate(this.date.getUTCDate() + dir);
16835 newViewDate = new Date(this.viewDate);
16836 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16838 if (this.dateWithinRange(newDate)){
16839 this.date = newDate;
16840 this.viewDate = newViewDate;
16841 this.setValue(this.formatDate(this.date));
16843 e.preventDefault();
16844 dateChanged = true;
16849 if (!this.keyboardNavigation) break;
16850 dir = e.keyCode == 38 ? -1 : 1;
16852 newDate = this.moveYear(this.date, dir);
16853 newViewDate = this.moveYear(this.viewDate, dir);
16854 } else if (e.shiftKey){
16855 newDate = this.moveMonth(this.date, dir);
16856 newViewDate = this.moveMonth(this.viewDate, dir);
16858 newDate = new Date(this.date);
16859 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16860 newViewDate = new Date(this.viewDate);
16861 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16863 if (this.dateWithinRange(newDate)){
16864 this.date = newDate;
16865 this.viewDate = newViewDate;
16866 this.setValue(this.formatDate(this.date));
16868 e.preventDefault();
16869 dateChanged = true;
16873 this.setValue(this.formatDate(this.date));
16875 e.preventDefault();
16878 this.setValue(this.formatDate(this.date));
16892 onClick: function(e)
16894 e.stopPropagation();
16895 e.preventDefault();
16897 var target = e.getTarget();
16899 if(target.nodeName.toLowerCase() === 'i'){
16900 target = Roo.get(target).dom.parentNode;
16903 var nodeName = target.nodeName;
16904 var className = target.className;
16905 var html = target.innerHTML;
16906 //Roo.log(nodeName);
16908 switch(nodeName.toLowerCase()) {
16910 switch(className) {
16916 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16917 switch(this.viewMode){
16919 this.viewDate = this.moveMonth(this.viewDate, dir);
16923 this.viewDate = this.moveYear(this.viewDate, dir);
16929 var date = new Date();
16930 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16932 this.setValue(this.formatDate(this.date));
16939 if (className.indexOf('disabled') < 0) {
16940 this.viewDate.setUTCDate(1);
16941 if (className.indexOf('month') > -1) {
16942 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16944 var year = parseInt(html, 10) || 0;
16945 this.viewDate.setUTCFullYear(year);
16949 if(this.singleMode){
16950 this.setValue(this.formatDate(this.viewDate));
16961 //Roo.log(className);
16962 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16963 var day = parseInt(html, 10) || 1;
16964 var year = this.viewDate.getUTCFullYear(),
16965 month = this.viewDate.getUTCMonth();
16967 if (className.indexOf('old') > -1) {
16974 } else if (className.indexOf('new') > -1) {
16982 //Roo.log([year,month,day]);
16983 this.date = this.UTCDate(year, month, day,0,0,0,0);
16984 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16986 //Roo.log(this.formatDate(this.date));
16987 this.setValue(this.formatDate(this.date));
16994 setStartDate: function(startDate)
16996 this.startDate = startDate || -Infinity;
16997 if (this.startDate !== -Infinity) {
16998 this.startDate = this.parseDate(this.startDate);
17001 this.updateNavArrows();
17004 setEndDate: function(endDate)
17006 this.endDate = endDate || Infinity;
17007 if (this.endDate !== Infinity) {
17008 this.endDate = this.parseDate(this.endDate);
17011 this.updateNavArrows();
17014 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17016 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17017 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17018 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17020 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17021 return parseInt(d, 10);
17024 this.updateNavArrows();
17027 updateNavArrows: function()
17029 if(this.singleMode){
17033 var d = new Date(this.viewDate),
17034 year = d.getUTCFullYear(),
17035 month = d.getUTCMonth();
17037 Roo.each(this.picker().select('.prev', true).elements, function(v){
17039 switch (this.viewMode) {
17042 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17048 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17055 Roo.each(this.picker().select('.next', true).elements, function(v){
17057 switch (this.viewMode) {
17060 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17066 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17074 moveMonth: function(date, dir)
17076 if (!dir) return date;
17077 var new_date = new Date(date.valueOf()),
17078 day = new_date.getUTCDate(),
17079 month = new_date.getUTCMonth(),
17080 mag = Math.abs(dir),
17082 dir = dir > 0 ? 1 : -1;
17085 // If going back one month, make sure month is not current month
17086 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17088 return new_date.getUTCMonth() == month;
17090 // If going forward one month, make sure month is as expected
17091 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17093 return new_date.getUTCMonth() != new_month;
17095 new_month = month + dir;
17096 new_date.setUTCMonth(new_month);
17097 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17098 if (new_month < 0 || new_month > 11)
17099 new_month = (new_month + 12) % 12;
17101 // For magnitudes >1, move one month at a time...
17102 for (var i=0; i<mag; i++)
17103 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17104 new_date = this.moveMonth(new_date, dir);
17105 // ...then reset the day, keeping it in the new month
17106 new_month = new_date.getUTCMonth();
17107 new_date.setUTCDate(day);
17109 return new_month != new_date.getUTCMonth();
17112 // Common date-resetting loop -- if date is beyond end of month, make it
17115 new_date.setUTCDate(--day);
17116 new_date.setUTCMonth(new_month);
17121 moveYear: function(date, dir)
17123 return this.moveMonth(date, dir*12);
17126 dateWithinRange: function(date)
17128 return date >= this.startDate && date <= this.endDate;
17134 this.picker().remove();
17139 Roo.apply(Roo.bootstrap.DateField, {
17150 html: '<i class="fa fa-arrow-left"/>'
17160 html: '<i class="fa fa-arrow-right"/>'
17202 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17203 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17204 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17205 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17206 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17219 navFnc: 'FullYear',
17224 navFnc: 'FullYear',
17229 Roo.apply(Roo.bootstrap.DateField, {
17233 cls: 'datepicker dropdown-menu roo-dynamic',
17237 cls: 'datepicker-days',
17241 cls: 'table-condensed',
17243 Roo.bootstrap.DateField.head,
17247 Roo.bootstrap.DateField.footer
17254 cls: 'datepicker-months',
17258 cls: 'table-condensed',
17260 Roo.bootstrap.DateField.head,
17261 Roo.bootstrap.DateField.content,
17262 Roo.bootstrap.DateField.footer
17269 cls: 'datepicker-years',
17273 cls: 'table-condensed',
17275 Roo.bootstrap.DateField.head,
17276 Roo.bootstrap.DateField.content,
17277 Roo.bootstrap.DateField.footer
17296 * @class Roo.bootstrap.TimeField
17297 * @extends Roo.bootstrap.Input
17298 * Bootstrap DateField class
17302 * Create a new TimeField
17303 * @param {Object} config The config object
17306 Roo.bootstrap.TimeField = function(config){
17307 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17311 * Fires when this field show.
17312 * @param {Roo.bootstrap.DateField} thisthis
17313 * @param {Mixed} date The date value
17318 * Fires when this field hide.
17319 * @param {Roo.bootstrap.DateField} this
17320 * @param {Mixed} date The date value
17325 * Fires when select a date.
17326 * @param {Roo.bootstrap.DateField} this
17327 * @param {Mixed} date The date value
17333 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17336 * @cfg {String} format
17337 * The default time format string which can be overriden for localization support. The format must be
17338 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17342 onRender: function(ct, position)
17345 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17347 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17349 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17351 this.pop = this.picker().select('>.datepicker-time',true).first();
17352 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17354 this.picker().on('mousedown', this.onMousedown, this);
17355 this.picker().on('click', this.onClick, this);
17357 this.picker().addClass('datepicker-dropdown');
17362 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17363 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17364 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17365 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17366 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17367 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17371 fireKey: function(e){
17372 if (!this.picker().isVisible()){
17373 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17379 e.preventDefault();
17387 this.onTogglePeriod();
17390 this.onIncrementMinutes();
17393 this.onDecrementMinutes();
17402 onClick: function(e) {
17403 e.stopPropagation();
17404 e.preventDefault();
17407 picker : function()
17409 return this.el.select('.datepicker', true).first();
17412 fillTime: function()
17414 var time = this.pop.select('tbody', true).first();
17416 time.dom.innerHTML = '';
17431 cls: 'hours-up glyphicon glyphicon-chevron-up'
17451 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17472 cls: 'timepicker-hour',
17487 cls: 'timepicker-minute',
17502 cls: 'btn btn-primary period',
17524 cls: 'hours-down glyphicon glyphicon-chevron-down'
17544 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17562 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17569 var hours = this.time.getHours();
17570 var minutes = this.time.getMinutes();
17583 hours = hours - 12;
17587 hours = '0' + hours;
17591 minutes = '0' + minutes;
17594 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17595 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17596 this.pop.select('button', true).first().dom.innerHTML = period;
17602 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17604 var cls = ['bottom'];
17606 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17613 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17618 this.picker().addClass(cls.join('-'));
17622 Roo.each(cls, function(c){
17624 _this.picker().setTop(_this.inputEl().getHeight());
17628 _this.picker().setTop(0 - _this.picker().getHeight());
17633 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17637 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17644 onFocus : function()
17646 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17650 onBlur : function()
17652 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17658 this.picker().show();
17663 this.fireEvent('show', this, this.date);
17668 this.picker().hide();
17671 this.fireEvent('hide', this, this.date);
17674 setTime : function()
17677 this.setValue(this.time.format(this.format));
17679 this.fireEvent('select', this, this.date);
17684 onMousedown: function(e){
17685 e.stopPropagation();
17686 e.preventDefault();
17689 onIncrementHours: function()
17691 Roo.log('onIncrementHours');
17692 this.time = this.time.add(Date.HOUR, 1);
17697 onDecrementHours: function()
17699 Roo.log('onDecrementHours');
17700 this.time = this.time.add(Date.HOUR, -1);
17704 onIncrementMinutes: function()
17706 Roo.log('onIncrementMinutes');
17707 this.time = this.time.add(Date.MINUTE, 1);
17711 onDecrementMinutes: function()
17713 Roo.log('onDecrementMinutes');
17714 this.time = this.time.add(Date.MINUTE, -1);
17718 onTogglePeriod: function()
17720 Roo.log('onTogglePeriod');
17721 this.time = this.time.add(Date.HOUR, 12);
17728 Roo.apply(Roo.bootstrap.TimeField, {
17758 cls: 'btn btn-info ok',
17770 Roo.apply(Roo.bootstrap.TimeField, {
17774 cls: 'datepicker dropdown-menu',
17778 cls: 'datepicker-time',
17782 cls: 'table-condensed',
17784 Roo.bootstrap.TimeField.content,
17785 Roo.bootstrap.TimeField.footer
17804 * @class Roo.bootstrap.MonthField
17805 * @extends Roo.bootstrap.Input
17806 * Bootstrap MonthField class
17808 * @cfg {String} language default en
17811 * Create a new MonthField
17812 * @param {Object} config The config object
17815 Roo.bootstrap.MonthField = function(config){
17816 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17821 * Fires when this field show.
17822 * @param {Roo.bootstrap.MonthField} this
17823 * @param {Mixed} date The date value
17828 * Fires when this field hide.
17829 * @param {Roo.bootstrap.MonthField} this
17830 * @param {Mixed} date The date value
17835 * Fires when select a date.
17836 * @param {Roo.bootstrap.MonthField} this
17837 * @param {String} oldvalue The old value
17838 * @param {String} newvalue The new value
17844 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17846 onRender: function(ct, position)
17849 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17851 this.language = this.language || 'en';
17852 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17853 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17855 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17856 this.isInline = false;
17857 this.isInput = true;
17858 this.component = this.el.select('.add-on', true).first() || false;
17859 this.component = (this.component && this.component.length === 0) ? false : this.component;
17860 this.hasInput = this.component && this.inputEL().length;
17862 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17864 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17866 this.picker().on('mousedown', this.onMousedown, this);
17867 this.picker().on('click', this.onClick, this);
17869 this.picker().addClass('datepicker-dropdown');
17871 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17872 v.setStyle('width', '189px');
17879 if(this.isInline) {
17885 setValue: function(v, suppressEvent)
17887 var o = this.getValue();
17889 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17893 if(suppressEvent !== true){
17894 this.fireEvent('select', this, o, v);
17899 getValue: function()
17904 onClick: function(e)
17906 e.stopPropagation();
17907 e.preventDefault();
17909 var target = e.getTarget();
17911 if(target.nodeName.toLowerCase() === 'i'){
17912 target = Roo.get(target).dom.parentNode;
17915 var nodeName = target.nodeName;
17916 var className = target.className;
17917 var html = target.innerHTML;
17919 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17923 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17925 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17931 picker : function()
17933 return this.pickerEl;
17936 fillMonths: function()
17939 var months = this.picker().select('>.datepicker-months td', true).first();
17941 months.dom.innerHTML = '';
17947 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17950 months.createChild(month);
17959 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17960 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17963 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17964 e.removeClass('active');
17966 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17967 e.addClass('active');
17974 if(this.isInline) return;
17976 this.picker().removeClass(['bottom', 'top']);
17978 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17980 * place to the top of element!
17984 this.picker().addClass('top');
17985 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17990 this.picker().addClass('bottom');
17992 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17995 onFocus : function()
17997 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18001 onBlur : function()
18003 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18005 var d = this.inputEl().getValue();
18014 this.picker().show();
18015 this.picker().select('>.datepicker-months', true).first().show();
18019 this.fireEvent('show', this, this.date);
18024 if(this.isInline) return;
18025 this.picker().hide();
18026 this.fireEvent('hide', this, this.date);
18030 onMousedown: function(e)
18032 e.stopPropagation();
18033 e.preventDefault();
18038 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18042 fireKey: function(e)
18044 if (!this.picker().isVisible()){
18045 if (e.keyCode == 27) // allow escape to hide and re-show picker
18055 e.preventDefault();
18059 dir = e.keyCode == 37 ? -1 : 1;
18061 this.vIndex = this.vIndex + dir;
18063 if(this.vIndex < 0){
18067 if(this.vIndex > 11){
18071 if(isNaN(this.vIndex)){
18075 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18081 dir = e.keyCode == 38 ? -1 : 1;
18083 this.vIndex = this.vIndex + dir * 4;
18085 if(this.vIndex < 0){
18089 if(this.vIndex > 11){
18093 if(isNaN(this.vIndex)){
18097 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18102 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18103 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18107 e.preventDefault();
18110 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18111 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18127 this.picker().remove();
18132 Roo.apply(Roo.bootstrap.MonthField, {
18151 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18152 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18157 Roo.apply(Roo.bootstrap.MonthField, {
18161 cls: 'datepicker dropdown-menu roo-dynamic',
18165 cls: 'datepicker-months',
18169 cls: 'table-condensed',
18171 Roo.bootstrap.DateField.content
18191 * @class Roo.bootstrap.CheckBox
18192 * @extends Roo.bootstrap.Input
18193 * Bootstrap CheckBox class
18195 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18196 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18197 * @cfg {String} boxLabel The text that appears beside the checkbox
18198 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18199 * @cfg {Boolean} checked initnal the element
18200 * @cfg {Boolean} inline inline the element (default false)
18201 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18204 * Create a new CheckBox
18205 * @param {Object} config The config object
18208 Roo.bootstrap.CheckBox = function(config){
18209 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18214 * Fires when the element is checked or unchecked.
18215 * @param {Roo.bootstrap.CheckBox} this This input
18216 * @param {Boolean} checked The new checked value
18223 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18225 inputType: 'checkbox',
18233 getAutoCreate : function()
18235 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18241 cfg.cls = 'form-group ' + this.inputType; //input-group
18244 cfg.cls += ' ' + this.inputType + '-inline';
18250 type : this.inputType,
18251 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18252 cls : 'roo-' + this.inputType, //'form-box',
18253 placeholder : this.placeholder || ''
18257 if (this.weight) { // Validity check?
18258 cfg.cls += " " + this.inputType + "-" + this.weight;
18261 if (this.disabled) {
18262 input.disabled=true;
18266 input.checked = this.checked;
18270 input.name = this.name;
18274 input.cls += ' input-' + this.size;
18279 ['xs','sm','md','lg'].map(function(size){
18280 if (settings[size]) {
18281 cfg.cls += ' col-' + size + '-' + settings[size];
18285 var inputblock = input;
18287 if (this.before || this.after) {
18290 cls : 'input-group',
18295 inputblock.cn.push({
18297 cls : 'input-group-addon',
18302 inputblock.cn.push(input);
18305 inputblock.cn.push({
18307 cls : 'input-group-addon',
18314 if (align ==='left' && this.fieldLabel.length) {
18315 Roo.log("left and has label");
18321 cls : 'control-label col-md-' + this.labelWidth,
18322 html : this.fieldLabel
18326 cls : "col-md-" + (12 - this.labelWidth),
18333 } else if ( this.fieldLabel.length) {
18338 tag: this.boxLabel ? 'span' : 'label',
18340 cls: 'control-label box-input-label',
18341 //cls : 'input-group-addon',
18342 html : this.fieldLabel
18352 Roo.log(" no label && no align");
18353 cfg.cn = [ inputblock ] ;
18358 var boxLabelCfg = {
18360 //'for': id, // box label is handled by onclick - so no for...
18362 html: this.boxLabel
18366 boxLabelCfg.tooltip = this.tooltip;
18369 cfg.cn.push(boxLabelCfg);
18379 * return the real input element.
18381 inputEl: function ()
18383 return this.el.select('input.roo-' + this.inputType,true).first();
18386 labelEl: function()
18388 return this.el.select('label.control-label',true).first();
18390 /* depricated... */
18394 return this.labelEl();
18397 initEvents : function()
18399 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18401 this.inputEl().on('click', this.onClick, this);
18403 if (this.boxLabel) {
18404 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18407 this.startValue = this.getValue();
18410 Roo.bootstrap.CheckBox.register(this);
18414 onClick : function()
18416 this.setChecked(!this.checked);
18419 setChecked : function(state,suppressEvent)
18421 this.startValue = this.getValue();
18423 if(this.inputType == 'radio'){
18425 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18426 e.dom.checked = false;
18429 this.inputEl().dom.checked = true;
18431 this.inputEl().dom.value = this.inputValue;
18433 if(suppressEvent !== true){
18434 this.fireEvent('check', this, true);
18442 this.checked = state;
18444 this.inputEl().dom.checked = state;
18446 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18448 if(suppressEvent !== true){
18449 this.fireEvent('check', this, state);
18455 getValue : function()
18457 if(this.inputType == 'radio'){
18458 return this.getGroupValue();
18461 return this.inputEl().getValue();
18465 getGroupValue : function()
18467 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18471 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18474 setValue : function(v,suppressEvent)
18476 if(this.inputType == 'radio'){
18477 this.setGroupValue(v, suppressEvent);
18481 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18486 setGroupValue : function(v, suppressEvent)
18488 this.startValue = this.getValue();
18490 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18491 e.dom.checked = false;
18493 if(e.dom.value == v){
18494 e.dom.checked = true;
18498 if(suppressEvent !== true){
18499 this.fireEvent('check', this, true);
18507 validate : function()
18511 (this.inputType == 'radio' && this.validateRadio()) ||
18512 (this.inputType == 'checkbox' && this.validateCheckbox())
18518 this.markInvalid();
18522 validateRadio : function()
18526 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18527 if(!e.dom.checked){
18539 validateCheckbox : function()
18542 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18545 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18553 for(var i in group){
18558 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18565 * Mark this field as valid
18567 markValid : function()
18569 if(this.allowBlank){
18575 this.fireEvent('valid', this);
18577 if(this.inputType == 'radio'){
18578 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18579 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18580 e.findParent('.form-group', false, true).addClass(_this.validClass);
18587 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18588 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18592 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18598 for(var i in group){
18599 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18600 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18605 * Mark this field as invalid
18606 * @param {String} msg The validation message
18608 markInvalid : function(msg)
18610 if(this.allowBlank){
18616 this.fireEvent('invalid', this, msg);
18618 if(this.inputType == 'radio'){
18619 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18620 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18621 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18628 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18629 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18633 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18639 for(var i in group){
18640 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18641 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18648 Roo.apply(Roo.bootstrap.CheckBox, {
18653 * register a CheckBox Group
18654 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18656 register : function(checkbox)
18658 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18659 this.groups[checkbox.groupId] = {};
18662 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18666 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18670 * fetch a CheckBox Group based on the group ID
18671 * @param {string} the group ID
18672 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18674 get: function(groupId) {
18675 if (typeof(this.groups[groupId]) == 'undefined') {
18679 return this.groups[groupId] ;
18691 *<div class="radio">
18693 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18694 Option one is this and that—be sure to include why it's great
18701 *<label class="radio-inline">fieldLabel</label>
18702 *<label class="radio-inline">
18703 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18711 * @class Roo.bootstrap.Radio
18712 * @extends Roo.bootstrap.CheckBox
18713 * Bootstrap Radio class
18716 * Create a new Radio
18717 * @param {Object} config The config object
18720 Roo.bootstrap.Radio = function(config){
18721 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18725 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18727 inputType: 'radio',
18731 getAutoCreate : function()
18733 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18734 align = align || 'left'; // default...
18741 tag : this.inline ? 'span' : 'div',
18746 var inline = this.inline ? ' radio-inline' : '';
18750 // does not need for, as we wrap the input with it..
18752 cls : 'control-label box-label' + inline,
18755 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18759 //cls : 'control-label' + inline,
18760 html : this.fieldLabel,
18761 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18770 type : this.inputType,
18771 //value : (!this.checked) ? this.valueOff : this.inputValue,
18772 value : this.inputValue,
18774 placeholder : this.placeholder || '' // ?? needed????
18777 if (this.weight) { // Validity check?
18778 input.cls += " radio-" + this.weight;
18780 if (this.disabled) {
18781 input.disabled=true;
18785 input.checked = this.checked;
18789 input.name = this.name;
18793 input.cls += ' input-' + this.size;
18796 //?? can span's inline have a width??
18799 ['xs','sm','md','lg'].map(function(size){
18800 if (settings[size]) {
18801 cfg.cls += ' col-' + size + '-' + settings[size];
18805 var inputblock = input;
18807 if (this.before || this.after) {
18810 cls : 'input-group',
18815 inputblock.cn.push({
18817 cls : 'input-group-addon',
18821 inputblock.cn.push(input);
18823 inputblock.cn.push({
18825 cls : 'input-group-addon',
18833 if (this.fieldLabel && this.fieldLabel.length) {
18834 cfg.cn.push(fieldLabel);
18837 // normal bootstrap puts the input inside the label.
18838 // however with our styled version - it has to go after the input.
18840 //lbl.cn.push(inputblock);
18844 cls: 'radio' + inline,
18851 cfg.cn.push( lblwrap);
18856 html: this.boxLabel
18865 initEvents : function()
18867 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18869 this.inputEl().on('click', this.onClick, this);
18870 if (this.boxLabel) {
18871 Roo.log('find label')
18872 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18877 inputEl: function ()
18879 return this.el.select('input.roo-radio',true).first();
18881 onClick : function()
18884 this.setChecked(true);
18887 setChecked : function(state,suppressEvent)
18890 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18891 v.dom.checked = false;
18894 Roo.log(this.inputEl().dom);
18895 this.checked = state;
18896 this.inputEl().dom.checked = state;
18898 if(suppressEvent !== true){
18899 this.fireEvent('check', this, state);
18902 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18906 getGroupValue : function()
18909 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18910 if(v.dom.checked == true){
18911 value = v.dom.value;
18919 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18920 * @return {Mixed} value The field value
18922 getValue : function(){
18923 return this.getGroupValue();
18929 //<script type="text/javascript">
18932 * Based Ext JS Library 1.1.1
18933 * Copyright(c) 2006-2007, Ext JS, LLC.
18939 * @class Roo.HtmlEditorCore
18940 * @extends Roo.Component
18941 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18943 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18946 Roo.HtmlEditorCore = function(config){
18949 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18954 * @event initialize
18955 * Fires when the editor is fully initialized (including the iframe)
18956 * @param {Roo.HtmlEditorCore} this
18961 * Fires when the editor is first receives the focus. Any insertion must wait
18962 * until after this event.
18963 * @param {Roo.HtmlEditorCore} this
18967 * @event beforesync
18968 * Fires before the textarea is updated with content from the editor iframe. Return false
18969 * to cancel the sync.
18970 * @param {Roo.HtmlEditorCore} this
18971 * @param {String} html
18975 * @event beforepush
18976 * Fires before the iframe editor is updated with content from the textarea. Return false
18977 * to cancel the push.
18978 * @param {Roo.HtmlEditorCore} this
18979 * @param {String} html
18984 * Fires when the textarea is updated with content from the editor iframe.
18985 * @param {Roo.HtmlEditorCore} this
18986 * @param {String} html
18991 * Fires when the iframe editor is updated with content from the textarea.
18992 * @param {Roo.HtmlEditorCore} this
18993 * @param {String} html
18998 * @event editorevent
18999 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19000 * @param {Roo.HtmlEditorCore} this
19006 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19008 // defaults : white / black...
19009 this.applyBlacklists();
19016 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19020 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19026 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19031 * @cfg {Number} height (in pixels)
19035 * @cfg {Number} width (in pixels)
19040 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19043 stylesheets: false,
19048 // private properties
19049 validationEvent : false,
19051 initialized : false,
19053 sourceEditMode : false,
19054 onFocus : Roo.emptyFn,
19056 hideMode:'offsets',
19060 // blacklist + whitelisted elements..
19067 * Protected method that will not generally be called directly. It
19068 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19069 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19071 getDocMarkup : function(){
19075 // inherit styels from page...??
19076 if (this.stylesheets === false) {
19078 Roo.get(document.head).select('style').each(function(node) {
19079 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19082 Roo.get(document.head).select('link').each(function(node) {
19083 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19086 } else if (!this.stylesheets.length) {
19088 st = '<style type="text/css">' +
19089 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19095 st += '<style type="text/css">' +
19096 'IMG { cursor: pointer } ' +
19100 return '<html><head>' + st +
19101 //<style type="text/css">' +
19102 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19104 ' </head><body class="roo-htmleditor-body"></body></html>';
19108 onRender : function(ct, position)
19111 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19112 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19115 this.el.dom.style.border = '0 none';
19116 this.el.dom.setAttribute('tabIndex', -1);
19117 this.el.addClass('x-hidden hide');
19121 if(Roo.isIE){ // fix IE 1px bogus margin
19122 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19126 this.frameId = Roo.id();
19130 var iframe = this.owner.wrap.createChild({
19132 cls: 'form-control', // bootstrap..
19134 name: this.frameId,
19135 frameBorder : 'no',
19136 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19141 this.iframe = iframe.dom;
19143 this.assignDocWin();
19145 this.doc.designMode = 'on';
19148 this.doc.write(this.getDocMarkup());
19152 var task = { // must defer to wait for browser to be ready
19154 //console.log("run task?" + this.doc.readyState);
19155 this.assignDocWin();
19156 if(this.doc.body || this.doc.readyState == 'complete'){
19158 this.doc.designMode="on";
19162 Roo.TaskMgr.stop(task);
19163 this.initEditor.defer(10, this);
19170 Roo.TaskMgr.start(task);
19175 onResize : function(w, h)
19177 Roo.log('resize: ' +w + ',' + h );
19178 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19182 if(typeof w == 'number'){
19184 this.iframe.style.width = w + 'px';
19186 if(typeof h == 'number'){
19188 this.iframe.style.height = h + 'px';
19190 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19197 * Toggles the editor between standard and source edit mode.
19198 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19200 toggleSourceEdit : function(sourceEditMode){
19202 this.sourceEditMode = sourceEditMode === true;
19204 if(this.sourceEditMode){
19206 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19209 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19210 //this.iframe.className = '';
19213 //this.setSize(this.owner.wrap.getSize());
19214 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19221 * Protected method that will not generally be called directly. If you need/want
19222 * custom HTML cleanup, this is the method you should override.
19223 * @param {String} html The HTML to be cleaned
19224 * return {String} The cleaned HTML
19226 cleanHtml : function(html){
19227 html = String(html);
19228 if(html.length > 5){
19229 if(Roo.isSafari){ // strip safari nonsense
19230 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19233 if(html == ' '){
19240 * HTML Editor -> Textarea
19241 * Protected method that will not generally be called directly. Syncs the contents
19242 * of the editor iframe with the textarea.
19244 syncValue : function(){
19245 if(this.initialized){
19246 var bd = (this.doc.body || this.doc.documentElement);
19247 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19248 var html = bd.innerHTML;
19250 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19251 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19253 html = '<div style="'+m[0]+'">' + html + '</div>';
19256 html = this.cleanHtml(html);
19257 // fix up the special chars.. normaly like back quotes in word...
19258 // however we do not want to do this with chinese..
19259 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19260 var cc = b.charCodeAt();
19262 (cc >= 0x4E00 && cc < 0xA000 ) ||
19263 (cc >= 0x3400 && cc < 0x4E00 ) ||
19264 (cc >= 0xf900 && cc < 0xfb00 )
19270 if(this.owner.fireEvent('beforesync', this, html) !== false){
19271 this.el.dom.value = html;
19272 this.owner.fireEvent('sync', this, html);
19278 * Protected method that will not generally be called directly. Pushes the value of the textarea
19279 * into the iframe editor.
19281 pushValue : function(){
19282 if(this.initialized){
19283 var v = this.el.dom.value.trim();
19285 // if(v.length < 1){
19289 if(this.owner.fireEvent('beforepush', this, v) !== false){
19290 var d = (this.doc.body || this.doc.documentElement);
19292 this.cleanUpPaste();
19293 this.el.dom.value = d.innerHTML;
19294 this.owner.fireEvent('push', this, v);
19300 deferFocus : function(){
19301 this.focus.defer(10, this);
19305 focus : function(){
19306 if(this.win && !this.sourceEditMode){
19313 assignDocWin: function()
19315 var iframe = this.iframe;
19318 this.doc = iframe.contentWindow.document;
19319 this.win = iframe.contentWindow;
19321 // if (!Roo.get(this.frameId)) {
19324 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19325 // this.win = Roo.get(this.frameId).dom.contentWindow;
19327 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19331 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19332 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19337 initEditor : function(){
19338 //console.log("INIT EDITOR");
19339 this.assignDocWin();
19343 this.doc.designMode="on";
19345 this.doc.write(this.getDocMarkup());
19348 var dbody = (this.doc.body || this.doc.documentElement);
19349 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19350 // this copies styles from the containing element into thsi one..
19351 // not sure why we need all of this..
19352 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19354 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19355 //ss['background-attachment'] = 'fixed'; // w3c
19356 dbody.bgProperties = 'fixed'; // ie
19357 //Roo.DomHelper.applyStyles(dbody, ss);
19358 Roo.EventManager.on(this.doc, {
19359 //'mousedown': this.onEditorEvent,
19360 'mouseup': this.onEditorEvent,
19361 'dblclick': this.onEditorEvent,
19362 'click': this.onEditorEvent,
19363 'keyup': this.onEditorEvent,
19368 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19370 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19371 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19373 this.initialized = true;
19375 this.owner.fireEvent('initialize', this);
19380 onDestroy : function(){
19386 //for (var i =0; i < this.toolbars.length;i++) {
19387 // // fixme - ask toolbars for heights?
19388 // this.toolbars[i].onDestroy();
19391 //this.wrap.dom.innerHTML = '';
19392 //this.wrap.remove();
19397 onFirstFocus : function(){
19399 this.assignDocWin();
19402 this.activated = true;
19405 if(Roo.isGecko){ // prevent silly gecko errors
19407 var s = this.win.getSelection();
19408 if(!s.focusNode || s.focusNode.nodeType != 3){
19409 var r = s.getRangeAt(0);
19410 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19415 this.execCmd('useCSS', true);
19416 this.execCmd('styleWithCSS', false);
19419 this.owner.fireEvent('activate', this);
19423 adjustFont: function(btn){
19424 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19425 //if(Roo.isSafari){ // safari
19428 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19429 if(Roo.isSafari){ // safari
19430 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19431 v = (v < 10) ? 10 : v;
19432 v = (v > 48) ? 48 : v;
19433 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19438 v = Math.max(1, v+adjust);
19440 this.execCmd('FontSize', v );
19443 onEditorEvent : function(e)
19445 this.owner.fireEvent('editorevent', this, e);
19446 // this.updateToolbar();
19447 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19450 insertTag : function(tg)
19452 // could be a bit smarter... -> wrap the current selected tRoo..
19453 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19455 range = this.createRange(this.getSelection());
19456 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19457 wrappingNode.appendChild(range.extractContents());
19458 range.insertNode(wrappingNode);
19465 this.execCmd("formatblock", tg);
19469 insertText : function(txt)
19473 var range = this.createRange();
19474 range.deleteContents();
19475 //alert(Sender.getAttribute('label'));
19477 range.insertNode(this.doc.createTextNode(txt));
19483 * Executes a Midas editor command on the editor document and performs necessary focus and
19484 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19485 * @param {String} cmd The Midas command
19486 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19488 relayCmd : function(cmd, value){
19490 this.execCmd(cmd, value);
19491 this.owner.fireEvent('editorevent', this);
19492 //this.updateToolbar();
19493 this.owner.deferFocus();
19497 * Executes a Midas editor command directly on the editor document.
19498 * For visual commands, you should use {@link #relayCmd} instead.
19499 * <b>This should only be called after the editor is initialized.</b>
19500 * @param {String} cmd The Midas command
19501 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19503 execCmd : function(cmd, value){
19504 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19511 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19513 * @param {String} text | dom node..
19515 insertAtCursor : function(text)
19520 if(!this.activated){
19526 var r = this.doc.selection.createRange();
19537 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19541 // from jquery ui (MIT licenced)
19543 var win = this.win;
19545 if (win.getSelection && win.getSelection().getRangeAt) {
19546 range = win.getSelection().getRangeAt(0);
19547 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19548 range.insertNode(node);
19549 } else if (win.document.selection && win.document.selection.createRange) {
19550 // no firefox support
19551 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19552 win.document.selection.createRange().pasteHTML(txt);
19554 // no firefox support
19555 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19556 this.execCmd('InsertHTML', txt);
19565 mozKeyPress : function(e){
19567 var c = e.getCharCode(), cmd;
19570 c = String.fromCharCode(c).toLowerCase();
19584 this.cleanUpPaste.defer(100, this);
19592 e.preventDefault();
19600 fixKeys : function(){ // load time branching for fastest keydown performance
19602 return function(e){
19603 var k = e.getKey(), r;
19606 r = this.doc.selection.createRange();
19609 r.pasteHTML('    ');
19616 r = this.doc.selection.createRange();
19618 var target = r.parentElement();
19619 if(!target || target.tagName.toLowerCase() != 'li'){
19621 r.pasteHTML('<br />');
19627 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19628 this.cleanUpPaste.defer(100, this);
19634 }else if(Roo.isOpera){
19635 return function(e){
19636 var k = e.getKey();
19640 this.execCmd('InsertHTML','    ');
19643 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19644 this.cleanUpPaste.defer(100, this);
19649 }else if(Roo.isSafari){
19650 return function(e){
19651 var k = e.getKey();
19655 this.execCmd('InsertText','\t');
19659 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19660 this.cleanUpPaste.defer(100, this);
19668 getAllAncestors: function()
19670 var p = this.getSelectedNode();
19673 a.push(p); // push blank onto stack..
19674 p = this.getParentElement();
19678 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19682 a.push(this.doc.body);
19686 lastSelNode : false,
19689 getSelection : function()
19691 this.assignDocWin();
19692 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19695 getSelectedNode: function()
19697 // this may only work on Gecko!!!
19699 // should we cache this!!!!
19704 var range = this.createRange(this.getSelection()).cloneRange();
19707 var parent = range.parentElement();
19709 var testRange = range.duplicate();
19710 testRange.moveToElementText(parent);
19711 if (testRange.inRange(range)) {
19714 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19717 parent = parent.parentElement;
19722 // is ancestor a text element.
19723 var ac = range.commonAncestorContainer;
19724 if (ac.nodeType == 3) {
19725 ac = ac.parentNode;
19728 var ar = ac.childNodes;
19731 var other_nodes = [];
19732 var has_other_nodes = false;
19733 for (var i=0;i<ar.length;i++) {
19734 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19737 // fullly contained node.
19739 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19744 // probably selected..
19745 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19746 other_nodes.push(ar[i]);
19750 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19755 has_other_nodes = true;
19757 if (!nodes.length && other_nodes.length) {
19758 nodes= other_nodes;
19760 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19766 createRange: function(sel)
19768 // this has strange effects when using with
19769 // top toolbar - not sure if it's a great idea.
19770 //this.editor.contentWindow.focus();
19771 if (typeof sel != "undefined") {
19773 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19775 return this.doc.createRange();
19778 return this.doc.createRange();
19781 getParentElement: function()
19784 this.assignDocWin();
19785 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19787 var range = this.createRange(sel);
19790 var p = range.commonAncestorContainer;
19791 while (p.nodeType == 3) { // text node
19802 * Range intersection.. the hard stuff...
19806 * [ -- selected range --- ]
19810 * if end is before start or hits it. fail.
19811 * if start is after end or hits it fail.
19813 * if either hits (but other is outside. - then it's not
19819 // @see http://www.thismuchiknow.co.uk/?p=64.
19820 rangeIntersectsNode : function(range, node)
19822 var nodeRange = node.ownerDocument.createRange();
19824 nodeRange.selectNode(node);
19826 nodeRange.selectNodeContents(node);
19829 var rangeStartRange = range.cloneRange();
19830 rangeStartRange.collapse(true);
19832 var rangeEndRange = range.cloneRange();
19833 rangeEndRange.collapse(false);
19835 var nodeStartRange = nodeRange.cloneRange();
19836 nodeStartRange.collapse(true);
19838 var nodeEndRange = nodeRange.cloneRange();
19839 nodeEndRange.collapse(false);
19841 return rangeStartRange.compareBoundaryPoints(
19842 Range.START_TO_START, nodeEndRange) == -1 &&
19843 rangeEndRange.compareBoundaryPoints(
19844 Range.START_TO_START, nodeStartRange) == 1;
19848 rangeCompareNode : function(range, node)
19850 var nodeRange = node.ownerDocument.createRange();
19852 nodeRange.selectNode(node);
19854 nodeRange.selectNodeContents(node);
19858 range.collapse(true);
19860 nodeRange.collapse(true);
19862 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19863 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19865 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19867 var nodeIsBefore = ss == 1;
19868 var nodeIsAfter = ee == -1;
19870 if (nodeIsBefore && nodeIsAfter)
19872 if (!nodeIsBefore && nodeIsAfter)
19873 return 1; //right trailed.
19875 if (nodeIsBefore && !nodeIsAfter)
19876 return 2; // left trailed.
19881 // private? - in a new class?
19882 cleanUpPaste : function()
19884 // cleans up the whole document..
19885 Roo.log('cleanuppaste');
19887 this.cleanUpChildren(this.doc.body);
19888 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19889 if (clean != this.doc.body.innerHTML) {
19890 this.doc.body.innerHTML = clean;
19895 cleanWordChars : function(input) {// change the chars to hex code
19896 var he = Roo.HtmlEditorCore;
19898 var output = input;
19899 Roo.each(he.swapCodes, function(sw) {
19900 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19902 output = output.replace(swapper, sw[1]);
19909 cleanUpChildren : function (n)
19911 if (!n.childNodes.length) {
19914 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19915 this.cleanUpChild(n.childNodes[i]);
19922 cleanUpChild : function (node)
19925 //console.log(node);
19926 if (node.nodeName == "#text") {
19927 // clean up silly Windows -- stuff?
19930 if (node.nodeName == "#comment") {
19931 node.parentNode.removeChild(node);
19932 // clean up silly Windows -- stuff?
19935 var lcname = node.tagName.toLowerCase();
19936 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19937 // whitelist of tags..
19939 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19941 node.parentNode.removeChild(node);
19946 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19948 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19949 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19951 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19952 // remove_keep_children = true;
19955 if (remove_keep_children) {
19956 this.cleanUpChildren(node);
19957 // inserts everything just before this node...
19958 while (node.childNodes.length) {
19959 var cn = node.childNodes[0];
19960 node.removeChild(cn);
19961 node.parentNode.insertBefore(cn, node);
19963 node.parentNode.removeChild(node);
19967 if (!node.attributes || !node.attributes.length) {
19968 this.cleanUpChildren(node);
19972 function cleanAttr(n,v)
19975 if (v.match(/^\./) || v.match(/^\//)) {
19978 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19981 if (v.match(/^#/)) {
19984 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19985 node.removeAttribute(n);
19989 var cwhite = this.cwhite;
19990 var cblack = this.cblack;
19992 function cleanStyle(n,v)
19994 if (v.match(/expression/)) { //XSS?? should we even bother..
19995 node.removeAttribute(n);
19999 var parts = v.split(/;/);
20002 Roo.each(parts, function(p) {
20003 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20007 var l = p.split(':').shift().replace(/\s+/g,'');
20008 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20010 if ( cwhite.length && cblack.indexOf(l) > -1) {
20011 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20012 //node.removeAttribute(n);
20016 // only allow 'c whitelisted system attributes'
20017 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20018 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20019 //node.removeAttribute(n);
20029 if (clean.length) {
20030 node.setAttribute(n, clean.join(';'));
20032 node.removeAttribute(n);
20038 for (var i = node.attributes.length-1; i > -1 ; i--) {
20039 var a = node.attributes[i];
20042 if (a.name.toLowerCase().substr(0,2)=='on') {
20043 node.removeAttribute(a.name);
20046 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20047 node.removeAttribute(a.name);
20050 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20051 cleanAttr(a.name,a.value); // fixme..
20054 if (a.name == 'style') {
20055 cleanStyle(a.name,a.value);
20058 /// clean up MS crap..
20059 // tecnically this should be a list of valid class'es..
20062 if (a.name == 'class') {
20063 if (a.value.match(/^Mso/)) {
20064 node.className = '';
20067 if (a.value.match(/body/)) {
20068 node.className = '';
20079 this.cleanUpChildren(node);
20085 * Clean up MS wordisms...
20087 cleanWord : function(node)
20092 this.cleanWord(this.doc.body);
20095 if (node.nodeName == "#text") {
20096 // clean up silly Windows -- stuff?
20099 if (node.nodeName == "#comment") {
20100 node.parentNode.removeChild(node);
20101 // clean up silly Windows -- stuff?
20105 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20106 node.parentNode.removeChild(node);
20110 // remove - but keep children..
20111 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20112 while (node.childNodes.length) {
20113 var cn = node.childNodes[0];
20114 node.removeChild(cn);
20115 node.parentNode.insertBefore(cn, node);
20117 node.parentNode.removeChild(node);
20118 this.iterateChildren(node, this.cleanWord);
20122 if (node.className.length) {
20124 var cn = node.className.split(/\W+/);
20126 Roo.each(cn, function(cls) {
20127 if (cls.match(/Mso[a-zA-Z]+/)) {
20132 node.className = cna.length ? cna.join(' ') : '';
20134 node.removeAttribute("class");
20138 if (node.hasAttribute("lang")) {
20139 node.removeAttribute("lang");
20142 if (node.hasAttribute("style")) {
20144 var styles = node.getAttribute("style").split(";");
20146 Roo.each(styles, function(s) {
20147 if (!s.match(/:/)) {
20150 var kv = s.split(":");
20151 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20154 // what ever is left... we allow.
20157 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20158 if (!nstyle.length) {
20159 node.removeAttribute('style');
20162 this.iterateChildren(node, this.cleanWord);
20168 * iterateChildren of a Node, calling fn each time, using this as the scole..
20169 * @param {DomNode} node node to iterate children of.
20170 * @param {Function} fn method of this class to call on each item.
20172 iterateChildren : function(node, fn)
20174 if (!node.childNodes.length) {
20177 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20178 fn.call(this, node.childNodes[i])
20184 * cleanTableWidths.
20186 * Quite often pasting from word etc.. results in tables with column and widths.
20187 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20190 cleanTableWidths : function(node)
20195 this.cleanTableWidths(this.doc.body);
20200 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20203 Roo.log(node.tagName);
20204 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20205 this.iterateChildren(node, this.cleanTableWidths);
20208 if (node.hasAttribute('width')) {
20209 node.removeAttribute('width');
20213 if (node.hasAttribute("style")) {
20216 var styles = node.getAttribute("style").split(";");
20218 Roo.each(styles, function(s) {
20219 if (!s.match(/:/)) {
20222 var kv = s.split(":");
20223 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20226 // what ever is left... we allow.
20229 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20230 if (!nstyle.length) {
20231 node.removeAttribute('style');
20235 this.iterateChildren(node, this.cleanTableWidths);
20243 domToHTML : function(currentElement, depth, nopadtext) {
20245 depth = depth || 0;
20246 nopadtext = nopadtext || false;
20248 if (!currentElement) {
20249 return this.domToHTML(this.doc.body);
20252 //Roo.log(currentElement);
20254 var allText = false;
20255 var nodeName = currentElement.nodeName;
20256 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20258 if (nodeName == '#text') {
20260 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20265 if (nodeName != 'BODY') {
20268 // Prints the node tagName, such as <A>, <IMG>, etc
20271 for(i = 0; i < currentElement.attributes.length;i++) {
20273 var aname = currentElement.attributes.item(i).name;
20274 if (!currentElement.attributes.item(i).value.length) {
20277 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20280 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20289 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20292 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20297 // Traverse the tree
20299 var currentElementChild = currentElement.childNodes.item(i);
20300 var allText = true;
20301 var innerHTML = '';
20303 while (currentElementChild) {
20304 // Formatting code (indent the tree so it looks nice on the screen)
20305 var nopad = nopadtext;
20306 if (lastnode == 'SPAN') {
20310 if (currentElementChild.nodeName == '#text') {
20311 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20312 toadd = nopadtext ? toadd : toadd.trim();
20313 if (!nopad && toadd.length > 80) {
20314 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20316 innerHTML += toadd;
20319 currentElementChild = currentElement.childNodes.item(i);
20325 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20327 // Recursively traverse the tree structure of the child node
20328 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20329 lastnode = currentElementChild.nodeName;
20331 currentElementChild=currentElement.childNodes.item(i);
20337 // The remaining code is mostly for formatting the tree
20338 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20343 ret+= "</"+tagName+">";
20349 applyBlacklists : function()
20351 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20352 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20356 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20357 if (b.indexOf(tag) > -1) {
20360 this.white.push(tag);
20364 Roo.each(w, function(tag) {
20365 if (b.indexOf(tag) > -1) {
20368 if (this.white.indexOf(tag) > -1) {
20371 this.white.push(tag);
20376 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20377 if (w.indexOf(tag) > -1) {
20380 this.black.push(tag);
20384 Roo.each(b, function(tag) {
20385 if (w.indexOf(tag) > -1) {
20388 if (this.black.indexOf(tag) > -1) {
20391 this.black.push(tag);
20396 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20397 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20401 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20402 if (b.indexOf(tag) > -1) {
20405 this.cwhite.push(tag);
20409 Roo.each(w, function(tag) {
20410 if (b.indexOf(tag) > -1) {
20413 if (this.cwhite.indexOf(tag) > -1) {
20416 this.cwhite.push(tag);
20421 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20422 if (w.indexOf(tag) > -1) {
20425 this.cblack.push(tag);
20429 Roo.each(b, function(tag) {
20430 if (w.indexOf(tag) > -1) {
20433 if (this.cblack.indexOf(tag) > -1) {
20436 this.cblack.push(tag);
20441 setStylesheets : function(stylesheets)
20443 if(typeof(stylesheets) == 'string'){
20444 Roo.get(this.iframe.contentDocument.head).createChild({
20446 rel : 'stylesheet',
20455 Roo.each(stylesheets, function(s) {
20460 Roo.get(_this.iframe.contentDocument.head).createChild({
20462 rel : 'stylesheet',
20471 removeStylesheets : function()
20475 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20480 // hide stuff that is not compatible
20494 * @event specialkey
20498 * @cfg {String} fieldClass @hide
20501 * @cfg {String} focusClass @hide
20504 * @cfg {String} autoCreate @hide
20507 * @cfg {String} inputType @hide
20510 * @cfg {String} invalidClass @hide
20513 * @cfg {String} invalidText @hide
20516 * @cfg {String} msgFx @hide
20519 * @cfg {String} validateOnBlur @hide
20523 Roo.HtmlEditorCore.white = [
20524 'area', 'br', 'img', 'input', 'hr', 'wbr',
20526 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20527 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20528 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20529 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20530 'table', 'ul', 'xmp',
20532 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20535 'dir', 'menu', 'ol', 'ul', 'dl',
20541 Roo.HtmlEditorCore.black = [
20542 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20544 'base', 'basefont', 'bgsound', 'blink', 'body',
20545 'frame', 'frameset', 'head', 'html', 'ilayer',
20546 'iframe', 'layer', 'link', 'meta', 'object',
20547 'script', 'style' ,'title', 'xml' // clean later..
20549 Roo.HtmlEditorCore.clean = [
20550 'script', 'style', 'title', 'xml'
20552 Roo.HtmlEditorCore.remove = [
20557 Roo.HtmlEditorCore.ablack = [
20561 Roo.HtmlEditorCore.aclean = [
20562 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20566 Roo.HtmlEditorCore.pwhite= [
20567 'http', 'https', 'mailto'
20570 // white listed style attributes.
20571 Roo.HtmlEditorCore.cwhite= [
20572 // 'text-align', /// default is to allow most things..
20578 // black listed style attributes.
20579 Roo.HtmlEditorCore.cblack= [
20580 // 'font-size' -- this can be set by the project
20584 Roo.HtmlEditorCore.swapCodes =[
20603 * @class Roo.bootstrap.HtmlEditor
20604 * @extends Roo.bootstrap.TextArea
20605 * Bootstrap HtmlEditor class
20608 * Create a new HtmlEditor
20609 * @param {Object} config The config object
20612 Roo.bootstrap.HtmlEditor = function(config){
20613 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20614 if (!this.toolbars) {
20615 this.toolbars = [];
20617 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20620 * @event initialize
20621 * Fires when the editor is fully initialized (including the iframe)
20622 * @param {HtmlEditor} this
20627 * Fires when the editor is first receives the focus. Any insertion must wait
20628 * until after this event.
20629 * @param {HtmlEditor} this
20633 * @event beforesync
20634 * Fires before the textarea is updated with content from the editor iframe. Return false
20635 * to cancel the sync.
20636 * @param {HtmlEditor} this
20637 * @param {String} html
20641 * @event beforepush
20642 * Fires before the iframe editor is updated with content from the textarea. Return false
20643 * to cancel the push.
20644 * @param {HtmlEditor} this
20645 * @param {String} html
20650 * Fires when the textarea is updated with content from the editor iframe.
20651 * @param {HtmlEditor} this
20652 * @param {String} html
20657 * Fires when the iframe editor is updated with content from the textarea.
20658 * @param {HtmlEditor} this
20659 * @param {String} html
20663 * @event editmodechange
20664 * Fires when the editor switches edit modes
20665 * @param {HtmlEditor} this
20666 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20668 editmodechange: true,
20670 * @event editorevent
20671 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20672 * @param {HtmlEditor} this
20676 * @event firstfocus
20677 * Fires when on first focus - needed by toolbars..
20678 * @param {HtmlEditor} this
20683 * Auto save the htmlEditor value as a file into Events
20684 * @param {HtmlEditor} this
20688 * @event savedpreview
20689 * preview the saved version of htmlEditor
20690 * @param {HtmlEditor} this
20697 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20701 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20706 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20711 * @cfg {Number} height (in pixels)
20715 * @cfg {Number} width (in pixels)
20720 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20723 stylesheets: false,
20728 // private properties
20729 validationEvent : false,
20731 initialized : false,
20734 onFocus : Roo.emptyFn,
20736 hideMode:'offsets',
20739 tbContainer : false,
20741 toolbarContainer :function() {
20742 return this.wrap.select('.x-html-editor-tb',true).first();
20746 * Protected method that will not generally be called directly. It
20747 * is called when the editor creates its toolbar. Override this method if you need to
20748 * add custom toolbar buttons.
20749 * @param {HtmlEditor} editor
20751 createToolbar : function(){
20753 Roo.log("create toolbars");
20755 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20756 this.toolbars[0].render(this.toolbarContainer());
20760 // if (!editor.toolbars || !editor.toolbars.length) {
20761 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20764 // for (var i =0 ; i < editor.toolbars.length;i++) {
20765 // editor.toolbars[i] = Roo.factory(
20766 // typeof(editor.toolbars[i]) == 'string' ?
20767 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20768 // Roo.bootstrap.HtmlEditor);
20769 // editor.toolbars[i].init(editor);
20775 onRender : function(ct, position)
20777 // Roo.log("Call onRender: " + this.xtype);
20779 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20781 this.wrap = this.inputEl().wrap({
20782 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20785 this.editorcore.onRender(ct, position);
20787 if (this.resizable) {
20788 this.resizeEl = new Roo.Resizable(this.wrap, {
20792 minHeight : this.height,
20793 height: this.height,
20794 handles : this.resizable,
20797 resize : function(r, w, h) {
20798 _t.onResize(w,h); // -something
20804 this.createToolbar(this);
20807 if(!this.width && this.resizable){
20808 this.setSize(this.wrap.getSize());
20810 if (this.resizeEl) {
20811 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20812 // should trigger onReize..
20818 onResize : function(w, h)
20820 Roo.log('resize: ' +w + ',' + h );
20821 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20825 if(this.inputEl() ){
20826 if(typeof w == 'number'){
20827 var aw = w - this.wrap.getFrameWidth('lr');
20828 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20831 if(typeof h == 'number'){
20832 var tbh = -11; // fixme it needs to tool bar size!
20833 for (var i =0; i < this.toolbars.length;i++) {
20834 // fixme - ask toolbars for heights?
20835 tbh += this.toolbars[i].el.getHeight();
20836 //if (this.toolbars[i].footer) {
20837 // tbh += this.toolbars[i].footer.el.getHeight();
20845 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20846 ah -= 5; // knock a few pixes off for look..
20847 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20851 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20852 this.editorcore.onResize(ew,eh);
20857 * Toggles the editor between standard and source edit mode.
20858 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20860 toggleSourceEdit : function(sourceEditMode)
20862 this.editorcore.toggleSourceEdit(sourceEditMode);
20864 if(this.editorcore.sourceEditMode){
20865 Roo.log('editor - showing textarea');
20868 // Roo.log(this.syncValue());
20870 this.inputEl().removeClass(['hide', 'x-hidden']);
20871 this.inputEl().dom.removeAttribute('tabIndex');
20872 this.inputEl().focus();
20874 Roo.log('editor - hiding textarea');
20876 // Roo.log(this.pushValue());
20879 this.inputEl().addClass(['hide', 'x-hidden']);
20880 this.inputEl().dom.setAttribute('tabIndex', -1);
20881 //this.deferFocus();
20884 if(this.resizable){
20885 this.setSize(this.wrap.getSize());
20888 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20891 // private (for BoxComponent)
20892 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20894 // private (for BoxComponent)
20895 getResizeEl : function(){
20899 // private (for BoxComponent)
20900 getPositionEl : function(){
20905 initEvents : function(){
20906 this.originalValue = this.getValue();
20910 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20913 // markInvalid : Roo.emptyFn,
20915 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20918 // clearInvalid : Roo.emptyFn,
20920 setValue : function(v){
20921 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20922 this.editorcore.pushValue();
20927 deferFocus : function(){
20928 this.focus.defer(10, this);
20932 focus : function(){
20933 this.editorcore.focus();
20939 onDestroy : function(){
20945 for (var i =0; i < this.toolbars.length;i++) {
20946 // fixme - ask toolbars for heights?
20947 this.toolbars[i].onDestroy();
20950 this.wrap.dom.innerHTML = '';
20951 this.wrap.remove();
20956 onFirstFocus : function(){
20957 //Roo.log("onFirstFocus");
20958 this.editorcore.onFirstFocus();
20959 for (var i =0; i < this.toolbars.length;i++) {
20960 this.toolbars[i].onFirstFocus();
20966 syncValue : function()
20968 this.editorcore.syncValue();
20971 pushValue : function()
20973 this.editorcore.pushValue();
20977 // hide stuff that is not compatible
20991 * @event specialkey
20995 * @cfg {String} fieldClass @hide
20998 * @cfg {String} focusClass @hide
21001 * @cfg {String} autoCreate @hide
21004 * @cfg {String} inputType @hide
21007 * @cfg {String} invalidClass @hide
21010 * @cfg {String} invalidText @hide
21013 * @cfg {String} msgFx @hide
21016 * @cfg {String} validateOnBlur @hide
21025 Roo.namespace('Roo.bootstrap.htmleditor');
21027 * @class Roo.bootstrap.HtmlEditorToolbar1
21032 new Roo.bootstrap.HtmlEditor({
21035 new Roo.bootstrap.HtmlEditorToolbar1({
21036 disable : { fonts: 1 , format: 1, ..., ... , ...],
21042 * @cfg {Object} disable List of elements to disable..
21043 * @cfg {Array} btns List of additional buttons.
21047 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21050 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21053 Roo.apply(this, config);
21055 // default disabled, based on 'good practice'..
21056 this.disable = this.disable || {};
21057 Roo.applyIf(this.disable, {
21060 specialElements : true
21062 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21064 this.editor = config.editor;
21065 this.editorcore = config.editor.editorcore;
21067 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21069 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21070 // dont call parent... till later.
21072 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21077 editorcore : false,
21082 "h1","h2","h3","h4","h5","h6",
21084 "abbr", "acronym", "address", "cite", "samp", "var",
21088 onRender : function(ct, position)
21090 // Roo.log("Call onRender: " + this.xtype);
21092 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21094 this.el.dom.style.marginBottom = '0';
21096 var editorcore = this.editorcore;
21097 var editor= this.editor;
21100 var btn = function(id,cmd , toggle, handler){
21102 var event = toggle ? 'toggle' : 'click';
21107 xns: Roo.bootstrap,
21110 enableToggle:toggle !== false,
21112 pressed : toggle ? false : null,
21115 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21116 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21125 xns: Roo.bootstrap,
21126 glyphicon : 'font',
21130 xns: Roo.bootstrap,
21134 Roo.each(this.formats, function(f) {
21135 style.menu.items.push({
21137 xns: Roo.bootstrap,
21138 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21143 editorcore.insertTag(this.tagname);
21150 children.push(style);
21153 btn('bold',false,true);
21154 btn('italic',false,true);
21155 btn('align-left', 'justifyleft',true);
21156 btn('align-center', 'justifycenter',true);
21157 btn('align-right' , 'justifyright',true);
21158 btn('link', false, false, function(btn) {
21159 //Roo.log("create link?");
21160 var url = prompt(this.createLinkText, this.defaultLinkValue);
21161 if(url && url != 'http:/'+'/'){
21162 this.editorcore.relayCmd('createlink', url);
21165 btn('list','insertunorderedlist',true);
21166 btn('pencil', false,true, function(btn){
21169 this.toggleSourceEdit(btn.pressed);
21175 xns: Roo.bootstrap,
21180 xns: Roo.bootstrap,
21185 cog.menu.items.push({
21187 xns: Roo.bootstrap,
21188 html : Clean styles,
21193 editorcore.insertTag(this.tagname);
21202 this.xtype = 'NavSimplebar';
21204 for(var i=0;i< children.length;i++) {
21206 this.buttons.add(this.addxtypeChild(children[i]));
21210 editor.on('editorevent', this.updateToolbar, this);
21212 onBtnClick : function(id)
21214 this.editorcore.relayCmd(id);
21215 this.editorcore.focus();
21219 * Protected method that will not generally be called directly. It triggers
21220 * a toolbar update by reading the markup state of the current selection in the editor.
21222 updateToolbar: function(){
21224 if(!this.editorcore.activated){
21225 this.editor.onFirstFocus(); // is this neeed?
21229 var btns = this.buttons;
21230 var doc = this.editorcore.doc;
21231 btns.get('bold').setActive(doc.queryCommandState('bold'));
21232 btns.get('italic').setActive(doc.queryCommandState('italic'));
21233 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21235 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21236 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21237 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21239 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21240 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21243 var ans = this.editorcore.getAllAncestors();
21244 if (this.formatCombo) {
21247 var store = this.formatCombo.store;
21248 this.formatCombo.setValue("");
21249 for (var i =0; i < ans.length;i++) {
21250 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21252 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21260 // hides menus... - so this cant be on a menu...
21261 Roo.bootstrap.MenuMgr.hideAll();
21263 Roo.bootstrap.MenuMgr.hideAll();
21264 //this.editorsyncValue();
21266 onFirstFocus: function() {
21267 this.buttons.each(function(item){
21271 toggleSourceEdit : function(sourceEditMode){
21274 if(sourceEditMode){
21275 Roo.log("disabling buttons");
21276 this.buttons.each( function(item){
21277 if(item.cmd != 'pencil'){
21283 Roo.log("enabling buttons");
21284 if(this.editorcore.initialized){
21285 this.buttons.each( function(item){
21291 Roo.log("calling toggole on editor");
21292 // tell the editor that it's been pressed..
21293 this.editor.toggleSourceEdit(sourceEditMode);
21303 * @class Roo.bootstrap.Table.AbstractSelectionModel
21304 * @extends Roo.util.Observable
21305 * Abstract base class for grid SelectionModels. It provides the interface that should be
21306 * implemented by descendant classes. This class should not be directly instantiated.
21309 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21310 this.locked = false;
21311 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21315 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21316 /** @ignore Called by the grid automatically. Do not call directly. */
21317 init : function(grid){
21323 * Locks the selections.
21326 this.locked = true;
21330 * Unlocks the selections.
21332 unlock : function(){
21333 this.locked = false;
21337 * Returns true if the selections are locked.
21338 * @return {Boolean}
21340 isLocked : function(){
21341 return this.locked;
21345 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21346 * @class Roo.bootstrap.Table.RowSelectionModel
21347 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21348 * It supports multiple selections and keyboard selection/navigation.
21350 * @param {Object} config
21353 Roo.bootstrap.Table.RowSelectionModel = function(config){
21354 Roo.apply(this, config);
21355 this.selections = new Roo.util.MixedCollection(false, function(o){
21360 this.lastActive = false;
21364 * @event selectionchange
21365 * Fires when the selection changes
21366 * @param {SelectionModel} this
21368 "selectionchange" : true,
21370 * @event afterselectionchange
21371 * Fires after the selection changes (eg. by key press or clicking)
21372 * @param {SelectionModel} this
21374 "afterselectionchange" : true,
21376 * @event beforerowselect
21377 * Fires when a row is selected being selected, return false to cancel.
21378 * @param {SelectionModel} this
21379 * @param {Number} rowIndex The selected index
21380 * @param {Boolean} keepExisting False if other selections will be cleared
21382 "beforerowselect" : true,
21385 * Fires when a row is selected.
21386 * @param {SelectionModel} this
21387 * @param {Number} rowIndex The selected index
21388 * @param {Roo.data.Record} r The record
21390 "rowselect" : true,
21392 * @event rowdeselect
21393 * Fires when a row is deselected.
21394 * @param {SelectionModel} this
21395 * @param {Number} rowIndex The selected index
21397 "rowdeselect" : true
21399 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21400 this.locked = false;
21403 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21405 * @cfg {Boolean} singleSelect
21406 * True to allow selection of only one row at a time (defaults to false)
21408 singleSelect : false,
21411 initEvents : function(){
21413 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21414 this.grid.on("mousedown", this.handleMouseDown, this);
21415 }else{ // allow click to work like normal
21416 this.grid.on("rowclick", this.handleDragableRowClick, this);
21419 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21420 "up" : function(e){
21422 this.selectPrevious(e.shiftKey);
21423 }else if(this.last !== false && this.lastActive !== false){
21424 var last = this.last;
21425 this.selectRange(this.last, this.lastActive-1);
21426 this.grid.getView().focusRow(this.lastActive);
21427 if(last !== false){
21431 this.selectFirstRow();
21433 this.fireEvent("afterselectionchange", this);
21435 "down" : function(e){
21437 this.selectNext(e.shiftKey);
21438 }else if(this.last !== false && this.lastActive !== false){
21439 var last = this.last;
21440 this.selectRange(this.last, this.lastActive+1);
21441 this.grid.getView().focusRow(this.lastActive);
21442 if(last !== false){
21446 this.selectFirstRow();
21448 this.fireEvent("afterselectionchange", this);
21453 var view = this.grid.view;
21454 view.on("refresh", this.onRefresh, this);
21455 view.on("rowupdated", this.onRowUpdated, this);
21456 view.on("rowremoved", this.onRemove, this);
21460 onRefresh : function(){
21461 var ds = this.grid.dataSource, i, v = this.grid.view;
21462 var s = this.selections;
21463 s.each(function(r){
21464 if((i = ds.indexOfId(r.id)) != -1){
21473 onRemove : function(v, index, r){
21474 this.selections.remove(r);
21478 onRowUpdated : function(v, index, r){
21479 if(this.isSelected(r)){
21480 v.onRowSelect(index);
21486 * @param {Array} records The records to select
21487 * @param {Boolean} keepExisting (optional) True to keep existing selections
21489 selectRecords : function(records, keepExisting){
21491 this.clearSelections();
21493 var ds = this.grid.dataSource;
21494 for(var i = 0, len = records.length; i < len; i++){
21495 this.selectRow(ds.indexOf(records[i]), true);
21500 * Gets the number of selected rows.
21503 getCount : function(){
21504 return this.selections.length;
21508 * Selects the first row in the grid.
21510 selectFirstRow : function(){
21515 * Select the last row.
21516 * @param {Boolean} keepExisting (optional) True to keep existing selections
21518 selectLastRow : function(keepExisting){
21519 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21523 * Selects the row immediately following the last selected row.
21524 * @param {Boolean} keepExisting (optional) True to keep existing selections
21526 selectNext : function(keepExisting){
21527 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21528 this.selectRow(this.last+1, keepExisting);
21529 this.grid.getView().focusRow(this.last);
21534 * Selects the row that precedes the last selected row.
21535 * @param {Boolean} keepExisting (optional) True to keep existing selections
21537 selectPrevious : function(keepExisting){
21539 this.selectRow(this.last-1, keepExisting);
21540 this.grid.getView().focusRow(this.last);
21545 * Returns the selected records
21546 * @return {Array} Array of selected records
21548 getSelections : function(){
21549 return [].concat(this.selections.items);
21553 * Returns the first selected record.
21556 getSelected : function(){
21557 return this.selections.itemAt(0);
21562 * Clears all selections.
21564 clearSelections : function(fast){
21565 if(this.locked) return;
21567 var ds = this.grid.dataSource;
21568 var s = this.selections;
21569 s.each(function(r){
21570 this.deselectRow(ds.indexOfId(r.id));
21574 this.selections.clear();
21581 * Selects all rows.
21583 selectAll : function(){
21584 if(this.locked) return;
21585 this.selections.clear();
21586 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21587 this.selectRow(i, true);
21592 * Returns True if there is a selection.
21593 * @return {Boolean}
21595 hasSelection : function(){
21596 return this.selections.length > 0;
21600 * Returns True if the specified row is selected.
21601 * @param {Number/Record} record The record or index of the record to check
21602 * @return {Boolean}
21604 isSelected : function(index){
21605 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21606 return (r && this.selections.key(r.id) ? true : false);
21610 * Returns True if the specified record id is selected.
21611 * @param {String} id The id of record to check
21612 * @return {Boolean}
21614 isIdSelected : function(id){
21615 return (this.selections.key(id) ? true : false);
21619 handleMouseDown : function(e, t){
21620 var view = this.grid.getView(), rowIndex;
21621 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21624 if(e.shiftKey && this.last !== false){
21625 var last = this.last;
21626 this.selectRange(last, rowIndex, e.ctrlKey);
21627 this.last = last; // reset the last
21628 view.focusRow(rowIndex);
21630 var isSelected = this.isSelected(rowIndex);
21631 if(e.button !== 0 && isSelected){
21632 view.focusRow(rowIndex);
21633 }else if(e.ctrlKey && isSelected){
21634 this.deselectRow(rowIndex);
21635 }else if(!isSelected){
21636 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21637 view.focusRow(rowIndex);
21640 this.fireEvent("afterselectionchange", this);
21643 handleDragableRowClick : function(grid, rowIndex, e)
21645 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21646 this.selectRow(rowIndex, false);
21647 grid.view.focusRow(rowIndex);
21648 this.fireEvent("afterselectionchange", this);
21653 * Selects multiple rows.
21654 * @param {Array} rows Array of the indexes of the row to select
21655 * @param {Boolean} keepExisting (optional) True to keep existing selections
21657 selectRows : function(rows, keepExisting){
21659 this.clearSelections();
21661 for(var i = 0, len = rows.length; i < len; i++){
21662 this.selectRow(rows[i], true);
21667 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21668 * @param {Number} startRow The index of the first row in the range
21669 * @param {Number} endRow The index of the last row in the range
21670 * @param {Boolean} keepExisting (optional) True to retain existing selections
21672 selectRange : function(startRow, endRow, keepExisting){
21673 if(this.locked) return;
21675 this.clearSelections();
21677 if(startRow <= endRow){
21678 for(var i = startRow; i <= endRow; i++){
21679 this.selectRow(i, true);
21682 for(var i = startRow; i >= endRow; i--){
21683 this.selectRow(i, true);
21689 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21690 * @param {Number} startRow The index of the first row in the range
21691 * @param {Number} endRow The index of the last row in the range
21693 deselectRange : function(startRow, endRow, preventViewNotify){
21694 if(this.locked) return;
21695 for(var i = startRow; i <= endRow; i++){
21696 this.deselectRow(i, preventViewNotify);
21702 * @param {Number} row The index of the row to select
21703 * @param {Boolean} keepExisting (optional) True to keep existing selections
21705 selectRow : function(index, keepExisting, preventViewNotify){
21706 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21707 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21708 if(!keepExisting || this.singleSelect){
21709 this.clearSelections();
21711 var r = this.grid.dataSource.getAt(index);
21712 this.selections.add(r);
21713 this.last = this.lastActive = index;
21714 if(!preventViewNotify){
21715 this.grid.getView().onRowSelect(index);
21717 this.fireEvent("rowselect", this, index, r);
21718 this.fireEvent("selectionchange", this);
21724 * @param {Number} row The index of the row to deselect
21726 deselectRow : function(index, preventViewNotify){
21727 if(this.locked) return;
21728 if(this.last == index){
21731 if(this.lastActive == index){
21732 this.lastActive = false;
21734 var r = this.grid.dataSource.getAt(index);
21735 this.selections.remove(r);
21736 if(!preventViewNotify){
21737 this.grid.getView().onRowDeselect(index);
21739 this.fireEvent("rowdeselect", this, index);
21740 this.fireEvent("selectionchange", this);
21744 restoreLast : function(){
21746 this.last = this._last;
21751 acceptsNav : function(row, col, cm){
21752 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21756 onEditorKey : function(field, e){
21757 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21762 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21764 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21766 }else if(k == e.ENTER && !e.ctrlKey){
21770 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21772 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21774 }else if(k == e.ESC){
21778 g.startEditing(newCell[0], newCell[1]);
21783 * Ext JS Library 1.1.1
21784 * Copyright(c) 2006-2007, Ext JS, LLC.
21786 * Originally Released Under LGPL - original licence link has changed is not relivant.
21789 * <script type="text/javascript">
21793 * @class Roo.bootstrap.PagingToolbar
21794 * @extends Roo.bootstrap.NavSimplebar
21795 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21797 * Create a new PagingToolbar
21798 * @param {Object} config The config object
21799 * @param {Roo.data.Store} store
21801 Roo.bootstrap.PagingToolbar = function(config)
21803 // old args format still supported... - xtype is prefered..
21804 // created from xtype...
21806 this.ds = config.dataSource;
21808 if (config.store && !this.ds) {
21809 this.store= Roo.factory(config.store, Roo.data);
21810 this.ds = this.store;
21811 this.ds.xmodule = this.xmodule || false;
21814 this.toolbarItems = [];
21815 if (config.items) {
21816 this.toolbarItems = config.items;
21819 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21824 this.bind(this.ds);
21827 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21831 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21833 * @cfg {Roo.data.Store} dataSource
21834 * The underlying data store providing the paged data
21837 * @cfg {String/HTMLElement/Element} container
21838 * container The id or element that will contain the toolbar
21841 * @cfg {Boolean} displayInfo
21842 * True to display the displayMsg (defaults to false)
21845 * @cfg {Number} pageSize
21846 * The number of records to display per page (defaults to 20)
21850 * @cfg {String} displayMsg
21851 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21853 displayMsg : 'Displaying {0} - {1} of {2}',
21855 * @cfg {String} emptyMsg
21856 * The message to display when no records are found (defaults to "No data to display")
21858 emptyMsg : 'No data to display',
21860 * Customizable piece of the default paging text (defaults to "Page")
21863 beforePageText : "Page",
21865 * Customizable piece of the default paging text (defaults to "of %0")
21868 afterPageText : "of {0}",
21870 * Customizable piece of the default paging text (defaults to "First Page")
21873 firstText : "First Page",
21875 * Customizable piece of the default paging text (defaults to "Previous Page")
21878 prevText : "Previous Page",
21880 * Customizable piece of the default paging text (defaults to "Next Page")
21883 nextText : "Next Page",
21885 * Customizable piece of the default paging text (defaults to "Last Page")
21888 lastText : "Last Page",
21890 * Customizable piece of the default paging text (defaults to "Refresh")
21893 refreshText : "Refresh",
21897 onRender : function(ct, position)
21899 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21900 this.navgroup.parentId = this.id;
21901 this.navgroup.onRender(this.el, null);
21902 // add the buttons to the navgroup
21904 if(this.displayInfo){
21905 Roo.log(this.el.select('ul.navbar-nav',true).first());
21906 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21907 this.displayEl = this.el.select('.x-paging-info', true).first();
21908 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21909 // this.displayEl = navel.el.select('span',true).first();
21915 Roo.each(_this.buttons, function(e){ // this might need to use render????
21916 Roo.factory(e).onRender(_this.el, null);
21920 Roo.each(_this.toolbarItems, function(e) {
21921 _this.navgroup.addItem(e);
21925 this.first = this.navgroup.addItem({
21926 tooltip: this.firstText,
21928 icon : 'fa fa-backward',
21930 preventDefault: true,
21931 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21934 this.prev = this.navgroup.addItem({
21935 tooltip: this.prevText,
21937 icon : 'fa fa-step-backward',
21939 preventDefault: true,
21940 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21942 //this.addSeparator();
21945 var field = this.navgroup.addItem( {
21947 cls : 'x-paging-position',
21949 html : this.beforePageText +
21950 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21951 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21954 this.field = field.el.select('input', true).first();
21955 this.field.on("keydown", this.onPagingKeydown, this);
21956 this.field.on("focus", function(){this.dom.select();});
21959 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21960 //this.field.setHeight(18);
21961 //this.addSeparator();
21962 this.next = this.navgroup.addItem({
21963 tooltip: this.nextText,
21965 html : ' <i class="fa fa-step-forward">',
21967 preventDefault: true,
21968 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21970 this.last = this.navgroup.addItem({
21971 tooltip: this.lastText,
21972 icon : 'fa fa-forward',
21975 preventDefault: true,
21976 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21978 //this.addSeparator();
21979 this.loading = this.navgroup.addItem({
21980 tooltip: this.refreshText,
21981 icon: 'fa fa-refresh',
21982 preventDefault: true,
21983 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21989 updateInfo : function(){
21990 if(this.displayEl){
21991 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21992 var msg = count == 0 ?
21996 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21998 this.displayEl.update(msg);
22003 onLoad : function(ds, r, o){
22004 this.cursor = o.params ? o.params.start : 0;
22005 var d = this.getPageData(),
22009 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22010 this.field.dom.value = ap;
22011 this.first.setDisabled(ap == 1);
22012 this.prev.setDisabled(ap == 1);
22013 this.next.setDisabled(ap == ps);
22014 this.last.setDisabled(ap == ps);
22015 this.loading.enable();
22020 getPageData : function(){
22021 var total = this.ds.getTotalCount();
22024 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22025 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22030 onLoadError : function(){
22031 this.loading.enable();
22035 onPagingKeydown : function(e){
22036 var k = e.getKey();
22037 var d = this.getPageData();
22039 var v = this.field.dom.value, pageNum;
22040 if(!v || isNaN(pageNum = parseInt(v, 10))){
22041 this.field.dom.value = d.activePage;
22044 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22045 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22048 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))
22050 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22051 this.field.dom.value = pageNum;
22052 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22055 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22057 var v = this.field.dom.value, pageNum;
22058 var increment = (e.shiftKey) ? 10 : 1;
22059 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22061 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22062 this.field.dom.value = d.activePage;
22065 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22067 this.field.dom.value = parseInt(v, 10) + increment;
22068 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22069 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22076 beforeLoad : function(){
22078 this.loading.disable();
22083 onClick : function(which){
22092 ds.load({params:{start: 0, limit: this.pageSize}});
22095 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22098 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22101 var total = ds.getTotalCount();
22102 var extra = total % this.pageSize;
22103 var lastStart = extra ? (total - extra) : total-this.pageSize;
22104 ds.load({params:{start: lastStart, limit: this.pageSize}});
22107 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22113 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22114 * @param {Roo.data.Store} store The data store to unbind
22116 unbind : function(ds){
22117 ds.un("beforeload", this.beforeLoad, this);
22118 ds.un("load", this.onLoad, this);
22119 ds.un("loadexception", this.onLoadError, this);
22120 ds.un("remove", this.updateInfo, this);
22121 ds.un("add", this.updateInfo, this);
22122 this.ds = undefined;
22126 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22127 * @param {Roo.data.Store} store The data store to bind
22129 bind : function(ds){
22130 ds.on("beforeload", this.beforeLoad, this);
22131 ds.on("load", this.onLoad, this);
22132 ds.on("loadexception", this.onLoadError, this);
22133 ds.on("remove", this.updateInfo, this);
22134 ds.on("add", this.updateInfo, this);
22145 * @class Roo.bootstrap.MessageBar
22146 * @extends Roo.bootstrap.Component
22147 * Bootstrap MessageBar class
22148 * @cfg {String} html contents of the MessageBar
22149 * @cfg {String} weight (info | success | warning | danger) default info
22150 * @cfg {String} beforeClass insert the bar before the given class
22151 * @cfg {Boolean} closable (true | false) default false
22152 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22155 * Create a new Element
22156 * @param {Object} config The config object
22159 Roo.bootstrap.MessageBar = function(config){
22160 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22163 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22169 beforeClass: 'bootstrap-sticky-wrap',
22171 getAutoCreate : function(){
22175 cls: 'alert alert-dismissable alert-' + this.weight,
22180 html: this.html || ''
22186 cfg.cls += ' alert-messages-fixed';
22200 onRender : function(ct, position)
22202 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22205 var cfg = Roo.apply({}, this.getAutoCreate());
22209 cfg.cls += ' ' + this.cls;
22212 cfg.style = this.style;
22214 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22216 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22219 this.el.select('>button.close').on('click', this.hide, this);
22225 if (!this.rendered) {
22231 this.fireEvent('show', this);
22237 if (!this.rendered) {
22243 this.fireEvent('hide', this);
22246 update : function()
22248 // var e = this.el.dom.firstChild;
22250 // if(this.closable){
22251 // e = e.nextSibling;
22254 // e.data = this.html || '';
22256 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22272 * @class Roo.bootstrap.Graph
22273 * @extends Roo.bootstrap.Component
22274 * Bootstrap Graph class
22278 @cfg {String} graphtype bar | vbar | pie
22279 @cfg {number} g_x coodinator | centre x (pie)
22280 @cfg {number} g_y coodinator | centre y (pie)
22281 @cfg {number} g_r radius (pie)
22282 @cfg {number} g_height height of the chart (respected by all elements in the set)
22283 @cfg {number} g_width width of the chart (respected by all elements in the set)
22284 @cfg {Object} title The title of the chart
22287 -opts (object) options for the chart
22289 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22290 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22292 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.
22293 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22295 o stretch (boolean)
22297 -opts (object) options for the pie
22300 o startAngle (number)
22301 o endAngle (number)
22305 * Create a new Input
22306 * @param {Object} config The config object
22309 Roo.bootstrap.Graph = function(config){
22310 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22316 * The img click event for the img.
22317 * @param {Roo.EventObject} e
22323 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22334 //g_colors: this.colors,
22341 getAutoCreate : function(){
22352 onRender : function(ct,position){
22353 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22354 this.raphael = Raphael(this.el.dom);
22356 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22357 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22358 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22359 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22361 r.text(160, 10, "Single Series Chart").attr(txtattr);
22362 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22363 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22364 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22366 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22367 r.barchart(330, 10, 300, 220, data1);
22368 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22369 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22372 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22373 // r.barchart(30, 30, 560, 250, xdata, {
22374 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22375 // axis : "0 0 1 1",
22376 // axisxlabels : xdata
22377 // //yvalues : cols,
22380 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22382 // this.load(null,xdata,{
22383 // axis : "0 0 1 1",
22384 // axisxlabels : xdata
22389 load : function(graphtype,xdata,opts){
22390 this.raphael.clear();
22392 graphtype = this.graphtype;
22397 var r = this.raphael,
22398 fin = function () {
22399 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22401 fout = function () {
22402 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22404 pfin = function() {
22405 this.sector.stop();
22406 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22409 this.label[0].stop();
22410 this.label[0].attr({ r: 7.5 });
22411 this.label[1].attr({ "font-weight": 800 });
22414 pfout = function() {
22415 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22418 this.label[0].animate({ r: 5 }, 500, "bounce");
22419 this.label[1].attr({ "font-weight": 400 });
22425 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22428 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22431 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22432 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22434 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22441 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22446 setTitle: function(o)
22451 initEvents: function() {
22454 this.el.on('click', this.onClick, this);
22458 onClick : function(e)
22460 Roo.log('img onclick');
22461 this.fireEvent('click', this, e);
22473 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22476 * @class Roo.bootstrap.dash.NumberBox
22477 * @extends Roo.bootstrap.Component
22478 * Bootstrap NumberBox class
22479 * @cfg {String} headline Box headline
22480 * @cfg {String} content Box content
22481 * @cfg {String} icon Box icon
22482 * @cfg {String} footer Footer text
22483 * @cfg {String} fhref Footer href
22486 * Create a new NumberBox
22487 * @param {Object} config The config object
22491 Roo.bootstrap.dash.NumberBox = function(config){
22492 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22496 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22505 getAutoCreate : function(){
22509 cls : 'small-box ',
22517 cls : 'roo-headline',
22518 html : this.headline
22522 cls : 'roo-content',
22523 html : this.content
22537 cls : 'ion ' + this.icon
22546 cls : 'small-box-footer',
22547 href : this.fhref || '#',
22551 cfg.cn.push(footer);
22558 onRender : function(ct,position){
22559 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22566 setHeadline: function (value)
22568 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22571 setFooter: function (value, href)
22573 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22576 this.el.select('a.small-box-footer',true).first().attr('href', href);
22581 setContent: function (value)
22583 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22586 initEvents: function()
22600 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22603 * @class Roo.bootstrap.dash.TabBox
22604 * @extends Roo.bootstrap.Component
22605 * Bootstrap TabBox class
22606 * @cfg {String} title Title of the TabBox
22607 * @cfg {String} icon Icon of the TabBox
22608 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22609 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22612 * Create a new TabBox
22613 * @param {Object} config The config object
22617 Roo.bootstrap.dash.TabBox = function(config){
22618 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22623 * When a pane is added
22624 * @param {Roo.bootstrap.dash.TabPane} pane
22628 * @event activatepane
22629 * When a pane is activated
22630 * @param {Roo.bootstrap.dash.TabPane} pane
22632 "activatepane" : true
22640 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22645 tabScrollable : false,
22647 getChildContainer : function()
22649 return this.el.select('.tab-content', true).first();
22652 getAutoCreate : function(){
22656 cls: 'pull-left header',
22664 cls: 'fa ' + this.icon
22670 cls: 'nav nav-tabs pull-right',
22676 if(this.tabScrollable){
22683 cls: 'nav nav-tabs pull-right',
22694 cls: 'nav-tabs-custom',
22699 cls: 'tab-content no-padding',
22707 initEvents : function()
22709 //Roo.log('add add pane handler');
22710 this.on('addpane', this.onAddPane, this);
22713 * Updates the box title
22714 * @param {String} html to set the title to.
22716 setTitle : function(value)
22718 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22720 onAddPane : function(pane)
22722 this.panes.push(pane);
22723 //Roo.log('addpane');
22725 // tabs are rendere left to right..
22726 if(!this.showtabs){
22730 var ctr = this.el.select('.nav-tabs', true).first();
22733 var existing = ctr.select('.nav-tab',true);
22734 var qty = existing.getCount();;
22737 var tab = ctr.createChild({
22739 cls : 'nav-tab' + (qty ? '' : ' active'),
22747 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22750 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22752 pane.el.addClass('active');
22757 onTabClick : function(ev,un,ob,pane)
22759 //Roo.log('tab - prev default');
22760 ev.preventDefault();
22763 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22764 pane.tab.addClass('active');
22765 //Roo.log(pane.title);
22766 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22767 // technically we should have a deactivate event.. but maybe add later.
22768 // and it should not de-activate the selected tab...
22769 this.fireEvent('activatepane', pane);
22770 pane.el.addClass('active');
22771 pane.fireEvent('activate');
22776 getActivePane : function()
22779 Roo.each(this.panes, function(p) {
22780 if(p.el.hasClass('active')){
22801 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22803 * @class Roo.bootstrap.TabPane
22804 * @extends Roo.bootstrap.Component
22805 * Bootstrap TabPane class
22806 * @cfg {Boolean} active (false | true) Default false
22807 * @cfg {String} title title of panel
22811 * Create a new TabPane
22812 * @param {Object} config The config object
22815 Roo.bootstrap.dash.TabPane = function(config){
22816 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22822 * When a pane is activated
22823 * @param {Roo.bootstrap.dash.TabPane} pane
22830 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22835 // the tabBox that this is attached to.
22838 getAutoCreate : function()
22846 cfg.cls += ' active';
22851 initEvents : function()
22853 //Roo.log('trigger add pane handler');
22854 this.parent().fireEvent('addpane', this)
22858 * Updates the tab title
22859 * @param {String} html to set the title to.
22861 setTitle: function(str)
22867 this.tab.select('a', true).first().dom.innerHTML = str;
22884 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22887 * @class Roo.bootstrap.menu.Menu
22888 * @extends Roo.bootstrap.Component
22889 * Bootstrap Menu class - container for Menu
22890 * @cfg {String} html Text of the menu
22891 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22892 * @cfg {String} icon Font awesome icon
22893 * @cfg {String} pos Menu align to (top | bottom) default bottom
22897 * Create a new Menu
22898 * @param {Object} config The config object
22902 Roo.bootstrap.menu.Menu = function(config){
22903 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22907 * @event beforeshow
22908 * Fires before this menu is displayed
22909 * @param {Roo.bootstrap.menu.Menu} this
22913 * @event beforehide
22914 * Fires before this menu is hidden
22915 * @param {Roo.bootstrap.menu.Menu} this
22920 * Fires after this menu is displayed
22921 * @param {Roo.bootstrap.menu.Menu} this
22926 * Fires after this menu is hidden
22927 * @param {Roo.bootstrap.menu.Menu} this
22932 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22933 * @param {Roo.bootstrap.menu.Menu} this
22934 * @param {Roo.EventObject} e
22941 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22945 weight : 'default',
22950 getChildContainer : function() {
22951 if(this.isSubMenu){
22955 return this.el.select('ul.dropdown-menu', true).first();
22958 getAutoCreate : function()
22963 cls : 'roo-menu-text',
22971 cls : 'fa ' + this.icon
22982 cls : 'dropdown-button btn btn-' + this.weight,
22987 cls : 'dropdown-toggle btn btn-' + this.weight,
22997 cls : 'dropdown-menu'
23003 if(this.pos == 'top'){
23004 cfg.cls += ' dropup';
23007 if(this.isSubMenu){
23010 cls : 'dropdown-menu'
23017 onRender : function(ct, position)
23019 this.isSubMenu = ct.hasClass('dropdown-submenu');
23021 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23024 initEvents : function()
23026 if(this.isSubMenu){
23030 this.hidden = true;
23032 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23033 this.triggerEl.on('click', this.onTriggerPress, this);
23035 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23036 this.buttonEl.on('click', this.onClick, this);
23042 if(this.isSubMenu){
23046 return this.el.select('ul.dropdown-menu', true).first();
23049 onClick : function(e)
23051 this.fireEvent("click", this, e);
23054 onTriggerPress : function(e)
23056 if (this.isVisible()) {
23063 isVisible : function(){
23064 return !this.hidden;
23069 this.fireEvent("beforeshow", this);
23071 this.hidden = false;
23072 this.el.addClass('open');
23074 Roo.get(document).on("mouseup", this.onMouseUp, this);
23076 this.fireEvent("show", this);
23083 this.fireEvent("beforehide", this);
23085 this.hidden = true;
23086 this.el.removeClass('open');
23088 Roo.get(document).un("mouseup", this.onMouseUp);
23090 this.fireEvent("hide", this);
23093 onMouseUp : function()
23107 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23110 * @class Roo.bootstrap.menu.Item
23111 * @extends Roo.bootstrap.Component
23112 * Bootstrap MenuItem class
23113 * @cfg {Boolean} submenu (true | false) default false
23114 * @cfg {String} html text of the item
23115 * @cfg {String} href the link
23116 * @cfg {Boolean} disable (true | false) default false
23117 * @cfg {Boolean} preventDefault (true | false) default true
23118 * @cfg {String} icon Font awesome icon
23119 * @cfg {String} pos Submenu align to (left | right) default right
23123 * Create a new Item
23124 * @param {Object} config The config object
23128 Roo.bootstrap.menu.Item = function(config){
23129 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23133 * Fires when the mouse is hovering over this menu
23134 * @param {Roo.bootstrap.menu.Item} this
23135 * @param {Roo.EventObject} e
23140 * Fires when the mouse exits this menu
23141 * @param {Roo.bootstrap.menu.Item} this
23142 * @param {Roo.EventObject} e
23148 * The raw click event for the entire grid.
23149 * @param {Roo.EventObject} e
23155 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23160 preventDefault: true,
23165 getAutoCreate : function()
23170 cls : 'roo-menu-item-text',
23178 cls : 'fa ' + this.icon
23187 href : this.href || '#',
23194 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23198 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23200 if(this.pos == 'left'){
23201 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23208 initEvents : function()
23210 this.el.on('mouseover', this.onMouseOver, this);
23211 this.el.on('mouseout', this.onMouseOut, this);
23213 this.el.select('a', true).first().on('click', this.onClick, this);
23217 onClick : function(e)
23219 if(this.preventDefault){
23220 e.preventDefault();
23223 this.fireEvent("click", this, e);
23226 onMouseOver : function(e)
23228 if(this.submenu && this.pos == 'left'){
23229 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23232 this.fireEvent("mouseover", this, e);
23235 onMouseOut : function(e)
23237 this.fireEvent("mouseout", this, e);
23249 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23252 * @class Roo.bootstrap.menu.Separator
23253 * @extends Roo.bootstrap.Component
23254 * Bootstrap Separator class
23257 * Create a new Separator
23258 * @param {Object} config The config object
23262 Roo.bootstrap.menu.Separator = function(config){
23263 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23266 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23268 getAutoCreate : function(){
23289 * @class Roo.bootstrap.Tooltip
23290 * Bootstrap Tooltip class
23291 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23292 * to determine which dom element triggers the tooltip.
23294 * It needs to add support for additional attributes like tooltip-position
23297 * Create a new Toolti
23298 * @param {Object} config The config object
23301 Roo.bootstrap.Tooltip = function(config){
23302 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23305 Roo.apply(Roo.bootstrap.Tooltip, {
23307 * @function init initialize tooltip monitoring.
23311 currentTip : false,
23312 currentRegion : false,
23318 Roo.get(document).on('mouseover', this.enter ,this);
23319 Roo.get(document).on('mouseout', this.leave, this);
23322 this.currentTip = new Roo.bootstrap.Tooltip();
23325 enter : function(ev)
23327 var dom = ev.getTarget();
23329 //Roo.log(['enter',dom]);
23330 var el = Roo.fly(dom);
23331 if (this.currentEl) {
23333 //Roo.log(this.currentEl);
23334 //Roo.log(this.currentEl.contains(dom));
23335 if (this.currentEl == el) {
23338 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23344 if (this.currentTip.el) {
23345 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23350 // you can not look for children, as if el is the body.. then everythign is the child..
23351 if (!el.attr('tooltip')) { //
23352 if (!el.select("[tooltip]").elements.length) {
23355 // is the mouse over this child...?
23356 bindEl = el.select("[tooltip]").first();
23357 var xy = ev.getXY();
23358 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23359 //Roo.log("not in region.");
23362 //Roo.log("child element over..");
23365 this.currentEl = bindEl;
23366 this.currentTip.bind(bindEl);
23367 this.currentRegion = Roo.lib.Region.getRegion(dom);
23368 this.currentTip.enter();
23371 leave : function(ev)
23373 var dom = ev.getTarget();
23374 //Roo.log(['leave',dom]);
23375 if (!this.currentEl) {
23380 if (dom != this.currentEl.dom) {
23383 var xy = ev.getXY();
23384 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23387 // only activate leave if mouse cursor is outside... bounding box..
23392 if (this.currentTip) {
23393 this.currentTip.leave();
23395 //Roo.log('clear currentEl');
23396 this.currentEl = false;
23401 'left' : ['r-l', [-2,0], 'right'],
23402 'right' : ['l-r', [2,0], 'left'],
23403 'bottom' : ['t-b', [0,2], 'top'],
23404 'top' : [ 'b-t', [0,-2], 'bottom']
23410 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23415 delay : null, // can be { show : 300 , hide: 500}
23419 hoverState : null, //???
23421 placement : 'bottom',
23423 getAutoCreate : function(){
23430 cls : 'tooltip-arrow'
23433 cls : 'tooltip-inner'
23440 bind : function(el)
23446 enter : function () {
23448 if (this.timeout != null) {
23449 clearTimeout(this.timeout);
23452 this.hoverState = 'in';
23453 //Roo.log("enter - show");
23454 if (!this.delay || !this.delay.show) {
23459 this.timeout = setTimeout(function () {
23460 if (_t.hoverState == 'in') {
23463 }, this.delay.show);
23467 clearTimeout(this.timeout);
23469 this.hoverState = 'out';
23470 if (!this.delay || !this.delay.hide) {
23476 this.timeout = setTimeout(function () {
23477 //Roo.log("leave - timeout");
23479 if (_t.hoverState == 'out') {
23481 Roo.bootstrap.Tooltip.currentEl = false;
23489 this.render(document.body);
23492 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23494 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23496 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23498 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23500 var placement = typeof this.placement == 'function' ?
23501 this.placement.call(this, this.el, on_el) :
23504 var autoToken = /\s?auto?\s?/i;
23505 var autoPlace = autoToken.test(placement);
23507 placement = placement.replace(autoToken, '') || 'top';
23511 //this.el.setXY([0,0]);
23513 //this.el.dom.style.display='block';
23515 //this.el.appendTo(on_el);
23517 var p = this.getPosition();
23518 var box = this.el.getBox();
23524 var align = Roo.bootstrap.Tooltip.alignment[placement];
23526 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23528 if(placement == 'top' || placement == 'bottom'){
23530 placement = 'right';
23533 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23534 placement = 'left';
23538 align = Roo.bootstrap.Tooltip.alignment[placement];
23540 this.el.alignTo(this.bindEl, align[0],align[1]);
23541 //var arrow = this.el.select('.arrow',true).first();
23542 //arrow.set(align[2],
23544 this.el.addClass(placement);
23546 this.el.addClass('in fade');
23548 this.hoverState = null;
23550 if (this.el.hasClass('fade')) {
23561 //this.el.setXY([0,0]);
23562 this.el.removeClass('in');
23578 * @class Roo.bootstrap.LocationPicker
23579 * @extends Roo.bootstrap.Component
23580 * Bootstrap LocationPicker class
23581 * @cfg {Number} latitude Position when init default 0
23582 * @cfg {Number} longitude Position when init default 0
23583 * @cfg {Number} zoom default 15
23584 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23585 * @cfg {Boolean} mapTypeControl default false
23586 * @cfg {Boolean} disableDoubleClickZoom default false
23587 * @cfg {Boolean} scrollwheel default true
23588 * @cfg {Boolean} streetViewControl default false
23589 * @cfg {Number} radius default 0
23590 * @cfg {String} locationName
23591 * @cfg {Boolean} draggable default true
23592 * @cfg {Boolean} enableAutocomplete default false
23593 * @cfg {Boolean} enableReverseGeocode default true
23594 * @cfg {String} markerTitle
23597 * Create a new LocationPicker
23598 * @param {Object} config The config object
23602 Roo.bootstrap.LocationPicker = function(config){
23604 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23609 * Fires when the picker initialized.
23610 * @param {Roo.bootstrap.LocationPicker} this
23611 * @param {Google Location} location
23615 * @event positionchanged
23616 * Fires when the picker position changed.
23617 * @param {Roo.bootstrap.LocationPicker} this
23618 * @param {Google Location} location
23620 positionchanged : true,
23623 * Fires when the map resize.
23624 * @param {Roo.bootstrap.LocationPicker} this
23629 * Fires when the map show.
23630 * @param {Roo.bootstrap.LocationPicker} this
23635 * Fires when the map hide.
23636 * @param {Roo.bootstrap.LocationPicker} this
23641 * Fires when click the map.
23642 * @param {Roo.bootstrap.LocationPicker} this
23643 * @param {Map event} e
23647 * @event mapRightClick
23648 * Fires when right click the map.
23649 * @param {Roo.bootstrap.LocationPicker} this
23650 * @param {Map event} e
23652 mapRightClick : true,
23654 * @event markerClick
23655 * Fires when click the marker.
23656 * @param {Roo.bootstrap.LocationPicker} this
23657 * @param {Map event} e
23659 markerClick : true,
23661 * @event markerRightClick
23662 * Fires when right click the marker.
23663 * @param {Roo.bootstrap.LocationPicker} this
23664 * @param {Map event} e
23666 markerRightClick : true,
23668 * @event OverlayViewDraw
23669 * Fires when OverlayView Draw
23670 * @param {Roo.bootstrap.LocationPicker} this
23672 OverlayViewDraw : true,
23674 * @event OverlayViewOnAdd
23675 * Fires when OverlayView Draw
23676 * @param {Roo.bootstrap.LocationPicker} this
23678 OverlayViewOnAdd : true,
23680 * @event OverlayViewOnRemove
23681 * Fires when OverlayView Draw
23682 * @param {Roo.bootstrap.LocationPicker} this
23684 OverlayViewOnRemove : true,
23686 * @event OverlayViewShow
23687 * Fires when OverlayView Draw
23688 * @param {Roo.bootstrap.LocationPicker} this
23689 * @param {Pixel} cpx
23691 OverlayViewShow : true,
23693 * @event OverlayViewHide
23694 * Fires when OverlayView Draw
23695 * @param {Roo.bootstrap.LocationPicker} this
23697 OverlayViewHide : true,
23699 * @event loadexception
23700 * Fires when load google lib failed.
23701 * @param {Roo.bootstrap.LocationPicker} this
23703 loadexception : true
23708 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23710 gMapContext: false,
23716 mapTypeControl: false,
23717 disableDoubleClickZoom: false,
23719 streetViewControl: false,
23723 enableAutocomplete: false,
23724 enableReverseGeocode: true,
23727 getAutoCreate: function()
23732 cls: 'roo-location-picker'
23738 initEvents: function(ct, position)
23740 if(!this.el.getWidth() || this.isApplied()){
23744 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23749 initial: function()
23751 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23752 this.fireEvent('loadexception', this);
23756 if(!this.mapTypeId){
23757 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23760 this.gMapContext = this.GMapContext();
23762 this.initOverlayView();
23764 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23768 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23769 _this.setPosition(_this.gMapContext.marker.position);
23772 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23773 _this.fireEvent('mapClick', this, event);
23777 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23778 _this.fireEvent('mapRightClick', this, event);
23782 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23783 _this.fireEvent('markerClick', this, event);
23787 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23788 _this.fireEvent('markerRightClick', this, event);
23792 this.setPosition(this.gMapContext.location);
23794 this.fireEvent('initial', this, this.gMapContext.location);
23797 initOverlayView: function()
23801 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23805 _this.fireEvent('OverlayViewDraw', _this);
23810 _this.fireEvent('OverlayViewOnAdd', _this);
23813 onRemove: function()
23815 _this.fireEvent('OverlayViewOnRemove', _this);
23818 show: function(cpx)
23820 _this.fireEvent('OverlayViewShow', _this, cpx);
23825 _this.fireEvent('OverlayViewHide', _this);
23831 fromLatLngToContainerPixel: function(event)
23833 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23836 isApplied: function()
23838 return this.getGmapContext() == false ? false : true;
23841 getGmapContext: function()
23843 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23846 GMapContext: function()
23848 var position = new google.maps.LatLng(this.latitude, this.longitude);
23850 var _map = new google.maps.Map(this.el.dom, {
23853 mapTypeId: this.mapTypeId,
23854 mapTypeControl: this.mapTypeControl,
23855 disableDoubleClickZoom: this.disableDoubleClickZoom,
23856 scrollwheel: this.scrollwheel,
23857 streetViewControl: this.streetViewControl,
23858 locationName: this.locationName,
23859 draggable: this.draggable,
23860 enableAutocomplete: this.enableAutocomplete,
23861 enableReverseGeocode: this.enableReverseGeocode
23864 var _marker = new google.maps.Marker({
23865 position: position,
23867 title: this.markerTitle,
23868 draggable: this.draggable
23875 location: position,
23876 radius: this.radius,
23877 locationName: this.locationName,
23878 addressComponents: {
23879 formatted_address: null,
23880 addressLine1: null,
23881 addressLine2: null,
23883 streetNumber: null,
23887 stateOrProvince: null
23890 domContainer: this.el.dom,
23891 geodecoder: new google.maps.Geocoder()
23895 drawCircle: function(center, radius, options)
23897 if (this.gMapContext.circle != null) {
23898 this.gMapContext.circle.setMap(null);
23902 options = Roo.apply({}, options, {
23903 strokeColor: "#0000FF",
23904 strokeOpacity: .35,
23906 fillColor: "#0000FF",
23910 options.map = this.gMapContext.map;
23911 options.radius = radius;
23912 options.center = center;
23913 this.gMapContext.circle = new google.maps.Circle(options);
23914 return this.gMapContext.circle;
23920 setPosition: function(location)
23922 this.gMapContext.location = location;
23923 this.gMapContext.marker.setPosition(location);
23924 this.gMapContext.map.panTo(location);
23925 this.drawCircle(location, this.gMapContext.radius, {});
23929 if (this.gMapContext.settings.enableReverseGeocode) {
23930 this.gMapContext.geodecoder.geocode({
23931 latLng: this.gMapContext.location
23932 }, function(results, status) {
23934 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23935 _this.gMapContext.locationName = results[0].formatted_address;
23936 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23938 _this.fireEvent('positionchanged', this, location);
23945 this.fireEvent('positionchanged', this, location);
23950 google.maps.event.trigger(this.gMapContext.map, "resize");
23952 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23954 this.fireEvent('resize', this);
23957 setPositionByLatLng: function(latitude, longitude)
23959 this.setPosition(new google.maps.LatLng(latitude, longitude));
23962 getCurrentPosition: function()
23965 latitude: this.gMapContext.location.lat(),
23966 longitude: this.gMapContext.location.lng()
23970 getAddressName: function()
23972 return this.gMapContext.locationName;
23975 getAddressComponents: function()
23977 return this.gMapContext.addressComponents;
23980 address_component_from_google_geocode: function(address_components)
23984 for (var i = 0; i < address_components.length; i++) {
23985 var component = address_components[i];
23986 if (component.types.indexOf("postal_code") >= 0) {
23987 result.postalCode = component.short_name;
23988 } else if (component.types.indexOf("street_number") >= 0) {
23989 result.streetNumber = component.short_name;
23990 } else if (component.types.indexOf("route") >= 0) {
23991 result.streetName = component.short_name;
23992 } else if (component.types.indexOf("neighborhood") >= 0) {
23993 result.city = component.short_name;
23994 } else if (component.types.indexOf("locality") >= 0) {
23995 result.city = component.short_name;
23996 } else if (component.types.indexOf("sublocality") >= 0) {
23997 result.district = component.short_name;
23998 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23999 result.stateOrProvince = component.short_name;
24000 } else if (component.types.indexOf("country") >= 0) {
24001 result.country = component.short_name;
24005 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24006 result.addressLine2 = "";
24010 setZoomLevel: function(zoom)
24012 this.gMapContext.map.setZoom(zoom);
24025 this.fireEvent('show', this);
24036 this.fireEvent('hide', this);
24041 Roo.apply(Roo.bootstrap.LocationPicker, {
24043 OverlayView : function(map, options)
24045 options = options || {};
24059 * @class Roo.bootstrap.Alert
24060 * @extends Roo.bootstrap.Component
24061 * Bootstrap Alert class
24062 * @cfg {String} title The title of alert
24063 * @cfg {String} html The content of alert
24064 * @cfg {String} weight ( success | info | warning | danger )
24065 * @cfg {String} faicon font-awesomeicon
24068 * Create a new alert
24069 * @param {Object} config The config object
24073 Roo.bootstrap.Alert = function(config){
24074 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24078 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24085 getAutoCreate : function()
24094 cls : 'roo-alert-icon'
24099 cls : 'roo-alert-title',
24104 cls : 'roo-alert-text',
24111 cfg.cn[0].cls += ' fa ' + this.faicon;
24115 cfg.cls += ' alert-' + this.weight;
24121 initEvents: function()
24123 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24126 setTitle : function(str)
24128 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24131 setText : function(str)
24133 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24136 setWeight : function(weight)
24139 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24142 this.weight = weight;
24144 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24147 setIcon : function(icon)
24150 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24153 this.faicon = icon;
24155 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24176 * @class Roo.bootstrap.UploadCropbox
24177 * @extends Roo.bootstrap.Component
24178 * Bootstrap UploadCropbox class
24179 * @cfg {String} emptyText show when image has been loaded
24180 * @cfg {String} rotateNotify show when image too small to rotate
24181 * @cfg {Number} errorTimeout default 3000
24182 * @cfg {Number} minWidth default 300
24183 * @cfg {Number} minHeight default 300
24184 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24185 * @cfg {Boolean} isDocument (true|false) default false
24186 * @cfg {String} url action url
24187 * @cfg {String} paramName default 'imageUpload'
24188 * @cfg {String} method default POST
24191 * Create a new UploadCropbox
24192 * @param {Object} config The config object
24195 Roo.bootstrap.UploadCropbox = function(config){
24196 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24200 * @event beforeselectfile
24201 * Fire before select file
24202 * @param {Roo.bootstrap.UploadCropbox} this
24204 "beforeselectfile" : true,
24207 * Fire after initEvent
24208 * @param {Roo.bootstrap.UploadCropbox} this
24213 * Fire after initEvent
24214 * @param {Roo.bootstrap.UploadCropbox} this
24215 * @param {String} data
24220 * Fire when preparing the file data
24221 * @param {Roo.bootstrap.UploadCropbox} this
24222 * @param {Object} file
24227 * Fire when get exception
24228 * @param {Roo.bootstrap.UploadCropbox} this
24229 * @param {XMLHttpRequest} xhr
24231 "exception" : true,
24233 * @event beforeloadcanvas
24234 * Fire before load the canvas
24235 * @param {Roo.bootstrap.UploadCropbox} this
24236 * @param {String} src
24238 "beforeloadcanvas" : true,
24241 * Fire when trash image
24242 * @param {Roo.bootstrap.UploadCropbox} this
24247 * Fire when download the image
24248 * @param {Roo.bootstrap.UploadCropbox} this
24252 * @event footerbuttonclick
24253 * Fire when footerbuttonclick
24254 * @param {Roo.bootstrap.UploadCropbox} this
24255 * @param {String} type
24257 "footerbuttonclick" : true,
24261 * @param {Roo.bootstrap.UploadCropbox} this
24266 * Fire when rotate the image
24267 * @param {Roo.bootstrap.UploadCropbox} this
24268 * @param {String} pos
24273 * Fire when inspect the file
24274 * @param {Roo.bootstrap.UploadCropbox} this
24275 * @param {Object} file
24280 * Fire when xhr upload the file
24281 * @param {Roo.bootstrap.UploadCropbox} this
24282 * @param {Object} data
24287 * Fire when arrange the file data
24288 * @param {Roo.bootstrap.UploadCropbox} this
24289 * @param {Object} formData
24294 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24297 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24299 emptyText : 'Click to upload image',
24300 rotateNotify : 'Image is too small to rotate',
24301 errorTimeout : 3000,
24315 cropType : 'image/jpeg',
24317 canvasLoaded : false,
24318 isDocument : false,
24320 paramName : 'imageUpload',
24322 getAutoCreate : function()
24326 cls : 'roo-upload-cropbox',
24330 cls : 'roo-upload-cropbox-selector',
24335 cls : 'roo-upload-cropbox-body',
24336 style : 'cursor:pointer',
24340 cls : 'roo-upload-cropbox-preview'
24344 cls : 'roo-upload-cropbox-thumb'
24348 cls : 'roo-upload-cropbox-empty-notify',
24349 html : this.emptyText
24353 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24354 html : this.rotateNotify
24360 cls : 'roo-upload-cropbox-footer',
24363 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24373 onRender : function(ct, position)
24375 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24377 if (this.buttons.length) {
24379 Roo.each(this.buttons, function(bb) {
24381 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24383 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24389 initEvents : function()
24391 this.urlAPI = (window.createObjectURL && window) ||
24392 (window.URL && URL.revokeObjectURL && URL) ||
24393 (window.webkitURL && webkitURL);
24395 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24396 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24398 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24399 this.selectorEl.hide();
24401 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24402 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24404 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24405 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24406 this.thumbEl.hide();
24408 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24409 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24411 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24412 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24413 this.errorEl.hide();
24415 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24416 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24417 this.footerEl.hide();
24419 this.setThumbBoxSize();
24425 this.fireEvent('initial', this);
24432 window.addEventListener("resize", function() { _this.resize(); } );
24434 this.bodyEl.on('click', this.beforeSelectFile, this);
24437 this.bodyEl.on('touchstart', this.onTouchStart, this);
24438 this.bodyEl.on('touchmove', this.onTouchMove, this);
24439 this.bodyEl.on('touchend', this.onTouchEnd, this);
24443 this.bodyEl.on('mousedown', this.onMouseDown, this);
24444 this.bodyEl.on('mousemove', this.onMouseMove, this);
24445 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24446 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24447 Roo.get(document).on('mouseup', this.onMouseUp, this);
24450 this.selectorEl.on('change', this.onFileSelected, this);
24456 this.baseScale = 1;
24458 this.baseRotate = 1;
24459 this.dragable = false;
24460 this.pinching = false;
24463 this.cropData = false;
24464 this.notifyEl.dom.innerHTML = this.emptyText;
24466 this.selectorEl.dom.value = '';
24470 resize : function()
24472 if(this.fireEvent('resize', this) != false){
24473 this.setThumbBoxPosition();
24474 this.setCanvasPosition();
24478 onFooterButtonClick : function(e, el, o, type)
24481 case 'rotate-left' :
24482 this.onRotateLeft(e);
24484 case 'rotate-right' :
24485 this.onRotateRight(e);
24488 this.beforeSelectFile(e);
24503 this.fireEvent('footerbuttonclick', this, type);
24506 beforeSelectFile : function(e)
24508 e.preventDefault();
24510 if(this.fireEvent('beforeselectfile', this) != false){
24511 this.selectorEl.dom.click();
24515 onFileSelected : function(e)
24517 e.preventDefault();
24519 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24523 var file = this.selectorEl.dom.files[0];
24525 if(this.fireEvent('inspect', this, file) != false){
24526 this.prepare(file);
24531 trash : function(e)
24533 this.fireEvent('trash', this);
24536 download : function(e)
24538 this.fireEvent('download', this);
24541 loadCanvas : function(src)
24543 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24547 this.imageEl = document.createElement('img');
24551 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24553 this.imageEl.src = src;
24557 onLoadCanvas : function()
24559 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24560 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24562 this.bodyEl.un('click', this.beforeSelectFile, this);
24564 this.notifyEl.hide();
24565 this.thumbEl.show();
24566 this.footerEl.show();
24568 this.baseRotateLevel();
24570 if(this.isDocument){
24571 this.setThumbBoxSize();
24574 this.setThumbBoxPosition();
24576 this.baseScaleLevel();
24582 this.canvasLoaded = true;
24586 setCanvasPosition : function()
24588 if(!this.canvasEl){
24592 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24593 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24595 this.previewEl.setLeft(pw);
24596 this.previewEl.setTop(ph);
24600 onMouseDown : function(e)
24604 this.dragable = true;
24605 this.pinching = false;
24607 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24608 this.dragable = false;
24612 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24613 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24617 onMouseMove : function(e)
24621 if(!this.canvasLoaded){
24625 if (!this.dragable){
24629 var minX = Math.ceil(this.thumbEl.getLeft(true));
24630 var minY = Math.ceil(this.thumbEl.getTop(true));
24632 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24633 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24635 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24636 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24638 x = x - this.mouseX;
24639 y = y - this.mouseY;
24641 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24642 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24644 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24645 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24647 this.previewEl.setLeft(bgX);
24648 this.previewEl.setTop(bgY);
24650 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24651 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24654 onMouseUp : function(e)
24658 this.dragable = false;
24661 onMouseWheel : function(e)
24665 this.startScale = this.scale;
24667 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24669 if(!this.zoomable()){
24670 this.scale = this.startScale;
24679 zoomable : function()
24681 var minScale = this.thumbEl.getWidth() / this.minWidth;
24683 if(this.minWidth < this.minHeight){
24684 minScale = this.thumbEl.getHeight() / this.minHeight;
24687 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24688 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24692 (this.rotate == 0 || this.rotate == 180) &&
24694 width > this.imageEl.OriginWidth ||
24695 height > this.imageEl.OriginHeight ||
24696 (width < this.minWidth && height < this.minHeight)
24704 (this.rotate == 90 || this.rotate == 270) &&
24706 width > this.imageEl.OriginWidth ||
24707 height > this.imageEl.OriginHeight ||
24708 (width < this.minHeight && height < this.minWidth)
24715 !this.isDocument &&
24716 (this.rotate == 0 || this.rotate == 180) &&
24718 width < this.minWidth ||
24719 width > this.imageEl.OriginWidth ||
24720 height < this.minHeight ||
24721 height > this.imageEl.OriginHeight
24728 !this.isDocument &&
24729 (this.rotate == 90 || this.rotate == 270) &&
24731 width < this.minHeight ||
24732 width > this.imageEl.OriginWidth ||
24733 height < this.minWidth ||
24734 height > this.imageEl.OriginHeight
24744 onRotateLeft : function(e)
24746 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24748 var minScale = this.thumbEl.getWidth() / this.minWidth;
24750 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24751 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24753 this.startScale = this.scale;
24755 while (this.getScaleLevel() < minScale){
24757 this.scale = this.scale + 1;
24759 if(!this.zoomable()){
24764 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24765 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24770 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24777 this.scale = this.startScale;
24779 this.onRotateFail();
24784 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24786 if(this.isDocument){
24787 this.setThumbBoxSize();
24788 this.setThumbBoxPosition();
24789 this.setCanvasPosition();
24794 this.fireEvent('rotate', this, 'left');
24798 onRotateRight : function(e)
24800 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24802 var minScale = this.thumbEl.getWidth() / this.minWidth;
24804 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24805 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24807 this.startScale = this.scale;
24809 while (this.getScaleLevel() < minScale){
24811 this.scale = this.scale + 1;
24813 if(!this.zoomable()){
24818 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24819 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24824 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24831 this.scale = this.startScale;
24833 this.onRotateFail();
24838 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24840 if(this.isDocument){
24841 this.setThumbBoxSize();
24842 this.setThumbBoxPosition();
24843 this.setCanvasPosition();
24848 this.fireEvent('rotate', this, 'right');
24851 onRotateFail : function()
24853 this.errorEl.show(true);
24857 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24862 this.previewEl.dom.innerHTML = '';
24864 var canvasEl = document.createElement("canvas");
24866 var contextEl = canvasEl.getContext("2d");
24868 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24869 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24870 var center = this.imageEl.OriginWidth / 2;
24872 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24873 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24874 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24875 center = this.imageEl.OriginHeight / 2;
24878 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24880 contextEl.translate(center, center);
24881 contextEl.rotate(this.rotate * Math.PI / 180);
24883 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24885 this.canvasEl = document.createElement("canvas");
24887 this.contextEl = this.canvasEl.getContext("2d");
24889 switch (this.rotate) {
24892 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24893 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24895 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24900 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24901 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24903 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24904 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);
24908 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24913 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24914 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24916 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24917 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);
24921 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);
24926 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24927 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24929 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24930 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24934 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);
24941 this.previewEl.appendChild(this.canvasEl);
24943 this.setCanvasPosition();
24948 if(!this.canvasLoaded){
24952 var imageCanvas = document.createElement("canvas");
24954 var imageContext = imageCanvas.getContext("2d");
24956 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24957 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24959 var center = imageCanvas.width / 2;
24961 imageContext.translate(center, center);
24963 imageContext.rotate(this.rotate * Math.PI / 180);
24965 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24967 var canvas = document.createElement("canvas");
24969 var context = canvas.getContext("2d");
24971 canvas.width = this.minWidth;
24972 canvas.height = this.minHeight;
24974 switch (this.rotate) {
24977 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24978 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24980 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24981 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24983 var targetWidth = this.minWidth - 2 * x;
24984 var targetHeight = this.minHeight - 2 * y;
24988 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24989 scale = targetWidth / width;
24992 if(x > 0 && y == 0){
24993 scale = targetHeight / height;
24996 if(x > 0 && y > 0){
24997 scale = targetWidth / width;
24999 if(width < height){
25000 scale = targetHeight / height;
25004 context.scale(scale, scale);
25006 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25007 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25009 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25010 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25012 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25017 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25018 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25020 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25021 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25023 var targetWidth = this.minWidth - 2 * x;
25024 var targetHeight = this.minHeight - 2 * y;
25028 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25029 scale = targetWidth / width;
25032 if(x > 0 && y == 0){
25033 scale = targetHeight / height;
25036 if(x > 0 && y > 0){
25037 scale = targetWidth / width;
25039 if(width < height){
25040 scale = targetHeight / height;
25044 context.scale(scale, scale);
25046 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25047 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25049 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25050 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25052 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25054 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25059 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25060 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25062 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25063 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25065 var targetWidth = this.minWidth - 2 * x;
25066 var targetHeight = this.minHeight - 2 * y;
25070 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25071 scale = targetWidth / width;
25074 if(x > 0 && y == 0){
25075 scale = targetHeight / height;
25078 if(x > 0 && y > 0){
25079 scale = targetWidth / width;
25081 if(width < height){
25082 scale = targetHeight / height;
25086 context.scale(scale, scale);
25088 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25089 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25091 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25092 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25094 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25095 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25097 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25102 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25103 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25105 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25106 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25108 var targetWidth = this.minWidth - 2 * x;
25109 var targetHeight = this.minHeight - 2 * y;
25113 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25114 scale = targetWidth / width;
25117 if(x > 0 && y == 0){
25118 scale = targetHeight / height;
25121 if(x > 0 && y > 0){
25122 scale = targetWidth / width;
25124 if(width < height){
25125 scale = targetHeight / height;
25129 context.scale(scale, scale);
25131 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25132 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25134 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25135 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25137 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25139 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25146 this.cropData = canvas.toDataURL(this.cropType);
25148 if(this.fireEvent('crop', this, this.cropData) !== false){
25149 this.process(this.file, this.cropData);
25156 setThumbBoxSize : function()
25160 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25161 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25162 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25164 this.minWidth = width;
25165 this.minHeight = height;
25167 if(this.rotate == 90 || this.rotate == 270){
25168 this.minWidth = height;
25169 this.minHeight = width;
25174 width = Math.ceil(this.minWidth * height / this.minHeight);
25176 if(this.minWidth > this.minHeight){
25178 height = Math.ceil(this.minHeight * width / this.minWidth);
25181 this.thumbEl.setStyle({
25182 width : width + 'px',
25183 height : height + 'px'
25190 setThumbBoxPosition : function()
25192 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25193 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25195 this.thumbEl.setLeft(x);
25196 this.thumbEl.setTop(y);
25200 baseRotateLevel : function()
25202 this.baseRotate = 1;
25205 typeof(this.exif) != 'undefined' &&
25206 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25207 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25209 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25212 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25216 baseScaleLevel : function()
25220 if(this.isDocument){
25222 if(this.baseRotate == 6 || this.baseRotate == 8){
25224 height = this.thumbEl.getHeight();
25225 this.baseScale = height / this.imageEl.OriginWidth;
25227 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25228 width = this.thumbEl.getWidth();
25229 this.baseScale = width / this.imageEl.OriginHeight;
25235 height = this.thumbEl.getHeight();
25236 this.baseScale = height / this.imageEl.OriginHeight;
25238 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25239 width = this.thumbEl.getWidth();
25240 this.baseScale = width / this.imageEl.OriginWidth;
25246 if(this.baseRotate == 6 || this.baseRotate == 8){
25248 width = this.thumbEl.getHeight();
25249 this.baseScale = width / this.imageEl.OriginHeight;
25251 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25252 height = this.thumbEl.getWidth();
25253 this.baseScale = height / this.imageEl.OriginHeight;
25256 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25257 height = this.thumbEl.getWidth();
25258 this.baseScale = height / this.imageEl.OriginHeight;
25260 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25261 width = this.thumbEl.getHeight();
25262 this.baseScale = width / this.imageEl.OriginWidth;
25269 width = this.thumbEl.getWidth();
25270 this.baseScale = width / this.imageEl.OriginWidth;
25272 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25273 height = this.thumbEl.getHeight();
25274 this.baseScale = height / this.imageEl.OriginHeight;
25277 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25279 height = this.thumbEl.getHeight();
25280 this.baseScale = height / this.imageEl.OriginHeight;
25282 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25283 width = this.thumbEl.getWidth();
25284 this.baseScale = width / this.imageEl.OriginWidth;
25292 getScaleLevel : function()
25294 return this.baseScale * Math.pow(1.1, this.scale);
25297 onTouchStart : function(e)
25299 if(!this.canvasLoaded){
25300 this.beforeSelectFile(e);
25304 var touches = e.browserEvent.touches;
25310 if(touches.length == 1){
25311 this.onMouseDown(e);
25315 if(touches.length != 2){
25321 for(var i = 0, finger; finger = touches[i]; i++){
25322 coords.push(finger.pageX, finger.pageY);
25325 var x = Math.pow(coords[0] - coords[2], 2);
25326 var y = Math.pow(coords[1] - coords[3], 2);
25328 this.startDistance = Math.sqrt(x + y);
25330 this.startScale = this.scale;
25332 this.pinching = true;
25333 this.dragable = false;
25337 onTouchMove : function(e)
25339 if(!this.pinching && !this.dragable){
25343 var touches = e.browserEvent.touches;
25350 this.onMouseMove(e);
25356 for(var i = 0, finger; finger = touches[i]; i++){
25357 coords.push(finger.pageX, finger.pageY);
25360 var x = Math.pow(coords[0] - coords[2], 2);
25361 var y = Math.pow(coords[1] - coords[3], 2);
25363 this.endDistance = Math.sqrt(x + y);
25365 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25367 if(!this.zoomable()){
25368 this.scale = this.startScale;
25376 onTouchEnd : function(e)
25378 this.pinching = false;
25379 this.dragable = false;
25383 process : function(file, crop)
25385 this.xhr = new XMLHttpRequest();
25387 file.xhr = this.xhr;
25389 this.xhr.open(this.method, this.url, true);
25392 "Accept": "application/json",
25393 "Cache-Control": "no-cache",
25394 "X-Requested-With": "XMLHttpRequest"
25397 for (var headerName in headers) {
25398 var headerValue = headers[headerName];
25400 this.xhr.setRequestHeader(headerName, headerValue);
25406 this.xhr.onload = function()
25408 _this.xhrOnLoad(_this.xhr);
25411 this.xhr.onerror = function()
25413 _this.xhrOnError(_this.xhr);
25416 var formData = new FormData();
25418 formData.append('returnHTML', 'NO');
25421 formData.append('crop', crop);
25424 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25425 formData.append(this.paramName, file, file.name);
25428 if(typeof(file.filename) != 'undefined'){
25429 formData.append('filename', file.filename);
25432 if(typeof(file.mimetype) != 'undefined'){
25433 formData.append('mimetype', file.mimetype);
25436 if(this.fireEvent('arrange', this, formData) != false){
25437 this.xhr.send(formData);
25441 xhrOnLoad : function(xhr)
25443 if (xhr.readyState !== 4) {
25444 this.fireEvent('exception', this, xhr);
25448 var response = Roo.decode(xhr.responseText);
25450 if(!response.success){
25451 this.fireEvent('exception', this, xhr);
25455 var response = Roo.decode(xhr.responseText);
25457 this.fireEvent('upload', this, response);
25461 xhrOnError : function()
25463 Roo.log('xhr on error');
25465 var response = Roo.decode(xhr.responseText);
25471 prepare : function(file)
25476 if(typeof(file) === 'string'){
25477 this.loadCanvas(file);
25481 if(!file || !this.urlAPI){
25486 this.cropType = file.type;
25490 if(this.fireEvent('prepare', this, this.file) != false){
25492 var reader = new FileReader();
25494 reader.onload = function (e) {
25495 if (e.target.error) {
25496 Roo.log(e.target.error);
25500 var buffer = e.target.result,
25501 dataView = new DataView(buffer),
25503 maxOffset = dataView.byteLength - 4,
25507 if (dataView.getUint16(0) === 0xffd8) {
25508 while (offset < maxOffset) {
25509 markerBytes = dataView.getUint16(offset);
25511 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25512 markerLength = dataView.getUint16(offset + 2) + 2;
25513 if (offset + markerLength > dataView.byteLength) {
25514 Roo.log('Invalid meta data: Invalid segment size.');
25518 if(markerBytes == 0xffe1){
25519 _this.parseExifData(
25526 offset += markerLength;
25536 var url = _this.urlAPI.createObjectURL(_this.file);
25538 _this.loadCanvas(url);
25543 reader.readAsArrayBuffer(this.file);
25549 parseExifData : function(dataView, offset, length)
25551 var tiffOffset = offset + 10,
25555 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25556 // No Exif data, might be XMP data instead
25560 // Check for the ASCII code for "Exif" (0x45786966):
25561 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25562 // No Exif data, might be XMP data instead
25565 if (tiffOffset + 8 > dataView.byteLength) {
25566 Roo.log('Invalid Exif data: Invalid segment size.');
25569 // Check for the two null bytes:
25570 if (dataView.getUint16(offset + 8) !== 0x0000) {
25571 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25574 // Check the byte alignment:
25575 switch (dataView.getUint16(tiffOffset)) {
25577 littleEndian = true;
25580 littleEndian = false;
25583 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25586 // Check for the TIFF tag marker (0x002A):
25587 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25588 Roo.log('Invalid Exif data: Missing TIFF marker.');
25591 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25592 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25594 this.parseExifTags(
25597 tiffOffset + dirOffset,
25602 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25607 if (dirOffset + 6 > dataView.byteLength) {
25608 Roo.log('Invalid Exif data: Invalid directory offset.');
25611 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25612 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25613 if (dirEndOffset + 4 > dataView.byteLength) {
25614 Roo.log('Invalid Exif data: Invalid directory size.');
25617 for (i = 0; i < tagsNumber; i += 1) {
25621 dirOffset + 2 + 12 * i, // tag offset
25625 // Return the offset to the next directory:
25626 return dataView.getUint32(dirEndOffset, littleEndian);
25629 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25631 var tag = dataView.getUint16(offset, littleEndian);
25633 this.exif[tag] = this.getExifValue(
25637 dataView.getUint16(offset + 2, littleEndian), // tag type
25638 dataView.getUint32(offset + 4, littleEndian), // tag length
25643 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25645 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25654 Roo.log('Invalid Exif data: Invalid tag type.');
25658 tagSize = tagType.size * length;
25659 // Determine if the value is contained in the dataOffset bytes,
25660 // or if the value at the dataOffset is a pointer to the actual data:
25661 dataOffset = tagSize > 4 ?
25662 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25663 if (dataOffset + tagSize > dataView.byteLength) {
25664 Roo.log('Invalid Exif data: Invalid data offset.');
25667 if (length === 1) {
25668 return tagType.getValue(dataView, dataOffset, littleEndian);
25671 for (i = 0; i < length; i += 1) {
25672 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25675 if (tagType.ascii) {
25677 // Concatenate the chars:
25678 for (i = 0; i < values.length; i += 1) {
25680 // Ignore the terminating NULL byte(s):
25681 if (c === '\u0000') {
25693 Roo.apply(Roo.bootstrap.UploadCropbox, {
25695 'Orientation': 0x0112
25699 1: 0, //'top-left',
25701 3: 180, //'bottom-right',
25702 // 4: 'bottom-left',
25704 6: 90, //'right-top',
25705 // 7: 'right-bottom',
25706 8: 270 //'left-bottom'
25710 // byte, 8-bit unsigned int:
25712 getValue: function (dataView, dataOffset) {
25713 return dataView.getUint8(dataOffset);
25717 // ascii, 8-bit byte:
25719 getValue: function (dataView, dataOffset) {
25720 return String.fromCharCode(dataView.getUint8(dataOffset));
25725 // short, 16 bit int:
25727 getValue: function (dataView, dataOffset, littleEndian) {
25728 return dataView.getUint16(dataOffset, littleEndian);
25732 // long, 32 bit int:
25734 getValue: function (dataView, dataOffset, littleEndian) {
25735 return dataView.getUint32(dataOffset, littleEndian);
25739 // rational = two long values, first is numerator, second is denominator:
25741 getValue: function (dataView, dataOffset, littleEndian) {
25742 return dataView.getUint32(dataOffset, littleEndian) /
25743 dataView.getUint32(dataOffset + 4, littleEndian);
25747 // slong, 32 bit signed int:
25749 getValue: function (dataView, dataOffset, littleEndian) {
25750 return dataView.getInt32(dataOffset, littleEndian);
25754 // srational, two slongs, first is numerator, second is denominator:
25756 getValue: function (dataView, dataOffset, littleEndian) {
25757 return dataView.getInt32(dataOffset, littleEndian) /
25758 dataView.getInt32(dataOffset + 4, littleEndian);
25768 cls : 'btn-group roo-upload-cropbox-rotate-left',
25769 action : 'rotate-left',
25773 cls : 'btn btn-default',
25774 html : '<i class="fa fa-undo"></i>'
25780 cls : 'btn-group roo-upload-cropbox-picture',
25781 action : 'picture',
25785 cls : 'btn btn-default',
25786 html : '<i class="fa fa-picture-o"></i>'
25792 cls : 'btn-group roo-upload-cropbox-rotate-right',
25793 action : 'rotate-right',
25797 cls : 'btn btn-default',
25798 html : '<i class="fa fa-repeat"></i>'
25806 cls : 'btn-group roo-upload-cropbox-rotate-left',
25807 action : 'rotate-left',
25811 cls : 'btn btn-default',
25812 html : '<i class="fa fa-undo"></i>'
25818 cls : 'btn-group roo-upload-cropbox-download',
25819 action : 'download',
25823 cls : 'btn btn-default',
25824 html : '<i class="fa fa-download"></i>'
25830 cls : 'btn-group roo-upload-cropbox-crop',
25835 cls : 'btn btn-default',
25836 html : '<i class="fa fa-crop"></i>'
25842 cls : 'btn-group roo-upload-cropbox-trash',
25847 cls : 'btn btn-default',
25848 html : '<i class="fa fa-trash"></i>'
25854 cls : 'btn-group roo-upload-cropbox-rotate-right',
25855 action : 'rotate-right',
25859 cls : 'btn btn-default',
25860 html : '<i class="fa fa-repeat"></i>'
25868 cls : 'btn-group roo-upload-cropbox-rotate-left',
25869 action : 'rotate-left',
25873 cls : 'btn btn-default',
25874 html : '<i class="fa fa-undo"></i>'
25880 cls : 'btn-group roo-upload-cropbox-rotate-right',
25881 action : 'rotate-right',
25885 cls : 'btn btn-default',
25886 html : '<i class="fa fa-repeat"></i>'
25899 * @class Roo.bootstrap.DocumentManager
25900 * @extends Roo.bootstrap.Component
25901 * Bootstrap DocumentManager class
25902 * @cfg {String} paramName default 'imageUpload'
25903 * @cfg {String} method default POST
25904 * @cfg {String} url action url
25905 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25906 * @cfg {Boolean} multiple multiple upload default true
25907 * @cfg {Number} thumbSize default 300
25908 * @cfg {String} fieldLabel
25909 * @cfg {Number} labelWidth default 4
25910 * @cfg {String} labelAlign (left|top) default left
25911 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25914 * Create a new DocumentManager
25915 * @param {Object} config The config object
25918 Roo.bootstrap.DocumentManager = function(config){
25919 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25924 * Fire when initial the DocumentManager
25925 * @param {Roo.bootstrap.DocumentManager} this
25930 * inspect selected file
25931 * @param {Roo.bootstrap.DocumentManager} this
25932 * @param {File} file
25937 * Fire when xhr load exception
25938 * @param {Roo.bootstrap.DocumentManager} this
25939 * @param {XMLHttpRequest} xhr
25941 "exception" : true,
25944 * prepare the form data
25945 * @param {Roo.bootstrap.DocumentManager} this
25946 * @param {Object} formData
25951 * Fire when remove the file
25952 * @param {Roo.bootstrap.DocumentManager} this
25953 * @param {Object} file
25958 * Fire after refresh the file
25959 * @param {Roo.bootstrap.DocumentManager} this
25964 * Fire after click the image
25965 * @param {Roo.bootstrap.DocumentManager} this
25966 * @param {Object} file
25971 * Fire when upload a image and editable set to true
25972 * @param {Roo.bootstrap.DocumentManager} this
25973 * @param {Object} file
25977 * @event beforeselectfile
25978 * Fire before select file
25979 * @param {Roo.bootstrap.DocumentManager} this
25981 "beforeselectfile" : true,
25984 * Fire before process file
25985 * @param {Roo.bootstrap.DocumentManager} this
25986 * @param {Object} file
25993 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26002 paramName : 'imageUpload',
26005 labelAlign : 'left',
26009 getAutoCreate : function()
26011 var managerWidget = {
26013 cls : 'roo-document-manager',
26017 cls : 'roo-document-manager-selector',
26022 cls : 'roo-document-manager-uploader',
26026 cls : 'roo-document-manager-upload-btn',
26027 html : '<i class="fa fa-plus"></i>'
26038 cls : 'column col-md-12',
26043 if(this.fieldLabel.length){
26048 cls : 'column col-md-12',
26049 html : this.fieldLabel
26053 cls : 'column col-md-12',
26058 if(this.labelAlign == 'left'){
26062 cls : 'column col-md-' + this.labelWidth,
26063 html : this.fieldLabel
26067 cls : 'column col-md-' + (12 - this.labelWidth),
26077 cls : 'row clearfix',
26085 initEvents : function()
26087 this.managerEl = this.el.select('.roo-document-manager', true).first();
26088 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26090 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26091 this.selectorEl.hide();
26094 this.selectorEl.attr('multiple', 'multiple');
26097 this.selectorEl.on('change', this.onFileSelected, this);
26099 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26100 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26102 this.uploader.on('click', this.onUploaderClick, this);
26104 this.renderProgressDialog();
26108 window.addEventListener("resize", function() { _this.refresh(); } );
26110 this.fireEvent('initial', this);
26113 renderProgressDialog : function()
26117 this.progressDialog = new Roo.bootstrap.Modal({
26118 cls : 'roo-document-manager-progress-dialog',
26119 allow_close : false,
26129 btnclick : function() {
26130 _this.uploadCancel();
26136 this.progressDialog.render(Roo.get(document.body));
26138 this.progress = new Roo.bootstrap.Progress({
26139 cls : 'roo-document-manager-progress',
26144 this.progress.render(this.progressDialog.getChildContainer());
26146 this.progressBar = new Roo.bootstrap.ProgressBar({
26147 cls : 'roo-document-manager-progress-bar',
26150 aria_valuemax : 12,
26154 this.progressBar.render(this.progress.getChildContainer());
26157 onUploaderClick : function(e)
26159 e.preventDefault();
26161 if(this.fireEvent('beforeselectfile', this) != false){
26162 this.selectorEl.dom.click();
26167 onFileSelected : function(e)
26169 e.preventDefault();
26171 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26175 Roo.each(this.selectorEl.dom.files, function(file){
26176 if(this.fireEvent('inspect', this, file) != false){
26177 this.files.push(file);
26187 this.selectorEl.dom.value = '';
26189 if(!this.files.length){
26193 if(this.boxes > 0 && this.files.length > this.boxes){
26194 this.files = this.files.slice(0, this.boxes);
26197 this.uploader.show();
26199 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26200 this.uploader.hide();
26209 Roo.each(this.files, function(file){
26211 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26212 var f = this.renderPreview(file);
26217 if(file.type.indexOf('image') != -1){
26218 this.delegates.push(
26220 _this.process(file);
26221 }).createDelegate(this)
26229 _this.process(file);
26230 }).createDelegate(this)
26235 this.files = files;
26237 this.delegates = this.delegates.concat(docs);
26239 if(!this.delegates.length){
26244 this.progressBar.aria_valuemax = this.delegates.length;
26251 arrange : function()
26253 if(!this.delegates.length){
26254 this.progressDialog.hide();
26259 var delegate = this.delegates.shift();
26261 this.progressDialog.show();
26263 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26265 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26270 refresh : function()
26272 this.uploader.show();
26274 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26275 this.uploader.hide();
26278 Roo.isTouch ? this.closable(false) : this.closable(true);
26280 this.fireEvent('refresh', this);
26283 onRemove : function(e, el, o)
26285 e.preventDefault();
26287 this.fireEvent('remove', this, o);
26291 remove : function(o)
26295 Roo.each(this.files, function(file){
26296 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26305 this.files = files;
26312 Roo.each(this.files, function(file){
26317 file.target.remove();
26326 onClick : function(e, el, o)
26328 e.preventDefault();
26330 this.fireEvent('click', this, o);
26334 closable : function(closable)
26336 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26338 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26350 xhrOnLoad : function(xhr)
26352 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26356 if (xhr.readyState !== 4) {
26358 this.fireEvent('exception', this, xhr);
26362 var response = Roo.decode(xhr.responseText);
26364 if(!response.success){
26366 this.fireEvent('exception', this, xhr);
26370 var file = this.renderPreview(response.data);
26372 this.files.push(file);
26378 xhrOnError : function()
26380 Roo.log('xhr on error');
26382 var response = Roo.decode(xhr.responseText);
26389 process : function(file)
26391 if(this.fireEvent('process', this, file) !== false){
26392 if(this.editable && file.type.indexOf('image') != -1){
26393 this.fireEvent('edit', this, file);
26397 this.uploadStart(file, false);
26404 uploadStart : function(file, crop)
26406 this.xhr = new XMLHttpRequest();
26408 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26413 file.xhr = this.xhr;
26415 this.managerEl.createChild({
26417 cls : 'roo-document-manager-loading',
26421 tooltip : file.name,
26422 cls : 'roo-document-manager-thumb',
26423 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26429 this.xhr.open(this.method, this.url, true);
26432 "Accept": "application/json",
26433 "Cache-Control": "no-cache",
26434 "X-Requested-With": "XMLHttpRequest"
26437 for (var headerName in headers) {
26438 var headerValue = headers[headerName];
26440 this.xhr.setRequestHeader(headerName, headerValue);
26446 this.xhr.onload = function()
26448 _this.xhrOnLoad(_this.xhr);
26451 this.xhr.onerror = function()
26453 _this.xhrOnError(_this.xhr);
26456 var formData = new FormData();
26458 formData.append('returnHTML', 'NO');
26461 formData.append('crop', crop);
26464 formData.append(this.paramName, file, file.name);
26466 if(this.fireEvent('prepare', this, formData) != false){
26467 this.xhr.send(formData);
26471 uploadCancel : function()
26475 this.delegates = [];
26477 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26484 renderPreview : function(file)
26486 if(typeof(file.target) != 'undefined' && file.target){
26490 var previewEl = this.managerEl.createChild({
26492 cls : 'roo-document-manager-preview',
26496 tooltip : file.filename,
26497 cls : 'roo-document-manager-thumb',
26498 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26503 html : '<i class="fa fa-times-circle"></i>'
26508 var close = previewEl.select('button.close', true).first();
26510 close.on('click', this.onRemove, this, file);
26512 file.target = previewEl;
26514 var image = previewEl.select('img', true).first();
26518 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26520 image.on('click', this.onClick, this, file);
26526 onPreviewLoad : function(file, image)
26528 if(typeof(file.target) == 'undefined' || !file.target){
26532 var width = image.dom.naturalWidth || image.dom.width;
26533 var height = image.dom.naturalHeight || image.dom.height;
26535 if(width > height){
26536 file.target.addClass('wide');
26540 file.target.addClass('tall');
26545 uploadFromSource : function(file, crop)
26547 this.xhr = new XMLHttpRequest();
26549 this.managerEl.createChild({
26551 cls : 'roo-document-manager-loading',
26555 tooltip : file.name,
26556 cls : 'roo-document-manager-thumb',
26557 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26563 this.xhr.open(this.method, this.url, true);
26566 "Accept": "application/json",
26567 "Cache-Control": "no-cache",
26568 "X-Requested-With": "XMLHttpRequest"
26571 for (var headerName in headers) {
26572 var headerValue = headers[headerName];
26574 this.xhr.setRequestHeader(headerName, headerValue);
26580 this.xhr.onload = function()
26582 _this.xhrOnLoad(_this.xhr);
26585 this.xhr.onerror = function()
26587 _this.xhrOnError(_this.xhr);
26590 var formData = new FormData();
26592 formData.append('returnHTML', 'NO');
26594 formData.append('crop', crop);
26596 if(typeof(file.filename) != 'undefined'){
26597 formData.append('filename', file.filename);
26600 if(typeof(file.mimetype) != 'undefined'){
26601 formData.append('mimetype', file.mimetype);
26604 if(this.fireEvent('prepare', this, formData) != false){
26605 this.xhr.send(formData);
26615 * @class Roo.bootstrap.DocumentViewer
26616 * @extends Roo.bootstrap.Component
26617 * Bootstrap DocumentViewer class
26620 * Create a new DocumentViewer
26621 * @param {Object} config The config object
26624 Roo.bootstrap.DocumentViewer = function(config){
26625 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26630 * Fire after initEvent
26631 * @param {Roo.bootstrap.DocumentViewer} this
26637 * @param {Roo.bootstrap.DocumentViewer} this
26642 * Fire after trash button
26643 * @param {Roo.bootstrap.DocumentViewer} this
26650 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26652 getAutoCreate : function()
26656 cls : 'roo-document-viewer',
26660 cls : 'roo-document-viewer-body',
26664 cls : 'roo-document-viewer-thumb',
26668 cls : 'roo-document-viewer-image'
26676 cls : 'roo-document-viewer-footer',
26679 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26687 cls : 'btn btn-default roo-document-viewer-trash',
26688 html : '<i class="fa fa-trash"></i>'
26701 initEvents : function()
26704 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26705 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26707 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26708 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26710 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26711 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26713 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26714 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26716 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26717 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26719 this.bodyEl.on('click', this.onClick, this);
26721 this.trashBtn.on('click', this.onTrash, this);
26725 initial : function()
26727 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26730 this.fireEvent('initial', this);
26734 onClick : function(e)
26736 e.preventDefault();
26738 this.fireEvent('click', this);
26741 onTrash : function(e)
26743 e.preventDefault();
26745 this.fireEvent('trash', this);
26757 * @class Roo.bootstrap.NavProgressBar
26758 * @extends Roo.bootstrap.Component
26759 * Bootstrap NavProgressBar class
26762 * Create a new nav progress bar
26763 * @param {Object} config The config object
26766 Roo.bootstrap.NavProgressBar = function(config){
26767 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26769 this.bullets = this.bullets || [];
26771 // Roo.bootstrap.NavProgressBar.register(this);
26775 * Fires when the active item changes
26776 * @param {Roo.bootstrap.NavProgressBar} this
26777 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26778 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26785 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26790 getAutoCreate : function()
26792 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26796 cls : 'roo-navigation-bar-group',
26800 cls : 'roo-navigation-top-bar'
26804 cls : 'roo-navigation-bullets-bar',
26808 cls : 'roo-navigation-bar'
26815 cls : 'roo-navigation-bottom-bar'
26825 initEvents: function()
26830 onRender : function(ct, position)
26832 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26834 if(this.bullets.length){
26835 Roo.each(this.bullets, function(b){
26844 addItem : function(cfg)
26846 var item = new Roo.bootstrap.NavProgressItem(cfg);
26848 item.parentId = this.id;
26849 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26852 var top = new Roo.bootstrap.Element({
26854 cls : 'roo-navigation-bar-text'
26857 var bottom = new Roo.bootstrap.Element({
26859 cls : 'roo-navigation-bar-text'
26862 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26863 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26865 var topText = new Roo.bootstrap.Element({
26867 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26870 var bottomText = new Roo.bootstrap.Element({
26872 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26875 topText.onRender(top.el, null);
26876 bottomText.onRender(bottom.el, null);
26879 item.bottomEl = bottom;
26882 this.barItems.push(item);
26887 getActive : function()
26889 var active = false;
26891 Roo.each(this.barItems, function(v){
26893 if (!v.isActive()) {
26905 setActiveItem : function(item)
26909 Roo.each(this.barItems, function(v){
26910 if (v.rid == item.rid) {
26914 if (v.isActive()) {
26915 v.setActive(false);
26920 item.setActive(true);
26922 this.fireEvent('changed', this, item, prev);
26925 getBarItem: function(rid)
26929 Roo.each(this.barItems, function(e) {
26930 if (e.rid != rid) {
26941 indexOfItem : function(item)
26945 Roo.each(this.barItems, function(v, i){
26947 if (v.rid != item.rid) {
26958 setActiveNext : function()
26960 var i = this.indexOfItem(this.getActive());
26962 if (i > this.barItems.length) {
26966 this.setActiveItem(this.barItems[i+1]);
26969 setActivePrev : function()
26971 var i = this.indexOfItem(this.getActive());
26977 this.setActiveItem(this.barItems[i-1]);
26980 format : function()
26982 if(!this.barItems.length){
26986 var width = 100 / this.barItems.length;
26988 Roo.each(this.barItems, function(i){
26989 i.el.setStyle('width', width + '%');
26990 i.topEl.el.setStyle('width', width + '%');
26991 i.bottomEl.el.setStyle('width', width + '%');
27000 * Nav Progress Item
27005 * @class Roo.bootstrap.NavProgressItem
27006 * @extends Roo.bootstrap.Component
27007 * Bootstrap NavProgressItem class
27008 * @cfg {String} rid the reference id
27009 * @cfg {Boolean} active (true|false) Is item active default false
27010 * @cfg {Boolean} disabled (true|false) Is item active default false
27011 * @cfg {String} html
27012 * @cfg {String} position (top|bottom) text position default bottom
27013 * @cfg {String} icon show icon instead of number
27016 * Create a new NavProgressItem
27017 * @param {Object} config The config object
27019 Roo.bootstrap.NavProgressItem = function(config){
27020 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27025 * The raw click event for the entire grid.
27026 * @param {Roo.bootstrap.NavProgressItem} this
27027 * @param {Roo.EventObject} e
27034 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27040 position : 'bottom',
27043 getAutoCreate : function()
27045 var iconCls = 'roo-navigation-bar-item-icon';
27047 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27051 cls: 'roo-navigation-bar-item',
27061 cfg.cls += ' active';
27064 cfg.cls += ' disabled';
27070 disable : function()
27072 this.setDisabled(true);
27075 enable : function()
27077 this.setDisabled(false);
27080 initEvents: function()
27082 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27084 this.iconEl.on('click', this.onClick, this);
27087 onClick : function(e)
27089 e.preventDefault();
27095 if(this.fireEvent('click', this, e) === false){
27099 this.parent().setActiveItem(this);
27102 isActive: function ()
27104 return this.active;
27107 setActive : function(state)
27109 if(this.active == state){
27113 this.active = state;
27116 this.el.addClass('active');
27120 this.el.removeClass('active');
27125 setDisabled : function(state)
27127 if(this.disabled == state){
27131 this.disabled = state;
27134 this.el.addClass('disabled');
27138 this.el.removeClass('disabled');
27141 tooltipEl : function()
27143 return this.el.select('.roo-navigation-bar-item-icon', true).first();;