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 this.fireEvent('valid', this);
8288 * Mark this field as valid
8290 markValid : function(){
8291 if(!this.el || this.preventMark){ // not rendered
8295 this.el.removeClass([this.invalidClass, this.validClass]);
8297 var feedback = this.el.select('.form-control-feedback', true).first();
8300 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8303 if(this.disabled || this.allowBlank){
8307 this.el.addClass(this.validClass);
8309 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8311 var feedback = this.el.select('.form-control-feedback', true).first();
8314 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8315 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8320 this.fireEvent('valid', this);
8324 * Mark this field as invalid
8325 * @param {String} msg The validation message
8327 markInvalid : function(msg){
8328 if(!this.el || this.preventMark){ // not rendered
8332 this.el.removeClass([this.invalidClass, this.validClass]);
8334 var feedback = this.el.select('.form-control-feedback', true).first();
8337 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8340 if(this.disabled || this.allowBlank){
8344 this.el.addClass(this.invalidClass);
8346 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8348 var feedback = this.el.select('.form-control-feedback', true).first();
8351 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8353 if(this.getValue().length || this.forceFeedback){
8354 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8361 this.fireEvent('invalid', this, msg);
8364 SafariOnKeyDown : function(event)
8366 // this is a workaround for a password hang bug on chrome/ webkit.
8368 var isSelectAll = false;
8370 if(this.inputEl().dom.selectionEnd > 0){
8371 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8373 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8374 event.preventDefault();
8379 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8381 event.preventDefault();
8382 // this is very hacky as keydown always get's upper case.
8384 var cc = String.fromCharCode(event.getCharCode());
8385 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8389 adjustWidth : function(tag, w){
8390 tag = tag.toLowerCase();
8391 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8392 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8396 if(tag == 'textarea'){
8399 }else if(Roo.isOpera){
8403 if(tag == 'textarea'){
8422 * @class Roo.bootstrap.TextArea
8423 * @extends Roo.bootstrap.Input
8424 * Bootstrap TextArea class
8425 * @cfg {Number} cols Specifies the visible width of a text area
8426 * @cfg {Number} rows Specifies the visible number of lines in a text area
8427 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8428 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8429 * @cfg {string} html text
8432 * Create a new TextArea
8433 * @param {Object} config The config object
8436 Roo.bootstrap.TextArea = function(config){
8437 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8441 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8451 getAutoCreate : function(){
8453 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8464 value : this.value || '',
8465 html: this.html || '',
8466 cls : 'form-control',
8467 placeholder : this.placeholder || ''
8471 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8472 input.maxLength = this.maxLength;
8476 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8480 input.cols = this.cols;
8483 if (this.readOnly) {
8484 input.readonly = true;
8488 input.name = this.name;
8492 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8496 ['xs','sm','md','lg'].map(function(size){
8497 if (settings[size]) {
8498 cfg.cls += ' col-' + size + '-' + settings[size];
8502 var inputblock = input;
8504 if(this.hasFeedback && !this.allowBlank){
8508 cls: 'glyphicon form-control-feedback'
8512 cls : 'has-feedback',
8521 if (this.before || this.after) {
8524 cls : 'input-group',
8528 inputblock.cn.push({
8530 cls : 'input-group-addon',
8535 inputblock.cn.push(input);
8537 if(this.hasFeedback && !this.allowBlank){
8538 inputblock.cls += ' has-feedback';
8539 inputblock.cn.push(feedback);
8543 inputblock.cn.push({
8545 cls : 'input-group-addon',
8552 if (align ==='left' && this.fieldLabel.length) {
8553 Roo.log("left and has label");
8559 cls : 'control-label col-sm-' + this.labelWidth,
8560 html : this.fieldLabel
8564 cls : "col-sm-" + (12 - this.labelWidth),
8571 } else if ( this.fieldLabel.length) {
8577 //cls : 'input-group-addon',
8578 html : this.fieldLabel
8588 Roo.log(" no label && no align");
8598 if (this.disabled) {
8599 input.disabled=true;
8606 * return the real textarea element.
8608 inputEl: function ()
8610 return this.el.select('textarea.form-control',true).first();
8618 * trigger field - base class for combo..
8623 * @class Roo.bootstrap.TriggerField
8624 * @extends Roo.bootstrap.Input
8625 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8626 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8627 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8628 * for which you can provide a custom implementation. For example:
8630 var trigger = new Roo.bootstrap.TriggerField();
8631 trigger.onTriggerClick = myTriggerFn;
8632 trigger.applyTo('my-field');
8635 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8636 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8637 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8638 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8639 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8642 * Create a new TriggerField.
8643 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8644 * to the base TextField)
8646 Roo.bootstrap.TriggerField = function(config){
8647 this.mimicing = false;
8648 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8651 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8653 * @cfg {String} triggerClass A CSS class to apply to the trigger
8656 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8661 * @cfg {Boolean} removable (true|false) special filter default false
8665 /** @cfg {Boolean} grow @hide */
8666 /** @cfg {Number} growMin @hide */
8667 /** @cfg {Number} growMax @hide */
8673 autoSize: Roo.emptyFn,
8680 actionMode : 'wrap',
8685 getAutoCreate : function(){
8687 var align = this.labelAlign || this.parentLabelAlign();
8692 cls: 'form-group' //input-group
8699 type : this.inputType,
8700 cls : 'form-control',
8701 autocomplete: 'new-password',
8702 placeholder : this.placeholder || ''
8706 input.name = this.name;
8709 input.cls += ' input-' + this.size;
8712 if (this.disabled) {
8713 input.disabled=true;
8716 var inputblock = input;
8718 if(this.hasFeedback && !this.allowBlank){
8722 cls: 'glyphicon form-control-feedback'
8725 if(this.removable && !this.editable && !this.tickable){
8727 cls : 'has-feedback',
8733 cls : 'roo-combo-removable-btn close'
8740 cls : 'has-feedback',
8749 if(this.removable && !this.editable && !this.tickable){
8751 cls : 'roo-removable',
8757 cls : 'roo-combo-removable-btn close'
8764 if (this.before || this.after) {
8767 cls : 'input-group',
8771 inputblock.cn.push({
8773 cls : 'input-group-addon',
8778 inputblock.cn.push(input);
8780 if(this.hasFeedback && !this.allowBlank){
8781 inputblock.cls += ' has-feedback';
8782 inputblock.cn.push(feedback);
8786 inputblock.cn.push({
8788 cls : 'input-group-addon',
8801 cls: 'form-hidden-field'
8809 Roo.log('multiple');
8817 cls: 'form-hidden-field'
8821 cls: 'select2-choices',
8825 cls: 'select2-search-field',
8838 cls: 'select2-container input-group',
8843 // cls: 'typeahead typeahead-long dropdown-menu',
8844 // style: 'display:none'
8849 if(!this.multiple && this.showToggleBtn){
8855 if (this.caret != false) {
8858 cls: 'fa fa-' + this.caret
8865 cls : 'input-group-addon btn dropdown-toggle',
8870 cls: 'combobox-clear',
8884 combobox.cls += ' select2-container-multi';
8887 if (align ==='left' && this.fieldLabel.length) {
8889 Roo.log("left and has label");
8895 cls : 'control-label col-sm-' + this.labelWidth,
8896 html : this.fieldLabel
8900 cls : "col-sm-" + (12 - this.labelWidth),
8907 } else if ( this.fieldLabel.length) {
8913 //cls : 'input-group-addon',
8914 html : this.fieldLabel
8924 Roo.log(" no label && no align");
8931 ['xs','sm','md','lg'].map(function(size){
8932 if (settings[size]) {
8933 cfg.cls += ' col-' + size + '-' + settings[size];
8944 onResize : function(w, h){
8945 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8946 // if(typeof w == 'number'){
8947 // var x = w - this.trigger.getWidth();
8948 // this.inputEl().setWidth(this.adjustWidth('input', x));
8949 // this.trigger.setStyle('left', x+'px');
8954 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8957 getResizeEl : function(){
8958 return this.inputEl();
8962 getPositionEl : function(){
8963 return this.inputEl();
8967 alignErrorIcon : function(){
8968 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8972 initEvents : function(){
8976 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8977 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8978 if(!this.multiple && this.showToggleBtn){
8979 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8980 if(this.hideTrigger){
8981 this.trigger.setDisplayed(false);
8983 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8987 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8990 if(this.removable && !this.editable && !this.tickable){
8991 var close = this.closeTriggerEl();
8994 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8995 close.on('click', this.removeBtnClick, this, close);
8999 //this.trigger.addClassOnOver('x-form-trigger-over');
9000 //this.trigger.addClassOnClick('x-form-trigger-click');
9003 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9007 closeTriggerEl : function()
9009 var close = this.el.select('.roo-combo-removable-btn', true).first();
9010 return close ? close : false;
9013 removeBtnClick : function(e, h, el)
9017 if(this.fireEvent("remove", this) !== false){
9022 createList : function()
9024 this.list = Roo.get(document.body).createChild({
9026 cls: 'typeahead typeahead-long dropdown-menu',
9027 style: 'display:none'
9030 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9035 initTrigger : function(){
9040 onDestroy : function(){
9042 this.trigger.removeAllListeners();
9043 // this.trigger.remove();
9046 // this.wrap.remove();
9048 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9052 onFocus : function(){
9053 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9056 this.wrap.addClass('x-trigger-wrap-focus');
9057 this.mimicing = true;
9058 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9059 if(this.monitorTab){
9060 this.el.on("keydown", this.checkTab, this);
9067 checkTab : function(e){
9068 if(e.getKey() == e.TAB){
9074 onBlur : function(){
9079 mimicBlur : function(e, t){
9081 if(!this.wrap.contains(t) && this.validateBlur()){
9088 triggerBlur : function(){
9089 this.mimicing = false;
9090 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9091 if(this.monitorTab){
9092 this.el.un("keydown", this.checkTab, this);
9094 //this.wrap.removeClass('x-trigger-wrap-focus');
9095 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9099 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9100 validateBlur : function(e, t){
9105 onDisable : function(){
9106 this.inputEl().dom.disabled = true;
9107 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9109 // this.wrap.addClass('x-item-disabled');
9114 onEnable : function(){
9115 this.inputEl().dom.disabled = false;
9116 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9118 // this.el.removeClass('x-item-disabled');
9123 onShow : function(){
9124 var ae = this.getActionEl();
9127 ae.dom.style.display = '';
9128 ae.dom.style.visibility = 'visible';
9134 onHide : function(){
9135 var ae = this.getActionEl();
9136 ae.dom.style.display = 'none';
9140 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9141 * by an implementing function.
9143 * @param {EventObject} e
9145 onTriggerClick : Roo.emptyFn
9149 * Ext JS Library 1.1.1
9150 * Copyright(c) 2006-2007, Ext JS, LLC.
9152 * Originally Released Under LGPL - original licence link has changed is not relivant.
9155 * <script type="text/javascript">
9160 * @class Roo.data.SortTypes
9162 * Defines the default sorting (casting?) comparison functions used when sorting data.
9164 Roo.data.SortTypes = {
9166 * Default sort that does nothing
9167 * @param {Mixed} s The value being converted
9168 * @return {Mixed} The comparison value
9175 * The regular expression used to strip tags
9179 stripTagsRE : /<\/?[^>]+>/gi,
9182 * Strips all HTML tags to sort on text only
9183 * @param {Mixed} s The value being converted
9184 * @return {String} The comparison value
9186 asText : function(s){
9187 return String(s).replace(this.stripTagsRE, "");
9191 * Strips all HTML tags to sort on text only - Case insensitive
9192 * @param {Mixed} s The value being converted
9193 * @return {String} The comparison value
9195 asUCText : function(s){
9196 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9200 * Case insensitive string
9201 * @param {Mixed} s The value being converted
9202 * @return {String} The comparison value
9204 asUCString : function(s) {
9205 return String(s).toUpperCase();
9210 * @param {Mixed} s The value being converted
9211 * @return {Number} The comparison value
9213 asDate : function(s) {
9217 if(s instanceof Date){
9220 return Date.parse(String(s));
9225 * @param {Mixed} s The value being converted
9226 * @return {Float} The comparison value
9228 asFloat : function(s) {
9229 var val = parseFloat(String(s).replace(/,/g, ""));
9230 if(isNaN(val)) val = 0;
9236 * @param {Mixed} s The value being converted
9237 * @return {Number} The comparison value
9239 asInt : function(s) {
9240 var val = parseInt(String(s).replace(/,/g, ""));
9241 if(isNaN(val)) val = 0;
9246 * Ext JS Library 1.1.1
9247 * Copyright(c) 2006-2007, Ext JS, LLC.
9249 * Originally Released Under LGPL - original licence link has changed is not relivant.
9252 * <script type="text/javascript">
9256 * @class Roo.data.Record
9257 * Instances of this class encapsulate both record <em>definition</em> information, and record
9258 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9259 * to access Records cached in an {@link Roo.data.Store} object.<br>
9261 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9262 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9265 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9267 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9268 * {@link #create}. The parameters are the same.
9269 * @param {Array} data An associative Array of data values keyed by the field name.
9270 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9271 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9272 * not specified an integer id is generated.
9274 Roo.data.Record = function(data, id){
9275 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9280 * Generate a constructor for a specific record layout.
9281 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9282 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9283 * Each field definition object may contain the following properties: <ul>
9284 * <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,
9285 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9286 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9287 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9288 * is being used, then this is a string containing the javascript expression to reference the data relative to
9289 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9290 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9291 * this may be omitted.</p></li>
9292 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9293 * <ul><li>auto (Default, implies no conversion)</li>
9298 * <li>date</li></ul></p></li>
9299 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9300 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9301 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9302 * by the Reader into an object that will be stored in the Record. It is passed the
9303 * following parameters:<ul>
9304 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9306 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9308 * <br>usage:<br><pre><code>
9309 var TopicRecord = Roo.data.Record.create(
9310 {name: 'title', mapping: 'topic_title'},
9311 {name: 'author', mapping: 'username'},
9312 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9313 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9314 {name: 'lastPoster', mapping: 'user2'},
9315 {name: 'excerpt', mapping: 'post_text'}
9318 var myNewRecord = new TopicRecord({
9319 title: 'Do my job please',
9322 lastPost: new Date(),
9323 lastPoster: 'Animal',
9324 excerpt: 'No way dude!'
9326 myStore.add(myNewRecord);
9331 Roo.data.Record.create = function(o){
9333 f.superclass.constructor.apply(this, arguments);
9335 Roo.extend(f, Roo.data.Record);
9336 var p = f.prototype;
9337 p.fields = new Roo.util.MixedCollection(false, function(field){
9340 for(var i = 0, len = o.length; i < len; i++){
9341 p.fields.add(new Roo.data.Field(o[i]));
9343 f.getField = function(name){
9344 return p.fields.get(name);
9349 Roo.data.Record.AUTO_ID = 1000;
9350 Roo.data.Record.EDIT = 'edit';
9351 Roo.data.Record.REJECT = 'reject';
9352 Roo.data.Record.COMMIT = 'commit';
9354 Roo.data.Record.prototype = {
9356 * Readonly flag - true if this record has been modified.
9365 join : function(store){
9370 * Set the named field to the specified value.
9371 * @param {String} name The name of the field to set.
9372 * @param {Object} value The value to set the field to.
9374 set : function(name, value){
9375 if(this.data[name] == value){
9382 if(typeof this.modified[name] == 'undefined'){
9383 this.modified[name] = this.data[name];
9385 this.data[name] = value;
9386 if(!this.editing && this.store){
9387 this.store.afterEdit(this);
9392 * Get the value of the named field.
9393 * @param {String} name The name of the field to get the value of.
9394 * @return {Object} The value of the field.
9396 get : function(name){
9397 return this.data[name];
9401 beginEdit : function(){
9402 this.editing = true;
9407 cancelEdit : function(){
9408 this.editing = false;
9409 delete this.modified;
9413 endEdit : function(){
9414 this.editing = false;
9415 if(this.dirty && this.store){
9416 this.store.afterEdit(this);
9421 * Usually called by the {@link Roo.data.Store} which owns the Record.
9422 * Rejects all changes made to the Record since either creation, or the last commit operation.
9423 * Modified fields are reverted to their original values.
9425 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9426 * of reject operations.
9428 reject : function(){
9429 var m = this.modified;
9431 if(typeof m[n] != "function"){
9432 this.data[n] = m[n];
9436 delete this.modified;
9437 this.editing = false;
9439 this.store.afterReject(this);
9444 * Usually called by the {@link Roo.data.Store} which owns the Record.
9445 * Commits all changes made to the Record since either creation, or the last commit operation.
9447 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9448 * of commit operations.
9450 commit : function(){
9452 delete this.modified;
9453 this.editing = false;
9455 this.store.afterCommit(this);
9460 hasError : function(){
9461 return this.error != null;
9465 clearError : function(){
9470 * Creates a copy of this record.
9471 * @param {String} id (optional) A new record id if you don't want to use this record's id
9474 copy : function(newId) {
9475 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9479 * Ext JS Library 1.1.1
9480 * Copyright(c) 2006-2007, Ext JS, LLC.
9482 * Originally Released Under LGPL - original licence link has changed is not relivant.
9485 * <script type="text/javascript">
9491 * @class Roo.data.Store
9492 * @extends Roo.util.Observable
9493 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9494 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9496 * 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
9497 * has no knowledge of the format of the data returned by the Proxy.<br>
9499 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9500 * instances from the data object. These records are cached and made available through accessor functions.
9502 * Creates a new Store.
9503 * @param {Object} config A config object containing the objects needed for the Store to access data,
9504 * and read the data into Records.
9506 Roo.data.Store = function(config){
9507 this.data = new Roo.util.MixedCollection(false);
9508 this.data.getKey = function(o){
9511 this.baseParams = {};
9518 "multisort" : "_multisort"
9521 if(config && config.data){
9522 this.inlineData = config.data;
9526 Roo.apply(this, config);
9528 if(this.reader){ // reader passed
9529 this.reader = Roo.factory(this.reader, Roo.data);
9530 this.reader.xmodule = this.xmodule || false;
9531 if(!this.recordType){
9532 this.recordType = this.reader.recordType;
9534 if(this.reader.onMetaChange){
9535 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9539 if(this.recordType){
9540 this.fields = this.recordType.prototype.fields;
9546 * @event datachanged
9547 * Fires when the data cache has changed, and a widget which is using this Store
9548 * as a Record cache should refresh its view.
9549 * @param {Store} this
9554 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9555 * @param {Store} this
9556 * @param {Object} meta The JSON metadata
9561 * Fires when Records have been added to the Store
9562 * @param {Store} this
9563 * @param {Roo.data.Record[]} records The array of Records added
9564 * @param {Number} index The index at which the record(s) were added
9569 * Fires when a Record has been removed from the Store
9570 * @param {Store} this
9571 * @param {Roo.data.Record} record The Record that was removed
9572 * @param {Number} index The index at which the record was removed
9577 * Fires when a Record has been updated
9578 * @param {Store} this
9579 * @param {Roo.data.Record} record The Record that was updated
9580 * @param {String} operation The update operation being performed. Value may be one of:
9582 Roo.data.Record.EDIT
9583 Roo.data.Record.REJECT
9584 Roo.data.Record.COMMIT
9590 * Fires when the data cache has been cleared.
9591 * @param {Store} this
9596 * Fires before a request is made for a new data object. If the beforeload handler returns false
9597 * the load action will be canceled.
9598 * @param {Store} this
9599 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9603 * @event beforeloadadd
9604 * Fires after a new set of Records has been loaded.
9605 * @param {Store} this
9606 * @param {Roo.data.Record[]} records The Records that were loaded
9607 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9609 beforeloadadd : true,
9612 * Fires after a new set of Records has been loaded, before they are added to the store.
9613 * @param {Store} this
9614 * @param {Roo.data.Record[]} records The Records that were loaded
9615 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9616 * @params {Object} return from reader
9620 * @event loadexception
9621 * Fires if an exception occurs in the Proxy during loading.
9622 * Called with the signature of the Proxy's "loadexception" event.
9623 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9626 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9627 * @param {Object} load options
9628 * @param {Object} jsonData from your request (normally this contains the Exception)
9630 loadexception : true
9634 this.proxy = Roo.factory(this.proxy, Roo.data);
9635 this.proxy.xmodule = this.xmodule || false;
9636 this.relayEvents(this.proxy, ["loadexception"]);
9638 this.sortToggle = {};
9639 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9641 Roo.data.Store.superclass.constructor.call(this);
9643 if(this.inlineData){
9644 this.loadData(this.inlineData);
9645 delete this.inlineData;
9649 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9651 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9652 * without a remote query - used by combo/forms at present.
9656 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9659 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9662 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9663 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9666 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9667 * on any HTTP request
9670 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9673 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9677 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9678 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9683 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9684 * loaded or when a record is removed. (defaults to false).
9686 pruneModifiedRecords : false,
9692 * Add Records to the Store and fires the add event.
9693 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9695 add : function(records){
9696 records = [].concat(records);
9697 for(var i = 0, len = records.length; i < len; i++){
9698 records[i].join(this);
9700 var index = this.data.length;
9701 this.data.addAll(records);
9702 this.fireEvent("add", this, records, index);
9706 * Remove a Record from the Store and fires the remove event.
9707 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9709 remove : function(record){
9710 var index = this.data.indexOf(record);
9711 this.data.removeAt(index);
9712 if(this.pruneModifiedRecords){
9713 this.modified.remove(record);
9715 this.fireEvent("remove", this, record, index);
9719 * Remove all Records from the Store and fires the clear event.
9721 removeAll : function(){
9723 if(this.pruneModifiedRecords){
9726 this.fireEvent("clear", this);
9730 * Inserts Records to the Store at the given index and fires the add event.
9731 * @param {Number} index The start index at which to insert the passed Records.
9732 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9734 insert : function(index, records){
9735 records = [].concat(records);
9736 for(var i = 0, len = records.length; i < len; i++){
9737 this.data.insert(index, records[i]);
9738 records[i].join(this);
9740 this.fireEvent("add", this, records, index);
9744 * Get the index within the cache of the passed Record.
9745 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9746 * @return {Number} The index of the passed Record. Returns -1 if not found.
9748 indexOf : function(record){
9749 return this.data.indexOf(record);
9753 * Get the index within the cache of the Record with the passed id.
9754 * @param {String} id The id of the Record to find.
9755 * @return {Number} The index of the Record. Returns -1 if not found.
9757 indexOfId : function(id){
9758 return this.data.indexOfKey(id);
9762 * Get the Record with the specified id.
9763 * @param {String} id The id of the Record to find.
9764 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9766 getById : function(id){
9767 return this.data.key(id);
9771 * Get the Record at the specified index.
9772 * @param {Number} index The index of the Record to find.
9773 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9775 getAt : function(index){
9776 return this.data.itemAt(index);
9780 * Returns a range of Records between specified indices.
9781 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9782 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9783 * @return {Roo.data.Record[]} An array of Records
9785 getRange : function(start, end){
9786 return this.data.getRange(start, end);
9790 storeOptions : function(o){
9791 o = Roo.apply({}, o);
9794 this.lastOptions = o;
9798 * Loads the Record cache from the configured Proxy using the configured Reader.
9800 * If using remote paging, then the first load call must specify the <em>start</em>
9801 * and <em>limit</em> properties in the options.params property to establish the initial
9802 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9804 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9805 * and this call will return before the new data has been loaded. Perform any post-processing
9806 * in a callback function, or in a "load" event handler.</strong>
9808 * @param {Object} options An object containing properties which control loading options:<ul>
9809 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9810 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9811 * passed the following arguments:<ul>
9812 * <li>r : Roo.data.Record[]</li>
9813 * <li>options: Options object from the load call</li>
9814 * <li>success: Boolean success indicator</li></ul></li>
9815 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9816 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9819 load : function(options){
9820 options = options || {};
9821 if(this.fireEvent("beforeload", this, options) !== false){
9822 this.storeOptions(options);
9823 var p = Roo.apply(options.params || {}, this.baseParams);
9824 // if meta was not loaded from remote source.. try requesting it.
9825 if (!this.reader.metaFromRemote) {
9828 if(this.sortInfo && this.remoteSort){
9829 var pn = this.paramNames;
9830 p[pn["sort"]] = this.sortInfo.field;
9831 p[pn["dir"]] = this.sortInfo.direction;
9833 if (this.multiSort) {
9834 var pn = this.paramNames;
9835 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9838 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9843 * Reloads the Record cache from the configured Proxy using the configured Reader and
9844 * the options from the last load operation performed.
9845 * @param {Object} options (optional) An object containing properties which may override the options
9846 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9847 * the most recently used options are reused).
9849 reload : function(options){
9850 this.load(Roo.applyIf(options||{}, this.lastOptions));
9854 // Called as a callback by the Reader during a load operation.
9855 loadRecords : function(o, options, success){
9856 if(!o || success === false){
9857 if(success !== false){
9858 this.fireEvent("load", this, [], options, o);
9860 if(options.callback){
9861 options.callback.call(options.scope || this, [], options, false);
9865 // if data returned failure - throw an exception.
9866 if (o.success === false) {
9867 // show a message if no listener is registered.
9868 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9869 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9871 // loadmask wil be hooked into this..
9872 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9875 var r = o.records, t = o.totalRecords || r.length;
9877 this.fireEvent("beforeloadadd", this, r, options, o);
9879 if(!options || options.add !== true){
9880 if(this.pruneModifiedRecords){
9883 for(var i = 0, len = r.length; i < len; i++){
9887 this.data = this.snapshot;
9888 delete this.snapshot;
9891 this.data.addAll(r);
9892 this.totalLength = t;
9894 this.fireEvent("datachanged", this);
9896 this.totalLength = Math.max(t, this.data.length+r.length);
9899 this.fireEvent("load", this, r, options, o);
9900 if(options.callback){
9901 options.callback.call(options.scope || this, r, options, true);
9907 * Loads data from a passed data block. A Reader which understands the format of the data
9908 * must have been configured in the constructor.
9909 * @param {Object} data The data block from which to read the Records. The format of the data expected
9910 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9911 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9913 loadData : function(o, append){
9914 var r = this.reader.readRecords(o);
9915 this.loadRecords(r, {add: append}, true);
9919 * Gets the number of cached records.
9921 * <em>If using paging, this may not be the total size of the dataset. If the data object
9922 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9923 * the data set size</em>
9925 getCount : function(){
9926 return this.data.length || 0;
9930 * Gets the total number of records in the dataset as returned by the server.
9932 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9933 * the dataset size</em>
9935 getTotalCount : function(){
9936 return this.totalLength || 0;
9940 * Returns the sort state of the Store as an object with two properties:
9942 field {String} The name of the field by which the Records are sorted
9943 direction {String} The sort order, "ASC" or "DESC"
9946 getSortState : function(){
9947 return this.sortInfo;
9951 applySort : function(){
9952 if(this.sortInfo && !this.remoteSort){
9953 var s = this.sortInfo, f = s.field;
9954 var st = this.fields.get(f).sortType;
9955 var fn = function(r1, r2){
9956 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9957 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9959 this.data.sort(s.direction, fn);
9960 if(this.snapshot && this.snapshot != this.data){
9961 this.snapshot.sort(s.direction, fn);
9967 * Sets the default sort column and order to be used by the next load operation.
9968 * @param {String} fieldName The name of the field to sort by.
9969 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9971 setDefaultSort : function(field, dir){
9972 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9977 * If remote sorting is used, the sort is performed on the server, and the cache is
9978 * reloaded. If local sorting is used, the cache is sorted internally.
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 sort : function(fieldName, dir){
9983 var f = this.fields.get(fieldName);
9985 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9987 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9988 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9993 this.sortToggle[f.name] = dir;
9994 this.sortInfo = {field: f.name, direction: dir};
9995 if(!this.remoteSort){
9997 this.fireEvent("datachanged", this);
9999 this.load(this.lastOptions);
10004 * Calls the specified function for each of the Records in the cache.
10005 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10006 * Returning <em>false</em> aborts and exits the iteration.
10007 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10009 each : function(fn, scope){
10010 this.data.each(fn, scope);
10014 * Gets all records modified since the last commit. Modified records are persisted across load operations
10015 * (e.g., during paging).
10016 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10018 getModifiedRecords : function(){
10019 return this.modified;
10023 createFilterFn : function(property, value, anyMatch){
10024 if(!value.exec){ // not a regex
10025 value = String(value);
10026 if(value.length == 0){
10029 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10031 return function(r){
10032 return value.test(r.data[property]);
10037 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10038 * @param {String} property A field on your records
10039 * @param {Number} start The record index to start at (defaults to 0)
10040 * @param {Number} end The last record index to include (defaults to length - 1)
10041 * @return {Number} The sum
10043 sum : function(property, start, end){
10044 var rs = this.data.items, v = 0;
10045 start = start || 0;
10046 end = (end || end === 0) ? end : rs.length-1;
10048 for(var i = start; i <= end; i++){
10049 v += (rs[i].data[property] || 0);
10055 * Filter the records by a specified property.
10056 * @param {String} field A field on your records
10057 * @param {String/RegExp} value Either a string that the field
10058 * should start with or a RegExp to test against the field
10059 * @param {Boolean} anyMatch True to match any part not just the beginning
10061 filter : function(property, value, anyMatch){
10062 var fn = this.createFilterFn(property, value, anyMatch);
10063 return fn ? this.filterBy(fn) : this.clearFilter();
10067 * Filter by a function. The specified function will be called with each
10068 * record in this data source. If the function returns true the record is included,
10069 * otherwise it is filtered.
10070 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10071 * @param {Object} scope (optional) The scope of the function (defaults to this)
10073 filterBy : function(fn, scope){
10074 this.snapshot = this.snapshot || this.data;
10075 this.data = this.queryBy(fn, scope||this);
10076 this.fireEvent("datachanged", this);
10080 * Query the records by a specified property.
10081 * @param {String} field A field on your records
10082 * @param {String/RegExp} value Either a string that the field
10083 * should start with or a RegExp to test against the field
10084 * @param {Boolean} anyMatch True to match any part not just the beginning
10085 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10087 query : function(property, value, anyMatch){
10088 var fn = this.createFilterFn(property, value, anyMatch);
10089 return fn ? this.queryBy(fn) : this.data.clone();
10093 * Query by a function. The specified function will be called with each
10094 * record in this data source. If the function returns true the record is included
10096 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10097 * @param {Object} scope (optional) The scope of the function (defaults to this)
10098 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10100 queryBy : function(fn, scope){
10101 var data = this.snapshot || this.data;
10102 return data.filterBy(fn, scope||this);
10106 * Collects unique values for a particular dataIndex from this store.
10107 * @param {String} dataIndex The property to collect
10108 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10109 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10110 * @return {Array} An array of the unique values
10112 collect : function(dataIndex, allowNull, bypassFilter){
10113 var d = (bypassFilter === true && this.snapshot) ?
10114 this.snapshot.items : this.data.items;
10115 var v, sv, r = [], l = {};
10116 for(var i = 0, len = d.length; i < len; i++){
10117 v = d[i].data[dataIndex];
10119 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10128 * Revert to a view of the Record cache with no filtering applied.
10129 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10131 clearFilter : function(suppressEvent){
10132 if(this.snapshot && this.snapshot != this.data){
10133 this.data = this.snapshot;
10134 delete this.snapshot;
10135 if(suppressEvent !== true){
10136 this.fireEvent("datachanged", this);
10142 afterEdit : function(record){
10143 if(this.modified.indexOf(record) == -1){
10144 this.modified.push(record);
10146 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10150 afterReject : function(record){
10151 this.modified.remove(record);
10152 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10156 afterCommit : function(record){
10157 this.modified.remove(record);
10158 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10162 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10163 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10165 commitChanges : function(){
10166 var m = this.modified.slice(0);
10167 this.modified = [];
10168 for(var i = 0, len = m.length; i < len; i++){
10174 * Cancel outstanding changes on all changed records.
10176 rejectChanges : function(){
10177 var m = this.modified.slice(0);
10178 this.modified = [];
10179 for(var i = 0, len = m.length; i < len; i++){
10184 onMetaChange : function(meta, rtype, o){
10185 this.recordType = rtype;
10186 this.fields = rtype.prototype.fields;
10187 delete this.snapshot;
10188 this.sortInfo = meta.sortInfo || this.sortInfo;
10189 this.modified = [];
10190 this.fireEvent('metachange', this, this.reader.meta);
10193 moveIndex : function(data, type)
10195 var index = this.indexOf(data);
10197 var newIndex = index + type;
10201 this.insert(newIndex, data);
10206 * Ext JS Library 1.1.1
10207 * Copyright(c) 2006-2007, Ext JS, LLC.
10209 * Originally Released Under LGPL - original licence link has changed is not relivant.
10212 * <script type="text/javascript">
10216 * @class Roo.data.SimpleStore
10217 * @extends Roo.data.Store
10218 * Small helper class to make creating Stores from Array data easier.
10219 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10220 * @cfg {Array} fields An array of field definition objects, or field name strings.
10221 * @cfg {Array} data The multi-dimensional array of data
10223 * @param {Object} config
10225 Roo.data.SimpleStore = function(config){
10226 Roo.data.SimpleStore.superclass.constructor.call(this, {
10228 reader: new Roo.data.ArrayReader({
10231 Roo.data.Record.create(config.fields)
10233 proxy : new Roo.data.MemoryProxy(config.data)
10237 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10239 * Ext JS Library 1.1.1
10240 * Copyright(c) 2006-2007, Ext JS, LLC.
10242 * Originally Released Under LGPL - original licence link has changed is not relivant.
10245 * <script type="text/javascript">
10250 * @extends Roo.data.Store
10251 * @class Roo.data.JsonStore
10252 * Small helper class to make creating Stores for JSON data easier. <br/>
10254 var store = new Roo.data.JsonStore({
10255 url: 'get-images.php',
10257 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10260 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10261 * JsonReader and HttpProxy (unless inline data is provided).</b>
10262 * @cfg {Array} fields An array of field definition objects, or field name strings.
10264 * @param {Object} config
10266 Roo.data.JsonStore = function(c){
10267 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10268 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10269 reader: new Roo.data.JsonReader(c, c.fields)
10272 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10274 * Ext JS Library 1.1.1
10275 * Copyright(c) 2006-2007, Ext JS, LLC.
10277 * Originally Released Under LGPL - original licence link has changed is not relivant.
10280 * <script type="text/javascript">
10284 Roo.data.Field = function(config){
10285 if(typeof config == "string"){
10286 config = {name: config};
10288 Roo.apply(this, config);
10291 this.type = "auto";
10294 var st = Roo.data.SortTypes;
10295 // named sortTypes are supported, here we look them up
10296 if(typeof this.sortType == "string"){
10297 this.sortType = st[this.sortType];
10300 // set default sortType for strings and dates
10301 if(!this.sortType){
10304 this.sortType = st.asUCString;
10307 this.sortType = st.asDate;
10310 this.sortType = st.none;
10315 var stripRe = /[\$,%]/g;
10317 // prebuilt conversion function for this field, instead of
10318 // switching every time we're reading a value
10320 var cv, dateFormat = this.dateFormat;
10325 cv = function(v){ return v; };
10328 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10332 return v !== undefined && v !== null && v !== '' ?
10333 parseInt(String(v).replace(stripRe, ""), 10) : '';
10338 return v !== undefined && v !== null && v !== '' ?
10339 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10344 cv = function(v){ return v === true || v === "true" || v == 1; };
10351 if(v instanceof Date){
10355 if(dateFormat == "timestamp"){
10356 return new Date(v*1000);
10358 return Date.parseDate(v, dateFormat);
10360 var parsed = Date.parse(v);
10361 return parsed ? new Date(parsed) : null;
10370 Roo.data.Field.prototype = {
10378 * Ext JS Library 1.1.1
10379 * Copyright(c) 2006-2007, Ext JS, LLC.
10381 * Originally Released Under LGPL - original licence link has changed is not relivant.
10384 * <script type="text/javascript">
10387 // Base class for reading structured data from a data source. This class is intended to be
10388 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10391 * @class Roo.data.DataReader
10392 * Base class for reading structured data from a data source. This class is intended to be
10393 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10396 Roo.data.DataReader = function(meta, recordType){
10400 this.recordType = recordType instanceof Array ?
10401 Roo.data.Record.create(recordType) : recordType;
10404 Roo.data.DataReader.prototype = {
10406 * Create an empty record
10407 * @param {Object} data (optional) - overlay some values
10408 * @return {Roo.data.Record} record created.
10410 newRow : function(d) {
10412 this.recordType.prototype.fields.each(function(c) {
10414 case 'int' : da[c.name] = 0; break;
10415 case 'date' : da[c.name] = new Date(); break;
10416 case 'float' : da[c.name] = 0.0; break;
10417 case 'boolean' : da[c.name] = false; break;
10418 default : da[c.name] = ""; break;
10422 return new this.recordType(Roo.apply(da, d));
10427 * Ext JS Library 1.1.1
10428 * Copyright(c) 2006-2007, Ext JS, LLC.
10430 * Originally Released Under LGPL - original licence link has changed is not relivant.
10433 * <script type="text/javascript">
10437 * @class Roo.data.DataProxy
10438 * @extends Roo.data.Observable
10439 * This class is an abstract base class for implementations which provide retrieval of
10440 * unformatted data objects.<br>
10442 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10443 * (of the appropriate type which knows how to parse the data object) to provide a block of
10444 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10446 * Custom implementations must implement the load method as described in
10447 * {@link Roo.data.HttpProxy#load}.
10449 Roo.data.DataProxy = function(){
10452 * @event beforeload
10453 * Fires before a network request is made to retrieve a data object.
10454 * @param {Object} This DataProxy object.
10455 * @param {Object} params The params parameter to the load function.
10460 * Fires before the load method's callback is called.
10461 * @param {Object} This DataProxy object.
10462 * @param {Object} o The data object.
10463 * @param {Object} arg The callback argument object passed to the load function.
10467 * @event loadexception
10468 * Fires if an Exception occurs during data retrieval.
10469 * @param {Object} This DataProxy object.
10470 * @param {Object} o The data object.
10471 * @param {Object} arg The callback argument object passed to the load function.
10472 * @param {Object} e The Exception.
10474 loadexception : true
10476 Roo.data.DataProxy.superclass.constructor.call(this);
10479 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10482 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10486 * Ext JS Library 1.1.1
10487 * Copyright(c) 2006-2007, Ext JS, LLC.
10489 * Originally Released Under LGPL - original licence link has changed is not relivant.
10492 * <script type="text/javascript">
10495 * @class Roo.data.MemoryProxy
10496 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10497 * to the Reader when its load method is called.
10499 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10501 Roo.data.MemoryProxy = function(data){
10505 Roo.data.MemoryProxy.superclass.constructor.call(this);
10509 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10511 * Load data from the requested source (in this case an in-memory
10512 * data object passed to the constructor), read the data object into
10513 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10514 * process that block using the passed callback.
10515 * @param {Object} params This parameter is not used by the MemoryProxy class.
10516 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10517 * object into a block of Roo.data.Records.
10518 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10519 * The function must be passed <ul>
10520 * <li>The Record block object</li>
10521 * <li>The "arg" argument from the load function</li>
10522 * <li>A boolean success indicator</li>
10524 * @param {Object} scope The scope in which to call the callback
10525 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10527 load : function(params, reader, callback, scope, arg){
10528 params = params || {};
10531 result = reader.readRecords(this.data);
10533 this.fireEvent("loadexception", this, arg, null, e);
10534 callback.call(scope, null, arg, false);
10537 callback.call(scope, result, arg, true);
10541 update : function(params, records){
10546 * Ext JS Library 1.1.1
10547 * Copyright(c) 2006-2007, Ext JS, LLC.
10549 * Originally Released Under LGPL - original licence link has changed is not relivant.
10552 * <script type="text/javascript">
10555 * @class Roo.data.HttpProxy
10556 * @extends Roo.data.DataProxy
10557 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10558 * configured to reference a certain URL.<br><br>
10560 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10561 * from which the running page was served.<br><br>
10563 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10565 * Be aware that to enable the browser to parse an XML document, the server must set
10566 * the Content-Type header in the HTTP response to "text/xml".
10568 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10569 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10570 * will be used to make the request.
10572 Roo.data.HttpProxy = function(conn){
10573 Roo.data.HttpProxy.superclass.constructor.call(this);
10574 // is conn a conn config or a real conn?
10576 this.useAjax = !conn || !conn.events;
10580 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10581 // thse are take from connection...
10584 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10587 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10588 * extra parameters to each request made by this object. (defaults to undefined)
10591 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10592 * to each request made by this object. (defaults to undefined)
10595 * @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)
10598 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10601 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10607 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10611 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10612 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10613 * a finer-grained basis than the DataProxy events.
10615 getConnection : function(){
10616 return this.useAjax ? Roo.Ajax : this.conn;
10620 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10621 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10622 * process that block using the passed callback.
10623 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10624 * for the request to the remote server.
10625 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10626 * object into a block of Roo.data.Records.
10627 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10628 * The function must be passed <ul>
10629 * <li>The Record block object</li>
10630 * <li>The "arg" argument from the load function</li>
10631 * <li>A boolean success indicator</li>
10633 * @param {Object} scope The scope in which to call the callback
10634 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10636 load : function(params, reader, callback, scope, arg){
10637 if(this.fireEvent("beforeload", this, params) !== false){
10639 params : params || {},
10641 callback : callback,
10646 callback : this.loadResponse,
10650 Roo.applyIf(o, this.conn);
10651 if(this.activeRequest){
10652 Roo.Ajax.abort(this.activeRequest);
10654 this.activeRequest = Roo.Ajax.request(o);
10656 this.conn.request(o);
10659 callback.call(scope||this, null, arg, false);
10664 loadResponse : function(o, success, response){
10665 delete this.activeRequest;
10667 this.fireEvent("loadexception", this, o, response);
10668 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10673 result = o.reader.read(response);
10675 this.fireEvent("loadexception", this, o, response, e);
10676 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10680 this.fireEvent("load", this, o, o.request.arg);
10681 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10685 update : function(dataSet){
10690 updateResponse : function(dataSet){
10695 * Ext JS Library 1.1.1
10696 * Copyright(c) 2006-2007, Ext JS, LLC.
10698 * Originally Released Under LGPL - original licence link has changed is not relivant.
10701 * <script type="text/javascript">
10705 * @class Roo.data.ScriptTagProxy
10706 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10707 * other than the originating domain of the running page.<br><br>
10709 * <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
10710 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10712 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10713 * source code that is used as the source inside a <script> tag.<br><br>
10715 * In order for the browser to process the returned data, the server must wrap the data object
10716 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10717 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10718 * depending on whether the callback name was passed:
10721 boolean scriptTag = false;
10722 String cb = request.getParameter("callback");
10725 response.setContentType("text/javascript");
10727 response.setContentType("application/x-json");
10729 Writer out = response.getWriter();
10731 out.write(cb + "(");
10733 out.print(dataBlock.toJsonString());
10740 * @param {Object} config A configuration object.
10742 Roo.data.ScriptTagProxy = function(config){
10743 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10744 Roo.apply(this, config);
10745 this.head = document.getElementsByTagName("head")[0];
10748 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10750 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10752 * @cfg {String} url The URL from which to request the data object.
10755 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10759 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10760 * the server the name of the callback function set up by the load call to process the returned data object.
10761 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10762 * javascript output which calls this named function passing the data object as its only parameter.
10764 callbackParam : "callback",
10766 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10767 * name to the request.
10772 * Load data from the configured URL, read the data object into
10773 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10774 * process that block using the passed callback.
10775 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10776 * for the request to the remote server.
10777 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10778 * object into a block of Roo.data.Records.
10779 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10780 * The function must be passed <ul>
10781 * <li>The Record block object</li>
10782 * <li>The "arg" argument from the load function</li>
10783 * <li>A boolean success indicator</li>
10785 * @param {Object} scope The scope in which to call the callback
10786 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10788 load : function(params, reader, callback, scope, arg){
10789 if(this.fireEvent("beforeload", this, params) !== false){
10791 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10793 var url = this.url;
10794 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10796 url += "&_dc=" + (new Date().getTime());
10798 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10801 cb : "stcCallback"+transId,
10802 scriptId : "stcScript"+transId,
10806 callback : callback,
10812 window[trans.cb] = function(o){
10813 conn.handleResponse(o, trans);
10816 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10818 if(this.autoAbort !== false){
10822 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10824 var script = document.createElement("script");
10825 script.setAttribute("src", url);
10826 script.setAttribute("type", "text/javascript");
10827 script.setAttribute("id", trans.scriptId);
10828 this.head.appendChild(script);
10830 this.trans = trans;
10832 callback.call(scope||this, null, arg, false);
10837 isLoading : function(){
10838 return this.trans ? true : false;
10842 * Abort the current server request.
10844 abort : function(){
10845 if(this.isLoading()){
10846 this.destroyTrans(this.trans);
10851 destroyTrans : function(trans, isLoaded){
10852 this.head.removeChild(document.getElementById(trans.scriptId));
10853 clearTimeout(trans.timeoutId);
10855 window[trans.cb] = undefined;
10857 delete window[trans.cb];
10860 // if hasn't been loaded, wait for load to remove it to prevent script error
10861 window[trans.cb] = function(){
10862 window[trans.cb] = undefined;
10864 delete window[trans.cb];
10871 handleResponse : function(o, trans){
10872 this.trans = false;
10873 this.destroyTrans(trans, true);
10876 result = trans.reader.readRecords(o);
10878 this.fireEvent("loadexception", this, o, trans.arg, e);
10879 trans.callback.call(trans.scope||window, null, trans.arg, false);
10882 this.fireEvent("load", this, o, trans.arg);
10883 trans.callback.call(trans.scope||window, result, trans.arg, true);
10887 handleFailure : function(trans){
10888 this.trans = false;
10889 this.destroyTrans(trans, false);
10890 this.fireEvent("loadexception", this, null, trans.arg);
10891 trans.callback.call(trans.scope||window, null, trans.arg, false);
10895 * Ext JS Library 1.1.1
10896 * Copyright(c) 2006-2007, Ext JS, LLC.
10898 * Originally Released Under LGPL - original licence link has changed is not relivant.
10901 * <script type="text/javascript">
10905 * @class Roo.data.JsonReader
10906 * @extends Roo.data.DataReader
10907 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10908 * based on mappings in a provided Roo.data.Record constructor.
10910 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10911 * in the reply previously.
10916 var RecordDef = Roo.data.Record.create([
10917 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10918 {name: 'occupation'} // This field will use "occupation" as the mapping.
10920 var myReader = new Roo.data.JsonReader({
10921 totalProperty: "results", // The property which contains the total dataset size (optional)
10922 root: "rows", // The property which contains an Array of row objects
10923 id: "id" // The property within each row object that provides an ID for the record (optional)
10927 * This would consume a JSON file like this:
10929 { 'results': 2, 'rows': [
10930 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10931 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10934 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10935 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10936 * paged from the remote server.
10937 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10938 * @cfg {String} root name of the property which contains the Array of row objects.
10939 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10940 * @cfg {Array} fields Array of field definition objects
10942 * Create a new JsonReader
10943 * @param {Object} meta Metadata configuration options
10944 * @param {Object} recordType Either an Array of field definition objects,
10945 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10947 Roo.data.JsonReader = function(meta, recordType){
10950 // set some defaults:
10951 Roo.applyIf(meta, {
10952 totalProperty: 'total',
10953 successProperty : 'success',
10958 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10960 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10963 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10964 * Used by Store query builder to append _requestMeta to params.
10967 metaFromRemote : false,
10969 * This method is only used by a DataProxy which has retrieved data from a remote server.
10970 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10971 * @return {Object} data A data block which is used by an Roo.data.Store object as
10972 * a cache of Roo.data.Records.
10974 read : function(response){
10975 var json = response.responseText;
10977 var o = /* eval:var:o */ eval("("+json+")");
10979 throw {message: "JsonReader.read: Json object not found"};
10985 this.metaFromRemote = true;
10986 this.meta = o.metaData;
10987 this.recordType = Roo.data.Record.create(o.metaData.fields);
10988 this.onMetaChange(this.meta, this.recordType, o);
10990 return this.readRecords(o);
10993 // private function a store will implement
10994 onMetaChange : function(meta, recordType, o){
11001 simpleAccess: function(obj, subsc) {
11008 getJsonAccessor: function(){
11010 return function(expr) {
11012 return(re.test(expr))
11013 ? new Function("obj", "return obj." + expr)
11018 return Roo.emptyFn;
11023 * Create a data block containing Roo.data.Records from an XML document.
11024 * @param {Object} o An object which contains an Array of row objects in the property specified
11025 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11026 * which contains the total size of the dataset.
11027 * @return {Object} data A data block which is used by an Roo.data.Store object as
11028 * a cache of Roo.data.Records.
11030 readRecords : function(o){
11032 * After any data loads, the raw JSON data is available for further custom processing.
11036 var s = this.meta, Record = this.recordType,
11037 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11039 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11041 if(s.totalProperty) {
11042 this.getTotal = this.getJsonAccessor(s.totalProperty);
11044 if(s.successProperty) {
11045 this.getSuccess = this.getJsonAccessor(s.successProperty);
11047 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11049 var g = this.getJsonAccessor(s.id);
11050 this.getId = function(rec) {
11052 return (r === undefined || r === "") ? null : r;
11055 this.getId = function(){return null;};
11058 for(var jj = 0; jj < fl; jj++){
11060 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11061 this.ef[jj] = this.getJsonAccessor(map);
11065 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11066 if(s.totalProperty){
11067 var vt = parseInt(this.getTotal(o), 10);
11072 if(s.successProperty){
11073 var vs = this.getSuccess(o);
11074 if(vs === false || vs === 'false'){
11079 for(var i = 0; i < c; i++){
11082 var id = this.getId(n);
11083 for(var j = 0; j < fl; j++){
11085 var v = this.ef[j](n);
11087 Roo.log('missing convert for ' + f.name);
11091 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11093 var record = new Record(values, id);
11095 records[i] = record;
11101 totalRecords : totalRecords
11106 * Ext JS Library 1.1.1
11107 * Copyright(c) 2006-2007, Ext JS, LLC.
11109 * Originally Released Under LGPL - original licence link has changed is not relivant.
11112 * <script type="text/javascript">
11116 * @class Roo.data.ArrayReader
11117 * @extends Roo.data.DataReader
11118 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11119 * Each element of that Array represents a row of data fields. The
11120 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11121 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11125 var RecordDef = Roo.data.Record.create([
11126 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11127 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11129 var myReader = new Roo.data.ArrayReader({
11130 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11134 * This would consume an Array like this:
11136 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11138 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11140 * Create a new JsonReader
11141 * @param {Object} meta Metadata configuration options.
11142 * @param {Object} recordType Either an Array of field definition objects
11143 * as specified to {@link Roo.data.Record#create},
11144 * or an {@link Roo.data.Record} object
11145 * created using {@link Roo.data.Record#create}.
11147 Roo.data.ArrayReader = function(meta, recordType){
11148 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11151 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11153 * Create a data block containing Roo.data.Records from an XML document.
11154 * @param {Object} o An Array of row objects which represents the dataset.
11155 * @return {Object} data A data block which is used by an Roo.data.Store object as
11156 * a cache of Roo.data.Records.
11158 readRecords : function(o){
11159 var sid = this.meta ? this.meta.id : null;
11160 var recordType = this.recordType, fields = recordType.prototype.fields;
11163 for(var i = 0; i < root.length; i++){
11166 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11167 for(var j = 0, jlen = fields.length; j < jlen; j++){
11168 var f = fields.items[j];
11169 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11170 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11172 values[f.name] = v;
11174 var record = new recordType(values, id);
11176 records[records.length] = record;
11180 totalRecords : records.length
11189 * @class Roo.bootstrap.ComboBox
11190 * @extends Roo.bootstrap.TriggerField
11191 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11192 * @cfg {Boolean} append (true|false) default false
11193 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11194 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11195 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11196 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11197 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11198 * @cfg {Boolean} animate default true
11199 * @cfg {Boolean} emptyResultText only for touch device
11200 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11202 * Create a new ComboBox.
11203 * @param {Object} config Configuration options
11205 Roo.bootstrap.ComboBox = function(config){
11206 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11210 * Fires when the dropdown list is expanded
11211 * @param {Roo.bootstrap.ComboBox} combo This combo box
11216 * Fires when the dropdown list is collapsed
11217 * @param {Roo.bootstrap.ComboBox} combo This combo box
11221 * @event beforeselect
11222 * Fires before a list item is selected. Return false to cancel the selection.
11223 * @param {Roo.bootstrap.ComboBox} combo This combo box
11224 * @param {Roo.data.Record} record The data record returned from the underlying store
11225 * @param {Number} index The index of the selected item in the dropdown list
11227 'beforeselect' : true,
11230 * Fires when a list item is selected
11231 * @param {Roo.bootstrap.ComboBox} combo This combo box
11232 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11233 * @param {Number} index The index of the selected item in the dropdown list
11237 * @event beforequery
11238 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11239 * The event object passed has these properties:
11240 * @param {Roo.bootstrap.ComboBox} combo This combo box
11241 * @param {String} query The query
11242 * @param {Boolean} forceAll true to force "all" query
11243 * @param {Boolean} cancel true to cancel the query
11244 * @param {Object} e The query event object
11246 'beforequery': true,
11249 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11250 * @param {Roo.bootstrap.ComboBox} combo This combo box
11255 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11256 * @param {Roo.bootstrap.ComboBox} combo This combo box
11257 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11262 * Fires when the remove value from the combobox array
11263 * @param {Roo.bootstrap.ComboBox} combo This combo box
11267 * @event specialfilter
11268 * Fires when specialfilter
11269 * @param {Roo.bootstrap.ComboBox} combo This combo box
11271 'specialfilter' : true
11276 this.tickItems = [];
11278 this.selectedIndex = -1;
11279 if(this.mode == 'local'){
11280 if(config.queryDelay === undefined){
11281 this.queryDelay = 10;
11283 if(config.minChars === undefined){
11289 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11292 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11293 * rendering into an Roo.Editor, defaults to false)
11296 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11297 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11300 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11303 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11304 * the dropdown list (defaults to undefined, with no header element)
11308 * @cfg {String/Roo.Template} tpl The template to use to render the output
11312 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11314 listWidth: undefined,
11316 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11317 * mode = 'remote' or 'text' if mode = 'local')
11319 displayField: undefined,
11322 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11323 * mode = 'remote' or 'value' if mode = 'local').
11324 * Note: use of a valueField requires the user make a selection
11325 * in order for a value to be mapped.
11327 valueField: undefined,
11331 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11332 * field's data value (defaults to the underlying DOM element's name)
11334 hiddenName: undefined,
11336 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11340 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11342 selectedClass: 'active',
11345 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11349 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11350 * anchor positions (defaults to 'tl-bl')
11352 listAlign: 'tl-bl?',
11354 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11358 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11359 * query specified by the allQuery config option (defaults to 'query')
11361 triggerAction: 'query',
11363 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11364 * (defaults to 4, does not apply if editable = false)
11368 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11369 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11373 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11374 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11378 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11379 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11383 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11384 * when editable = true (defaults to false)
11386 selectOnFocus:false,
11388 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11390 queryParam: 'query',
11392 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11393 * when mode = 'remote' (defaults to 'Loading...')
11395 loadingText: 'Loading...',
11397 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11401 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11405 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11406 * traditional select (defaults to true)
11410 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11414 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11418 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11419 * listWidth has a higher value)
11423 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11424 * allow the user to set arbitrary text into the field (defaults to false)
11426 forceSelection:false,
11428 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11429 * if typeAhead = true (defaults to 250)
11431 typeAheadDelay : 250,
11433 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11434 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11436 valueNotFoundText : undefined,
11438 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11440 blockFocus : false,
11443 * @cfg {Boolean} disableClear Disable showing of clear button.
11445 disableClear : false,
11447 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11449 alwaysQuery : false,
11452 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11457 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11459 invalidClass : "has-warning",
11462 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11464 validClass : "has-success",
11467 * @cfg {Boolean} specialFilter (true|false) special filter default false
11469 specialFilter : false,
11472 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11474 mobileTouchView : true,
11486 btnPosition : 'right',
11487 triggerList : true,
11488 showToggleBtn : true,
11490 emptyResultText: 'Empty',
11491 triggerText : 'Select',
11493 // element that contains real text value.. (when hidden is used..)
11495 getAutoCreate : function()
11503 if(Roo.isTouch && this.mobileTouchView){
11504 cfg = this.getAutoCreateTouchView();
11511 if(!this.tickable){
11512 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11517 * ComboBox with tickable selections
11520 var align = this.labelAlign || this.parentLabelAlign();
11523 cls : 'form-group roo-combobox-tickable' //input-group
11528 cls : 'tickable-buttons',
11533 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11534 html : this.triggerText
11540 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11547 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11554 buttons.cn.unshift({
11556 cls: 'select2-search-field-input'
11562 Roo.each(buttons.cn, function(c){
11564 c.cls += ' btn-' + _this.size;
11567 if (_this.disabled) {
11578 cls: 'form-hidden-field'
11582 cls: 'select2-choices',
11586 cls: 'select2-search-field',
11598 cls: 'select2-container input-group select2-container-multi',
11603 // cls: 'typeahead typeahead-long dropdown-menu',
11604 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11609 if(this.hasFeedback && !this.allowBlank){
11613 cls: 'glyphicon form-control-feedback'
11616 combobox.cn.push(feedback);
11619 if (align ==='left' && this.fieldLabel.length) {
11621 Roo.log("left and has label");
11627 cls : 'control-label col-sm-' + this.labelWidth,
11628 html : this.fieldLabel
11632 cls : "col-sm-" + (12 - this.labelWidth),
11639 } else if ( this.fieldLabel.length) {
11645 //cls : 'input-group-addon',
11646 html : this.fieldLabel
11656 Roo.log(" no label && no align");
11663 ['xs','sm','md','lg'].map(function(size){
11664 if (settings[size]) {
11665 cfg.cls += ' col-' + size + '-' + settings[size];
11673 _initEventsCalled : false,
11676 initEvents: function()
11679 if (this._initEventsCalled) { // as we call render... prevent looping...
11682 this._initEventsCalled = true;
11685 throw "can not find store for combo";
11688 this.store = Roo.factory(this.store, Roo.data);
11690 // if we are building from html. then this element is so complex, that we can not really
11691 // use the rendered HTML.
11692 // so we have to trash and replace the previous code.
11693 if (Roo.XComponent.build_from_html) {
11695 // remove this element....
11696 var e = this.el.dom, k=0;
11697 while (e ) { e = e.previousSibling; ++k;}
11702 this.rendered = false;
11704 this.render(this.parent().getChildContainer(true), k);
11715 if(Roo.isTouch && this.mobileTouchView){
11716 this.initTouchView();
11721 this.initTickableEvents();
11725 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11727 if(this.hiddenName){
11729 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11731 this.hiddenField.dom.value =
11732 this.hiddenValue !== undefined ? this.hiddenValue :
11733 this.value !== undefined ? this.value : '';
11735 // prevent input submission
11736 this.el.dom.removeAttribute('name');
11737 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11742 // this.el.dom.setAttribute('autocomplete', 'off');
11745 var cls = 'x-combo-list';
11747 //this.list = new Roo.Layer({
11748 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11754 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11755 _this.list.setWidth(lw);
11758 this.list.on('mouseover', this.onViewOver, this);
11759 this.list.on('mousemove', this.onViewMove, this);
11761 this.list.on('scroll', this.onViewScroll, this);
11764 this.list.swallowEvent('mousewheel');
11765 this.assetHeight = 0;
11768 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11769 this.assetHeight += this.header.getHeight();
11772 this.innerList = this.list.createChild({cls:cls+'-inner'});
11773 this.innerList.on('mouseover', this.onViewOver, this);
11774 this.innerList.on('mousemove', this.onViewMove, this);
11775 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11777 if(this.allowBlank && !this.pageSize && !this.disableClear){
11778 this.footer = this.list.createChild({cls:cls+'-ft'});
11779 this.pageTb = new Roo.Toolbar(this.footer);
11783 this.footer = this.list.createChild({cls:cls+'-ft'});
11784 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11785 {pageSize: this.pageSize});
11789 if (this.pageTb && this.allowBlank && !this.disableClear) {
11791 this.pageTb.add(new Roo.Toolbar.Fill(), {
11792 cls: 'x-btn-icon x-btn-clear',
11794 handler: function()
11797 _this.clearValue();
11798 _this.onSelect(false, -1);
11803 this.assetHeight += this.footer.getHeight();
11808 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11811 this.view = new Roo.View(this.list, this.tpl, {
11812 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11814 //this.view.wrapEl.setDisplayed(false);
11815 this.view.on('click', this.onViewClick, this);
11819 this.store.on('beforeload', this.onBeforeLoad, this);
11820 this.store.on('load', this.onLoad, this);
11821 this.store.on('loadexception', this.onLoadException, this);
11823 if(this.resizable){
11824 this.resizer = new Roo.Resizable(this.list, {
11825 pinned:true, handles:'se'
11827 this.resizer.on('resize', function(r, w, h){
11828 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11829 this.listWidth = w;
11830 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11831 this.restrictHeight();
11833 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11836 if(!this.editable){
11837 this.editable = true;
11838 this.setEditable(false);
11843 if (typeof(this.events.add.listeners) != 'undefined') {
11845 this.addicon = this.wrap.createChild(
11846 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11848 this.addicon.on('click', function(e) {
11849 this.fireEvent('add', this);
11852 if (typeof(this.events.edit.listeners) != 'undefined') {
11854 this.editicon = this.wrap.createChild(
11855 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11856 if (this.addicon) {
11857 this.editicon.setStyle('margin-left', '40px');
11859 this.editicon.on('click', function(e) {
11861 // we fire even if inothing is selected..
11862 this.fireEvent('edit', this, this.lastData );
11868 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11869 "up" : function(e){
11870 this.inKeyMode = true;
11874 "down" : function(e){
11875 if(!this.isExpanded()){
11876 this.onTriggerClick();
11878 this.inKeyMode = true;
11883 "enter" : function(e){
11884 // this.onViewClick();
11888 if(this.fireEvent("specialkey", this, e)){
11889 this.onViewClick(false);
11895 "esc" : function(e){
11899 "tab" : function(e){
11902 if(this.fireEvent("specialkey", this, e)){
11903 this.onViewClick(false);
11911 doRelay : function(foo, bar, hname){
11912 if(hname == 'down' || this.scope.isExpanded()){
11913 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11922 this.queryDelay = Math.max(this.queryDelay || 10,
11923 this.mode == 'local' ? 10 : 250);
11926 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11928 if(this.typeAhead){
11929 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11931 if(this.editable !== false){
11932 this.inputEl().on("keyup", this.onKeyUp, this);
11934 if(this.forceSelection){
11935 this.inputEl().on('blur', this.doForce, this);
11939 this.choices = this.el.select('ul.select2-choices', true).first();
11940 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11944 initTickableEvents: function()
11948 if(this.hiddenName){
11950 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11952 this.hiddenField.dom.value =
11953 this.hiddenValue !== undefined ? this.hiddenValue :
11954 this.value !== undefined ? this.value : '';
11956 // prevent input submission
11957 this.el.dom.removeAttribute('name');
11958 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11963 // this.list = this.el.select('ul.dropdown-menu',true).first();
11965 this.choices = this.el.select('ul.select2-choices', true).first();
11966 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11967 if(this.triggerList){
11968 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11971 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11972 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11974 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11975 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11977 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11978 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11980 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11981 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11982 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11985 this.cancelBtn.hide();
11990 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11991 _this.list.setWidth(lw);
11994 this.list.on('mouseover', this.onViewOver, this);
11995 this.list.on('mousemove', this.onViewMove, this);
11997 this.list.on('scroll', this.onViewScroll, this);
12000 this.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>';
12003 this.view = new Roo.View(this.list, this.tpl, {
12004 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12007 //this.view.wrapEl.setDisplayed(false);
12008 this.view.on('click', this.onViewClick, this);
12012 this.store.on('beforeload', this.onBeforeLoad, this);
12013 this.store.on('load', this.onLoad, this);
12014 this.store.on('loadexception', this.onLoadException, this);
12017 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12018 "up" : function(e){
12019 this.inKeyMode = true;
12023 "down" : function(e){
12024 this.inKeyMode = true;
12028 "enter" : function(e){
12029 if(this.fireEvent("specialkey", this, e)){
12030 this.onViewClick(false);
12036 "esc" : function(e){
12037 this.onTickableFooterButtonClick(e, false, false);
12040 "tab" : function(e){
12041 this.fireEvent("specialkey", this, e);
12043 this.onTickableFooterButtonClick(e, false, false);
12050 doRelay : function(e, fn, key){
12051 if(this.scope.isExpanded()){
12052 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12061 this.queryDelay = Math.max(this.queryDelay || 10,
12062 this.mode == 'local' ? 10 : 250);
12065 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12067 if(this.typeAhead){
12068 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12071 if(this.editable !== false){
12072 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12077 onDestroy : function(){
12079 this.view.setStore(null);
12080 this.view.el.removeAllListeners();
12081 this.view.el.remove();
12082 this.view.purgeListeners();
12085 this.list.dom.innerHTML = '';
12089 this.store.un('beforeload', this.onBeforeLoad, this);
12090 this.store.un('load', this.onLoad, this);
12091 this.store.un('loadexception', this.onLoadException, this);
12093 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12097 fireKey : function(e){
12098 if(e.isNavKeyPress() && !this.list.isVisible()){
12099 this.fireEvent("specialkey", this, e);
12104 onResize: function(w, h){
12105 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12107 // if(typeof w != 'number'){
12108 // // we do not handle it!?!?
12111 // var tw = this.trigger.getWidth();
12112 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12113 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12115 // this.inputEl().setWidth( this.adjustWidth('input', x));
12117 // //this.trigger.setStyle('left', x+'px');
12119 // if(this.list && this.listWidth === undefined){
12120 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12121 // this.list.setWidth(lw);
12122 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12130 * Allow or prevent the user from directly editing the field text. If false is passed,
12131 * the user will only be able to select from the items defined in the dropdown list. This method
12132 * is the runtime equivalent of setting the 'editable' config option at config time.
12133 * @param {Boolean} value True to allow the user to directly edit the field text
12135 setEditable : function(value){
12136 if(value == this.editable){
12139 this.editable = value;
12141 this.inputEl().dom.setAttribute('readOnly', true);
12142 this.inputEl().on('mousedown', this.onTriggerClick, this);
12143 this.inputEl().addClass('x-combo-noedit');
12145 this.inputEl().dom.setAttribute('readOnly', false);
12146 this.inputEl().un('mousedown', this.onTriggerClick, this);
12147 this.inputEl().removeClass('x-combo-noedit');
12153 onBeforeLoad : function(combo,opts){
12154 if(!this.hasFocus){
12158 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12160 this.restrictHeight();
12161 this.selectedIndex = -1;
12165 onLoad : function(){
12167 this.hasQuery = false;
12169 if(!this.hasFocus){
12173 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12174 this.loading.hide();
12177 if(this.store.getCount() > 0){
12179 this.restrictHeight();
12180 if(this.lastQuery == this.allQuery){
12181 if(this.editable && !this.tickable){
12182 this.inputEl().dom.select();
12186 !this.selectByValue(this.value, true) &&
12189 !this.store.lastOptions ||
12190 typeof(this.store.lastOptions.add) == 'undefined' ||
12191 this.store.lastOptions.add != true
12194 this.select(0, true);
12197 if(this.autoFocus){
12200 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12201 this.taTask.delay(this.typeAheadDelay);
12205 this.onEmptyResults();
12211 onLoadException : function()
12213 this.hasQuery = false;
12215 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12216 this.loading.hide();
12219 if(this.tickable && this.editable){
12225 Roo.log(this.store.reader.jsonData);
12226 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12228 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12234 onTypeAhead : function(){
12235 if(this.store.getCount() > 0){
12236 var r = this.store.getAt(0);
12237 var newValue = r.data[this.displayField];
12238 var len = newValue.length;
12239 var selStart = this.getRawValue().length;
12241 if(selStart != len){
12242 this.setRawValue(newValue);
12243 this.selectText(selStart, newValue.length);
12249 onSelect : function(record, index){
12251 if(this.fireEvent('beforeselect', this, record, index) !== false){
12253 this.setFromData(index > -1 ? record.data : false);
12256 this.fireEvent('select', this, record, index);
12261 * Returns the currently selected field value or empty string if no value is set.
12262 * @return {String} value The selected value
12264 getValue : function(){
12267 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12270 if(this.valueField){
12271 return typeof this.value != 'undefined' ? this.value : '';
12273 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12278 * Clears any text/value currently set in the field
12280 clearValue : function(){
12281 if(this.hiddenField){
12282 this.hiddenField.dom.value = '';
12285 this.setRawValue('');
12286 this.lastSelectionText = '';
12287 this.lastData = false;
12289 var close = this.closeTriggerEl();
12298 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12299 * will be displayed in the field. If the value does not match the data value of an existing item,
12300 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12301 * Otherwise the field will be blank (although the value will still be set).
12302 * @param {String} value The value to match
12304 setValue : function(v){
12311 if(this.valueField){
12312 var r = this.findRecord(this.valueField, v);
12314 text = r.data[this.displayField];
12315 }else if(this.valueNotFoundText !== undefined){
12316 text = this.valueNotFoundText;
12319 this.lastSelectionText = text;
12320 if(this.hiddenField){
12321 this.hiddenField.dom.value = v;
12323 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12326 var close = this.closeTriggerEl();
12329 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12333 * @property {Object} the last set data for the element
12338 * Sets the value of the field based on a object which is related to the record format for the store.
12339 * @param {Object} value the value to set as. or false on reset?
12341 setFromData : function(o){
12348 var dv = ''; // display value
12349 var vv = ''; // value value..
12351 if (this.displayField) {
12352 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12354 // this is an error condition!!!
12355 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12358 if(this.valueField){
12359 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12362 var close = this.closeTriggerEl();
12365 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12368 if(this.hiddenField){
12369 this.hiddenField.dom.value = vv;
12371 this.lastSelectionText = dv;
12372 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12376 // no hidden field.. - we store the value in 'value', but still display
12377 // display field!!!!
12378 this.lastSelectionText = dv;
12379 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12386 reset : function(){
12387 // overridden so that last data is reset..
12394 this.setValue(this.originalValue);
12395 this.clearInvalid();
12396 this.lastData = false;
12398 this.view.clearSelections();
12402 findRecord : function(prop, value){
12404 if(this.store.getCount() > 0){
12405 this.store.each(function(r){
12406 if(r.data[prop] == value){
12416 getName: function()
12418 // returns hidden if it's set..
12419 if (!this.rendered) {return ''};
12420 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12424 onViewMove : function(e, t){
12425 this.inKeyMode = false;
12429 onViewOver : function(e, t){
12430 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12433 var item = this.view.findItemFromChild(t);
12436 var index = this.view.indexOf(item);
12437 this.select(index, false);
12442 onViewClick : function(view, doFocus, el, e)
12444 var index = this.view.getSelectedIndexes()[0];
12446 var r = this.store.getAt(index);
12450 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12457 Roo.each(this.tickItems, function(v,k){
12459 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12460 _this.tickItems.splice(k, 1);
12462 if(typeof(e) == 'undefined' && view == false){
12463 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12475 this.tickItems.push(r.data);
12477 if(typeof(e) == 'undefined' && view == false){
12478 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12485 this.onSelect(r, index);
12487 if(doFocus !== false && !this.blockFocus){
12488 this.inputEl().focus();
12493 restrictHeight : function(){
12494 //this.innerList.dom.style.height = '';
12495 //var inner = this.innerList.dom;
12496 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12497 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12498 //this.list.beginUpdate();
12499 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12500 this.list.alignTo(this.inputEl(), this.listAlign);
12501 this.list.alignTo(this.inputEl(), this.listAlign);
12502 //this.list.endUpdate();
12506 onEmptyResults : function(){
12508 if(this.tickable && this.editable){
12509 this.restrictHeight();
12517 * Returns true if the dropdown list is expanded, else false.
12519 isExpanded : function(){
12520 return this.list.isVisible();
12524 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12525 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12526 * @param {String} value The data value of the item to select
12527 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12528 * selected item if it is not currently in view (defaults to true)
12529 * @return {Boolean} True if the value matched an item in the list, else false
12531 selectByValue : function(v, scrollIntoView){
12532 if(v !== undefined && v !== null){
12533 var r = this.findRecord(this.valueField || this.displayField, v);
12535 this.select(this.store.indexOf(r), scrollIntoView);
12543 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12544 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12545 * @param {Number} index The zero-based index of the list item to select
12546 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12547 * selected item if it is not currently in view (defaults to true)
12549 select : function(index, scrollIntoView){
12550 this.selectedIndex = index;
12551 this.view.select(index);
12552 if(scrollIntoView !== false){
12553 var el = this.view.getNode(index);
12555 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12558 this.list.scrollChildIntoView(el, false);
12564 selectNext : function(){
12565 var ct = this.store.getCount();
12567 if(this.selectedIndex == -1){
12569 }else if(this.selectedIndex < ct-1){
12570 this.select(this.selectedIndex+1);
12576 selectPrev : function(){
12577 var ct = this.store.getCount();
12579 if(this.selectedIndex == -1){
12581 }else if(this.selectedIndex != 0){
12582 this.select(this.selectedIndex-1);
12588 onKeyUp : function(e){
12589 if(this.editable !== false && !e.isSpecialKey()){
12590 this.lastKey = e.getKey();
12591 this.dqTask.delay(this.queryDelay);
12596 validateBlur : function(){
12597 return !this.list || !this.list.isVisible();
12601 initQuery : function(){
12603 var v = this.getRawValue();
12605 if(this.tickable && this.editable){
12606 v = this.tickableInputEl().getValue();
12613 doForce : function(){
12614 if(this.inputEl().dom.value.length > 0){
12615 this.inputEl().dom.value =
12616 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12622 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12623 * query allowing the query action to be canceled if needed.
12624 * @param {String} query The SQL query to execute
12625 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12626 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12627 * saved in the current store (defaults to false)
12629 doQuery : function(q, forceAll){
12631 if(q === undefined || q === null){
12636 forceAll: forceAll,
12640 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12645 forceAll = qe.forceAll;
12646 if(forceAll === true || (q.length >= this.minChars)){
12648 this.hasQuery = true;
12650 if(this.lastQuery != q || this.alwaysQuery){
12651 this.lastQuery = q;
12652 if(this.mode == 'local'){
12653 this.selectedIndex = -1;
12655 this.store.clearFilter();
12658 if(this.specialFilter){
12659 this.fireEvent('specialfilter', this);
12664 this.store.filter(this.displayField, q);
12667 this.store.fireEvent("datachanged", this.store);
12674 this.store.baseParams[this.queryParam] = q;
12676 var options = {params : this.getParams(q)};
12679 options.add = true;
12680 options.params.start = this.page * this.pageSize;
12683 this.store.load(options);
12686 * this code will make the page width larger, at the beginning, the list not align correctly,
12687 * we should expand the list on onLoad
12688 * so command out it
12693 this.selectedIndex = -1;
12698 this.loadNext = false;
12702 getParams : function(q){
12704 //p[this.queryParam] = q;
12708 p.limit = this.pageSize;
12714 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12716 collapse : function(){
12717 if(!this.isExpanded()){
12724 this.hasFocus = false;
12726 this.cancelBtn.hide();
12727 this.trigger.show();
12730 this.tickableInputEl().dom.value = '';
12731 this.tickableInputEl().blur();
12736 Roo.get(document).un('mousedown', this.collapseIf, this);
12737 Roo.get(document).un('mousewheel', this.collapseIf, this);
12738 if (!this.editable) {
12739 Roo.get(document).un('keydown', this.listKeyPress, this);
12741 this.fireEvent('collapse', this);
12745 collapseIf : function(e){
12746 var in_combo = e.within(this.el);
12747 var in_list = e.within(this.list);
12748 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12750 if (in_combo || in_list || is_list) {
12751 //e.stopPropagation();
12756 this.onTickableFooterButtonClick(e, false, false);
12764 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12766 expand : function(){
12768 if(this.isExpanded() || !this.hasFocus){
12772 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12773 this.list.setWidth(lw);
12780 this.restrictHeight();
12784 this.tickItems = Roo.apply([], this.item);
12787 this.cancelBtn.show();
12788 this.trigger.hide();
12791 this.tickableInputEl().focus();
12796 Roo.get(document).on('mousedown', this.collapseIf, this);
12797 Roo.get(document).on('mousewheel', this.collapseIf, this);
12798 if (!this.editable) {
12799 Roo.get(document).on('keydown', this.listKeyPress, this);
12802 this.fireEvent('expand', this);
12806 // Implements the default empty TriggerField.onTriggerClick function
12807 onTriggerClick : function(e)
12809 Roo.log('trigger click');
12811 if(this.disabled || !this.triggerList){
12816 this.loadNext = false;
12818 if(this.isExpanded()){
12820 if (!this.blockFocus) {
12821 this.inputEl().focus();
12825 this.hasFocus = true;
12826 if(this.triggerAction == 'all') {
12827 this.doQuery(this.allQuery, true);
12829 this.doQuery(this.getRawValue());
12831 if (!this.blockFocus) {
12832 this.inputEl().focus();
12837 onTickableTriggerClick : function(e)
12844 this.loadNext = false;
12845 this.hasFocus = true;
12847 if(this.triggerAction == 'all') {
12848 this.doQuery(this.allQuery, true);
12850 this.doQuery(this.getRawValue());
12854 onSearchFieldClick : function(e)
12856 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12857 this.onTickableFooterButtonClick(e, false, false);
12861 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12866 this.loadNext = false;
12867 this.hasFocus = true;
12869 if(this.triggerAction == 'all') {
12870 this.doQuery(this.allQuery, true);
12872 this.doQuery(this.getRawValue());
12876 listKeyPress : function(e)
12878 //Roo.log('listkeypress');
12879 // scroll to first matching element based on key pres..
12880 if (e.isSpecialKey()) {
12883 var k = String.fromCharCode(e.getKey()).toUpperCase();
12886 var csel = this.view.getSelectedNodes();
12887 var cselitem = false;
12889 var ix = this.view.indexOf(csel[0]);
12890 cselitem = this.store.getAt(ix);
12891 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12897 this.store.each(function(v) {
12899 // start at existing selection.
12900 if (cselitem.id == v.id) {
12906 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12907 match = this.store.indexOf(v);
12913 if (match === false) {
12914 return true; // no more action?
12917 this.view.select(match);
12918 var sn = Roo.get(this.view.getSelectedNodes()[0])
12919 sn.scrollIntoView(sn.dom.parentNode, false);
12922 onViewScroll : function(e, t){
12924 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){
12928 this.hasQuery = true;
12930 this.loading = this.list.select('.loading', true).first();
12932 if(this.loading === null){
12933 this.list.createChild({
12935 cls: 'loading select2-more-results select2-active',
12936 html: 'Loading more results...'
12939 this.loading = this.list.select('.loading', true).first();
12941 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12943 this.loading.hide();
12946 this.loading.show();
12951 this.loadNext = true;
12953 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12958 addItem : function(o)
12960 var dv = ''; // display value
12962 if (this.displayField) {
12963 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12965 // this is an error condition!!!
12966 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12973 var choice = this.choices.createChild({
12975 cls: 'select2-search-choice',
12984 cls: 'select2-search-choice-close',
12989 }, this.searchField);
12991 var close = choice.select('a.select2-search-choice-close', true).first()
12993 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13001 this.inputEl().dom.value = '';
13006 onRemoveItem : function(e, _self, o)
13008 e.preventDefault();
13010 this.lastItem = Roo.apply([], this.item);
13012 var index = this.item.indexOf(o.data) * 1;
13015 Roo.log('not this item?!');
13019 this.item.splice(index, 1);
13024 this.fireEvent('remove', this, e);
13030 syncValue : function()
13032 if(!this.item.length){
13039 Roo.each(this.item, function(i){
13040 if(_this.valueField){
13041 value.push(i[_this.valueField]);
13048 this.value = value.join(',');
13050 if(this.hiddenField){
13051 this.hiddenField.dom.value = this.value;
13054 this.store.fireEvent("datachanged", this.store);
13057 clearItem : function()
13059 if(!this.multiple){
13065 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13073 if(this.tickable && !Roo.isTouch){
13074 this.view.refresh();
13078 inputEl: function ()
13080 if(Roo.isTouch && this.mobileTouchView){
13081 return this.el.select('input.form-control',true).first();
13085 return this.searchField;
13088 return this.el.select('input.form-control',true).first();
13092 onTickableFooterButtonClick : function(e, btn, el)
13094 e.preventDefault();
13096 this.lastItem = Roo.apply([], this.item);
13098 if(btn && btn.name == 'cancel'){
13099 this.tickItems = Roo.apply([], this.item);
13108 Roo.each(this.tickItems, function(o){
13116 validate : function()
13118 var v = this.getRawValue();
13121 v = this.getValue();
13124 if(this.disabled || this.allowBlank || v.length){
13129 this.markInvalid();
13133 tickableInputEl : function()
13135 if(!this.tickable || !this.editable){
13136 return this.inputEl();
13139 return this.inputEl().select('.select2-search-field-input', true).first();
13143 getAutoCreateTouchView : function()
13148 cls: 'form-group' //input-group
13154 type : this.inputType,
13155 cls : 'form-control x-combo-noedit',
13156 autocomplete: 'new-password',
13157 placeholder : this.placeholder || '',
13162 input.name = this.name;
13166 input.cls += ' input-' + this.size;
13169 if (this.disabled) {
13170 input.disabled = true;
13181 inputblock.cls += ' input-group';
13183 inputblock.cn.unshift({
13185 cls : 'input-group-addon',
13190 if(this.removable && !this.multiple){
13191 inputblock.cls += ' roo-removable';
13193 inputblock.cn.push({
13196 cls : 'roo-combo-removable-btn close'
13200 if(this.hasFeedback && !this.allowBlank){
13202 inputblock.cls += ' has-feedback';
13204 inputblock.cn.push({
13206 cls: 'glyphicon form-control-feedback'
13213 inputblock.cls += (this.before) ? '' : ' input-group';
13215 inputblock.cn.push({
13217 cls : 'input-group-addon',
13228 cls: 'form-hidden-field'
13242 cls: 'form-hidden-field'
13246 cls: 'select2-choices',
13250 cls: 'select2-search-field',
13263 cls: 'select2-container input-group',
13270 combobox.cls += ' select2-container-multi';
13273 var align = this.labelAlign || this.parentLabelAlign();
13277 if(this.fieldLabel.length){
13279 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13280 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13285 cls : 'control-label ' + lw,
13286 html : this.fieldLabel
13298 var settings = this;
13300 ['xs','sm','md','lg'].map(function(size){
13301 if (settings[size]) {
13302 cfg.cls += ' col-' + size + '-' + settings[size];
13309 initTouchView : function()
13311 this.renderTouchView();
13313 this.touchViewEl.on('scroll', function(){
13314 this.el.dom.scrollTop = 0;
13317 this.inputEl().on("click", this.showTouchView, this);
13318 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13319 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13321 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13323 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13324 this.store.on('load', this.onTouchViewLoad, this);
13325 this.store.on('loadexception', this.onTouchViewLoadException, this);
13327 if(this.hiddenName){
13329 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13331 this.hiddenField.dom.value =
13332 this.hiddenValue !== undefined ? this.hiddenValue :
13333 this.value !== undefined ? this.value : '';
13335 this.el.dom.removeAttribute('name');
13336 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13340 this.choices = this.el.select('ul.select2-choices', true).first();
13341 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13344 if(this.removable && !this.multiple){
13345 var close = this.closeTriggerEl();
13347 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13348 close.on('click', this.removeBtnClick, this, close);
13357 renderTouchView : function()
13359 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13360 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13362 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13363 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13365 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13366 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13367 this.touchViewBodyEl.setStyle('overflow', 'auto');
13369 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13370 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13372 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13373 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13377 showTouchView : function()
13379 this.touchViewHeaderEl.hide();
13381 if(this.fieldLabel.length){
13382 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13383 this.touchViewHeaderEl.show();
13386 this.touchViewEl.show();
13388 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13389 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13391 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13393 if(this.fieldLabel.length){
13394 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13397 this.touchViewBodyEl.setHeight(bodyHeight);
13401 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13403 this.touchViewEl.addClass('in');
13406 this.doTouchViewQuery();
13410 hideTouchView : function()
13412 this.touchViewEl.removeClass('in');
13416 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13418 this.touchViewEl.setStyle('display', 'none');
13423 setTouchViewValue : function()
13430 Roo.each(this.tickItems, function(o){
13435 this.hideTouchView();
13438 doTouchViewQuery : function()
13447 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13451 if(!this.alwaysQuery || this.mode == 'local'){
13452 this.onTouchViewLoad();
13459 onTouchViewBeforeLoad : function(combo,opts)
13465 onTouchViewLoad : function()
13467 if(this.store.getCount() < 1){
13468 this.onTouchViewEmptyResults();
13472 this.clearTouchView();
13474 var rawValue = this.getRawValue();
13476 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13478 this.tickItems = [];
13480 this.store.data.each(function(d, rowIndex){
13481 var row = this.touchViewListGroup.createChild(template);
13483 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13484 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13487 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13488 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13491 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13492 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13493 this.tickItems.push(d.data);
13496 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13500 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13502 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13504 if(this.fieldLabel.length){
13505 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13508 var listHeight = this.touchViewListGroup.getHeight();
13512 if(firstChecked && listHeight > bodyHeight){
13513 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13518 onTouchViewLoadException : function()
13520 this.hideTouchView();
13523 onTouchViewEmptyResults : function()
13525 this.clearTouchView();
13527 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13529 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13533 clearTouchView : function()
13535 this.touchViewListGroup.dom.innerHTML = '';
13538 onTouchViewClick : function(e, el, o)
13540 e.preventDefault();
13543 var rowIndex = o.rowIndex;
13545 var r = this.store.getAt(rowIndex);
13547 if(!this.multiple){
13548 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13549 c.dom.removeAttribute('checked');
13552 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13554 this.setFromData(r.data);
13556 var close = this.closeTriggerEl();
13562 this.hideTouchView();
13564 this.fireEvent('select', this, r, rowIndex);
13569 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13570 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13571 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13575 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13576 this.addItem(r.data);
13577 this.tickItems.push(r.data);
13583 * @cfg {Boolean} grow
13587 * @cfg {Number} growMin
13591 * @cfg {Number} growMax
13600 Roo.apply(Roo.bootstrap.ComboBox, {
13604 cls: 'modal-header',
13626 cls: 'list-group-item',
13630 cls: 'roo-combobox-list-group-item-value'
13634 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13648 listItemCheckbox : {
13650 cls: 'list-group-item',
13654 cls: 'roo-combobox-list-group-item-value'
13658 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13674 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13679 cls: 'modal-footer',
13687 cls: 'col-xs-6 text-left',
13690 cls: 'btn btn-danger roo-touch-view-cancel',
13696 cls: 'col-xs-6 text-right',
13699 cls: 'btn btn-success roo-touch-view-ok',
13710 Roo.apply(Roo.bootstrap.ComboBox, {
13712 touchViewTemplate : {
13714 cls: 'modal fade roo-combobox-touch-view',
13718 cls: 'modal-dialog',
13722 cls: 'modal-content',
13724 Roo.bootstrap.ComboBox.header,
13725 Roo.bootstrap.ComboBox.body,
13726 Roo.bootstrap.ComboBox.footer
13735 * Ext JS Library 1.1.1
13736 * Copyright(c) 2006-2007, Ext JS, LLC.
13738 * Originally Released Under LGPL - original licence link has changed is not relivant.
13741 * <script type="text/javascript">
13746 * @extends Roo.util.Observable
13747 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13748 * This class also supports single and multi selection modes. <br>
13749 * Create a data model bound view:
13751 var store = new Roo.data.Store(...);
13753 var view = new Roo.View({
13755 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13757 singleSelect: true,
13758 selectedClass: "ydataview-selected",
13762 // listen for node click?
13763 view.on("click", function(vw, index, node, e){
13764 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13768 dataModel.load("foobar.xml");
13770 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13772 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13773 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13775 * Note: old style constructor is still suported (container, template, config)
13778 * Create a new View
13779 * @param {Object} config The config object
13782 Roo.View = function(config, depreciated_tpl, depreciated_config){
13784 this.parent = false;
13786 if (typeof(depreciated_tpl) == 'undefined') {
13787 // new way.. - universal constructor.
13788 Roo.apply(this, config);
13789 this.el = Roo.get(this.el);
13792 this.el = Roo.get(config);
13793 this.tpl = depreciated_tpl;
13794 Roo.apply(this, depreciated_config);
13796 this.wrapEl = this.el.wrap().wrap();
13797 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13800 if(typeof(this.tpl) == "string"){
13801 this.tpl = new Roo.Template(this.tpl);
13803 // support xtype ctors..
13804 this.tpl = new Roo.factory(this.tpl, Roo);
13808 this.tpl.compile();
13813 * @event beforeclick
13814 * Fires before a click is processed. Returns false to cancel the default action.
13815 * @param {Roo.View} this
13816 * @param {Number} index The index of the target node
13817 * @param {HTMLElement} node The target node
13818 * @param {Roo.EventObject} e The raw event object
13820 "beforeclick" : true,
13823 * Fires when a template node is clicked.
13824 * @param {Roo.View} this
13825 * @param {Number} index The index of the target node
13826 * @param {HTMLElement} node The target node
13827 * @param {Roo.EventObject} e The raw event object
13832 * Fires when a template node is double clicked.
13833 * @param {Roo.View} this
13834 * @param {Number} index The index of the target node
13835 * @param {HTMLElement} node The target node
13836 * @param {Roo.EventObject} e The raw event object
13840 * @event contextmenu
13841 * Fires when a template node is right clicked.
13842 * @param {Roo.View} this
13843 * @param {Number} index The index of the target node
13844 * @param {HTMLElement} node The target node
13845 * @param {Roo.EventObject} e The raw event object
13847 "contextmenu" : true,
13849 * @event selectionchange
13850 * Fires when the selected nodes change.
13851 * @param {Roo.View} this
13852 * @param {Array} selections Array of the selected nodes
13854 "selectionchange" : true,
13857 * @event beforeselect
13858 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13859 * @param {Roo.View} this
13860 * @param {HTMLElement} node The node to be selected
13861 * @param {Array} selections Array of currently selected nodes
13863 "beforeselect" : true,
13865 * @event preparedata
13866 * Fires on every row to render, to allow you to change the data.
13867 * @param {Roo.View} this
13868 * @param {Object} data to be rendered (change this)
13870 "preparedata" : true
13878 "click": this.onClick,
13879 "dblclick": this.onDblClick,
13880 "contextmenu": this.onContextMenu,
13884 this.selections = [];
13886 this.cmp = new Roo.CompositeElementLite([]);
13888 this.store = Roo.factory(this.store, Roo.data);
13889 this.setStore(this.store, true);
13892 if ( this.footer && this.footer.xtype) {
13894 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13896 this.footer.dataSource = this.store;
13897 this.footer.container = fctr;
13898 this.footer = Roo.factory(this.footer, Roo);
13899 fctr.insertFirst(this.el);
13901 // this is a bit insane - as the paging toolbar seems to detach the el..
13902 // dom.parentNode.parentNode.parentNode
13903 // they get detached?
13907 Roo.View.superclass.constructor.call(this);
13912 Roo.extend(Roo.View, Roo.util.Observable, {
13915 * @cfg {Roo.data.Store} store Data store to load data from.
13920 * @cfg {String|Roo.Element} el The container element.
13925 * @cfg {String|Roo.Template} tpl The template used by this View
13929 * @cfg {String} dataName the named area of the template to use as the data area
13930 * Works with domtemplates roo-name="name"
13934 * @cfg {String} selectedClass The css class to add to selected nodes
13936 selectedClass : "x-view-selected",
13938 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13943 * @cfg {String} text to display on mask (default Loading)
13947 * @cfg {Boolean} multiSelect Allow multiple selection
13949 multiSelect : false,
13951 * @cfg {Boolean} singleSelect Allow single selection
13953 singleSelect: false,
13956 * @cfg {Boolean} toggleSelect - selecting
13958 toggleSelect : false,
13961 * @cfg {Boolean} tickable - selecting
13966 * Returns the element this view is bound to.
13967 * @return {Roo.Element}
13969 getEl : function(){
13970 return this.wrapEl;
13976 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13978 refresh : function(){
13979 //Roo.log('refresh');
13982 // if we are using something like 'domtemplate', then
13983 // the what gets used is:
13984 // t.applySubtemplate(NAME, data, wrapping data..)
13985 // the outer template then get' applied with
13986 // the store 'extra data'
13987 // and the body get's added to the
13988 // roo-name="data" node?
13989 // <span class='roo-tpl-{name}'></span> ?????
13993 this.clearSelections();
13994 this.el.update("");
13996 var records = this.store.getRange();
13997 if(records.length < 1) {
13999 // is this valid?? = should it render a template??
14001 this.el.update(this.emptyText);
14005 if (this.dataName) {
14006 this.el.update(t.apply(this.store.meta)); //????
14007 el = this.el.child('.roo-tpl-' + this.dataName);
14010 for(var i = 0, len = records.length; i < len; i++){
14011 var data = this.prepareData(records[i].data, i, records[i]);
14012 this.fireEvent("preparedata", this, data, i, records[i]);
14014 var d = Roo.apply({}, data);
14017 Roo.apply(d, {'roo-id' : Roo.id()});
14021 Roo.each(this.parent.item, function(item){
14022 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14025 Roo.apply(d, {'roo-data-checked' : 'checked'});
14029 html[html.length] = Roo.util.Format.trim(
14031 t.applySubtemplate(this.dataName, d, this.store.meta) :
14038 el.update(html.join(""));
14039 this.nodes = el.dom.childNodes;
14040 this.updateIndexes(0);
14045 * Function to override to reformat the data that is sent to
14046 * the template for each node.
14047 * DEPRICATED - use the preparedata event handler.
14048 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14049 * a JSON object for an UpdateManager bound view).
14051 prepareData : function(data, index, record)
14053 this.fireEvent("preparedata", this, data, index, record);
14057 onUpdate : function(ds, record){
14058 // Roo.log('on update');
14059 this.clearSelections();
14060 var index = this.store.indexOf(record);
14061 var n = this.nodes[index];
14062 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14063 n.parentNode.removeChild(n);
14064 this.updateIndexes(index, index);
14070 onAdd : function(ds, records, index)
14072 //Roo.log(['on Add', ds, records, index] );
14073 this.clearSelections();
14074 if(this.nodes.length == 0){
14078 var n = this.nodes[index];
14079 for(var i = 0, len = records.length; i < len; i++){
14080 var d = this.prepareData(records[i].data, i, records[i]);
14082 this.tpl.insertBefore(n, d);
14085 this.tpl.append(this.el, d);
14088 this.updateIndexes(index);
14091 onRemove : function(ds, record, index){
14092 // Roo.log('onRemove');
14093 this.clearSelections();
14094 var el = this.dataName ?
14095 this.el.child('.roo-tpl-' + this.dataName) :
14098 el.dom.removeChild(this.nodes[index]);
14099 this.updateIndexes(index);
14103 * Refresh an individual node.
14104 * @param {Number} index
14106 refreshNode : function(index){
14107 this.onUpdate(this.store, this.store.getAt(index));
14110 updateIndexes : function(startIndex, endIndex){
14111 var ns = this.nodes;
14112 startIndex = startIndex || 0;
14113 endIndex = endIndex || ns.length - 1;
14114 for(var i = startIndex; i <= endIndex; i++){
14115 ns[i].nodeIndex = i;
14120 * Changes the data store this view uses and refresh the view.
14121 * @param {Store} store
14123 setStore : function(store, initial){
14124 if(!initial && this.store){
14125 this.store.un("datachanged", this.refresh);
14126 this.store.un("add", this.onAdd);
14127 this.store.un("remove", this.onRemove);
14128 this.store.un("update", this.onUpdate);
14129 this.store.un("clear", this.refresh);
14130 this.store.un("beforeload", this.onBeforeLoad);
14131 this.store.un("load", this.onLoad);
14132 this.store.un("loadexception", this.onLoad);
14136 store.on("datachanged", this.refresh, this);
14137 store.on("add", this.onAdd, this);
14138 store.on("remove", this.onRemove, this);
14139 store.on("update", this.onUpdate, this);
14140 store.on("clear", this.refresh, this);
14141 store.on("beforeload", this.onBeforeLoad, this);
14142 store.on("load", this.onLoad, this);
14143 store.on("loadexception", this.onLoad, this);
14151 * onbeforeLoad - masks the loading area.
14154 onBeforeLoad : function(store,opts)
14156 //Roo.log('onBeforeLoad');
14158 this.el.update("");
14160 this.el.mask(this.mask ? this.mask : "Loading" );
14162 onLoad : function ()
14169 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14170 * @param {HTMLElement} node
14171 * @return {HTMLElement} The template node
14173 findItemFromChild : function(node){
14174 var el = this.dataName ?
14175 this.el.child('.roo-tpl-' + this.dataName,true) :
14178 if(!node || node.parentNode == el){
14181 var p = node.parentNode;
14182 while(p && p != el){
14183 if(p.parentNode == el){
14192 onClick : function(e){
14193 var item = this.findItemFromChild(e.getTarget());
14195 var index = this.indexOf(item);
14196 if(this.onItemClick(item, index, e) !== false){
14197 this.fireEvent("click", this, index, item, e);
14200 this.clearSelections();
14205 onContextMenu : function(e){
14206 var item = this.findItemFromChild(e.getTarget());
14208 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14213 onDblClick : function(e){
14214 var item = this.findItemFromChild(e.getTarget());
14216 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14220 onItemClick : function(item, index, e)
14222 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14225 if (this.toggleSelect) {
14226 var m = this.isSelected(item) ? 'unselect' : 'select';
14229 _t[m](item, true, false);
14232 if(this.multiSelect || this.singleSelect){
14233 if(this.multiSelect && e.shiftKey && this.lastSelection){
14234 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14236 this.select(item, this.multiSelect && e.ctrlKey);
14237 this.lastSelection = item;
14240 if(!this.tickable){
14241 e.preventDefault();
14249 * Get the number of selected nodes.
14252 getSelectionCount : function(){
14253 return this.selections.length;
14257 * Get the currently selected nodes.
14258 * @return {Array} An array of HTMLElements
14260 getSelectedNodes : function(){
14261 return this.selections;
14265 * Get the indexes of the selected nodes.
14268 getSelectedIndexes : function(){
14269 var indexes = [], s = this.selections;
14270 for(var i = 0, len = s.length; i < len; i++){
14271 indexes.push(s[i].nodeIndex);
14277 * Clear all selections
14278 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14280 clearSelections : function(suppressEvent){
14281 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14282 this.cmp.elements = this.selections;
14283 this.cmp.removeClass(this.selectedClass);
14284 this.selections = [];
14285 if(!suppressEvent){
14286 this.fireEvent("selectionchange", this, this.selections);
14292 * Returns true if the passed node is selected
14293 * @param {HTMLElement/Number} node The node or node index
14294 * @return {Boolean}
14296 isSelected : function(node){
14297 var s = this.selections;
14301 node = this.getNode(node);
14302 return s.indexOf(node) !== -1;
14307 * @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
14308 * @param {Boolean} keepExisting (optional) true to keep existing selections
14309 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14311 select : function(nodeInfo, keepExisting, suppressEvent){
14312 if(nodeInfo instanceof Array){
14314 this.clearSelections(true);
14316 for(var i = 0, len = nodeInfo.length; i < len; i++){
14317 this.select(nodeInfo[i], true, true);
14321 var node = this.getNode(nodeInfo);
14322 if(!node || this.isSelected(node)){
14323 return; // already selected.
14326 this.clearSelections(true);
14329 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14330 Roo.fly(node).addClass(this.selectedClass);
14331 this.selections.push(node);
14332 if(!suppressEvent){
14333 this.fireEvent("selectionchange", this, this.selections);
14341 * @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
14342 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14343 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14345 unselect : function(nodeInfo, keepExisting, suppressEvent)
14347 if(nodeInfo instanceof Array){
14348 Roo.each(this.selections, function(s) {
14349 this.unselect(s, nodeInfo);
14353 var node = this.getNode(nodeInfo);
14354 if(!node || !this.isSelected(node)){
14355 //Roo.log("not selected");
14356 return; // not selected.
14360 Roo.each(this.selections, function(s) {
14362 Roo.fly(node).removeClass(this.selectedClass);
14369 this.selections= ns;
14370 this.fireEvent("selectionchange", this, this.selections);
14374 * Gets a template node.
14375 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14376 * @return {HTMLElement} The node or null if it wasn't found
14378 getNode : function(nodeInfo){
14379 if(typeof nodeInfo == "string"){
14380 return document.getElementById(nodeInfo);
14381 }else if(typeof nodeInfo == "number"){
14382 return this.nodes[nodeInfo];
14388 * Gets a range template nodes.
14389 * @param {Number} startIndex
14390 * @param {Number} endIndex
14391 * @return {Array} An array of nodes
14393 getNodes : function(start, end){
14394 var ns = this.nodes;
14395 start = start || 0;
14396 end = typeof end == "undefined" ? ns.length - 1 : end;
14399 for(var i = start; i <= end; i++){
14403 for(var i = start; i >= end; i--){
14411 * Finds the index of the passed node
14412 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14413 * @return {Number} The index of the node or -1
14415 indexOf : function(node){
14416 node = this.getNode(node);
14417 if(typeof node.nodeIndex == "number"){
14418 return node.nodeIndex;
14420 var ns = this.nodes;
14421 for(var i = 0, len = ns.length; i < len; i++){
14432 * based on jquery fullcalendar
14436 Roo.bootstrap = Roo.bootstrap || {};
14438 * @class Roo.bootstrap.Calendar
14439 * @extends Roo.bootstrap.Component
14440 * Bootstrap Calendar class
14441 * @cfg {Boolean} loadMask (true|false) default false
14442 * @cfg {Object} header generate the user specific header of the calendar, default false
14445 * Create a new Container
14446 * @param {Object} config The config object
14451 Roo.bootstrap.Calendar = function(config){
14452 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14456 * Fires when a date is selected
14457 * @param {DatePicker} this
14458 * @param {Date} date The selected date
14462 * @event monthchange
14463 * Fires when the displayed month changes
14464 * @param {DatePicker} this
14465 * @param {Date} date The selected month
14467 'monthchange': true,
14469 * @event evententer
14470 * Fires when mouse over an event
14471 * @param {Calendar} this
14472 * @param {event} Event
14474 'evententer': true,
14476 * @event eventleave
14477 * Fires when the mouse leaves an
14478 * @param {Calendar} this
14481 'eventleave': true,
14483 * @event eventclick
14484 * Fires when the mouse click an
14485 * @param {Calendar} this
14494 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14497 * @cfg {Number} startDay
14498 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14506 getAutoCreate : function(){
14509 var fc_button = function(name, corner, style, content ) {
14510 return Roo.apply({},{
14512 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14514 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14517 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14528 style : 'width:100%',
14535 cls : 'fc-header-left',
14537 fc_button('prev', 'left', 'arrow', '‹' ),
14538 fc_button('next', 'right', 'arrow', '›' ),
14539 { tag: 'span', cls: 'fc-header-space' },
14540 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14548 cls : 'fc-header-center',
14552 cls: 'fc-header-title',
14555 html : 'month / year'
14563 cls : 'fc-header-right',
14565 /* fc_button('month', 'left', '', 'month' ),
14566 fc_button('week', '', '', 'week' ),
14567 fc_button('day', 'right', '', 'day' )
14579 header = this.header;
14582 var cal_heads = function() {
14584 // fixme - handle this.
14586 for (var i =0; i < Date.dayNames.length; i++) {
14587 var d = Date.dayNames[i];
14590 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14591 html : d.substring(0,3)
14595 ret[0].cls += ' fc-first';
14596 ret[6].cls += ' fc-last';
14599 var cal_cell = function(n) {
14602 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14607 cls: 'fc-day-number',
14611 cls: 'fc-day-content',
14615 style: 'position: relative;' // height: 17px;
14627 var cal_rows = function() {
14630 for (var r = 0; r < 6; r++) {
14637 for (var i =0; i < Date.dayNames.length; i++) {
14638 var d = Date.dayNames[i];
14639 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14642 row.cn[0].cls+=' fc-first';
14643 row.cn[0].cn[0].style = 'min-height:90px';
14644 row.cn[6].cls+=' fc-last';
14648 ret[0].cls += ' fc-first';
14649 ret[4].cls += ' fc-prev-last';
14650 ret[5].cls += ' fc-last';
14657 cls: 'fc-border-separate',
14658 style : 'width:100%',
14666 cls : 'fc-first fc-last',
14684 cls : 'fc-content',
14685 style : "position: relative;",
14688 cls : 'fc-view fc-view-month fc-grid',
14689 style : 'position: relative',
14690 unselectable : 'on',
14693 cls : 'fc-event-container',
14694 style : 'position:absolute;z-index:8;top:0;left:0;'
14712 initEvents : function()
14715 throw "can not find store for calendar";
14721 style: "text-align:center",
14725 style: "background-color:white;width:50%;margin:250 auto",
14729 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14740 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14742 var size = this.el.select('.fc-content', true).first().getSize();
14743 this.maskEl.setSize(size.width, size.height);
14744 this.maskEl.enableDisplayMode("block");
14745 if(!this.loadMask){
14746 this.maskEl.hide();
14749 this.store = Roo.factory(this.store, Roo.data);
14750 this.store.on('load', this.onLoad, this);
14751 this.store.on('beforeload', this.onBeforeLoad, this);
14755 this.cells = this.el.select('.fc-day',true);
14756 //Roo.log(this.cells);
14757 this.textNodes = this.el.query('.fc-day-number');
14758 this.cells.addClassOnOver('fc-state-hover');
14760 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14761 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14762 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14763 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14765 this.on('monthchange', this.onMonthChange, this);
14767 this.update(new Date().clearTime());
14770 resize : function() {
14771 var sz = this.el.getSize();
14773 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14774 this.el.select('.fc-day-content div',true).setHeight(34);
14779 showPrevMonth : function(e){
14780 this.update(this.activeDate.add("mo", -1));
14782 showToday : function(e){
14783 this.update(new Date().clearTime());
14786 showNextMonth : function(e){
14787 this.update(this.activeDate.add("mo", 1));
14791 showPrevYear : function(){
14792 this.update(this.activeDate.add("y", -1));
14796 showNextYear : function(){
14797 this.update(this.activeDate.add("y", 1));
14802 update : function(date)
14804 var vd = this.activeDate;
14805 this.activeDate = date;
14806 // if(vd && this.el){
14807 // var t = date.getTime();
14808 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14809 // Roo.log('using add remove');
14811 // this.fireEvent('monthchange', this, date);
14813 // this.cells.removeClass("fc-state-highlight");
14814 // this.cells.each(function(c){
14815 // if(c.dateValue == t){
14816 // c.addClass("fc-state-highlight");
14817 // setTimeout(function(){
14818 // try{c.dom.firstChild.focus();}catch(e){}
14828 var days = date.getDaysInMonth();
14830 var firstOfMonth = date.getFirstDateOfMonth();
14831 var startingPos = firstOfMonth.getDay()-this.startDay;
14833 if(startingPos < this.startDay){
14837 var pm = date.add(Date.MONTH, -1);
14838 var prevStart = pm.getDaysInMonth()-startingPos;
14840 this.cells = this.el.select('.fc-day',true);
14841 this.textNodes = this.el.query('.fc-day-number');
14842 this.cells.addClassOnOver('fc-state-hover');
14844 var cells = this.cells.elements;
14845 var textEls = this.textNodes;
14847 Roo.each(cells, function(cell){
14848 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14851 days += startingPos;
14853 // convert everything to numbers so it's fast
14854 var day = 86400000;
14855 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14858 //Roo.log(prevStart);
14860 var today = new Date().clearTime().getTime();
14861 var sel = date.clearTime().getTime();
14862 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14863 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14864 var ddMatch = this.disabledDatesRE;
14865 var ddText = this.disabledDatesText;
14866 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14867 var ddaysText = this.disabledDaysText;
14868 var format = this.format;
14870 var setCellClass = function(cal, cell){
14874 //Roo.log('set Cell Class');
14876 var t = d.getTime();
14880 cell.dateValue = t;
14882 cell.className += " fc-today";
14883 cell.className += " fc-state-highlight";
14884 cell.title = cal.todayText;
14887 // disable highlight in other month..
14888 //cell.className += " fc-state-highlight";
14893 cell.className = " fc-state-disabled";
14894 cell.title = cal.minText;
14898 cell.className = " fc-state-disabled";
14899 cell.title = cal.maxText;
14903 if(ddays.indexOf(d.getDay()) != -1){
14904 cell.title = ddaysText;
14905 cell.className = " fc-state-disabled";
14908 if(ddMatch && format){
14909 var fvalue = d.dateFormat(format);
14910 if(ddMatch.test(fvalue)){
14911 cell.title = ddText.replace("%0", fvalue);
14912 cell.className = " fc-state-disabled";
14916 if (!cell.initialClassName) {
14917 cell.initialClassName = cell.dom.className;
14920 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14925 for(; i < startingPos; i++) {
14926 textEls[i].innerHTML = (++prevStart);
14927 d.setDate(d.getDate()+1);
14929 cells[i].className = "fc-past fc-other-month";
14930 setCellClass(this, cells[i]);
14935 for(; i < days; i++){
14936 intDay = i - startingPos + 1;
14937 textEls[i].innerHTML = (intDay);
14938 d.setDate(d.getDate()+1);
14940 cells[i].className = ''; // "x-date-active";
14941 setCellClass(this, cells[i]);
14945 for(; i < 42; i++) {
14946 textEls[i].innerHTML = (++extraDays);
14947 d.setDate(d.getDate()+1);
14949 cells[i].className = "fc-future fc-other-month";
14950 setCellClass(this, cells[i]);
14953 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14955 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14957 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14958 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14960 if(totalRows != 6){
14961 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14962 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14965 this.fireEvent('monthchange', this, date);
14969 if(!this.internalRender){
14970 var main = this.el.dom.firstChild;
14971 var w = main.offsetWidth;
14972 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14973 Roo.fly(main).setWidth(w);
14974 this.internalRender = true;
14975 // opera does not respect the auto grow header center column
14976 // then, after it gets a width opera refuses to recalculate
14977 // without a second pass
14978 if(Roo.isOpera && !this.secondPass){
14979 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14980 this.secondPass = true;
14981 this.update.defer(10, this, [date]);
14988 findCell : function(dt) {
14989 dt = dt.clearTime().getTime();
14991 this.cells.each(function(c){
14992 //Roo.log("check " +c.dateValue + '?=' + dt);
14993 if(c.dateValue == dt){
15003 findCells : function(ev) {
15004 var s = ev.start.clone().clearTime().getTime();
15006 var e= ev.end.clone().clearTime().getTime();
15009 this.cells.each(function(c){
15010 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15012 if(c.dateValue > e){
15015 if(c.dateValue < s){
15024 // findBestRow: function(cells)
15028 // for (var i =0 ; i < cells.length;i++) {
15029 // ret = Math.max(cells[i].rows || 0,ret);
15036 addItem : function(ev)
15038 // look for vertical location slot in
15039 var cells = this.findCells(ev);
15041 // ev.row = this.findBestRow(cells);
15043 // work out the location.
15047 for(var i =0; i < cells.length; i++) {
15049 cells[i].row = cells[0].row;
15052 cells[i].row = cells[i].row + 1;
15062 if (crow.start.getY() == cells[i].getY()) {
15064 crow.end = cells[i];
15081 cells[0].events.push(ev);
15083 this.calevents.push(ev);
15086 clearEvents: function() {
15088 if(!this.calevents){
15092 Roo.each(this.cells.elements, function(c){
15098 Roo.each(this.calevents, function(e) {
15099 Roo.each(e.els, function(el) {
15100 el.un('mouseenter' ,this.onEventEnter, this);
15101 el.un('mouseleave' ,this.onEventLeave, this);
15106 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15112 renderEvents: function()
15116 this.cells.each(function(c) {
15125 if(c.row != c.events.length){
15126 r = 4 - (4 - (c.row - c.events.length));
15129 c.events = ev.slice(0, r);
15130 c.more = ev.slice(r);
15132 if(c.more.length && c.more.length == 1){
15133 c.events.push(c.more.pop());
15136 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15140 this.cells.each(function(c) {
15142 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15145 for (var e = 0; e < c.events.length; e++){
15146 var ev = c.events[e];
15147 var rows = ev.rows;
15149 for(var i = 0; i < rows.length; i++) {
15151 // how many rows should it span..
15154 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15155 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15157 unselectable : "on",
15160 cls: 'fc-event-inner',
15164 // cls: 'fc-event-time',
15165 // html : cells.length > 1 ? '' : ev.time
15169 cls: 'fc-event-title',
15170 html : String.format('{0}', ev.title)
15177 cls: 'ui-resizable-handle ui-resizable-e',
15178 html : '  '
15185 cfg.cls += ' fc-event-start';
15187 if ((i+1) == rows.length) {
15188 cfg.cls += ' fc-event-end';
15191 var ctr = _this.el.select('.fc-event-container',true).first();
15192 var cg = ctr.createChild(cfg);
15194 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15195 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15197 var r = (c.more.length) ? 1 : 0;
15198 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15199 cg.setWidth(ebox.right - sbox.x -2);
15201 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15202 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15203 cg.on('click', _this.onEventClick, _this, ev);
15214 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15215 style : 'position: absolute',
15216 unselectable : "on",
15219 cls: 'fc-event-inner',
15223 cls: 'fc-event-title',
15231 cls: 'ui-resizable-handle ui-resizable-e',
15232 html : '  '
15238 var ctr = _this.el.select('.fc-event-container',true).first();
15239 var cg = ctr.createChild(cfg);
15241 var sbox = c.select('.fc-day-content',true).first().getBox();
15242 var ebox = c.select('.fc-day-content',true).first().getBox();
15244 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15245 cg.setWidth(ebox.right - sbox.x -2);
15247 cg.on('click', _this.onMoreEventClick, _this, c.more);
15257 onEventEnter: function (e, el,event,d) {
15258 this.fireEvent('evententer', this, el, event);
15261 onEventLeave: function (e, el,event,d) {
15262 this.fireEvent('eventleave', this, el, event);
15265 onEventClick: function (e, el,event,d) {
15266 this.fireEvent('eventclick', this, el, event);
15269 onMonthChange: function () {
15273 onMoreEventClick: function(e, el, more)
15277 this.calpopover.placement = 'right';
15278 this.calpopover.setTitle('More');
15280 this.calpopover.setContent('');
15282 var ctr = this.calpopover.el.select('.popover-content', true).first();
15284 Roo.each(more, function(m){
15286 cls : 'fc-event-hori fc-event-draggable',
15289 var cg = ctr.createChild(cfg);
15291 cg.on('click', _this.onEventClick, _this, m);
15294 this.calpopover.show(el);
15299 onLoad: function ()
15301 this.calevents = [];
15304 if(this.store.getCount() > 0){
15305 this.store.data.each(function(d){
15308 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15309 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15310 time : d.data.start_time,
15311 title : d.data.title,
15312 description : d.data.description,
15313 venue : d.data.venue
15318 this.renderEvents();
15320 if(this.calevents.length && this.loadMask){
15321 this.maskEl.hide();
15325 onBeforeLoad: function()
15327 this.clearEvents();
15329 this.maskEl.show();
15343 * @class Roo.bootstrap.Popover
15344 * @extends Roo.bootstrap.Component
15345 * Bootstrap Popover class
15346 * @cfg {String} html contents of the popover (or false to use children..)
15347 * @cfg {String} title of popover (or false to hide)
15348 * @cfg {String} placement how it is placed
15349 * @cfg {String} trigger click || hover (or false to trigger manually)
15350 * @cfg {String} over what (parent or false to trigger manually.)
15351 * @cfg {Number} delay - delay before showing
15354 * Create a new Popover
15355 * @param {Object} config The config object
15358 Roo.bootstrap.Popover = function(config){
15359 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15362 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15364 title: 'Fill in a title',
15367 placement : 'right',
15368 trigger : 'hover', // hover
15374 can_build_overlaid : false,
15376 getChildContainer : function()
15378 return this.el.select('.popover-content',true).first();
15381 getAutoCreate : function(){
15382 Roo.log('make popover?');
15384 cls : 'popover roo-dynamic',
15385 style: 'display:block',
15391 cls : 'popover-inner',
15395 cls: 'popover-title',
15399 cls : 'popover-content',
15410 setTitle: function(str)
15413 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15415 setContent: function(str)
15418 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15420 // as it get's added to the bottom of the page.
15421 onRender : function(ct, position)
15423 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15425 var cfg = Roo.apply({}, this.getAutoCreate());
15429 cfg.cls += ' ' + this.cls;
15432 cfg.style = this.style;
15434 Roo.log("adding to ")
15435 this.el = Roo.get(document.body).createChild(cfg, position);
15441 initEvents : function()
15443 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15444 this.el.enableDisplayMode('block');
15446 if (this.over === false) {
15449 if (this.triggers === false) {
15452 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15453 var triggers = this.trigger ? this.trigger.split(' ') : [];
15454 Roo.each(triggers, function(trigger) {
15456 if (trigger == 'click') {
15457 on_el.on('click', this.toggle, this);
15458 } else if (trigger != 'manual') {
15459 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15460 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15462 on_el.on(eventIn ,this.enter, this);
15463 on_el.on(eventOut, this.leave, this);
15474 toggle : function () {
15475 this.hoverState == 'in' ? this.leave() : this.enter();
15478 enter : function () {
15481 clearTimeout(this.timeout);
15483 this.hoverState = 'in';
15485 if (!this.delay || !this.delay.show) {
15490 this.timeout = setTimeout(function () {
15491 if (_t.hoverState == 'in') {
15494 }, this.delay.show)
15496 leave : function() {
15497 clearTimeout(this.timeout);
15499 this.hoverState = 'out';
15501 if (!this.delay || !this.delay.hide) {
15506 this.timeout = setTimeout(function () {
15507 if (_t.hoverState == 'out') {
15510 }, this.delay.hide)
15513 show : function (on_el)
15516 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15519 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15520 if (this.html !== false) {
15521 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15523 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15524 if (!this.title.length) {
15525 this.el.select('.popover-title',true).hide();
15528 var placement = typeof this.placement == 'function' ?
15529 this.placement.call(this, this.el, on_el) :
15532 var autoToken = /\s?auto?\s?/i;
15533 var autoPlace = autoToken.test(placement);
15535 placement = placement.replace(autoToken, '') || 'top';
15539 //this.el.setXY([0,0]);
15541 this.el.dom.style.display='block';
15542 this.el.addClass(placement);
15544 //this.el.appendTo(on_el);
15546 var p = this.getPosition();
15547 var box = this.el.getBox();
15552 var align = Roo.bootstrap.Popover.alignment[placement];
15553 this.el.alignTo(on_el, align[0],align[1]);
15554 //var arrow = this.el.select('.arrow',true).first();
15555 //arrow.set(align[2],
15557 this.el.addClass('in');
15560 if (this.el.hasClass('fade')) {
15567 this.el.setXY([0,0]);
15568 this.el.removeClass('in');
15570 this.hoverState = null;
15576 Roo.bootstrap.Popover.alignment = {
15577 'left' : ['r-l', [-10,0], 'right'],
15578 'right' : ['l-r', [10,0], 'left'],
15579 'bottom' : ['t-b', [0,10], 'top'],
15580 'top' : [ 'b-t', [0,-10], 'bottom']
15591 * @class Roo.bootstrap.Progress
15592 * @extends Roo.bootstrap.Component
15593 * Bootstrap Progress class
15594 * @cfg {Boolean} striped striped of the progress bar
15595 * @cfg {Boolean} active animated of the progress bar
15599 * Create a new Progress
15600 * @param {Object} config The config object
15603 Roo.bootstrap.Progress = function(config){
15604 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15607 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15612 getAutoCreate : function(){
15620 cfg.cls += ' progress-striped';
15624 cfg.cls += ' active';
15643 * @class Roo.bootstrap.ProgressBar
15644 * @extends Roo.bootstrap.Component
15645 * Bootstrap ProgressBar class
15646 * @cfg {Number} aria_valuenow aria-value now
15647 * @cfg {Number} aria_valuemin aria-value min
15648 * @cfg {Number} aria_valuemax aria-value max
15649 * @cfg {String} label label for the progress bar
15650 * @cfg {String} panel (success | info | warning | danger )
15651 * @cfg {String} role role of the progress bar
15652 * @cfg {String} sr_only text
15656 * Create a new ProgressBar
15657 * @param {Object} config The config object
15660 Roo.bootstrap.ProgressBar = function(config){
15661 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15664 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15668 aria_valuemax : 100,
15674 getAutoCreate : function()
15679 cls: 'progress-bar',
15680 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15692 cfg.role = this.role;
15695 if(this.aria_valuenow){
15696 cfg['aria-valuenow'] = this.aria_valuenow;
15699 if(this.aria_valuemin){
15700 cfg['aria-valuemin'] = this.aria_valuemin;
15703 if(this.aria_valuemax){
15704 cfg['aria-valuemax'] = this.aria_valuemax;
15707 if(this.label && !this.sr_only){
15708 cfg.html = this.label;
15712 cfg.cls += ' progress-bar-' + this.panel;
15718 update : function(aria_valuenow)
15720 this.aria_valuenow = aria_valuenow;
15722 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15737 * @class Roo.bootstrap.TabGroup
15738 * @extends Roo.bootstrap.Column
15739 * Bootstrap Column class
15740 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15741 * @cfg {Boolean} carousel true to make the group behave like a carousel
15742 * @cfg {Number} bullets show the panel pointer.. default 0
15743 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15744 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15745 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15748 * Create a new TabGroup
15749 * @param {Object} config The config object
15752 Roo.bootstrap.TabGroup = function(config){
15753 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15755 this.navId = Roo.id();
15758 Roo.bootstrap.TabGroup.register(this);
15762 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15765 transition : false,
15770 slideOnTouch : false,
15772 getAutoCreate : function()
15774 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15776 cfg.cls += ' tab-content';
15778 Roo.log('get auto create...............');
15780 if (this.carousel) {
15781 cfg.cls += ' carousel slide';
15784 cls : 'carousel-inner'
15787 if(this.bullets > 0 && !Roo.isTouch){
15790 cls : 'carousel-bullets',
15794 if(this.bullets_cls){
15795 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15798 for (var i = 0; i < this.bullets; i++){
15800 cls : 'bullet bullet-' + i
15808 cfg.cn[0].cn = bullets;
15815 initEvents: function()
15817 Roo.log('-------- init events on tab group ---------');
15819 if(this.bullets > 0 && !Roo.isTouch){
15825 if(Roo.isTouch && this.slideOnTouch){
15826 this.el.on("touchstart", this.onTouchStart, this);
15829 if(this.autoslide){
15832 this.slideFn = window.setInterval(function() {
15833 _this.showPanelNext();
15839 onTouchStart : function(e, el, o)
15841 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15845 this.showPanelNext();
15848 getChildContainer : function()
15850 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15854 * register a Navigation item
15855 * @param {Roo.bootstrap.NavItem} the navitem to add
15857 register : function(item)
15859 this.tabs.push( item);
15860 item.navId = this.navId; // not really needed..
15864 getActivePanel : function()
15867 Roo.each(this.tabs, function(t) {
15877 getPanelByName : function(n)
15880 Roo.each(this.tabs, function(t) {
15881 if (t.tabId == n) {
15889 indexOfPanel : function(p)
15892 Roo.each(this.tabs, function(t,i) {
15893 if (t.tabId == p.tabId) {
15902 * show a specific panel
15903 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15904 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15906 showPanel : function (pan)
15908 if(this.transition){
15909 Roo.log("waiting for the transitionend");
15913 if (typeof(pan) == 'number') {
15914 pan = this.tabs[pan];
15916 if (typeof(pan) == 'string') {
15917 pan = this.getPanelByName(pan);
15919 if (pan.tabId == this.getActivePanel().tabId) {
15922 var cur = this.getActivePanel();
15924 if (false === cur.fireEvent('beforedeactivate')) {
15928 if(this.bullets > 0 && !Roo.isTouch){
15929 this.setActiveBullet(this.indexOfPanel(pan));
15932 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15934 this.transition = true;
15935 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15936 var lr = dir == 'next' ? 'left' : 'right';
15937 pan.el.addClass(dir); // or prev
15938 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15939 cur.el.addClass(lr); // or right
15940 pan.el.addClass(lr);
15943 cur.el.on('transitionend', function() {
15944 Roo.log("trans end?");
15946 pan.el.removeClass([lr,dir]);
15947 pan.setActive(true);
15949 cur.el.removeClass([lr]);
15950 cur.setActive(false);
15952 _this.transition = false;
15954 }, this, { single: true } );
15959 cur.setActive(false);
15960 pan.setActive(true);
15965 showPanelNext : function()
15967 var i = this.indexOfPanel(this.getActivePanel());
15969 if (i >= this.tabs.length - 1 && !this.autoslide) {
15973 if (i >= this.tabs.length - 1 && this.autoslide) {
15977 this.showPanel(this.tabs[i+1]);
15980 showPanelPrev : function()
15982 var i = this.indexOfPanel(this.getActivePanel());
15984 if (i < 1 && !this.autoslide) {
15988 if (i < 1 && this.autoslide) {
15989 i = this.tabs.length;
15992 this.showPanel(this.tabs[i-1]);
15995 initBullet : function()
16003 for (var i = 0; i < this.bullets; i++){
16004 var bullet = this.el.select('.bullet-' + i, true).first();
16010 bullet.on('click', (function(e, el, o, ii, t){
16012 e.preventDefault();
16014 _this.showPanel(ii);
16016 if(_this.autoslide && _this.slideFn){
16017 clearInterval(_this.slideFn);
16018 _this.slideFn = window.setInterval(function() {
16019 _this.showPanelNext();
16023 }).createDelegate(this, [i, bullet], true));
16027 setActiveBullet : function(i)
16033 Roo.each(this.el.select('.bullet', true).elements, function(el){
16034 el.removeClass('selected');
16037 var bullet = this.el.select('.bullet-' + i, true).first();
16043 bullet.addClass('selected');
16054 Roo.apply(Roo.bootstrap.TabGroup, {
16058 * register a Navigation Group
16059 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16061 register : function(navgrp)
16063 this.groups[navgrp.navId] = navgrp;
16067 * fetch a Navigation Group based on the navigation ID
16068 * if one does not exist , it will get created.
16069 * @param {string} the navgroup to add
16070 * @returns {Roo.bootstrap.NavGroup} the navgroup
16072 get: function(navId) {
16073 if (typeof(this.groups[navId]) == 'undefined') {
16074 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16076 return this.groups[navId] ;
16091 * @class Roo.bootstrap.TabPanel
16092 * @extends Roo.bootstrap.Component
16093 * Bootstrap TabPanel class
16094 * @cfg {Boolean} active panel active
16095 * @cfg {String} html panel content
16096 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16097 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16101 * Create a new TabPanel
16102 * @param {Object} config The config object
16105 Roo.bootstrap.TabPanel = function(config){
16106 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16110 * Fires when the active status changes
16111 * @param {Roo.bootstrap.TabPanel} this
16112 * @param {Boolean} state the new state
16117 * @event beforedeactivate
16118 * Fires before a tab is de-activated - can be used to do validation on a form.
16119 * @param {Roo.bootstrap.TabPanel} this
16120 * @return {Boolean} false if there is an error
16123 'beforedeactivate': true
16126 this.tabId = this.tabId || Roo.id();
16130 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16137 getAutoCreate : function(){
16140 // item is needed for carousel - not sure if it has any effect otherwise
16141 cls: 'tab-pane item',
16142 html: this.html || ''
16146 cfg.cls += ' active';
16150 cfg.tabId = this.tabId;
16157 initEvents: function()
16159 Roo.log('-------- init events on tab panel ---------');
16161 var p = this.parent();
16162 this.navId = this.navId || p.navId;
16164 if (typeof(this.navId) != 'undefined') {
16165 // not really needed.. but just in case.. parent should be a NavGroup.
16166 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16167 Roo.log(['register', tg, this]);
16170 var i = tg.tabs.length - 1;
16172 if(this.active && tg.bullets > 0 && i < tg.bullets){
16173 tg.setActiveBullet(i);
16180 onRender : function(ct, position)
16182 // Roo.log("Call onRender: " + this.xtype);
16184 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16192 setActive: function(state)
16194 Roo.log("panel - set active " + this.tabId + "=" + state);
16196 this.active = state;
16198 this.el.removeClass('active');
16200 } else if (!this.el.hasClass('active')) {
16201 this.el.addClass('active');
16204 this.fireEvent('changed', this, state);
16221 * @class Roo.bootstrap.DateField
16222 * @extends Roo.bootstrap.Input
16223 * Bootstrap DateField class
16224 * @cfg {Number} weekStart default 0
16225 * @cfg {String} viewMode default empty, (months|years)
16226 * @cfg {String} minViewMode default empty, (months|years)
16227 * @cfg {Number} startDate default -Infinity
16228 * @cfg {Number} endDate default Infinity
16229 * @cfg {Boolean} todayHighlight default false
16230 * @cfg {Boolean} todayBtn default false
16231 * @cfg {Boolean} calendarWeeks default false
16232 * @cfg {Object} daysOfWeekDisabled default empty
16233 * @cfg {Boolean} singleMode default false (true | false)
16235 * @cfg {Boolean} keyboardNavigation default true
16236 * @cfg {String} language default en
16239 * Create a new DateField
16240 * @param {Object} config The config object
16243 Roo.bootstrap.DateField = function(config){
16244 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16248 * Fires when this field show.
16249 * @param {Roo.bootstrap.DateField} this
16250 * @param {Mixed} date The date value
16255 * Fires when this field hide.
16256 * @param {Roo.bootstrap.DateField} this
16257 * @param {Mixed} date The date value
16262 * Fires when select a date.
16263 * @param {Roo.bootstrap.DateField} this
16264 * @param {Mixed} date The date value
16270 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16273 * @cfg {String} format
16274 * The default date format string which can be overriden for localization support. The format must be
16275 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16279 * @cfg {String} altFormats
16280 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16281 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16283 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16291 todayHighlight : false,
16297 keyboardNavigation: true,
16299 calendarWeeks: false,
16301 startDate: -Infinity,
16305 daysOfWeekDisabled: [],
16309 singleMode : false,
16311 UTCDate: function()
16313 return new Date(Date.UTC.apply(Date, arguments));
16316 UTCToday: function()
16318 var today = new Date();
16319 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16322 getDate: function() {
16323 var d = this.getUTCDate();
16324 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16327 getUTCDate: function() {
16331 setDate: function(d) {
16332 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16335 setUTCDate: function(d) {
16337 this.setValue(this.formatDate(this.date));
16340 onRender: function(ct, position)
16343 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16345 this.language = this.language || 'en';
16346 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16347 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16349 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16350 this.format = this.format || 'm/d/y';
16351 this.isInline = false;
16352 this.isInput = true;
16353 this.component = this.el.select('.add-on', true).first() || false;
16354 this.component = (this.component && this.component.length === 0) ? false : this.component;
16355 this.hasInput = this.component && this.inputEL().length;
16357 if (typeof(this.minViewMode === 'string')) {
16358 switch (this.minViewMode) {
16360 this.minViewMode = 1;
16363 this.minViewMode = 2;
16366 this.minViewMode = 0;
16371 if (typeof(this.viewMode === 'string')) {
16372 switch (this.viewMode) {
16385 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16387 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16389 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16391 this.picker().on('mousedown', this.onMousedown, this);
16392 this.picker().on('click', this.onClick, this);
16394 this.picker().addClass('datepicker-dropdown');
16396 this.startViewMode = this.viewMode;
16398 if(this.singleMode){
16399 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16400 v.setVisibilityMode(Roo.Element.DISPLAY)
16404 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16405 v.setStyle('width', '189px');
16409 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16410 if(!this.calendarWeeks){
16415 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16416 v.attr('colspan', function(i, val){
16417 return parseInt(val) + 1;
16422 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16424 this.setStartDate(this.startDate);
16425 this.setEndDate(this.endDate);
16427 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16434 if(this.isInline) {
16439 picker : function()
16441 return this.pickerEl;
16442 // return this.el.select('.datepicker', true).first();
16445 fillDow: function()
16447 var dowCnt = this.weekStart;
16456 if(this.calendarWeeks){
16464 while (dowCnt < this.weekStart + 7) {
16468 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16472 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16475 fillMonths: function()
16478 var months = this.picker().select('>.datepicker-months td', true).first();
16480 months.dom.innerHTML = '';
16486 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16489 months.createChild(month);
16496 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;
16498 if (this.date < this.startDate) {
16499 this.viewDate = new Date(this.startDate);
16500 } else if (this.date > this.endDate) {
16501 this.viewDate = new Date(this.endDate);
16503 this.viewDate = new Date(this.date);
16511 var d = new Date(this.viewDate),
16512 year = d.getUTCFullYear(),
16513 month = d.getUTCMonth(),
16514 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16515 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16516 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16517 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16518 currentDate = this.date && this.date.valueOf(),
16519 today = this.UTCToday();
16521 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16523 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16525 // this.picker.select('>tfoot th.today').
16526 // .text(dates[this.language].today)
16527 // .toggle(this.todayBtn !== false);
16529 this.updateNavArrows();
16532 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16534 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16536 prevMonth.setUTCDate(day);
16538 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16540 var nextMonth = new Date(prevMonth);
16542 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16544 nextMonth = nextMonth.valueOf();
16546 var fillMonths = false;
16548 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16550 while(prevMonth.valueOf() < nextMonth) {
16553 if (prevMonth.getUTCDay() === this.weekStart) {
16555 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16563 if(this.calendarWeeks){
16564 // ISO 8601: First week contains first thursday.
16565 // ISO also states week starts on Monday, but we can be more abstract here.
16567 // Start of current week: based on weekstart/current date
16568 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16569 // Thursday of this week
16570 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16571 // First Thursday of year, year from thursday
16572 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16573 // Calendar week: ms between thursdays, div ms per day, div 7 days
16574 calWeek = (th - yth) / 864e5 / 7 + 1;
16576 fillMonths.cn.push({
16584 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16586 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16589 if (this.todayHighlight &&
16590 prevMonth.getUTCFullYear() == today.getFullYear() &&
16591 prevMonth.getUTCMonth() == today.getMonth() &&
16592 prevMonth.getUTCDate() == today.getDate()) {
16593 clsName += ' today';
16596 if (currentDate && prevMonth.valueOf() === currentDate) {
16597 clsName += ' active';
16600 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16601 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16602 clsName += ' disabled';
16605 fillMonths.cn.push({
16607 cls: 'day ' + clsName,
16608 html: prevMonth.getDate()
16611 prevMonth.setDate(prevMonth.getDate()+1);
16614 var currentYear = this.date && this.date.getUTCFullYear();
16615 var currentMonth = this.date && this.date.getUTCMonth();
16617 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16619 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16620 v.removeClass('active');
16622 if(currentYear === year && k === currentMonth){
16623 v.addClass('active');
16626 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16627 v.addClass('disabled');
16633 year = parseInt(year/10, 10) * 10;
16635 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16637 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16640 for (var i = -1; i < 11; i++) {
16641 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16643 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16651 showMode: function(dir)
16654 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16657 Roo.each(this.picker().select('>div',true).elements, function(v){
16658 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16661 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16666 if(this.isInline) return;
16668 this.picker().removeClass(['bottom', 'top']);
16670 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16672 * place to the top of element!
16676 this.picker().addClass('top');
16677 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16682 this.picker().addClass('bottom');
16684 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16687 parseDate : function(value)
16689 if(!value || value instanceof Date){
16692 var v = Date.parseDate(value, this.format);
16693 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16694 v = Date.parseDate(value, 'Y-m-d');
16696 if(!v && this.altFormats){
16697 if(!this.altFormatsArray){
16698 this.altFormatsArray = this.altFormats.split("|");
16700 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16701 v = Date.parseDate(value, this.altFormatsArray[i]);
16707 formatDate : function(date, fmt)
16709 return (!date || !(date instanceof Date)) ?
16710 date : date.dateFormat(fmt || this.format);
16713 onFocus : function()
16715 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16719 onBlur : function()
16721 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16723 var d = this.inputEl().getValue();
16732 this.picker().show();
16736 this.fireEvent('show', this, this.date);
16741 if(this.isInline) return;
16742 this.picker().hide();
16743 this.viewMode = this.startViewMode;
16746 this.fireEvent('hide', this, this.date);
16750 onMousedown: function(e)
16752 e.stopPropagation();
16753 e.preventDefault();
16758 Roo.bootstrap.DateField.superclass.keyup.call(this);
16762 setValue: function(v)
16765 // v can be a string or a date..
16768 var d = new Date(this.parseDate(v) ).clearTime();
16770 if(isNaN(d.getTime())){
16771 this.date = this.viewDate = '';
16772 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16776 v = this.formatDate(d);
16778 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16780 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16784 this.fireEvent('select', this, this.date);
16788 getValue: function()
16790 return this.formatDate(this.date);
16793 fireKey: function(e)
16795 if (!this.picker().isVisible()){
16796 if (e.keyCode == 27) // allow escape to hide and re-show picker
16801 var dateChanged = false,
16803 newDate, newViewDate;
16808 e.preventDefault();
16812 if (!this.keyboardNavigation) break;
16813 dir = e.keyCode == 37 ? -1 : 1;
16816 newDate = this.moveYear(this.date, dir);
16817 newViewDate = this.moveYear(this.viewDate, dir);
16818 } else if (e.shiftKey){
16819 newDate = this.moveMonth(this.date, dir);
16820 newViewDate = this.moveMonth(this.viewDate, dir);
16822 newDate = new Date(this.date);
16823 newDate.setUTCDate(this.date.getUTCDate() + dir);
16824 newViewDate = new Date(this.viewDate);
16825 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16827 if (this.dateWithinRange(newDate)){
16828 this.date = newDate;
16829 this.viewDate = newViewDate;
16830 this.setValue(this.formatDate(this.date));
16832 e.preventDefault();
16833 dateChanged = true;
16838 if (!this.keyboardNavigation) break;
16839 dir = e.keyCode == 38 ? -1 : 1;
16841 newDate = this.moveYear(this.date, dir);
16842 newViewDate = this.moveYear(this.viewDate, dir);
16843 } else if (e.shiftKey){
16844 newDate = this.moveMonth(this.date, dir);
16845 newViewDate = this.moveMonth(this.viewDate, dir);
16847 newDate = new Date(this.date);
16848 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16849 newViewDate = new Date(this.viewDate);
16850 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16852 if (this.dateWithinRange(newDate)){
16853 this.date = newDate;
16854 this.viewDate = newViewDate;
16855 this.setValue(this.formatDate(this.date));
16857 e.preventDefault();
16858 dateChanged = true;
16862 this.setValue(this.formatDate(this.date));
16864 e.preventDefault();
16867 this.setValue(this.formatDate(this.date));
16881 onClick: function(e)
16883 e.stopPropagation();
16884 e.preventDefault();
16886 var target = e.getTarget();
16888 if(target.nodeName.toLowerCase() === 'i'){
16889 target = Roo.get(target).dom.parentNode;
16892 var nodeName = target.nodeName;
16893 var className = target.className;
16894 var html = target.innerHTML;
16895 //Roo.log(nodeName);
16897 switch(nodeName.toLowerCase()) {
16899 switch(className) {
16905 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16906 switch(this.viewMode){
16908 this.viewDate = this.moveMonth(this.viewDate, dir);
16912 this.viewDate = this.moveYear(this.viewDate, dir);
16918 var date = new Date();
16919 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16921 this.setValue(this.formatDate(this.date));
16928 if (className.indexOf('disabled') < 0) {
16929 this.viewDate.setUTCDate(1);
16930 if (className.indexOf('month') > -1) {
16931 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16933 var year = parseInt(html, 10) || 0;
16934 this.viewDate.setUTCFullYear(year);
16938 if(this.singleMode){
16939 this.setValue(this.formatDate(this.viewDate));
16950 //Roo.log(className);
16951 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16952 var day = parseInt(html, 10) || 1;
16953 var year = this.viewDate.getUTCFullYear(),
16954 month = this.viewDate.getUTCMonth();
16956 if (className.indexOf('old') > -1) {
16963 } else if (className.indexOf('new') > -1) {
16971 //Roo.log([year,month,day]);
16972 this.date = this.UTCDate(year, month, day,0,0,0,0);
16973 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16975 //Roo.log(this.formatDate(this.date));
16976 this.setValue(this.formatDate(this.date));
16983 setStartDate: function(startDate)
16985 this.startDate = startDate || -Infinity;
16986 if (this.startDate !== -Infinity) {
16987 this.startDate = this.parseDate(this.startDate);
16990 this.updateNavArrows();
16993 setEndDate: function(endDate)
16995 this.endDate = endDate || Infinity;
16996 if (this.endDate !== Infinity) {
16997 this.endDate = this.parseDate(this.endDate);
17000 this.updateNavArrows();
17003 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17005 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17006 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17007 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17009 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17010 return parseInt(d, 10);
17013 this.updateNavArrows();
17016 updateNavArrows: function()
17018 if(this.singleMode){
17022 var d = new Date(this.viewDate),
17023 year = d.getUTCFullYear(),
17024 month = d.getUTCMonth();
17026 Roo.each(this.picker().select('.prev', true).elements, function(v){
17028 switch (this.viewMode) {
17031 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17037 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17044 Roo.each(this.picker().select('.next', true).elements, function(v){
17046 switch (this.viewMode) {
17049 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17055 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17063 moveMonth: function(date, dir)
17065 if (!dir) return date;
17066 var new_date = new Date(date.valueOf()),
17067 day = new_date.getUTCDate(),
17068 month = new_date.getUTCMonth(),
17069 mag = Math.abs(dir),
17071 dir = dir > 0 ? 1 : -1;
17074 // If going back one month, make sure month is not current month
17075 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17077 return new_date.getUTCMonth() == month;
17079 // If going forward one month, make sure month is as expected
17080 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17082 return new_date.getUTCMonth() != new_month;
17084 new_month = month + dir;
17085 new_date.setUTCMonth(new_month);
17086 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17087 if (new_month < 0 || new_month > 11)
17088 new_month = (new_month + 12) % 12;
17090 // For magnitudes >1, move one month at a time...
17091 for (var i=0; i<mag; i++)
17092 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17093 new_date = this.moveMonth(new_date, dir);
17094 // ...then reset the day, keeping it in the new month
17095 new_month = new_date.getUTCMonth();
17096 new_date.setUTCDate(day);
17098 return new_month != new_date.getUTCMonth();
17101 // Common date-resetting loop -- if date is beyond end of month, make it
17104 new_date.setUTCDate(--day);
17105 new_date.setUTCMonth(new_month);
17110 moveYear: function(date, dir)
17112 return this.moveMonth(date, dir*12);
17115 dateWithinRange: function(date)
17117 return date >= this.startDate && date <= this.endDate;
17123 this.picker().remove();
17128 Roo.apply(Roo.bootstrap.DateField, {
17139 html: '<i class="fa fa-arrow-left"/>'
17149 html: '<i class="fa fa-arrow-right"/>'
17191 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17192 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17193 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17194 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17195 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17208 navFnc: 'FullYear',
17213 navFnc: 'FullYear',
17218 Roo.apply(Roo.bootstrap.DateField, {
17222 cls: 'datepicker dropdown-menu roo-dynamic',
17226 cls: 'datepicker-days',
17230 cls: 'table-condensed',
17232 Roo.bootstrap.DateField.head,
17236 Roo.bootstrap.DateField.footer
17243 cls: 'datepicker-months',
17247 cls: 'table-condensed',
17249 Roo.bootstrap.DateField.head,
17250 Roo.bootstrap.DateField.content,
17251 Roo.bootstrap.DateField.footer
17258 cls: 'datepicker-years',
17262 cls: 'table-condensed',
17264 Roo.bootstrap.DateField.head,
17265 Roo.bootstrap.DateField.content,
17266 Roo.bootstrap.DateField.footer
17285 * @class Roo.bootstrap.TimeField
17286 * @extends Roo.bootstrap.Input
17287 * Bootstrap DateField class
17291 * Create a new TimeField
17292 * @param {Object} config The config object
17295 Roo.bootstrap.TimeField = function(config){
17296 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17300 * Fires when this field show.
17301 * @param {Roo.bootstrap.DateField} thisthis
17302 * @param {Mixed} date The date value
17307 * Fires when this field hide.
17308 * @param {Roo.bootstrap.DateField} this
17309 * @param {Mixed} date The date value
17314 * Fires when select a date.
17315 * @param {Roo.bootstrap.DateField} this
17316 * @param {Mixed} date The date value
17322 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17325 * @cfg {String} format
17326 * The default time format string which can be overriden for localization support. The format must be
17327 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17331 onRender: function(ct, position)
17334 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17336 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17338 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17340 this.pop = this.picker().select('>.datepicker-time',true).first();
17341 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17343 this.picker().on('mousedown', this.onMousedown, this);
17344 this.picker().on('click', this.onClick, this);
17346 this.picker().addClass('datepicker-dropdown');
17351 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17352 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17353 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17354 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17355 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17356 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17360 fireKey: function(e){
17361 if (!this.picker().isVisible()){
17362 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17368 e.preventDefault();
17376 this.onTogglePeriod();
17379 this.onIncrementMinutes();
17382 this.onDecrementMinutes();
17391 onClick: function(e) {
17392 e.stopPropagation();
17393 e.preventDefault();
17396 picker : function()
17398 return this.el.select('.datepicker', true).first();
17401 fillTime: function()
17403 var time = this.pop.select('tbody', true).first();
17405 time.dom.innerHTML = '';
17420 cls: 'hours-up glyphicon glyphicon-chevron-up'
17440 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17461 cls: 'timepicker-hour',
17476 cls: 'timepicker-minute',
17491 cls: 'btn btn-primary period',
17513 cls: 'hours-down glyphicon glyphicon-chevron-down'
17533 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17551 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17558 var hours = this.time.getHours();
17559 var minutes = this.time.getMinutes();
17572 hours = hours - 12;
17576 hours = '0' + hours;
17580 minutes = '0' + minutes;
17583 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17584 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17585 this.pop.select('button', true).first().dom.innerHTML = period;
17591 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17593 var cls = ['bottom'];
17595 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17602 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17607 this.picker().addClass(cls.join('-'));
17611 Roo.each(cls, function(c){
17613 _this.picker().setTop(_this.inputEl().getHeight());
17617 _this.picker().setTop(0 - _this.picker().getHeight());
17622 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17626 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17633 onFocus : function()
17635 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17639 onBlur : function()
17641 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17647 this.picker().show();
17652 this.fireEvent('show', this, this.date);
17657 this.picker().hide();
17660 this.fireEvent('hide', this, this.date);
17663 setTime : function()
17666 this.setValue(this.time.format(this.format));
17668 this.fireEvent('select', this, this.date);
17673 onMousedown: function(e){
17674 e.stopPropagation();
17675 e.preventDefault();
17678 onIncrementHours: function()
17680 Roo.log('onIncrementHours');
17681 this.time = this.time.add(Date.HOUR, 1);
17686 onDecrementHours: function()
17688 Roo.log('onDecrementHours');
17689 this.time = this.time.add(Date.HOUR, -1);
17693 onIncrementMinutes: function()
17695 Roo.log('onIncrementMinutes');
17696 this.time = this.time.add(Date.MINUTE, 1);
17700 onDecrementMinutes: function()
17702 Roo.log('onDecrementMinutes');
17703 this.time = this.time.add(Date.MINUTE, -1);
17707 onTogglePeriod: function()
17709 Roo.log('onTogglePeriod');
17710 this.time = this.time.add(Date.HOUR, 12);
17717 Roo.apply(Roo.bootstrap.TimeField, {
17747 cls: 'btn btn-info ok',
17759 Roo.apply(Roo.bootstrap.TimeField, {
17763 cls: 'datepicker dropdown-menu',
17767 cls: 'datepicker-time',
17771 cls: 'table-condensed',
17773 Roo.bootstrap.TimeField.content,
17774 Roo.bootstrap.TimeField.footer
17793 * @class Roo.bootstrap.MonthField
17794 * @extends Roo.bootstrap.Input
17795 * Bootstrap MonthField class
17797 * @cfg {String} language default en
17800 * Create a new MonthField
17801 * @param {Object} config The config object
17804 Roo.bootstrap.MonthField = function(config){
17805 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17810 * Fires when this field show.
17811 * @param {Roo.bootstrap.MonthField} this
17812 * @param {Mixed} date The date value
17817 * Fires when this field hide.
17818 * @param {Roo.bootstrap.MonthField} this
17819 * @param {Mixed} date The date value
17824 * Fires when select a date.
17825 * @param {Roo.bootstrap.MonthField} this
17826 * @param {String} oldvalue The old value
17827 * @param {String} newvalue The new value
17833 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17835 onRender: function(ct, position)
17838 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17840 this.language = this.language || 'en';
17841 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17842 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17844 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17845 this.isInline = false;
17846 this.isInput = true;
17847 this.component = this.el.select('.add-on', true).first() || false;
17848 this.component = (this.component && this.component.length === 0) ? false : this.component;
17849 this.hasInput = this.component && this.inputEL().length;
17851 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17853 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17855 this.picker().on('mousedown', this.onMousedown, this);
17856 this.picker().on('click', this.onClick, this);
17858 this.picker().addClass('datepicker-dropdown');
17860 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17861 v.setStyle('width', '189px');
17868 if(this.isInline) {
17874 setValue: function(v, suppressEvent)
17876 var o = this.getValue();
17878 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17882 if(suppressEvent !== true){
17883 this.fireEvent('select', this, o, v);
17888 getValue: function()
17893 onClick: function(e)
17895 e.stopPropagation();
17896 e.preventDefault();
17898 var target = e.getTarget();
17900 if(target.nodeName.toLowerCase() === 'i'){
17901 target = Roo.get(target).dom.parentNode;
17904 var nodeName = target.nodeName;
17905 var className = target.className;
17906 var html = target.innerHTML;
17908 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17912 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17914 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17920 picker : function()
17922 return this.pickerEl;
17925 fillMonths: function()
17928 var months = this.picker().select('>.datepicker-months td', true).first();
17930 months.dom.innerHTML = '';
17936 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17939 months.createChild(month);
17948 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17949 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17952 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17953 e.removeClass('active');
17955 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17956 e.addClass('active');
17963 if(this.isInline) return;
17965 this.picker().removeClass(['bottom', 'top']);
17967 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17969 * place to the top of element!
17973 this.picker().addClass('top');
17974 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17979 this.picker().addClass('bottom');
17981 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17984 onFocus : function()
17986 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17990 onBlur : function()
17992 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17994 var d = this.inputEl().getValue();
18003 this.picker().show();
18004 this.picker().select('>.datepicker-months', true).first().show();
18008 this.fireEvent('show', this, this.date);
18013 if(this.isInline) return;
18014 this.picker().hide();
18015 this.fireEvent('hide', this, this.date);
18019 onMousedown: function(e)
18021 e.stopPropagation();
18022 e.preventDefault();
18027 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18031 fireKey: function(e)
18033 if (!this.picker().isVisible()){
18034 if (e.keyCode == 27) // allow escape to hide and re-show picker
18044 e.preventDefault();
18048 dir = e.keyCode == 37 ? -1 : 1;
18050 this.vIndex = this.vIndex + dir;
18052 if(this.vIndex < 0){
18056 if(this.vIndex > 11){
18060 if(isNaN(this.vIndex)){
18064 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18070 dir = e.keyCode == 38 ? -1 : 1;
18072 this.vIndex = this.vIndex + dir * 4;
18074 if(this.vIndex < 0){
18078 if(this.vIndex > 11){
18082 if(isNaN(this.vIndex)){
18086 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18091 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18092 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18096 e.preventDefault();
18099 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18100 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18116 this.picker().remove();
18121 Roo.apply(Roo.bootstrap.MonthField, {
18140 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18141 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18146 Roo.apply(Roo.bootstrap.MonthField, {
18150 cls: 'datepicker dropdown-menu roo-dynamic',
18154 cls: 'datepicker-months',
18158 cls: 'table-condensed',
18160 Roo.bootstrap.DateField.content
18180 * @class Roo.bootstrap.CheckBox
18181 * @extends Roo.bootstrap.Input
18182 * Bootstrap CheckBox class
18184 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18185 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18186 * @cfg {String} boxLabel The text that appears beside the checkbox
18187 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18188 * @cfg {Boolean} checked initnal the element
18189 * @cfg {Boolean} inline inline the element (default false)
18190 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18193 * Create a new CheckBox
18194 * @param {Object} config The config object
18197 Roo.bootstrap.CheckBox = function(config){
18198 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18203 * Fires when the element is checked or unchecked.
18204 * @param {Roo.bootstrap.CheckBox} this This input
18205 * @param {Boolean} checked The new checked value
18212 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18214 inputType: 'checkbox',
18222 getAutoCreate : function()
18224 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18230 cfg.cls = 'form-group ' + this.inputType; //input-group
18233 cfg.cls += ' ' + this.inputType + '-inline';
18239 type : this.inputType,
18240 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18241 cls : 'roo-' + this.inputType, //'form-box',
18242 placeholder : this.placeholder || ''
18246 if (this.weight) { // Validity check?
18247 cfg.cls += " " + this.inputType + "-" + this.weight;
18250 if (this.disabled) {
18251 input.disabled=true;
18255 input.checked = this.checked;
18259 input.name = this.name;
18263 input.cls += ' input-' + this.size;
18268 ['xs','sm','md','lg'].map(function(size){
18269 if (settings[size]) {
18270 cfg.cls += ' col-' + size + '-' + settings[size];
18274 var inputblock = input;
18276 if (this.before || this.after) {
18279 cls : 'input-group',
18284 inputblock.cn.push({
18286 cls : 'input-group-addon',
18291 inputblock.cn.push(input);
18294 inputblock.cn.push({
18296 cls : 'input-group-addon',
18303 if (align ==='left' && this.fieldLabel.length) {
18304 Roo.log("left and has label");
18310 cls : 'control-label col-md-' + this.labelWidth,
18311 html : this.fieldLabel
18315 cls : "col-md-" + (12 - this.labelWidth),
18322 } else if ( this.fieldLabel.length) {
18327 tag: this.boxLabel ? 'span' : 'label',
18329 cls: 'control-label box-input-label',
18330 //cls : 'input-group-addon',
18331 html : this.fieldLabel
18341 Roo.log(" no label && no align");
18342 cfg.cn = [ inputblock ] ;
18347 var boxLabelCfg = {
18349 //'for': id, // box label is handled by onclick - so no for...
18351 html: this.boxLabel
18355 boxLabelCfg.tooltip = this.tooltip;
18358 cfg.cn.push(boxLabelCfg);
18368 * return the real input element.
18370 inputEl: function ()
18372 return this.el.select('input.roo-' + this.inputType,true).first();
18375 labelEl: function()
18377 return this.el.select('label.control-label',true).first();
18379 /* depricated... */
18383 return this.labelEl();
18386 initEvents : function()
18388 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18390 this.inputEl().on('click', this.onClick, this);
18392 if (this.boxLabel) {
18393 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18396 this.startValue = this.getValue();
18399 Roo.bootstrap.CheckBox.register(this);
18403 onClick : function()
18405 this.setChecked(!this.checked);
18408 setChecked : function(state,suppressEvent)
18410 this.startValue = this.getValue();
18412 if(this.inputType == 'radio'){
18414 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18415 e.dom.checked = false;
18418 this.inputEl().dom.checked = true;
18420 this.inputEl().dom.value = this.inputValue;
18422 if(suppressEvent !== true){
18423 this.fireEvent('check', this, true);
18431 this.checked = state;
18433 this.inputEl().dom.checked = state;
18435 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18437 if(suppressEvent !== true){
18438 this.fireEvent('check', this, state);
18444 getValue : function()
18446 if(this.inputType == 'radio'){
18447 return this.getGroupValue();
18450 return this.inputEl().getValue();
18454 getGroupValue : function()
18456 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18460 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18463 setValue : function(v,suppressEvent)
18465 if(this.inputType == 'radio'){
18466 this.setGroupValue(v, suppressEvent);
18470 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18475 setGroupValue : function(v, suppressEvent)
18477 this.startValue = this.getValue();
18479 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18480 e.dom.checked = false;
18482 if(e.dom.value == v){
18483 e.dom.checked = true;
18487 if(suppressEvent !== true){
18488 this.fireEvent('check', this, true);
18496 validate : function()
18500 (this.inputType == 'radio' && this.validateRadio()) ||
18501 (this.inputType == 'checkbox' && this.validateCheckbox())
18507 this.markInvalid();
18511 validateRadio : function()
18515 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18516 if(!e.dom.checked){
18528 validateCheckbox : function()
18531 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18534 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18542 for(var i in group){
18547 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18554 * Mark this field as valid
18556 markValid : function()
18558 if(this.allowBlank){
18564 this.fireEvent('valid', this);
18566 if(this.inputType == 'radio'){
18567 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18568 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18569 e.findParent('.form-group', false, true).addClass(_this.validClass);
18576 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18577 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18581 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18587 for(var i in group){
18588 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18589 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18594 * Mark this field as invalid
18595 * @param {String} msg The validation message
18597 markInvalid : function(msg)
18599 if(this.allowBlank){
18605 this.fireEvent('invalid', this, msg);
18607 if(this.inputType == 'radio'){
18608 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18609 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18610 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18617 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18618 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18622 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18628 for(var i in group){
18629 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18630 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18637 Roo.apply(Roo.bootstrap.CheckBox, {
18642 * register a CheckBox Group
18643 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18645 register : function(checkbox)
18647 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18648 this.groups[checkbox.groupId] = {};
18651 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18655 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18659 * fetch a CheckBox Group based on the group ID
18660 * @param {string} the group ID
18661 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18663 get: function(groupId) {
18664 if (typeof(this.groups[groupId]) == 'undefined') {
18668 return this.groups[groupId] ;
18680 *<div class="radio">
18682 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18683 Option one is this and that—be sure to include why it's great
18690 *<label class="radio-inline">fieldLabel</label>
18691 *<label class="radio-inline">
18692 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18700 * @class Roo.bootstrap.Radio
18701 * @extends Roo.bootstrap.CheckBox
18702 * Bootstrap Radio class
18705 * Create a new Radio
18706 * @param {Object} config The config object
18709 Roo.bootstrap.Radio = function(config){
18710 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18714 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18716 inputType: 'radio',
18720 getAutoCreate : function()
18722 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18723 align = align || 'left'; // default...
18730 tag : this.inline ? 'span' : 'div',
18735 var inline = this.inline ? ' radio-inline' : '';
18739 // does not need for, as we wrap the input with it..
18741 cls : 'control-label box-label' + inline,
18744 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18748 //cls : 'control-label' + inline,
18749 html : this.fieldLabel,
18750 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18759 type : this.inputType,
18760 //value : (!this.checked) ? this.valueOff : this.inputValue,
18761 value : this.inputValue,
18763 placeholder : this.placeholder || '' // ?? needed????
18766 if (this.weight) { // Validity check?
18767 input.cls += " radio-" + this.weight;
18769 if (this.disabled) {
18770 input.disabled=true;
18774 input.checked = this.checked;
18778 input.name = this.name;
18782 input.cls += ' input-' + this.size;
18785 //?? can span's inline have a width??
18788 ['xs','sm','md','lg'].map(function(size){
18789 if (settings[size]) {
18790 cfg.cls += ' col-' + size + '-' + settings[size];
18794 var inputblock = input;
18796 if (this.before || this.after) {
18799 cls : 'input-group',
18804 inputblock.cn.push({
18806 cls : 'input-group-addon',
18810 inputblock.cn.push(input);
18812 inputblock.cn.push({
18814 cls : 'input-group-addon',
18822 if (this.fieldLabel && this.fieldLabel.length) {
18823 cfg.cn.push(fieldLabel);
18826 // normal bootstrap puts the input inside the label.
18827 // however with our styled version - it has to go after the input.
18829 //lbl.cn.push(inputblock);
18833 cls: 'radio' + inline,
18840 cfg.cn.push( lblwrap);
18845 html: this.boxLabel
18854 initEvents : function()
18856 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18858 this.inputEl().on('click', this.onClick, this);
18859 if (this.boxLabel) {
18860 Roo.log('find label')
18861 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18866 inputEl: function ()
18868 return this.el.select('input.roo-radio',true).first();
18870 onClick : function()
18873 this.setChecked(true);
18876 setChecked : function(state,suppressEvent)
18879 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18880 v.dom.checked = false;
18883 Roo.log(this.inputEl().dom);
18884 this.checked = state;
18885 this.inputEl().dom.checked = state;
18887 if(suppressEvent !== true){
18888 this.fireEvent('check', this, state);
18891 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18895 getGroupValue : function()
18898 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18899 if(v.dom.checked == true){
18900 value = v.dom.value;
18908 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18909 * @return {Mixed} value The field value
18911 getValue : function(){
18912 return this.getGroupValue();
18918 //<script type="text/javascript">
18921 * Based Ext JS Library 1.1.1
18922 * Copyright(c) 2006-2007, Ext JS, LLC.
18928 * @class Roo.HtmlEditorCore
18929 * @extends Roo.Component
18930 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18932 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18935 Roo.HtmlEditorCore = function(config){
18938 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18943 * @event initialize
18944 * Fires when the editor is fully initialized (including the iframe)
18945 * @param {Roo.HtmlEditorCore} this
18950 * Fires when the editor is first receives the focus. Any insertion must wait
18951 * until after this event.
18952 * @param {Roo.HtmlEditorCore} this
18956 * @event beforesync
18957 * Fires before the textarea is updated with content from the editor iframe. Return false
18958 * to cancel the sync.
18959 * @param {Roo.HtmlEditorCore} this
18960 * @param {String} html
18964 * @event beforepush
18965 * Fires before the iframe editor is updated with content from the textarea. Return false
18966 * to cancel the push.
18967 * @param {Roo.HtmlEditorCore} this
18968 * @param {String} html
18973 * Fires when the textarea is updated with content from the editor iframe.
18974 * @param {Roo.HtmlEditorCore} this
18975 * @param {String} html
18980 * Fires when the iframe editor is updated with content from the textarea.
18981 * @param {Roo.HtmlEditorCore} this
18982 * @param {String} html
18987 * @event editorevent
18988 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18989 * @param {Roo.HtmlEditorCore} this
18995 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18997 // defaults : white / black...
18998 this.applyBlacklists();
19005 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19009 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19015 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19020 * @cfg {Number} height (in pixels)
19024 * @cfg {Number} width (in pixels)
19029 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19032 stylesheets: false,
19037 // private properties
19038 validationEvent : false,
19040 initialized : false,
19042 sourceEditMode : false,
19043 onFocus : Roo.emptyFn,
19045 hideMode:'offsets',
19049 // blacklist + whitelisted elements..
19056 * Protected method that will not generally be called directly. It
19057 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19058 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19060 getDocMarkup : function(){
19064 // inherit styels from page...??
19065 if (this.stylesheets === false) {
19067 Roo.get(document.head).select('style').each(function(node) {
19068 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19071 Roo.get(document.head).select('link').each(function(node) {
19072 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19075 } else if (!this.stylesheets.length) {
19077 st = '<style type="text/css">' +
19078 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19084 st += '<style type="text/css">' +
19085 'IMG { cursor: pointer } ' +
19089 return '<html><head>' + st +
19090 //<style type="text/css">' +
19091 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19093 ' </head><body class="roo-htmleditor-body"></body></html>';
19097 onRender : function(ct, position)
19100 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19101 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19104 this.el.dom.style.border = '0 none';
19105 this.el.dom.setAttribute('tabIndex', -1);
19106 this.el.addClass('x-hidden hide');
19110 if(Roo.isIE){ // fix IE 1px bogus margin
19111 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19115 this.frameId = Roo.id();
19119 var iframe = this.owner.wrap.createChild({
19121 cls: 'form-control', // bootstrap..
19123 name: this.frameId,
19124 frameBorder : 'no',
19125 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19130 this.iframe = iframe.dom;
19132 this.assignDocWin();
19134 this.doc.designMode = 'on';
19137 this.doc.write(this.getDocMarkup());
19141 var task = { // must defer to wait for browser to be ready
19143 //console.log("run task?" + this.doc.readyState);
19144 this.assignDocWin();
19145 if(this.doc.body || this.doc.readyState == 'complete'){
19147 this.doc.designMode="on";
19151 Roo.TaskMgr.stop(task);
19152 this.initEditor.defer(10, this);
19159 Roo.TaskMgr.start(task);
19164 onResize : function(w, h)
19166 Roo.log('resize: ' +w + ',' + h );
19167 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19171 if(typeof w == 'number'){
19173 this.iframe.style.width = w + 'px';
19175 if(typeof h == 'number'){
19177 this.iframe.style.height = h + 'px';
19179 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19186 * Toggles the editor between standard and source edit mode.
19187 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19189 toggleSourceEdit : function(sourceEditMode){
19191 this.sourceEditMode = sourceEditMode === true;
19193 if(this.sourceEditMode){
19195 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19198 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19199 //this.iframe.className = '';
19202 //this.setSize(this.owner.wrap.getSize());
19203 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19210 * Protected method that will not generally be called directly. If you need/want
19211 * custom HTML cleanup, this is the method you should override.
19212 * @param {String} html The HTML to be cleaned
19213 * return {String} The cleaned HTML
19215 cleanHtml : function(html){
19216 html = String(html);
19217 if(html.length > 5){
19218 if(Roo.isSafari){ // strip safari nonsense
19219 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19222 if(html == ' '){
19229 * HTML Editor -> Textarea
19230 * Protected method that will not generally be called directly. Syncs the contents
19231 * of the editor iframe with the textarea.
19233 syncValue : function(){
19234 if(this.initialized){
19235 var bd = (this.doc.body || this.doc.documentElement);
19236 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19237 var html = bd.innerHTML;
19239 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19240 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19242 html = '<div style="'+m[0]+'">' + html + '</div>';
19245 html = this.cleanHtml(html);
19246 // fix up the special chars.. normaly like back quotes in word...
19247 // however we do not want to do this with chinese..
19248 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19249 var cc = b.charCodeAt();
19251 (cc >= 0x4E00 && cc < 0xA000 ) ||
19252 (cc >= 0x3400 && cc < 0x4E00 ) ||
19253 (cc >= 0xf900 && cc < 0xfb00 )
19259 if(this.owner.fireEvent('beforesync', this, html) !== false){
19260 this.el.dom.value = html;
19261 this.owner.fireEvent('sync', this, html);
19267 * Protected method that will not generally be called directly. Pushes the value of the textarea
19268 * into the iframe editor.
19270 pushValue : function(){
19271 if(this.initialized){
19272 var v = this.el.dom.value.trim();
19274 // if(v.length < 1){
19278 if(this.owner.fireEvent('beforepush', this, v) !== false){
19279 var d = (this.doc.body || this.doc.documentElement);
19281 this.cleanUpPaste();
19282 this.el.dom.value = d.innerHTML;
19283 this.owner.fireEvent('push', this, v);
19289 deferFocus : function(){
19290 this.focus.defer(10, this);
19294 focus : function(){
19295 if(this.win && !this.sourceEditMode){
19302 assignDocWin: function()
19304 var iframe = this.iframe;
19307 this.doc = iframe.contentWindow.document;
19308 this.win = iframe.contentWindow;
19310 // if (!Roo.get(this.frameId)) {
19313 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19314 // this.win = Roo.get(this.frameId).dom.contentWindow;
19316 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19320 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19321 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19326 initEditor : function(){
19327 //console.log("INIT EDITOR");
19328 this.assignDocWin();
19332 this.doc.designMode="on";
19334 this.doc.write(this.getDocMarkup());
19337 var dbody = (this.doc.body || this.doc.documentElement);
19338 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19339 // this copies styles from the containing element into thsi one..
19340 // not sure why we need all of this..
19341 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19343 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19344 //ss['background-attachment'] = 'fixed'; // w3c
19345 dbody.bgProperties = 'fixed'; // ie
19346 //Roo.DomHelper.applyStyles(dbody, ss);
19347 Roo.EventManager.on(this.doc, {
19348 //'mousedown': this.onEditorEvent,
19349 'mouseup': this.onEditorEvent,
19350 'dblclick': this.onEditorEvent,
19351 'click': this.onEditorEvent,
19352 'keyup': this.onEditorEvent,
19357 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19359 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19360 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19362 this.initialized = true;
19364 this.owner.fireEvent('initialize', this);
19369 onDestroy : function(){
19375 //for (var i =0; i < this.toolbars.length;i++) {
19376 // // fixme - ask toolbars for heights?
19377 // this.toolbars[i].onDestroy();
19380 //this.wrap.dom.innerHTML = '';
19381 //this.wrap.remove();
19386 onFirstFocus : function(){
19388 this.assignDocWin();
19391 this.activated = true;
19394 if(Roo.isGecko){ // prevent silly gecko errors
19396 var s = this.win.getSelection();
19397 if(!s.focusNode || s.focusNode.nodeType != 3){
19398 var r = s.getRangeAt(0);
19399 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19404 this.execCmd('useCSS', true);
19405 this.execCmd('styleWithCSS', false);
19408 this.owner.fireEvent('activate', this);
19412 adjustFont: function(btn){
19413 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19414 //if(Roo.isSafari){ // safari
19417 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19418 if(Roo.isSafari){ // safari
19419 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19420 v = (v < 10) ? 10 : v;
19421 v = (v > 48) ? 48 : v;
19422 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19427 v = Math.max(1, v+adjust);
19429 this.execCmd('FontSize', v );
19432 onEditorEvent : function(e)
19434 this.owner.fireEvent('editorevent', this, e);
19435 // this.updateToolbar();
19436 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19439 insertTag : function(tg)
19441 // could be a bit smarter... -> wrap the current selected tRoo..
19442 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19444 range = this.createRange(this.getSelection());
19445 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19446 wrappingNode.appendChild(range.extractContents());
19447 range.insertNode(wrappingNode);
19454 this.execCmd("formatblock", tg);
19458 insertText : function(txt)
19462 var range = this.createRange();
19463 range.deleteContents();
19464 //alert(Sender.getAttribute('label'));
19466 range.insertNode(this.doc.createTextNode(txt));
19472 * Executes a Midas editor command on the editor document and performs necessary focus and
19473 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19474 * @param {String} cmd The Midas command
19475 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19477 relayCmd : function(cmd, value){
19479 this.execCmd(cmd, value);
19480 this.owner.fireEvent('editorevent', this);
19481 //this.updateToolbar();
19482 this.owner.deferFocus();
19486 * Executes a Midas editor command directly on the editor document.
19487 * For visual commands, you should use {@link #relayCmd} instead.
19488 * <b>This should only be called after the editor is initialized.</b>
19489 * @param {String} cmd The Midas command
19490 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19492 execCmd : function(cmd, value){
19493 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19500 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19502 * @param {String} text | dom node..
19504 insertAtCursor : function(text)
19509 if(!this.activated){
19515 var r = this.doc.selection.createRange();
19526 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19530 // from jquery ui (MIT licenced)
19532 var win = this.win;
19534 if (win.getSelection && win.getSelection().getRangeAt) {
19535 range = win.getSelection().getRangeAt(0);
19536 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19537 range.insertNode(node);
19538 } else if (win.document.selection && win.document.selection.createRange) {
19539 // no firefox support
19540 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19541 win.document.selection.createRange().pasteHTML(txt);
19543 // no firefox support
19544 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19545 this.execCmd('InsertHTML', txt);
19554 mozKeyPress : function(e){
19556 var c = e.getCharCode(), cmd;
19559 c = String.fromCharCode(c).toLowerCase();
19573 this.cleanUpPaste.defer(100, this);
19581 e.preventDefault();
19589 fixKeys : function(){ // load time branching for fastest keydown performance
19591 return function(e){
19592 var k = e.getKey(), r;
19595 r = this.doc.selection.createRange();
19598 r.pasteHTML('    ');
19605 r = this.doc.selection.createRange();
19607 var target = r.parentElement();
19608 if(!target || target.tagName.toLowerCase() != 'li'){
19610 r.pasteHTML('<br />');
19616 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19617 this.cleanUpPaste.defer(100, this);
19623 }else if(Roo.isOpera){
19624 return function(e){
19625 var k = e.getKey();
19629 this.execCmd('InsertHTML','    ');
19632 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19633 this.cleanUpPaste.defer(100, this);
19638 }else if(Roo.isSafari){
19639 return function(e){
19640 var k = e.getKey();
19644 this.execCmd('InsertText','\t');
19648 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19649 this.cleanUpPaste.defer(100, this);
19657 getAllAncestors: function()
19659 var p = this.getSelectedNode();
19662 a.push(p); // push blank onto stack..
19663 p = this.getParentElement();
19667 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19671 a.push(this.doc.body);
19675 lastSelNode : false,
19678 getSelection : function()
19680 this.assignDocWin();
19681 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19684 getSelectedNode: function()
19686 // this may only work on Gecko!!!
19688 // should we cache this!!!!
19693 var range = this.createRange(this.getSelection()).cloneRange();
19696 var parent = range.parentElement();
19698 var testRange = range.duplicate();
19699 testRange.moveToElementText(parent);
19700 if (testRange.inRange(range)) {
19703 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19706 parent = parent.parentElement;
19711 // is ancestor a text element.
19712 var ac = range.commonAncestorContainer;
19713 if (ac.nodeType == 3) {
19714 ac = ac.parentNode;
19717 var ar = ac.childNodes;
19720 var other_nodes = [];
19721 var has_other_nodes = false;
19722 for (var i=0;i<ar.length;i++) {
19723 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19726 // fullly contained node.
19728 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19733 // probably selected..
19734 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19735 other_nodes.push(ar[i]);
19739 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19744 has_other_nodes = true;
19746 if (!nodes.length && other_nodes.length) {
19747 nodes= other_nodes;
19749 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19755 createRange: function(sel)
19757 // this has strange effects when using with
19758 // top toolbar - not sure if it's a great idea.
19759 //this.editor.contentWindow.focus();
19760 if (typeof sel != "undefined") {
19762 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19764 return this.doc.createRange();
19767 return this.doc.createRange();
19770 getParentElement: function()
19773 this.assignDocWin();
19774 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19776 var range = this.createRange(sel);
19779 var p = range.commonAncestorContainer;
19780 while (p.nodeType == 3) { // text node
19791 * Range intersection.. the hard stuff...
19795 * [ -- selected range --- ]
19799 * if end is before start or hits it. fail.
19800 * if start is after end or hits it fail.
19802 * if either hits (but other is outside. - then it's not
19808 // @see http://www.thismuchiknow.co.uk/?p=64.
19809 rangeIntersectsNode : function(range, node)
19811 var nodeRange = node.ownerDocument.createRange();
19813 nodeRange.selectNode(node);
19815 nodeRange.selectNodeContents(node);
19818 var rangeStartRange = range.cloneRange();
19819 rangeStartRange.collapse(true);
19821 var rangeEndRange = range.cloneRange();
19822 rangeEndRange.collapse(false);
19824 var nodeStartRange = nodeRange.cloneRange();
19825 nodeStartRange.collapse(true);
19827 var nodeEndRange = nodeRange.cloneRange();
19828 nodeEndRange.collapse(false);
19830 return rangeStartRange.compareBoundaryPoints(
19831 Range.START_TO_START, nodeEndRange) == -1 &&
19832 rangeEndRange.compareBoundaryPoints(
19833 Range.START_TO_START, nodeStartRange) == 1;
19837 rangeCompareNode : function(range, node)
19839 var nodeRange = node.ownerDocument.createRange();
19841 nodeRange.selectNode(node);
19843 nodeRange.selectNodeContents(node);
19847 range.collapse(true);
19849 nodeRange.collapse(true);
19851 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19852 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19854 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19856 var nodeIsBefore = ss == 1;
19857 var nodeIsAfter = ee == -1;
19859 if (nodeIsBefore && nodeIsAfter)
19861 if (!nodeIsBefore && nodeIsAfter)
19862 return 1; //right trailed.
19864 if (nodeIsBefore && !nodeIsAfter)
19865 return 2; // left trailed.
19870 // private? - in a new class?
19871 cleanUpPaste : function()
19873 // cleans up the whole document..
19874 Roo.log('cleanuppaste');
19876 this.cleanUpChildren(this.doc.body);
19877 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19878 if (clean != this.doc.body.innerHTML) {
19879 this.doc.body.innerHTML = clean;
19884 cleanWordChars : function(input) {// change the chars to hex code
19885 var he = Roo.HtmlEditorCore;
19887 var output = input;
19888 Roo.each(he.swapCodes, function(sw) {
19889 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19891 output = output.replace(swapper, sw[1]);
19898 cleanUpChildren : function (n)
19900 if (!n.childNodes.length) {
19903 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19904 this.cleanUpChild(n.childNodes[i]);
19911 cleanUpChild : function (node)
19914 //console.log(node);
19915 if (node.nodeName == "#text") {
19916 // clean up silly Windows -- stuff?
19919 if (node.nodeName == "#comment") {
19920 node.parentNode.removeChild(node);
19921 // clean up silly Windows -- stuff?
19924 var lcname = node.tagName.toLowerCase();
19925 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19926 // whitelist of tags..
19928 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19930 node.parentNode.removeChild(node);
19935 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19937 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19938 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19940 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19941 // remove_keep_children = true;
19944 if (remove_keep_children) {
19945 this.cleanUpChildren(node);
19946 // inserts everything just before this node...
19947 while (node.childNodes.length) {
19948 var cn = node.childNodes[0];
19949 node.removeChild(cn);
19950 node.parentNode.insertBefore(cn, node);
19952 node.parentNode.removeChild(node);
19956 if (!node.attributes || !node.attributes.length) {
19957 this.cleanUpChildren(node);
19961 function cleanAttr(n,v)
19964 if (v.match(/^\./) || v.match(/^\//)) {
19967 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19970 if (v.match(/^#/)) {
19973 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19974 node.removeAttribute(n);
19978 var cwhite = this.cwhite;
19979 var cblack = this.cblack;
19981 function cleanStyle(n,v)
19983 if (v.match(/expression/)) { //XSS?? should we even bother..
19984 node.removeAttribute(n);
19988 var parts = v.split(/;/);
19991 Roo.each(parts, function(p) {
19992 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19996 var l = p.split(':').shift().replace(/\s+/g,'');
19997 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19999 if ( cwhite.length && cblack.indexOf(l) > -1) {
20000 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20001 //node.removeAttribute(n);
20005 // only allow 'c whitelisted system attributes'
20006 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20007 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20008 //node.removeAttribute(n);
20018 if (clean.length) {
20019 node.setAttribute(n, clean.join(';'));
20021 node.removeAttribute(n);
20027 for (var i = node.attributes.length-1; i > -1 ; i--) {
20028 var a = node.attributes[i];
20031 if (a.name.toLowerCase().substr(0,2)=='on') {
20032 node.removeAttribute(a.name);
20035 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20036 node.removeAttribute(a.name);
20039 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20040 cleanAttr(a.name,a.value); // fixme..
20043 if (a.name == 'style') {
20044 cleanStyle(a.name,a.value);
20047 /// clean up MS crap..
20048 // tecnically this should be a list of valid class'es..
20051 if (a.name == 'class') {
20052 if (a.value.match(/^Mso/)) {
20053 node.className = '';
20056 if (a.value.match(/body/)) {
20057 node.className = '';
20068 this.cleanUpChildren(node);
20074 * Clean up MS wordisms...
20076 cleanWord : function(node)
20081 this.cleanWord(this.doc.body);
20084 if (node.nodeName == "#text") {
20085 // clean up silly Windows -- stuff?
20088 if (node.nodeName == "#comment") {
20089 node.parentNode.removeChild(node);
20090 // clean up silly Windows -- stuff?
20094 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20095 node.parentNode.removeChild(node);
20099 // remove - but keep children..
20100 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20101 while (node.childNodes.length) {
20102 var cn = node.childNodes[0];
20103 node.removeChild(cn);
20104 node.parentNode.insertBefore(cn, node);
20106 node.parentNode.removeChild(node);
20107 this.iterateChildren(node, this.cleanWord);
20111 if (node.className.length) {
20113 var cn = node.className.split(/\W+/);
20115 Roo.each(cn, function(cls) {
20116 if (cls.match(/Mso[a-zA-Z]+/)) {
20121 node.className = cna.length ? cna.join(' ') : '';
20123 node.removeAttribute("class");
20127 if (node.hasAttribute("lang")) {
20128 node.removeAttribute("lang");
20131 if (node.hasAttribute("style")) {
20133 var styles = node.getAttribute("style").split(";");
20135 Roo.each(styles, function(s) {
20136 if (!s.match(/:/)) {
20139 var kv = s.split(":");
20140 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20143 // what ever is left... we allow.
20146 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20147 if (!nstyle.length) {
20148 node.removeAttribute('style');
20151 this.iterateChildren(node, this.cleanWord);
20157 * iterateChildren of a Node, calling fn each time, using this as the scole..
20158 * @param {DomNode} node node to iterate children of.
20159 * @param {Function} fn method of this class to call on each item.
20161 iterateChildren : function(node, fn)
20163 if (!node.childNodes.length) {
20166 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20167 fn.call(this, node.childNodes[i])
20173 * cleanTableWidths.
20175 * Quite often pasting from word etc.. results in tables with column and widths.
20176 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20179 cleanTableWidths : function(node)
20184 this.cleanTableWidths(this.doc.body);
20189 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20192 Roo.log(node.tagName);
20193 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20194 this.iterateChildren(node, this.cleanTableWidths);
20197 if (node.hasAttribute('width')) {
20198 node.removeAttribute('width');
20202 if (node.hasAttribute("style")) {
20205 var styles = node.getAttribute("style").split(";");
20207 Roo.each(styles, function(s) {
20208 if (!s.match(/:/)) {
20211 var kv = s.split(":");
20212 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20215 // what ever is left... we allow.
20218 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20219 if (!nstyle.length) {
20220 node.removeAttribute('style');
20224 this.iterateChildren(node, this.cleanTableWidths);
20232 domToHTML : function(currentElement, depth, nopadtext) {
20234 depth = depth || 0;
20235 nopadtext = nopadtext || false;
20237 if (!currentElement) {
20238 return this.domToHTML(this.doc.body);
20241 //Roo.log(currentElement);
20243 var allText = false;
20244 var nodeName = currentElement.nodeName;
20245 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20247 if (nodeName == '#text') {
20249 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20254 if (nodeName != 'BODY') {
20257 // Prints the node tagName, such as <A>, <IMG>, etc
20260 for(i = 0; i < currentElement.attributes.length;i++) {
20262 var aname = currentElement.attributes.item(i).name;
20263 if (!currentElement.attributes.item(i).value.length) {
20266 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20269 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20278 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20281 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20286 // Traverse the tree
20288 var currentElementChild = currentElement.childNodes.item(i);
20289 var allText = true;
20290 var innerHTML = '';
20292 while (currentElementChild) {
20293 // Formatting code (indent the tree so it looks nice on the screen)
20294 var nopad = nopadtext;
20295 if (lastnode == 'SPAN') {
20299 if (currentElementChild.nodeName == '#text') {
20300 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20301 toadd = nopadtext ? toadd : toadd.trim();
20302 if (!nopad && toadd.length > 80) {
20303 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20305 innerHTML += toadd;
20308 currentElementChild = currentElement.childNodes.item(i);
20314 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20316 // Recursively traverse the tree structure of the child node
20317 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20318 lastnode = currentElementChild.nodeName;
20320 currentElementChild=currentElement.childNodes.item(i);
20326 // The remaining code is mostly for formatting the tree
20327 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20332 ret+= "</"+tagName+">";
20338 applyBlacklists : function()
20340 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20341 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20345 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20346 if (b.indexOf(tag) > -1) {
20349 this.white.push(tag);
20353 Roo.each(w, function(tag) {
20354 if (b.indexOf(tag) > -1) {
20357 if (this.white.indexOf(tag) > -1) {
20360 this.white.push(tag);
20365 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20366 if (w.indexOf(tag) > -1) {
20369 this.black.push(tag);
20373 Roo.each(b, function(tag) {
20374 if (w.indexOf(tag) > -1) {
20377 if (this.black.indexOf(tag) > -1) {
20380 this.black.push(tag);
20385 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20386 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20390 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20391 if (b.indexOf(tag) > -1) {
20394 this.cwhite.push(tag);
20398 Roo.each(w, function(tag) {
20399 if (b.indexOf(tag) > -1) {
20402 if (this.cwhite.indexOf(tag) > -1) {
20405 this.cwhite.push(tag);
20410 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20411 if (w.indexOf(tag) > -1) {
20414 this.cblack.push(tag);
20418 Roo.each(b, function(tag) {
20419 if (w.indexOf(tag) > -1) {
20422 if (this.cblack.indexOf(tag) > -1) {
20425 this.cblack.push(tag);
20430 setStylesheets : function(stylesheets)
20432 if(typeof(stylesheets) == 'string'){
20433 Roo.get(this.iframe.contentDocument.head).createChild({
20435 rel : 'stylesheet',
20444 Roo.each(stylesheets, function(s) {
20449 Roo.get(_this.iframe.contentDocument.head).createChild({
20451 rel : 'stylesheet',
20460 removeStylesheets : function()
20464 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20469 // hide stuff that is not compatible
20483 * @event specialkey
20487 * @cfg {String} fieldClass @hide
20490 * @cfg {String} focusClass @hide
20493 * @cfg {String} autoCreate @hide
20496 * @cfg {String} inputType @hide
20499 * @cfg {String} invalidClass @hide
20502 * @cfg {String} invalidText @hide
20505 * @cfg {String} msgFx @hide
20508 * @cfg {String} validateOnBlur @hide
20512 Roo.HtmlEditorCore.white = [
20513 'area', 'br', 'img', 'input', 'hr', 'wbr',
20515 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20516 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20517 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20518 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20519 'table', 'ul', 'xmp',
20521 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20524 'dir', 'menu', 'ol', 'ul', 'dl',
20530 Roo.HtmlEditorCore.black = [
20531 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20533 'base', 'basefont', 'bgsound', 'blink', 'body',
20534 'frame', 'frameset', 'head', 'html', 'ilayer',
20535 'iframe', 'layer', 'link', 'meta', 'object',
20536 'script', 'style' ,'title', 'xml' // clean later..
20538 Roo.HtmlEditorCore.clean = [
20539 'script', 'style', 'title', 'xml'
20541 Roo.HtmlEditorCore.remove = [
20546 Roo.HtmlEditorCore.ablack = [
20550 Roo.HtmlEditorCore.aclean = [
20551 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20555 Roo.HtmlEditorCore.pwhite= [
20556 'http', 'https', 'mailto'
20559 // white listed style attributes.
20560 Roo.HtmlEditorCore.cwhite= [
20561 // 'text-align', /// default is to allow most things..
20567 // black listed style attributes.
20568 Roo.HtmlEditorCore.cblack= [
20569 // 'font-size' -- this can be set by the project
20573 Roo.HtmlEditorCore.swapCodes =[
20592 * @class Roo.bootstrap.HtmlEditor
20593 * @extends Roo.bootstrap.TextArea
20594 * Bootstrap HtmlEditor class
20597 * Create a new HtmlEditor
20598 * @param {Object} config The config object
20601 Roo.bootstrap.HtmlEditor = function(config){
20602 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20603 if (!this.toolbars) {
20604 this.toolbars = [];
20606 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20609 * @event initialize
20610 * Fires when the editor is fully initialized (including the iframe)
20611 * @param {HtmlEditor} this
20616 * Fires when the editor is first receives the focus. Any insertion must wait
20617 * until after this event.
20618 * @param {HtmlEditor} this
20622 * @event beforesync
20623 * Fires before the textarea is updated with content from the editor iframe. Return false
20624 * to cancel the sync.
20625 * @param {HtmlEditor} this
20626 * @param {String} html
20630 * @event beforepush
20631 * Fires before the iframe editor is updated with content from the textarea. Return false
20632 * to cancel the push.
20633 * @param {HtmlEditor} this
20634 * @param {String} html
20639 * Fires when the textarea is updated with content from the editor iframe.
20640 * @param {HtmlEditor} this
20641 * @param {String} html
20646 * Fires when the iframe editor is updated with content from the textarea.
20647 * @param {HtmlEditor} this
20648 * @param {String} html
20652 * @event editmodechange
20653 * Fires when the editor switches edit modes
20654 * @param {HtmlEditor} this
20655 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20657 editmodechange: true,
20659 * @event editorevent
20660 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20661 * @param {HtmlEditor} this
20665 * @event firstfocus
20666 * Fires when on first focus - needed by toolbars..
20667 * @param {HtmlEditor} this
20672 * Auto save the htmlEditor value as a file into Events
20673 * @param {HtmlEditor} this
20677 * @event savedpreview
20678 * preview the saved version of htmlEditor
20679 * @param {HtmlEditor} this
20686 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20690 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20695 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20700 * @cfg {Number} height (in pixels)
20704 * @cfg {Number} width (in pixels)
20709 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20712 stylesheets: false,
20717 // private properties
20718 validationEvent : false,
20720 initialized : false,
20723 onFocus : Roo.emptyFn,
20725 hideMode:'offsets',
20728 tbContainer : false,
20730 toolbarContainer :function() {
20731 return this.wrap.select('.x-html-editor-tb',true).first();
20735 * Protected method that will not generally be called directly. It
20736 * is called when the editor creates its toolbar. Override this method if you need to
20737 * add custom toolbar buttons.
20738 * @param {HtmlEditor} editor
20740 createToolbar : function(){
20742 Roo.log("create toolbars");
20744 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20745 this.toolbars[0].render(this.toolbarContainer());
20749 // if (!editor.toolbars || !editor.toolbars.length) {
20750 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20753 // for (var i =0 ; i < editor.toolbars.length;i++) {
20754 // editor.toolbars[i] = Roo.factory(
20755 // typeof(editor.toolbars[i]) == 'string' ?
20756 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20757 // Roo.bootstrap.HtmlEditor);
20758 // editor.toolbars[i].init(editor);
20764 onRender : function(ct, position)
20766 // Roo.log("Call onRender: " + this.xtype);
20768 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20770 this.wrap = this.inputEl().wrap({
20771 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20774 this.editorcore.onRender(ct, position);
20776 if (this.resizable) {
20777 this.resizeEl = new Roo.Resizable(this.wrap, {
20781 minHeight : this.height,
20782 height: this.height,
20783 handles : this.resizable,
20786 resize : function(r, w, h) {
20787 _t.onResize(w,h); // -something
20793 this.createToolbar(this);
20796 if(!this.width && this.resizable){
20797 this.setSize(this.wrap.getSize());
20799 if (this.resizeEl) {
20800 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20801 // should trigger onReize..
20807 onResize : function(w, h)
20809 Roo.log('resize: ' +w + ',' + h );
20810 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20814 if(this.inputEl() ){
20815 if(typeof w == 'number'){
20816 var aw = w - this.wrap.getFrameWidth('lr');
20817 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20820 if(typeof h == 'number'){
20821 var tbh = -11; // fixme it needs to tool bar size!
20822 for (var i =0; i < this.toolbars.length;i++) {
20823 // fixme - ask toolbars for heights?
20824 tbh += this.toolbars[i].el.getHeight();
20825 //if (this.toolbars[i].footer) {
20826 // tbh += this.toolbars[i].footer.el.getHeight();
20834 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20835 ah -= 5; // knock a few pixes off for look..
20836 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20840 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20841 this.editorcore.onResize(ew,eh);
20846 * Toggles the editor between standard and source edit mode.
20847 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20849 toggleSourceEdit : function(sourceEditMode)
20851 this.editorcore.toggleSourceEdit(sourceEditMode);
20853 if(this.editorcore.sourceEditMode){
20854 Roo.log('editor - showing textarea');
20857 // Roo.log(this.syncValue());
20859 this.inputEl().removeClass(['hide', 'x-hidden']);
20860 this.inputEl().dom.removeAttribute('tabIndex');
20861 this.inputEl().focus();
20863 Roo.log('editor - hiding textarea');
20865 // Roo.log(this.pushValue());
20868 this.inputEl().addClass(['hide', 'x-hidden']);
20869 this.inputEl().dom.setAttribute('tabIndex', -1);
20870 //this.deferFocus();
20873 if(this.resizable){
20874 this.setSize(this.wrap.getSize());
20877 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20880 // private (for BoxComponent)
20881 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20883 // private (for BoxComponent)
20884 getResizeEl : function(){
20888 // private (for BoxComponent)
20889 getPositionEl : function(){
20894 initEvents : function(){
20895 this.originalValue = this.getValue();
20899 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20902 // markInvalid : Roo.emptyFn,
20904 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20907 // clearInvalid : Roo.emptyFn,
20909 setValue : function(v){
20910 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20911 this.editorcore.pushValue();
20916 deferFocus : function(){
20917 this.focus.defer(10, this);
20921 focus : function(){
20922 this.editorcore.focus();
20928 onDestroy : function(){
20934 for (var i =0; i < this.toolbars.length;i++) {
20935 // fixme - ask toolbars for heights?
20936 this.toolbars[i].onDestroy();
20939 this.wrap.dom.innerHTML = '';
20940 this.wrap.remove();
20945 onFirstFocus : function(){
20946 //Roo.log("onFirstFocus");
20947 this.editorcore.onFirstFocus();
20948 for (var i =0; i < this.toolbars.length;i++) {
20949 this.toolbars[i].onFirstFocus();
20955 syncValue : function()
20957 this.editorcore.syncValue();
20960 pushValue : function()
20962 this.editorcore.pushValue();
20966 // hide stuff that is not compatible
20980 * @event specialkey
20984 * @cfg {String} fieldClass @hide
20987 * @cfg {String} focusClass @hide
20990 * @cfg {String} autoCreate @hide
20993 * @cfg {String} inputType @hide
20996 * @cfg {String} invalidClass @hide
20999 * @cfg {String} invalidText @hide
21002 * @cfg {String} msgFx @hide
21005 * @cfg {String} validateOnBlur @hide
21014 Roo.namespace('Roo.bootstrap.htmleditor');
21016 * @class Roo.bootstrap.HtmlEditorToolbar1
21021 new Roo.bootstrap.HtmlEditor({
21024 new Roo.bootstrap.HtmlEditorToolbar1({
21025 disable : { fonts: 1 , format: 1, ..., ... , ...],
21031 * @cfg {Object} disable List of elements to disable..
21032 * @cfg {Array} btns List of additional buttons.
21036 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21039 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21042 Roo.apply(this, config);
21044 // default disabled, based on 'good practice'..
21045 this.disable = this.disable || {};
21046 Roo.applyIf(this.disable, {
21049 specialElements : true
21051 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21053 this.editor = config.editor;
21054 this.editorcore = config.editor.editorcore;
21056 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21058 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21059 // dont call parent... till later.
21061 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21066 editorcore : false,
21071 "h1","h2","h3","h4","h5","h6",
21073 "abbr", "acronym", "address", "cite", "samp", "var",
21077 onRender : function(ct, position)
21079 // Roo.log("Call onRender: " + this.xtype);
21081 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21083 this.el.dom.style.marginBottom = '0';
21085 var editorcore = this.editorcore;
21086 var editor= this.editor;
21089 var btn = function(id,cmd , toggle, handler){
21091 var event = toggle ? 'toggle' : 'click';
21096 xns: Roo.bootstrap,
21099 enableToggle:toggle !== false,
21101 pressed : toggle ? false : null,
21104 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21105 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21114 xns: Roo.bootstrap,
21115 glyphicon : 'font',
21119 xns: Roo.bootstrap,
21123 Roo.each(this.formats, function(f) {
21124 style.menu.items.push({
21126 xns: Roo.bootstrap,
21127 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21132 editorcore.insertTag(this.tagname);
21139 children.push(style);
21142 btn('bold',false,true);
21143 btn('italic',false,true);
21144 btn('align-left', 'justifyleft',true);
21145 btn('align-center', 'justifycenter',true);
21146 btn('align-right' , 'justifyright',true);
21147 btn('link', false, false, function(btn) {
21148 //Roo.log("create link?");
21149 var url = prompt(this.createLinkText, this.defaultLinkValue);
21150 if(url && url != 'http:/'+'/'){
21151 this.editorcore.relayCmd('createlink', url);
21154 btn('list','insertunorderedlist',true);
21155 btn('pencil', false,true, function(btn){
21158 this.toggleSourceEdit(btn.pressed);
21164 xns: Roo.bootstrap,
21169 xns: Roo.bootstrap,
21174 cog.menu.items.push({
21176 xns: Roo.bootstrap,
21177 html : Clean styles,
21182 editorcore.insertTag(this.tagname);
21191 this.xtype = 'NavSimplebar';
21193 for(var i=0;i< children.length;i++) {
21195 this.buttons.add(this.addxtypeChild(children[i]));
21199 editor.on('editorevent', this.updateToolbar, this);
21201 onBtnClick : function(id)
21203 this.editorcore.relayCmd(id);
21204 this.editorcore.focus();
21208 * Protected method that will not generally be called directly. It triggers
21209 * a toolbar update by reading the markup state of the current selection in the editor.
21211 updateToolbar: function(){
21213 if(!this.editorcore.activated){
21214 this.editor.onFirstFocus(); // is this neeed?
21218 var btns = this.buttons;
21219 var doc = this.editorcore.doc;
21220 btns.get('bold').setActive(doc.queryCommandState('bold'));
21221 btns.get('italic').setActive(doc.queryCommandState('italic'));
21222 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21224 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21225 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21226 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21228 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21229 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21232 var ans = this.editorcore.getAllAncestors();
21233 if (this.formatCombo) {
21236 var store = this.formatCombo.store;
21237 this.formatCombo.setValue("");
21238 for (var i =0; i < ans.length;i++) {
21239 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21241 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21249 // hides menus... - so this cant be on a menu...
21250 Roo.bootstrap.MenuMgr.hideAll();
21252 Roo.bootstrap.MenuMgr.hideAll();
21253 //this.editorsyncValue();
21255 onFirstFocus: function() {
21256 this.buttons.each(function(item){
21260 toggleSourceEdit : function(sourceEditMode){
21263 if(sourceEditMode){
21264 Roo.log("disabling buttons");
21265 this.buttons.each( function(item){
21266 if(item.cmd != 'pencil'){
21272 Roo.log("enabling buttons");
21273 if(this.editorcore.initialized){
21274 this.buttons.each( function(item){
21280 Roo.log("calling toggole on editor");
21281 // tell the editor that it's been pressed..
21282 this.editor.toggleSourceEdit(sourceEditMode);
21292 * @class Roo.bootstrap.Table.AbstractSelectionModel
21293 * @extends Roo.util.Observable
21294 * Abstract base class for grid SelectionModels. It provides the interface that should be
21295 * implemented by descendant classes. This class should not be directly instantiated.
21298 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21299 this.locked = false;
21300 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21304 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21305 /** @ignore Called by the grid automatically. Do not call directly. */
21306 init : function(grid){
21312 * Locks the selections.
21315 this.locked = true;
21319 * Unlocks the selections.
21321 unlock : function(){
21322 this.locked = false;
21326 * Returns true if the selections are locked.
21327 * @return {Boolean}
21329 isLocked : function(){
21330 return this.locked;
21334 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21335 * @class Roo.bootstrap.Table.RowSelectionModel
21336 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21337 * It supports multiple selections and keyboard selection/navigation.
21339 * @param {Object} config
21342 Roo.bootstrap.Table.RowSelectionModel = function(config){
21343 Roo.apply(this, config);
21344 this.selections = new Roo.util.MixedCollection(false, function(o){
21349 this.lastActive = false;
21353 * @event selectionchange
21354 * Fires when the selection changes
21355 * @param {SelectionModel} this
21357 "selectionchange" : true,
21359 * @event afterselectionchange
21360 * Fires after the selection changes (eg. by key press or clicking)
21361 * @param {SelectionModel} this
21363 "afterselectionchange" : true,
21365 * @event beforerowselect
21366 * Fires when a row is selected being selected, return false to cancel.
21367 * @param {SelectionModel} this
21368 * @param {Number} rowIndex The selected index
21369 * @param {Boolean} keepExisting False if other selections will be cleared
21371 "beforerowselect" : true,
21374 * Fires when a row is selected.
21375 * @param {SelectionModel} this
21376 * @param {Number} rowIndex The selected index
21377 * @param {Roo.data.Record} r The record
21379 "rowselect" : true,
21381 * @event rowdeselect
21382 * Fires when a row is deselected.
21383 * @param {SelectionModel} this
21384 * @param {Number} rowIndex The selected index
21386 "rowdeselect" : true
21388 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21389 this.locked = false;
21392 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21394 * @cfg {Boolean} singleSelect
21395 * True to allow selection of only one row at a time (defaults to false)
21397 singleSelect : false,
21400 initEvents : function(){
21402 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21403 this.grid.on("mousedown", this.handleMouseDown, this);
21404 }else{ // allow click to work like normal
21405 this.grid.on("rowclick", this.handleDragableRowClick, this);
21408 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21409 "up" : function(e){
21411 this.selectPrevious(e.shiftKey);
21412 }else if(this.last !== false && this.lastActive !== false){
21413 var last = this.last;
21414 this.selectRange(this.last, this.lastActive-1);
21415 this.grid.getView().focusRow(this.lastActive);
21416 if(last !== false){
21420 this.selectFirstRow();
21422 this.fireEvent("afterselectionchange", this);
21424 "down" : function(e){
21426 this.selectNext(e.shiftKey);
21427 }else if(this.last !== false && this.lastActive !== false){
21428 var last = this.last;
21429 this.selectRange(this.last, this.lastActive+1);
21430 this.grid.getView().focusRow(this.lastActive);
21431 if(last !== false){
21435 this.selectFirstRow();
21437 this.fireEvent("afterselectionchange", this);
21442 var view = this.grid.view;
21443 view.on("refresh", this.onRefresh, this);
21444 view.on("rowupdated", this.onRowUpdated, this);
21445 view.on("rowremoved", this.onRemove, this);
21449 onRefresh : function(){
21450 var ds = this.grid.dataSource, i, v = this.grid.view;
21451 var s = this.selections;
21452 s.each(function(r){
21453 if((i = ds.indexOfId(r.id)) != -1){
21462 onRemove : function(v, index, r){
21463 this.selections.remove(r);
21467 onRowUpdated : function(v, index, r){
21468 if(this.isSelected(r)){
21469 v.onRowSelect(index);
21475 * @param {Array} records The records to select
21476 * @param {Boolean} keepExisting (optional) True to keep existing selections
21478 selectRecords : function(records, keepExisting){
21480 this.clearSelections();
21482 var ds = this.grid.dataSource;
21483 for(var i = 0, len = records.length; i < len; i++){
21484 this.selectRow(ds.indexOf(records[i]), true);
21489 * Gets the number of selected rows.
21492 getCount : function(){
21493 return this.selections.length;
21497 * Selects the first row in the grid.
21499 selectFirstRow : function(){
21504 * Select the last row.
21505 * @param {Boolean} keepExisting (optional) True to keep existing selections
21507 selectLastRow : function(keepExisting){
21508 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21512 * Selects the row immediately following the last selected row.
21513 * @param {Boolean} keepExisting (optional) True to keep existing selections
21515 selectNext : function(keepExisting){
21516 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21517 this.selectRow(this.last+1, keepExisting);
21518 this.grid.getView().focusRow(this.last);
21523 * Selects the row that precedes the last selected row.
21524 * @param {Boolean} keepExisting (optional) True to keep existing selections
21526 selectPrevious : function(keepExisting){
21528 this.selectRow(this.last-1, keepExisting);
21529 this.grid.getView().focusRow(this.last);
21534 * Returns the selected records
21535 * @return {Array} Array of selected records
21537 getSelections : function(){
21538 return [].concat(this.selections.items);
21542 * Returns the first selected record.
21545 getSelected : function(){
21546 return this.selections.itemAt(0);
21551 * Clears all selections.
21553 clearSelections : function(fast){
21554 if(this.locked) return;
21556 var ds = this.grid.dataSource;
21557 var s = this.selections;
21558 s.each(function(r){
21559 this.deselectRow(ds.indexOfId(r.id));
21563 this.selections.clear();
21570 * Selects all rows.
21572 selectAll : function(){
21573 if(this.locked) return;
21574 this.selections.clear();
21575 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21576 this.selectRow(i, true);
21581 * Returns True if there is a selection.
21582 * @return {Boolean}
21584 hasSelection : function(){
21585 return this.selections.length > 0;
21589 * Returns True if the specified row is selected.
21590 * @param {Number/Record} record The record or index of the record to check
21591 * @return {Boolean}
21593 isSelected : function(index){
21594 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21595 return (r && this.selections.key(r.id) ? true : false);
21599 * Returns True if the specified record id is selected.
21600 * @param {String} id The id of record to check
21601 * @return {Boolean}
21603 isIdSelected : function(id){
21604 return (this.selections.key(id) ? true : false);
21608 handleMouseDown : function(e, t){
21609 var view = this.grid.getView(), rowIndex;
21610 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21613 if(e.shiftKey && this.last !== false){
21614 var last = this.last;
21615 this.selectRange(last, rowIndex, e.ctrlKey);
21616 this.last = last; // reset the last
21617 view.focusRow(rowIndex);
21619 var isSelected = this.isSelected(rowIndex);
21620 if(e.button !== 0 && isSelected){
21621 view.focusRow(rowIndex);
21622 }else if(e.ctrlKey && isSelected){
21623 this.deselectRow(rowIndex);
21624 }else if(!isSelected){
21625 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21626 view.focusRow(rowIndex);
21629 this.fireEvent("afterselectionchange", this);
21632 handleDragableRowClick : function(grid, rowIndex, e)
21634 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21635 this.selectRow(rowIndex, false);
21636 grid.view.focusRow(rowIndex);
21637 this.fireEvent("afterselectionchange", this);
21642 * Selects multiple rows.
21643 * @param {Array} rows Array of the indexes of the row to select
21644 * @param {Boolean} keepExisting (optional) True to keep existing selections
21646 selectRows : function(rows, keepExisting){
21648 this.clearSelections();
21650 for(var i = 0, len = rows.length; i < len; i++){
21651 this.selectRow(rows[i], true);
21656 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21657 * @param {Number} startRow The index of the first row in the range
21658 * @param {Number} endRow The index of the last row in the range
21659 * @param {Boolean} keepExisting (optional) True to retain existing selections
21661 selectRange : function(startRow, endRow, keepExisting){
21662 if(this.locked) return;
21664 this.clearSelections();
21666 if(startRow <= endRow){
21667 for(var i = startRow; i <= endRow; i++){
21668 this.selectRow(i, true);
21671 for(var i = startRow; i >= endRow; i--){
21672 this.selectRow(i, true);
21678 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21679 * @param {Number} startRow The index of the first row in the range
21680 * @param {Number} endRow The index of the last row in the range
21682 deselectRange : function(startRow, endRow, preventViewNotify){
21683 if(this.locked) return;
21684 for(var i = startRow; i <= endRow; i++){
21685 this.deselectRow(i, preventViewNotify);
21691 * @param {Number} row The index of the row to select
21692 * @param {Boolean} keepExisting (optional) True to keep existing selections
21694 selectRow : function(index, keepExisting, preventViewNotify){
21695 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21696 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21697 if(!keepExisting || this.singleSelect){
21698 this.clearSelections();
21700 var r = this.grid.dataSource.getAt(index);
21701 this.selections.add(r);
21702 this.last = this.lastActive = index;
21703 if(!preventViewNotify){
21704 this.grid.getView().onRowSelect(index);
21706 this.fireEvent("rowselect", this, index, r);
21707 this.fireEvent("selectionchange", this);
21713 * @param {Number} row The index of the row to deselect
21715 deselectRow : function(index, preventViewNotify){
21716 if(this.locked) return;
21717 if(this.last == index){
21720 if(this.lastActive == index){
21721 this.lastActive = false;
21723 var r = this.grid.dataSource.getAt(index);
21724 this.selections.remove(r);
21725 if(!preventViewNotify){
21726 this.grid.getView().onRowDeselect(index);
21728 this.fireEvent("rowdeselect", this, index);
21729 this.fireEvent("selectionchange", this);
21733 restoreLast : function(){
21735 this.last = this._last;
21740 acceptsNav : function(row, col, cm){
21741 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21745 onEditorKey : function(field, e){
21746 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21751 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21753 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21755 }else if(k == e.ENTER && !e.ctrlKey){
21759 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21761 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21763 }else if(k == e.ESC){
21767 g.startEditing(newCell[0], newCell[1]);
21772 * Ext JS Library 1.1.1
21773 * Copyright(c) 2006-2007, Ext JS, LLC.
21775 * Originally Released Under LGPL - original licence link has changed is not relivant.
21778 * <script type="text/javascript">
21782 * @class Roo.bootstrap.PagingToolbar
21783 * @extends Roo.bootstrap.NavSimplebar
21784 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21786 * Create a new PagingToolbar
21787 * @param {Object} config The config object
21788 * @param {Roo.data.Store} store
21790 Roo.bootstrap.PagingToolbar = function(config)
21792 // old args format still supported... - xtype is prefered..
21793 // created from xtype...
21795 this.ds = config.dataSource;
21797 if (config.store && !this.ds) {
21798 this.store= Roo.factory(config.store, Roo.data);
21799 this.ds = this.store;
21800 this.ds.xmodule = this.xmodule || false;
21803 this.toolbarItems = [];
21804 if (config.items) {
21805 this.toolbarItems = config.items;
21808 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21813 this.bind(this.ds);
21816 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21820 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21822 * @cfg {Roo.data.Store} dataSource
21823 * The underlying data store providing the paged data
21826 * @cfg {String/HTMLElement/Element} container
21827 * container The id or element that will contain the toolbar
21830 * @cfg {Boolean} displayInfo
21831 * True to display the displayMsg (defaults to false)
21834 * @cfg {Number} pageSize
21835 * The number of records to display per page (defaults to 20)
21839 * @cfg {String} displayMsg
21840 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21842 displayMsg : 'Displaying {0} - {1} of {2}',
21844 * @cfg {String} emptyMsg
21845 * The message to display when no records are found (defaults to "No data to display")
21847 emptyMsg : 'No data to display',
21849 * Customizable piece of the default paging text (defaults to "Page")
21852 beforePageText : "Page",
21854 * Customizable piece of the default paging text (defaults to "of %0")
21857 afterPageText : "of {0}",
21859 * Customizable piece of the default paging text (defaults to "First Page")
21862 firstText : "First Page",
21864 * Customizable piece of the default paging text (defaults to "Previous Page")
21867 prevText : "Previous Page",
21869 * Customizable piece of the default paging text (defaults to "Next Page")
21872 nextText : "Next Page",
21874 * Customizable piece of the default paging text (defaults to "Last Page")
21877 lastText : "Last Page",
21879 * Customizable piece of the default paging text (defaults to "Refresh")
21882 refreshText : "Refresh",
21886 onRender : function(ct, position)
21888 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21889 this.navgroup.parentId = this.id;
21890 this.navgroup.onRender(this.el, null);
21891 // add the buttons to the navgroup
21893 if(this.displayInfo){
21894 Roo.log(this.el.select('ul.navbar-nav',true).first());
21895 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21896 this.displayEl = this.el.select('.x-paging-info', true).first();
21897 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21898 // this.displayEl = navel.el.select('span',true).first();
21904 Roo.each(_this.buttons, function(e){ // this might need to use render????
21905 Roo.factory(e).onRender(_this.el, null);
21909 Roo.each(_this.toolbarItems, function(e) {
21910 _this.navgroup.addItem(e);
21914 this.first = this.navgroup.addItem({
21915 tooltip: this.firstText,
21917 icon : 'fa fa-backward',
21919 preventDefault: true,
21920 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21923 this.prev = this.navgroup.addItem({
21924 tooltip: this.prevText,
21926 icon : 'fa fa-step-backward',
21928 preventDefault: true,
21929 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21931 //this.addSeparator();
21934 var field = this.navgroup.addItem( {
21936 cls : 'x-paging-position',
21938 html : this.beforePageText +
21939 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21940 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21943 this.field = field.el.select('input', true).first();
21944 this.field.on("keydown", this.onPagingKeydown, this);
21945 this.field.on("focus", function(){this.dom.select();});
21948 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21949 //this.field.setHeight(18);
21950 //this.addSeparator();
21951 this.next = this.navgroup.addItem({
21952 tooltip: this.nextText,
21954 html : ' <i class="fa fa-step-forward">',
21956 preventDefault: true,
21957 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21959 this.last = this.navgroup.addItem({
21960 tooltip: this.lastText,
21961 icon : 'fa fa-forward',
21964 preventDefault: true,
21965 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21967 //this.addSeparator();
21968 this.loading = this.navgroup.addItem({
21969 tooltip: this.refreshText,
21970 icon: 'fa fa-refresh',
21971 preventDefault: true,
21972 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21978 updateInfo : function(){
21979 if(this.displayEl){
21980 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21981 var msg = count == 0 ?
21985 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21987 this.displayEl.update(msg);
21992 onLoad : function(ds, r, o){
21993 this.cursor = o.params ? o.params.start : 0;
21994 var d = this.getPageData(),
21998 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21999 this.field.dom.value = ap;
22000 this.first.setDisabled(ap == 1);
22001 this.prev.setDisabled(ap == 1);
22002 this.next.setDisabled(ap == ps);
22003 this.last.setDisabled(ap == ps);
22004 this.loading.enable();
22009 getPageData : function(){
22010 var total = this.ds.getTotalCount();
22013 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22014 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22019 onLoadError : function(){
22020 this.loading.enable();
22024 onPagingKeydown : function(e){
22025 var k = e.getKey();
22026 var d = this.getPageData();
22028 var v = this.field.dom.value, pageNum;
22029 if(!v || isNaN(pageNum = parseInt(v, 10))){
22030 this.field.dom.value = d.activePage;
22033 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22034 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22037 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))
22039 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22040 this.field.dom.value = pageNum;
22041 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22044 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22046 var v = this.field.dom.value, pageNum;
22047 var increment = (e.shiftKey) ? 10 : 1;
22048 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22050 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22051 this.field.dom.value = d.activePage;
22054 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22056 this.field.dom.value = parseInt(v, 10) + increment;
22057 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22058 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22065 beforeLoad : function(){
22067 this.loading.disable();
22072 onClick : function(which){
22081 ds.load({params:{start: 0, limit: this.pageSize}});
22084 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22087 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22090 var total = ds.getTotalCount();
22091 var extra = total % this.pageSize;
22092 var lastStart = extra ? (total - extra) : total-this.pageSize;
22093 ds.load({params:{start: lastStart, limit: this.pageSize}});
22096 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22102 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22103 * @param {Roo.data.Store} store The data store to unbind
22105 unbind : function(ds){
22106 ds.un("beforeload", this.beforeLoad, this);
22107 ds.un("load", this.onLoad, this);
22108 ds.un("loadexception", this.onLoadError, this);
22109 ds.un("remove", this.updateInfo, this);
22110 ds.un("add", this.updateInfo, this);
22111 this.ds = undefined;
22115 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22116 * @param {Roo.data.Store} store The data store to bind
22118 bind : function(ds){
22119 ds.on("beforeload", this.beforeLoad, this);
22120 ds.on("load", this.onLoad, this);
22121 ds.on("loadexception", this.onLoadError, this);
22122 ds.on("remove", this.updateInfo, this);
22123 ds.on("add", this.updateInfo, this);
22134 * @class Roo.bootstrap.MessageBar
22135 * @extends Roo.bootstrap.Component
22136 * Bootstrap MessageBar class
22137 * @cfg {String} html contents of the MessageBar
22138 * @cfg {String} weight (info | success | warning | danger) default info
22139 * @cfg {String} beforeClass insert the bar before the given class
22140 * @cfg {Boolean} closable (true | false) default false
22141 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22144 * Create a new Element
22145 * @param {Object} config The config object
22148 Roo.bootstrap.MessageBar = function(config){
22149 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22152 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22158 beforeClass: 'bootstrap-sticky-wrap',
22160 getAutoCreate : function(){
22164 cls: 'alert alert-dismissable alert-' + this.weight,
22169 html: this.html || ''
22175 cfg.cls += ' alert-messages-fixed';
22189 onRender : function(ct, position)
22191 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22194 var cfg = Roo.apply({}, this.getAutoCreate());
22198 cfg.cls += ' ' + this.cls;
22201 cfg.style = this.style;
22203 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22205 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22208 this.el.select('>button.close').on('click', this.hide, this);
22214 if (!this.rendered) {
22220 this.fireEvent('show', this);
22226 if (!this.rendered) {
22232 this.fireEvent('hide', this);
22235 update : function()
22237 // var e = this.el.dom.firstChild;
22239 // if(this.closable){
22240 // e = e.nextSibling;
22243 // e.data = this.html || '';
22245 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22261 * @class Roo.bootstrap.Graph
22262 * @extends Roo.bootstrap.Component
22263 * Bootstrap Graph class
22267 @cfg {String} graphtype bar | vbar | pie
22268 @cfg {number} g_x coodinator | centre x (pie)
22269 @cfg {number} g_y coodinator | centre y (pie)
22270 @cfg {number} g_r radius (pie)
22271 @cfg {number} g_height height of the chart (respected by all elements in the set)
22272 @cfg {number} g_width width of the chart (respected by all elements in the set)
22273 @cfg {Object} title The title of the chart
22276 -opts (object) options for the chart
22278 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22279 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22281 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.
22282 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22284 o stretch (boolean)
22286 -opts (object) options for the pie
22289 o startAngle (number)
22290 o endAngle (number)
22294 * Create a new Input
22295 * @param {Object} config The config object
22298 Roo.bootstrap.Graph = function(config){
22299 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22305 * The img click event for the img.
22306 * @param {Roo.EventObject} e
22312 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22323 //g_colors: this.colors,
22330 getAutoCreate : function(){
22341 onRender : function(ct,position){
22342 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22343 this.raphael = Raphael(this.el.dom);
22345 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22346 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22347 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22348 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22350 r.text(160, 10, "Single Series Chart").attr(txtattr);
22351 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22352 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22353 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22355 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22356 r.barchart(330, 10, 300, 220, data1);
22357 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22358 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22361 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22362 // r.barchart(30, 30, 560, 250, xdata, {
22363 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22364 // axis : "0 0 1 1",
22365 // axisxlabels : xdata
22366 // //yvalues : cols,
22369 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22371 // this.load(null,xdata,{
22372 // axis : "0 0 1 1",
22373 // axisxlabels : xdata
22378 load : function(graphtype,xdata,opts){
22379 this.raphael.clear();
22381 graphtype = this.graphtype;
22386 var r = this.raphael,
22387 fin = function () {
22388 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22390 fout = function () {
22391 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22393 pfin = function() {
22394 this.sector.stop();
22395 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22398 this.label[0].stop();
22399 this.label[0].attr({ r: 7.5 });
22400 this.label[1].attr({ "font-weight": 800 });
22403 pfout = function() {
22404 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22407 this.label[0].animate({ r: 5 }, 500, "bounce");
22408 this.label[1].attr({ "font-weight": 400 });
22414 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22417 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22420 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22421 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22423 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22430 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22435 setTitle: function(o)
22440 initEvents: function() {
22443 this.el.on('click', this.onClick, this);
22447 onClick : function(e)
22449 Roo.log('img onclick');
22450 this.fireEvent('click', this, e);
22462 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22465 * @class Roo.bootstrap.dash.NumberBox
22466 * @extends Roo.bootstrap.Component
22467 * Bootstrap NumberBox class
22468 * @cfg {String} headline Box headline
22469 * @cfg {String} content Box content
22470 * @cfg {String} icon Box icon
22471 * @cfg {String} footer Footer text
22472 * @cfg {String} fhref Footer href
22475 * Create a new NumberBox
22476 * @param {Object} config The config object
22480 Roo.bootstrap.dash.NumberBox = function(config){
22481 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22485 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22494 getAutoCreate : function(){
22498 cls : 'small-box ',
22506 cls : 'roo-headline',
22507 html : this.headline
22511 cls : 'roo-content',
22512 html : this.content
22526 cls : 'ion ' + this.icon
22535 cls : 'small-box-footer',
22536 href : this.fhref || '#',
22540 cfg.cn.push(footer);
22547 onRender : function(ct,position){
22548 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22555 setHeadline: function (value)
22557 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22560 setFooter: function (value, href)
22562 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22565 this.el.select('a.small-box-footer',true).first().attr('href', href);
22570 setContent: function (value)
22572 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22575 initEvents: function()
22589 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22592 * @class Roo.bootstrap.dash.TabBox
22593 * @extends Roo.bootstrap.Component
22594 * Bootstrap TabBox class
22595 * @cfg {String} title Title of the TabBox
22596 * @cfg {String} icon Icon of the TabBox
22597 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22598 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22601 * Create a new TabBox
22602 * @param {Object} config The config object
22606 Roo.bootstrap.dash.TabBox = function(config){
22607 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22612 * When a pane is added
22613 * @param {Roo.bootstrap.dash.TabPane} pane
22617 * @event activatepane
22618 * When a pane is activated
22619 * @param {Roo.bootstrap.dash.TabPane} pane
22621 "activatepane" : true
22629 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22634 tabScrollable : false,
22636 getChildContainer : function()
22638 return this.el.select('.tab-content', true).first();
22641 getAutoCreate : function(){
22645 cls: 'pull-left header',
22653 cls: 'fa ' + this.icon
22659 cls: 'nav nav-tabs pull-right',
22665 if(this.tabScrollable){
22672 cls: 'nav nav-tabs pull-right',
22683 cls: 'nav-tabs-custom',
22688 cls: 'tab-content no-padding',
22696 initEvents : function()
22698 //Roo.log('add add pane handler');
22699 this.on('addpane', this.onAddPane, this);
22702 * Updates the box title
22703 * @param {String} html to set the title to.
22705 setTitle : function(value)
22707 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22709 onAddPane : function(pane)
22711 this.panes.push(pane);
22712 //Roo.log('addpane');
22714 // tabs are rendere left to right..
22715 if(!this.showtabs){
22719 var ctr = this.el.select('.nav-tabs', true).first();
22722 var existing = ctr.select('.nav-tab',true);
22723 var qty = existing.getCount();;
22726 var tab = ctr.createChild({
22728 cls : 'nav-tab' + (qty ? '' : ' active'),
22736 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22739 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22741 pane.el.addClass('active');
22746 onTabClick : function(ev,un,ob,pane)
22748 //Roo.log('tab - prev default');
22749 ev.preventDefault();
22752 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22753 pane.tab.addClass('active');
22754 //Roo.log(pane.title);
22755 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22756 // technically we should have a deactivate event.. but maybe add later.
22757 // and it should not de-activate the selected tab...
22758 this.fireEvent('activatepane', pane);
22759 pane.el.addClass('active');
22760 pane.fireEvent('activate');
22765 getActivePane : function()
22768 Roo.each(this.panes, function(p) {
22769 if(p.el.hasClass('active')){
22790 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22792 * @class Roo.bootstrap.TabPane
22793 * @extends Roo.bootstrap.Component
22794 * Bootstrap TabPane class
22795 * @cfg {Boolean} active (false | true) Default false
22796 * @cfg {String} title title of panel
22800 * Create a new TabPane
22801 * @param {Object} config The config object
22804 Roo.bootstrap.dash.TabPane = function(config){
22805 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22811 * When a pane is activated
22812 * @param {Roo.bootstrap.dash.TabPane} pane
22819 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22824 // the tabBox that this is attached to.
22827 getAutoCreate : function()
22835 cfg.cls += ' active';
22840 initEvents : function()
22842 //Roo.log('trigger add pane handler');
22843 this.parent().fireEvent('addpane', this)
22847 * Updates the tab title
22848 * @param {String} html to set the title to.
22850 setTitle: function(str)
22856 this.tab.select('a', true).first().dom.innerHTML = str;
22873 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22876 * @class Roo.bootstrap.menu.Menu
22877 * @extends Roo.bootstrap.Component
22878 * Bootstrap Menu class - container for Menu
22879 * @cfg {String} html Text of the menu
22880 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22881 * @cfg {String} icon Font awesome icon
22882 * @cfg {String} pos Menu align to (top | bottom) default bottom
22886 * Create a new Menu
22887 * @param {Object} config The config object
22891 Roo.bootstrap.menu.Menu = function(config){
22892 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22896 * @event beforeshow
22897 * Fires before this menu is displayed
22898 * @param {Roo.bootstrap.menu.Menu} this
22902 * @event beforehide
22903 * Fires before this menu is hidden
22904 * @param {Roo.bootstrap.menu.Menu} this
22909 * Fires after this menu is displayed
22910 * @param {Roo.bootstrap.menu.Menu} this
22915 * Fires after this menu is hidden
22916 * @param {Roo.bootstrap.menu.Menu} this
22921 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22922 * @param {Roo.bootstrap.menu.Menu} this
22923 * @param {Roo.EventObject} e
22930 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22934 weight : 'default',
22939 getChildContainer : function() {
22940 if(this.isSubMenu){
22944 return this.el.select('ul.dropdown-menu', true).first();
22947 getAutoCreate : function()
22952 cls : 'roo-menu-text',
22960 cls : 'fa ' + this.icon
22971 cls : 'dropdown-button btn btn-' + this.weight,
22976 cls : 'dropdown-toggle btn btn-' + this.weight,
22986 cls : 'dropdown-menu'
22992 if(this.pos == 'top'){
22993 cfg.cls += ' dropup';
22996 if(this.isSubMenu){
22999 cls : 'dropdown-menu'
23006 onRender : function(ct, position)
23008 this.isSubMenu = ct.hasClass('dropdown-submenu');
23010 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23013 initEvents : function()
23015 if(this.isSubMenu){
23019 this.hidden = true;
23021 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23022 this.triggerEl.on('click', this.onTriggerPress, this);
23024 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23025 this.buttonEl.on('click', this.onClick, this);
23031 if(this.isSubMenu){
23035 return this.el.select('ul.dropdown-menu', true).first();
23038 onClick : function(e)
23040 this.fireEvent("click", this, e);
23043 onTriggerPress : function(e)
23045 if (this.isVisible()) {
23052 isVisible : function(){
23053 return !this.hidden;
23058 this.fireEvent("beforeshow", this);
23060 this.hidden = false;
23061 this.el.addClass('open');
23063 Roo.get(document).on("mouseup", this.onMouseUp, this);
23065 this.fireEvent("show", this);
23072 this.fireEvent("beforehide", this);
23074 this.hidden = true;
23075 this.el.removeClass('open');
23077 Roo.get(document).un("mouseup", this.onMouseUp);
23079 this.fireEvent("hide", this);
23082 onMouseUp : function()
23096 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23099 * @class Roo.bootstrap.menu.Item
23100 * @extends Roo.bootstrap.Component
23101 * Bootstrap MenuItem class
23102 * @cfg {Boolean} submenu (true | false) default false
23103 * @cfg {String} html text of the item
23104 * @cfg {String} href the link
23105 * @cfg {Boolean} disable (true | false) default false
23106 * @cfg {Boolean} preventDefault (true | false) default true
23107 * @cfg {String} icon Font awesome icon
23108 * @cfg {String} pos Submenu align to (left | right) default right
23112 * Create a new Item
23113 * @param {Object} config The config object
23117 Roo.bootstrap.menu.Item = function(config){
23118 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23122 * Fires when the mouse is hovering over this menu
23123 * @param {Roo.bootstrap.menu.Item} this
23124 * @param {Roo.EventObject} e
23129 * Fires when the mouse exits this menu
23130 * @param {Roo.bootstrap.menu.Item} this
23131 * @param {Roo.EventObject} e
23137 * The raw click event for the entire grid.
23138 * @param {Roo.EventObject} e
23144 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23149 preventDefault: true,
23154 getAutoCreate : function()
23159 cls : 'roo-menu-item-text',
23167 cls : 'fa ' + this.icon
23176 href : this.href || '#',
23183 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23187 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23189 if(this.pos == 'left'){
23190 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23197 initEvents : function()
23199 this.el.on('mouseover', this.onMouseOver, this);
23200 this.el.on('mouseout', this.onMouseOut, this);
23202 this.el.select('a', true).first().on('click', this.onClick, this);
23206 onClick : function(e)
23208 if(this.preventDefault){
23209 e.preventDefault();
23212 this.fireEvent("click", this, e);
23215 onMouseOver : function(e)
23217 if(this.submenu && this.pos == 'left'){
23218 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23221 this.fireEvent("mouseover", this, e);
23224 onMouseOut : function(e)
23226 this.fireEvent("mouseout", this, e);
23238 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23241 * @class Roo.bootstrap.menu.Separator
23242 * @extends Roo.bootstrap.Component
23243 * Bootstrap Separator class
23246 * Create a new Separator
23247 * @param {Object} config The config object
23251 Roo.bootstrap.menu.Separator = function(config){
23252 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23255 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23257 getAutoCreate : function(){
23278 * @class Roo.bootstrap.Tooltip
23279 * Bootstrap Tooltip class
23280 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23281 * to determine which dom element triggers the tooltip.
23283 * It needs to add support for additional attributes like tooltip-position
23286 * Create a new Toolti
23287 * @param {Object} config The config object
23290 Roo.bootstrap.Tooltip = function(config){
23291 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23294 Roo.apply(Roo.bootstrap.Tooltip, {
23296 * @function init initialize tooltip monitoring.
23300 currentTip : false,
23301 currentRegion : false,
23307 Roo.get(document).on('mouseover', this.enter ,this);
23308 Roo.get(document).on('mouseout', this.leave, this);
23311 this.currentTip = new Roo.bootstrap.Tooltip();
23314 enter : function(ev)
23316 var dom = ev.getTarget();
23318 //Roo.log(['enter',dom]);
23319 var el = Roo.fly(dom);
23320 if (this.currentEl) {
23322 //Roo.log(this.currentEl);
23323 //Roo.log(this.currentEl.contains(dom));
23324 if (this.currentEl == el) {
23327 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23333 if (this.currentTip.el) {
23334 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23339 // you can not look for children, as if el is the body.. then everythign is the child..
23340 if (!el.attr('tooltip')) { //
23341 if (!el.select("[tooltip]").elements.length) {
23344 // is the mouse over this child...?
23345 bindEl = el.select("[tooltip]").first();
23346 var xy = ev.getXY();
23347 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23348 //Roo.log("not in region.");
23351 //Roo.log("child element over..");
23354 this.currentEl = bindEl;
23355 this.currentTip.bind(bindEl);
23356 this.currentRegion = Roo.lib.Region.getRegion(dom);
23357 this.currentTip.enter();
23360 leave : function(ev)
23362 var dom = ev.getTarget();
23363 //Roo.log(['leave',dom]);
23364 if (!this.currentEl) {
23369 if (dom != this.currentEl.dom) {
23372 var xy = ev.getXY();
23373 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23376 // only activate leave if mouse cursor is outside... bounding box..
23381 if (this.currentTip) {
23382 this.currentTip.leave();
23384 //Roo.log('clear currentEl');
23385 this.currentEl = false;
23390 'left' : ['r-l', [-2,0], 'right'],
23391 'right' : ['l-r', [2,0], 'left'],
23392 'bottom' : ['t-b', [0,2], 'top'],
23393 'top' : [ 'b-t', [0,-2], 'bottom']
23399 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23404 delay : null, // can be { show : 300 , hide: 500}
23408 hoverState : null, //???
23410 placement : 'bottom',
23412 getAutoCreate : function(){
23419 cls : 'tooltip-arrow'
23422 cls : 'tooltip-inner'
23429 bind : function(el)
23435 enter : function () {
23437 if (this.timeout != null) {
23438 clearTimeout(this.timeout);
23441 this.hoverState = 'in';
23442 //Roo.log("enter - show");
23443 if (!this.delay || !this.delay.show) {
23448 this.timeout = setTimeout(function () {
23449 if (_t.hoverState == 'in') {
23452 }, this.delay.show);
23456 clearTimeout(this.timeout);
23458 this.hoverState = 'out';
23459 if (!this.delay || !this.delay.hide) {
23465 this.timeout = setTimeout(function () {
23466 //Roo.log("leave - timeout");
23468 if (_t.hoverState == 'out') {
23470 Roo.bootstrap.Tooltip.currentEl = false;
23478 this.render(document.body);
23481 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23483 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23485 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23487 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23489 var placement = typeof this.placement == 'function' ?
23490 this.placement.call(this, this.el, on_el) :
23493 var autoToken = /\s?auto?\s?/i;
23494 var autoPlace = autoToken.test(placement);
23496 placement = placement.replace(autoToken, '') || 'top';
23500 //this.el.setXY([0,0]);
23502 //this.el.dom.style.display='block';
23504 //this.el.appendTo(on_el);
23506 var p = this.getPosition();
23507 var box = this.el.getBox();
23513 var align = Roo.bootstrap.Tooltip.alignment[placement];
23515 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23517 if(placement == 'top' || placement == 'bottom'){
23519 placement = 'right';
23522 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23523 placement = 'left';
23527 align = Roo.bootstrap.Tooltip.alignment[placement];
23529 this.el.alignTo(this.bindEl, align[0],align[1]);
23530 //var arrow = this.el.select('.arrow',true).first();
23531 //arrow.set(align[2],
23533 this.el.addClass(placement);
23535 this.el.addClass('in fade');
23537 this.hoverState = null;
23539 if (this.el.hasClass('fade')) {
23550 //this.el.setXY([0,0]);
23551 this.el.removeClass('in');
23567 * @class Roo.bootstrap.LocationPicker
23568 * @extends Roo.bootstrap.Component
23569 * Bootstrap LocationPicker class
23570 * @cfg {Number} latitude Position when init default 0
23571 * @cfg {Number} longitude Position when init default 0
23572 * @cfg {Number} zoom default 15
23573 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23574 * @cfg {Boolean} mapTypeControl default false
23575 * @cfg {Boolean} disableDoubleClickZoom default false
23576 * @cfg {Boolean} scrollwheel default true
23577 * @cfg {Boolean} streetViewControl default false
23578 * @cfg {Number} radius default 0
23579 * @cfg {String} locationName
23580 * @cfg {Boolean} draggable default true
23581 * @cfg {Boolean} enableAutocomplete default false
23582 * @cfg {Boolean} enableReverseGeocode default true
23583 * @cfg {String} markerTitle
23586 * Create a new LocationPicker
23587 * @param {Object} config The config object
23591 Roo.bootstrap.LocationPicker = function(config){
23593 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23598 * Fires when the picker initialized.
23599 * @param {Roo.bootstrap.LocationPicker} this
23600 * @param {Google Location} location
23604 * @event positionchanged
23605 * Fires when the picker position changed.
23606 * @param {Roo.bootstrap.LocationPicker} this
23607 * @param {Google Location} location
23609 positionchanged : true,
23612 * Fires when the map resize.
23613 * @param {Roo.bootstrap.LocationPicker} this
23618 * Fires when the map show.
23619 * @param {Roo.bootstrap.LocationPicker} this
23624 * Fires when the map hide.
23625 * @param {Roo.bootstrap.LocationPicker} this
23630 * Fires when click the map.
23631 * @param {Roo.bootstrap.LocationPicker} this
23632 * @param {Map event} e
23636 * @event mapRightClick
23637 * Fires when right click the map.
23638 * @param {Roo.bootstrap.LocationPicker} this
23639 * @param {Map event} e
23641 mapRightClick : true,
23643 * @event markerClick
23644 * Fires when click the marker.
23645 * @param {Roo.bootstrap.LocationPicker} this
23646 * @param {Map event} e
23648 markerClick : true,
23650 * @event markerRightClick
23651 * Fires when right click the marker.
23652 * @param {Roo.bootstrap.LocationPicker} this
23653 * @param {Map event} e
23655 markerRightClick : true,
23657 * @event OverlayViewDraw
23658 * Fires when OverlayView Draw
23659 * @param {Roo.bootstrap.LocationPicker} this
23661 OverlayViewDraw : true,
23663 * @event OverlayViewOnAdd
23664 * Fires when OverlayView Draw
23665 * @param {Roo.bootstrap.LocationPicker} this
23667 OverlayViewOnAdd : true,
23669 * @event OverlayViewOnRemove
23670 * Fires when OverlayView Draw
23671 * @param {Roo.bootstrap.LocationPicker} this
23673 OverlayViewOnRemove : true,
23675 * @event OverlayViewShow
23676 * Fires when OverlayView Draw
23677 * @param {Roo.bootstrap.LocationPicker} this
23678 * @param {Pixel} cpx
23680 OverlayViewShow : true,
23682 * @event OverlayViewHide
23683 * Fires when OverlayView Draw
23684 * @param {Roo.bootstrap.LocationPicker} this
23686 OverlayViewHide : true
23691 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23693 gMapContext: false,
23699 mapTypeControl: false,
23700 disableDoubleClickZoom: false,
23702 streetViewControl: false,
23706 enableAutocomplete: false,
23707 enableReverseGeocode: true,
23710 getAutoCreate: function()
23715 cls: 'roo-location-picker'
23721 initEvents: function(ct, position)
23723 if(!this.el.getWidth() || this.isApplied()){
23727 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23732 initial: function()
23734 if(!this.mapTypeId){
23735 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23738 this.gMapContext = this.GMapContext();
23740 this.initOverlayView();
23742 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23746 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23747 _this.setPosition(_this.gMapContext.marker.position);
23750 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23751 _this.fireEvent('mapClick', this, event);
23755 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23756 _this.fireEvent('mapRightClick', this, event);
23760 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23761 _this.fireEvent('markerClick', this, event);
23765 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23766 _this.fireEvent('markerRightClick', this, event);
23770 this.setPosition(this.gMapContext.location);
23772 this.fireEvent('initial', this, this.gMapContext.location);
23775 initOverlayView: function()
23779 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23783 _this.fireEvent('OverlayViewDraw', _this);
23788 _this.fireEvent('OverlayViewOnAdd', _this);
23791 onRemove: function()
23793 _this.fireEvent('OverlayViewOnRemove', _this);
23796 show: function(cpx)
23798 _this.fireEvent('OverlayViewShow', _this, cpx);
23803 _this.fireEvent('OverlayViewHide', _this);
23809 fromLatLngToContainerPixel: function(event)
23811 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23814 isApplied: function()
23816 return this.getGmapContext() == false ? false : true;
23819 getGmapContext: function()
23821 return this.gMapContext
23824 GMapContext: function()
23826 var position = new google.maps.LatLng(this.latitude, this.longitude);
23828 var _map = new google.maps.Map(this.el.dom, {
23831 mapTypeId: this.mapTypeId,
23832 mapTypeControl: this.mapTypeControl,
23833 disableDoubleClickZoom: this.disableDoubleClickZoom,
23834 scrollwheel: this.scrollwheel,
23835 streetViewControl: this.streetViewControl,
23836 locationName: this.locationName,
23837 draggable: this.draggable,
23838 enableAutocomplete: this.enableAutocomplete,
23839 enableReverseGeocode: this.enableReverseGeocode
23842 var _marker = new google.maps.Marker({
23843 position: position,
23845 title: this.markerTitle,
23846 draggable: this.draggable
23853 location: position,
23854 radius: this.radius,
23855 locationName: this.locationName,
23856 addressComponents: {
23857 formatted_address: null,
23858 addressLine1: null,
23859 addressLine2: null,
23861 streetNumber: null,
23865 stateOrProvince: null
23868 domContainer: this.el.dom,
23869 geodecoder: new google.maps.Geocoder()
23873 drawCircle: function(center, radius, options)
23875 if (this.gMapContext.circle != null) {
23876 this.gMapContext.circle.setMap(null);
23880 options = Roo.apply({}, options, {
23881 strokeColor: "#0000FF",
23882 strokeOpacity: .35,
23884 fillColor: "#0000FF",
23888 options.map = this.gMapContext.map;
23889 options.radius = radius;
23890 options.center = center;
23891 this.gMapContext.circle = new google.maps.Circle(options);
23892 return this.gMapContext.circle;
23898 setPosition: function(location)
23900 this.gMapContext.location = location;
23901 this.gMapContext.marker.setPosition(location);
23902 this.gMapContext.map.panTo(location);
23903 this.drawCircle(location, this.gMapContext.radius, {});
23907 if (this.gMapContext.settings.enableReverseGeocode) {
23908 this.gMapContext.geodecoder.geocode({
23909 latLng: this.gMapContext.location
23910 }, function(results, status) {
23912 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23913 _this.gMapContext.locationName = results[0].formatted_address;
23914 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23916 _this.fireEvent('positionchanged', this, location);
23923 this.fireEvent('positionchanged', this, location);
23928 google.maps.event.trigger(this.gMapContext.map, "resize");
23930 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23932 this.fireEvent('resize', this);
23935 setPositionByLatLng: function(latitude, longitude)
23937 this.setPosition(new google.maps.LatLng(latitude, longitude));
23940 getCurrentPosition: function()
23943 latitude: this.gMapContext.location.lat(),
23944 longitude: this.gMapContext.location.lng()
23948 getAddressName: function()
23950 return this.gMapContext.locationName;
23953 getAddressComponents: function()
23955 return this.gMapContext.addressComponents;
23958 address_component_from_google_geocode: function(address_components)
23962 for (var i = 0; i < address_components.length; i++) {
23963 var component = address_components[i];
23964 if (component.types.indexOf("postal_code") >= 0) {
23965 result.postalCode = component.short_name;
23966 } else if (component.types.indexOf("street_number") >= 0) {
23967 result.streetNumber = component.short_name;
23968 } else if (component.types.indexOf("route") >= 0) {
23969 result.streetName = component.short_name;
23970 } else if (component.types.indexOf("neighborhood") >= 0) {
23971 result.city = component.short_name;
23972 } else if (component.types.indexOf("locality") >= 0) {
23973 result.city = component.short_name;
23974 } else if (component.types.indexOf("sublocality") >= 0) {
23975 result.district = component.short_name;
23976 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23977 result.stateOrProvince = component.short_name;
23978 } else if (component.types.indexOf("country") >= 0) {
23979 result.country = component.short_name;
23983 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23984 result.addressLine2 = "";
23988 setZoomLevel: function(zoom)
23990 this.gMapContext.map.setZoom(zoom);
24003 this.fireEvent('show', this);
24014 this.fireEvent('hide', this);
24019 Roo.apply(Roo.bootstrap.LocationPicker, {
24021 OverlayView : function(map, options)
24023 options = options || {};
24037 * @class Roo.bootstrap.Alert
24038 * @extends Roo.bootstrap.Component
24039 * Bootstrap Alert class
24040 * @cfg {String} title The title of alert
24041 * @cfg {String} html The content of alert
24042 * @cfg {String} weight ( success | info | warning | danger )
24043 * @cfg {String} faicon font-awesomeicon
24046 * Create a new alert
24047 * @param {Object} config The config object
24051 Roo.bootstrap.Alert = function(config){
24052 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24056 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24063 getAutoCreate : function()
24072 cls : 'roo-alert-icon'
24077 cls : 'roo-alert-title',
24082 cls : 'roo-alert-text',
24089 cfg.cn[0].cls += ' fa ' + this.faicon;
24093 cfg.cls += ' alert-' + this.weight;
24099 initEvents: function()
24101 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24104 setTitle : function(str)
24106 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24109 setText : function(str)
24111 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24114 setWeight : function(weight)
24117 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24120 this.weight = weight;
24122 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24125 setIcon : function(icon)
24128 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24131 this.faicon = icon;
24133 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24154 * @class Roo.bootstrap.UploadCropbox
24155 * @extends Roo.bootstrap.Component
24156 * Bootstrap UploadCropbox class
24157 * @cfg {String} emptyText show when image has been loaded
24158 * @cfg {String} rotateNotify show when image too small to rotate
24159 * @cfg {Number} errorTimeout default 3000
24160 * @cfg {Number} minWidth default 300
24161 * @cfg {Number} minHeight default 300
24162 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24163 * @cfg {Boolean} isDocument (true|false) default false
24164 * @cfg {String} url action url
24165 * @cfg {String} paramName default 'imageUpload'
24166 * @cfg {String} method default POST
24169 * Create a new UploadCropbox
24170 * @param {Object} config The config object
24173 Roo.bootstrap.UploadCropbox = function(config){
24174 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24178 * @event beforeselectfile
24179 * Fire before select file
24180 * @param {Roo.bootstrap.UploadCropbox} this
24182 "beforeselectfile" : true,
24185 * Fire after initEvent
24186 * @param {Roo.bootstrap.UploadCropbox} this
24191 * Fire after initEvent
24192 * @param {Roo.bootstrap.UploadCropbox} this
24193 * @param {String} data
24198 * Fire when preparing the file data
24199 * @param {Roo.bootstrap.UploadCropbox} this
24200 * @param {Object} file
24205 * Fire when get exception
24206 * @param {Roo.bootstrap.UploadCropbox} this
24207 * @param {XMLHttpRequest} xhr
24209 "exception" : true,
24211 * @event beforeloadcanvas
24212 * Fire before load the canvas
24213 * @param {Roo.bootstrap.UploadCropbox} this
24214 * @param {String} src
24216 "beforeloadcanvas" : true,
24219 * Fire when trash image
24220 * @param {Roo.bootstrap.UploadCropbox} this
24225 * Fire when download the image
24226 * @param {Roo.bootstrap.UploadCropbox} this
24230 * @event footerbuttonclick
24231 * Fire when footerbuttonclick
24232 * @param {Roo.bootstrap.UploadCropbox} this
24233 * @param {String} type
24235 "footerbuttonclick" : true,
24239 * @param {Roo.bootstrap.UploadCropbox} this
24244 * Fire when rotate the image
24245 * @param {Roo.bootstrap.UploadCropbox} this
24246 * @param {String} pos
24251 * Fire when inspect the file
24252 * @param {Roo.bootstrap.UploadCropbox} this
24253 * @param {Object} file
24258 * Fire when xhr upload the file
24259 * @param {Roo.bootstrap.UploadCropbox} this
24260 * @param {Object} data
24265 * Fire when arrange the file data
24266 * @param {Roo.bootstrap.UploadCropbox} this
24267 * @param {Object} formData
24272 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24275 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24277 emptyText : 'Click to upload image',
24278 rotateNotify : 'Image is too small to rotate',
24279 errorTimeout : 3000,
24293 cropType : 'image/jpeg',
24295 canvasLoaded : false,
24296 isDocument : false,
24298 paramName : 'imageUpload',
24300 getAutoCreate : function()
24304 cls : 'roo-upload-cropbox',
24308 cls : 'roo-upload-cropbox-selector',
24313 cls : 'roo-upload-cropbox-body',
24314 style : 'cursor:pointer',
24318 cls : 'roo-upload-cropbox-preview'
24322 cls : 'roo-upload-cropbox-thumb'
24326 cls : 'roo-upload-cropbox-empty-notify',
24327 html : this.emptyText
24331 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24332 html : this.rotateNotify
24338 cls : 'roo-upload-cropbox-footer',
24341 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24351 onRender : function(ct, position)
24353 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24355 if (this.buttons.length) {
24357 Roo.each(this.buttons, function(bb) {
24359 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24361 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24367 initEvents : function()
24369 this.urlAPI = (window.createObjectURL && window) ||
24370 (window.URL && URL.revokeObjectURL && URL) ||
24371 (window.webkitURL && webkitURL);
24373 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24374 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24376 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24377 this.selectorEl.hide();
24379 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24380 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24382 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24383 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24384 this.thumbEl.hide();
24386 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24387 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24389 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24390 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24391 this.errorEl.hide();
24393 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24394 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24395 this.footerEl.hide();
24397 this.setThumbBoxSize();
24403 this.fireEvent('initial', this);
24410 window.addEventListener("resize", function() { _this.resize(); } );
24412 this.bodyEl.on('click', this.beforeSelectFile, this);
24415 this.bodyEl.on('touchstart', this.onTouchStart, this);
24416 this.bodyEl.on('touchmove', this.onTouchMove, this);
24417 this.bodyEl.on('touchend', this.onTouchEnd, this);
24421 this.bodyEl.on('mousedown', this.onMouseDown, this);
24422 this.bodyEl.on('mousemove', this.onMouseMove, this);
24423 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24424 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24425 Roo.get(document).on('mouseup', this.onMouseUp, this);
24428 this.selectorEl.on('change', this.onFileSelected, this);
24434 this.baseScale = 1;
24436 this.baseRotate = 1;
24437 this.dragable = false;
24438 this.pinching = false;
24441 this.cropData = false;
24442 this.notifyEl.dom.innerHTML = this.emptyText;
24444 this.selectorEl.dom.value = '';
24448 resize : function()
24450 if(this.fireEvent('resize', this) != false){
24451 this.setThumbBoxPosition();
24452 this.setCanvasPosition();
24456 onFooterButtonClick : function(e, el, o, type)
24459 case 'rotate-left' :
24460 this.onRotateLeft(e);
24462 case 'rotate-right' :
24463 this.onRotateRight(e);
24466 this.beforeSelectFile(e);
24481 this.fireEvent('footerbuttonclick', this, type);
24484 beforeSelectFile : function(e)
24486 e.preventDefault();
24488 if(this.fireEvent('beforeselectfile', this) != false){
24489 this.selectorEl.dom.click();
24493 onFileSelected : function(e)
24495 e.preventDefault();
24497 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24501 var file = this.selectorEl.dom.files[0];
24503 if(this.fireEvent('inspect', this, file) != false){
24504 this.prepare(file);
24509 trash : function(e)
24511 this.fireEvent('trash', this);
24514 download : function(e)
24516 this.fireEvent('download', this);
24519 loadCanvas : function(src)
24521 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24525 this.imageEl = document.createElement('img');
24529 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24531 this.imageEl.src = src;
24535 onLoadCanvas : function()
24537 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24538 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24540 this.bodyEl.un('click', this.beforeSelectFile, this);
24542 this.notifyEl.hide();
24543 this.thumbEl.show();
24544 this.footerEl.show();
24546 this.baseRotateLevel();
24548 if(this.isDocument){
24549 this.setThumbBoxSize();
24552 this.setThumbBoxPosition();
24554 this.baseScaleLevel();
24560 this.canvasLoaded = true;
24564 setCanvasPosition : function()
24566 if(!this.canvasEl){
24570 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24571 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24573 this.previewEl.setLeft(pw);
24574 this.previewEl.setTop(ph);
24578 onMouseDown : function(e)
24582 this.dragable = true;
24583 this.pinching = false;
24585 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24586 this.dragable = false;
24590 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24591 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24595 onMouseMove : function(e)
24599 if(!this.canvasLoaded){
24603 if (!this.dragable){
24607 var minX = Math.ceil(this.thumbEl.getLeft(true));
24608 var minY = Math.ceil(this.thumbEl.getTop(true));
24610 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24611 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24613 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24614 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24616 x = x - this.mouseX;
24617 y = y - this.mouseY;
24619 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24620 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24622 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24623 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24625 this.previewEl.setLeft(bgX);
24626 this.previewEl.setTop(bgY);
24628 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24629 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24632 onMouseUp : function(e)
24636 this.dragable = false;
24639 onMouseWheel : function(e)
24643 this.startScale = this.scale;
24645 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24647 if(!this.zoomable()){
24648 this.scale = this.startScale;
24657 zoomable : function()
24659 var minScale = this.thumbEl.getWidth() / this.minWidth;
24661 if(this.minWidth < this.minHeight){
24662 minScale = this.thumbEl.getHeight() / this.minHeight;
24665 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24666 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24670 (this.rotate == 0 || this.rotate == 180) &&
24672 width > this.imageEl.OriginWidth ||
24673 height > this.imageEl.OriginHeight ||
24674 (width < this.minWidth && height < this.minHeight)
24682 (this.rotate == 90 || this.rotate == 270) &&
24684 width > this.imageEl.OriginWidth ||
24685 height > this.imageEl.OriginHeight ||
24686 (width < this.minHeight && height < this.minWidth)
24693 !this.isDocument &&
24694 (this.rotate == 0 || this.rotate == 180) &&
24696 width < this.minWidth ||
24697 width > this.imageEl.OriginWidth ||
24698 height < this.minHeight ||
24699 height > this.imageEl.OriginHeight
24706 !this.isDocument &&
24707 (this.rotate == 90 || this.rotate == 270) &&
24709 width < this.minHeight ||
24710 width > this.imageEl.OriginWidth ||
24711 height < this.minWidth ||
24712 height > this.imageEl.OriginHeight
24722 onRotateLeft : function(e)
24724 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24726 var minScale = this.thumbEl.getWidth() / this.minWidth;
24728 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24729 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24731 this.startScale = this.scale;
24733 while (this.getScaleLevel() < minScale){
24735 this.scale = this.scale + 1;
24737 if(!this.zoomable()){
24742 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24743 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24748 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24755 this.scale = this.startScale;
24757 this.onRotateFail();
24762 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24764 if(this.isDocument){
24765 this.setThumbBoxSize();
24766 this.setThumbBoxPosition();
24767 this.setCanvasPosition();
24772 this.fireEvent('rotate', this, 'left');
24776 onRotateRight : function(e)
24778 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24780 var minScale = this.thumbEl.getWidth() / this.minWidth;
24782 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24783 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24785 this.startScale = this.scale;
24787 while (this.getScaleLevel() < minScale){
24789 this.scale = this.scale + 1;
24791 if(!this.zoomable()){
24796 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24797 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24802 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24809 this.scale = this.startScale;
24811 this.onRotateFail();
24816 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24818 if(this.isDocument){
24819 this.setThumbBoxSize();
24820 this.setThumbBoxPosition();
24821 this.setCanvasPosition();
24826 this.fireEvent('rotate', this, 'right');
24829 onRotateFail : function()
24831 this.errorEl.show(true);
24835 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24840 this.previewEl.dom.innerHTML = '';
24842 var canvasEl = document.createElement("canvas");
24844 var contextEl = canvasEl.getContext("2d");
24846 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24847 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24848 var center = this.imageEl.OriginWidth / 2;
24850 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24851 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24852 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24853 center = this.imageEl.OriginHeight / 2;
24856 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24858 contextEl.translate(center, center);
24859 contextEl.rotate(this.rotate * Math.PI / 180);
24861 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24863 this.canvasEl = document.createElement("canvas");
24865 this.contextEl = this.canvasEl.getContext("2d");
24867 switch (this.rotate) {
24870 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24871 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24873 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24878 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24879 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24881 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24882 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);
24886 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24891 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24892 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24894 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24895 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);
24899 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);
24904 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24905 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24907 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24908 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24912 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);
24919 this.previewEl.appendChild(this.canvasEl);
24921 this.setCanvasPosition();
24926 if(!this.canvasLoaded){
24930 var imageCanvas = document.createElement("canvas");
24932 var imageContext = imageCanvas.getContext("2d");
24934 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24935 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24937 var center = imageCanvas.width / 2;
24939 imageContext.translate(center, center);
24941 imageContext.rotate(this.rotate * Math.PI / 180);
24943 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24945 var canvas = document.createElement("canvas");
24947 var context = canvas.getContext("2d");
24949 canvas.width = this.minWidth;
24950 canvas.height = this.minHeight;
24952 switch (this.rotate) {
24955 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24956 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24958 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24959 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24961 var targetWidth = this.minWidth - 2 * x;
24962 var targetHeight = this.minHeight - 2 * y;
24966 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24967 scale = targetWidth / width;
24970 if(x > 0 && y == 0){
24971 scale = targetHeight / height;
24974 if(x > 0 && y > 0){
24975 scale = targetWidth / width;
24977 if(width < height){
24978 scale = targetHeight / height;
24982 context.scale(scale, scale);
24984 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24985 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24987 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24988 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24990 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24995 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24996 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24998 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24999 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25001 var targetWidth = this.minWidth - 2 * x;
25002 var targetHeight = this.minHeight - 2 * y;
25006 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25007 scale = targetWidth / width;
25010 if(x > 0 && y == 0){
25011 scale = targetHeight / height;
25014 if(x > 0 && y > 0){
25015 scale = targetWidth / width;
25017 if(width < height){
25018 scale = targetHeight / height;
25022 context.scale(scale, scale);
25024 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25025 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25027 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25028 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25030 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25032 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25037 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25038 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25040 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25041 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25043 var targetWidth = this.minWidth - 2 * x;
25044 var targetHeight = this.minHeight - 2 * y;
25048 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25049 scale = targetWidth / width;
25052 if(x > 0 && y == 0){
25053 scale = targetHeight / height;
25056 if(x > 0 && y > 0){
25057 scale = targetWidth / width;
25059 if(width < height){
25060 scale = targetHeight / height;
25064 context.scale(scale, scale);
25066 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25067 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25069 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25070 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25072 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25073 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25075 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25080 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25081 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25083 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25084 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25086 var targetWidth = this.minWidth - 2 * x;
25087 var targetHeight = this.minHeight - 2 * y;
25091 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25092 scale = targetWidth / width;
25095 if(x > 0 && y == 0){
25096 scale = targetHeight / height;
25099 if(x > 0 && y > 0){
25100 scale = targetWidth / width;
25102 if(width < height){
25103 scale = targetHeight / height;
25107 context.scale(scale, scale);
25109 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25110 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25112 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25113 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25115 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25117 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25124 this.cropData = canvas.toDataURL(this.cropType);
25126 if(this.fireEvent('crop', this, this.cropData) !== false){
25127 this.process(this.file, this.cropData);
25134 setThumbBoxSize : function()
25138 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25139 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25140 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25142 this.minWidth = width;
25143 this.minHeight = height;
25145 if(this.rotate == 90 || this.rotate == 270){
25146 this.minWidth = height;
25147 this.minHeight = width;
25152 width = Math.ceil(this.minWidth * height / this.minHeight);
25154 if(this.minWidth > this.minHeight){
25156 height = Math.ceil(this.minHeight * width / this.minWidth);
25159 this.thumbEl.setStyle({
25160 width : width + 'px',
25161 height : height + 'px'
25168 setThumbBoxPosition : function()
25170 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25171 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25173 this.thumbEl.setLeft(x);
25174 this.thumbEl.setTop(y);
25178 baseRotateLevel : function()
25180 this.baseRotate = 1;
25183 typeof(this.exif) != 'undefined' &&
25184 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25185 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25187 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25190 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25194 baseScaleLevel : function()
25198 if(this.isDocument){
25200 if(this.baseRotate == 6 || this.baseRotate == 8){
25202 height = this.thumbEl.getHeight();
25203 this.baseScale = height / this.imageEl.OriginWidth;
25205 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25206 width = this.thumbEl.getWidth();
25207 this.baseScale = width / this.imageEl.OriginHeight;
25213 height = this.thumbEl.getHeight();
25214 this.baseScale = height / this.imageEl.OriginHeight;
25216 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25217 width = this.thumbEl.getWidth();
25218 this.baseScale = width / this.imageEl.OriginWidth;
25224 if(this.baseRotate == 6 || this.baseRotate == 8){
25226 width = this.thumbEl.getHeight();
25227 this.baseScale = width / this.imageEl.OriginHeight;
25229 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25230 height = this.thumbEl.getWidth();
25231 this.baseScale = height / this.imageEl.OriginHeight;
25234 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25235 height = this.thumbEl.getWidth();
25236 this.baseScale = height / this.imageEl.OriginHeight;
25238 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25239 width = this.thumbEl.getHeight();
25240 this.baseScale = width / this.imageEl.OriginWidth;
25247 width = this.thumbEl.getWidth();
25248 this.baseScale = width / this.imageEl.OriginWidth;
25250 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25251 height = this.thumbEl.getHeight();
25252 this.baseScale = height / this.imageEl.OriginHeight;
25255 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25257 height = this.thumbEl.getHeight();
25258 this.baseScale = height / this.imageEl.OriginHeight;
25260 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25261 width = this.thumbEl.getWidth();
25262 this.baseScale = width / this.imageEl.OriginWidth;
25270 getScaleLevel : function()
25272 return this.baseScale * Math.pow(1.1, this.scale);
25275 onTouchStart : function(e)
25277 if(!this.canvasLoaded){
25278 this.beforeSelectFile(e);
25282 var touches = e.browserEvent.touches;
25288 if(touches.length == 1){
25289 this.onMouseDown(e);
25293 if(touches.length != 2){
25299 for(var i = 0, finger; finger = touches[i]; i++){
25300 coords.push(finger.pageX, finger.pageY);
25303 var x = Math.pow(coords[0] - coords[2], 2);
25304 var y = Math.pow(coords[1] - coords[3], 2);
25306 this.startDistance = Math.sqrt(x + y);
25308 this.startScale = this.scale;
25310 this.pinching = true;
25311 this.dragable = false;
25315 onTouchMove : function(e)
25317 if(!this.pinching && !this.dragable){
25321 var touches = e.browserEvent.touches;
25328 this.onMouseMove(e);
25334 for(var i = 0, finger; finger = touches[i]; i++){
25335 coords.push(finger.pageX, finger.pageY);
25338 var x = Math.pow(coords[0] - coords[2], 2);
25339 var y = Math.pow(coords[1] - coords[3], 2);
25341 this.endDistance = Math.sqrt(x + y);
25343 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25345 if(!this.zoomable()){
25346 this.scale = this.startScale;
25354 onTouchEnd : function(e)
25356 this.pinching = false;
25357 this.dragable = false;
25361 process : function(file, crop)
25363 this.xhr = new XMLHttpRequest();
25365 file.xhr = this.xhr;
25367 this.xhr.open(this.method, this.url, true);
25370 "Accept": "application/json",
25371 "Cache-Control": "no-cache",
25372 "X-Requested-With": "XMLHttpRequest"
25375 for (var headerName in headers) {
25376 var headerValue = headers[headerName];
25378 this.xhr.setRequestHeader(headerName, headerValue);
25384 this.xhr.onload = function()
25386 _this.xhrOnLoad(_this.xhr);
25389 this.xhr.onerror = function()
25391 _this.xhrOnError(_this.xhr);
25394 var formData = new FormData();
25396 formData.append('returnHTML', 'NO');
25399 formData.append('crop', crop);
25402 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25403 formData.append(this.paramName, file, file.name);
25406 if(typeof(file.filename) != 'undefined'){
25407 formData.append('filename', file.filename);
25410 if(typeof(file.mimetype) != 'undefined'){
25411 formData.append('mimetype', file.mimetype);
25414 if(this.fireEvent('arrange', this, formData) != false){
25415 this.xhr.send(formData);
25419 xhrOnLoad : function(xhr)
25421 if (xhr.readyState !== 4) {
25422 this.fireEvent('exception', this, xhr);
25426 var response = Roo.decode(xhr.responseText);
25428 if(!response.success){
25429 this.fireEvent('exception', this, xhr);
25433 var response = Roo.decode(xhr.responseText);
25435 this.fireEvent('upload', this, response);
25439 xhrOnError : function()
25441 Roo.log('xhr on error');
25443 var response = Roo.decode(xhr.responseText);
25449 prepare : function(file)
25454 if(typeof(file) === 'string'){
25455 this.loadCanvas(file);
25459 if(!file || !this.urlAPI){
25464 this.cropType = file.type;
25468 if(this.fireEvent('prepare', this, this.file) != false){
25470 var reader = new FileReader();
25472 reader.onload = function (e) {
25473 if (e.target.error) {
25474 Roo.log(e.target.error);
25478 var buffer = e.target.result,
25479 dataView = new DataView(buffer),
25481 maxOffset = dataView.byteLength - 4,
25485 if (dataView.getUint16(0) === 0xffd8) {
25486 while (offset < maxOffset) {
25487 markerBytes = dataView.getUint16(offset);
25489 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25490 markerLength = dataView.getUint16(offset + 2) + 2;
25491 if (offset + markerLength > dataView.byteLength) {
25492 Roo.log('Invalid meta data: Invalid segment size.');
25496 if(markerBytes == 0xffe1){
25497 _this.parseExifData(
25504 offset += markerLength;
25514 var url = _this.urlAPI.createObjectURL(_this.file);
25516 _this.loadCanvas(url);
25521 reader.readAsArrayBuffer(this.file);
25527 parseExifData : function(dataView, offset, length)
25529 var tiffOffset = offset + 10,
25533 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25534 // No Exif data, might be XMP data instead
25538 // Check for the ASCII code for "Exif" (0x45786966):
25539 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25540 // No Exif data, might be XMP data instead
25543 if (tiffOffset + 8 > dataView.byteLength) {
25544 Roo.log('Invalid Exif data: Invalid segment size.');
25547 // Check for the two null bytes:
25548 if (dataView.getUint16(offset + 8) !== 0x0000) {
25549 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25552 // Check the byte alignment:
25553 switch (dataView.getUint16(tiffOffset)) {
25555 littleEndian = true;
25558 littleEndian = false;
25561 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25564 // Check for the TIFF tag marker (0x002A):
25565 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25566 Roo.log('Invalid Exif data: Missing TIFF marker.');
25569 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25570 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25572 this.parseExifTags(
25575 tiffOffset + dirOffset,
25580 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25585 if (dirOffset + 6 > dataView.byteLength) {
25586 Roo.log('Invalid Exif data: Invalid directory offset.');
25589 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25590 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25591 if (dirEndOffset + 4 > dataView.byteLength) {
25592 Roo.log('Invalid Exif data: Invalid directory size.');
25595 for (i = 0; i < tagsNumber; i += 1) {
25599 dirOffset + 2 + 12 * i, // tag offset
25603 // Return the offset to the next directory:
25604 return dataView.getUint32(dirEndOffset, littleEndian);
25607 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25609 var tag = dataView.getUint16(offset, littleEndian);
25611 this.exif[tag] = this.getExifValue(
25615 dataView.getUint16(offset + 2, littleEndian), // tag type
25616 dataView.getUint32(offset + 4, littleEndian), // tag length
25621 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25623 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25632 Roo.log('Invalid Exif data: Invalid tag type.');
25636 tagSize = tagType.size * length;
25637 // Determine if the value is contained in the dataOffset bytes,
25638 // or if the value at the dataOffset is a pointer to the actual data:
25639 dataOffset = tagSize > 4 ?
25640 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25641 if (dataOffset + tagSize > dataView.byteLength) {
25642 Roo.log('Invalid Exif data: Invalid data offset.');
25645 if (length === 1) {
25646 return tagType.getValue(dataView, dataOffset, littleEndian);
25649 for (i = 0; i < length; i += 1) {
25650 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25653 if (tagType.ascii) {
25655 // Concatenate the chars:
25656 for (i = 0; i < values.length; i += 1) {
25658 // Ignore the terminating NULL byte(s):
25659 if (c === '\u0000') {
25671 Roo.apply(Roo.bootstrap.UploadCropbox, {
25673 'Orientation': 0x0112
25677 1: 0, //'top-left',
25679 3: 180, //'bottom-right',
25680 // 4: 'bottom-left',
25682 6: 90, //'right-top',
25683 // 7: 'right-bottom',
25684 8: 270 //'left-bottom'
25688 // byte, 8-bit unsigned int:
25690 getValue: function (dataView, dataOffset) {
25691 return dataView.getUint8(dataOffset);
25695 // ascii, 8-bit byte:
25697 getValue: function (dataView, dataOffset) {
25698 return String.fromCharCode(dataView.getUint8(dataOffset));
25703 // short, 16 bit int:
25705 getValue: function (dataView, dataOffset, littleEndian) {
25706 return dataView.getUint16(dataOffset, littleEndian);
25710 // long, 32 bit int:
25712 getValue: function (dataView, dataOffset, littleEndian) {
25713 return dataView.getUint32(dataOffset, littleEndian);
25717 // rational = two long values, first is numerator, second is denominator:
25719 getValue: function (dataView, dataOffset, littleEndian) {
25720 return dataView.getUint32(dataOffset, littleEndian) /
25721 dataView.getUint32(dataOffset + 4, littleEndian);
25725 // slong, 32 bit signed int:
25727 getValue: function (dataView, dataOffset, littleEndian) {
25728 return dataView.getInt32(dataOffset, littleEndian);
25732 // srational, two slongs, first is numerator, second is denominator:
25734 getValue: function (dataView, dataOffset, littleEndian) {
25735 return dataView.getInt32(dataOffset, littleEndian) /
25736 dataView.getInt32(dataOffset + 4, littleEndian);
25746 cls : 'btn-group roo-upload-cropbox-rotate-left',
25747 action : 'rotate-left',
25751 cls : 'btn btn-default',
25752 html : '<i class="fa fa-undo"></i>'
25758 cls : 'btn-group roo-upload-cropbox-picture',
25759 action : 'picture',
25763 cls : 'btn btn-default',
25764 html : '<i class="fa fa-picture-o"></i>'
25770 cls : 'btn-group roo-upload-cropbox-rotate-right',
25771 action : 'rotate-right',
25775 cls : 'btn btn-default',
25776 html : '<i class="fa fa-repeat"></i>'
25784 cls : 'btn-group roo-upload-cropbox-rotate-left',
25785 action : 'rotate-left',
25789 cls : 'btn btn-default',
25790 html : '<i class="fa fa-undo"></i>'
25796 cls : 'btn-group roo-upload-cropbox-download',
25797 action : 'download',
25801 cls : 'btn btn-default',
25802 html : '<i class="fa fa-download"></i>'
25808 cls : 'btn-group roo-upload-cropbox-crop',
25813 cls : 'btn btn-default',
25814 html : '<i class="fa fa-crop"></i>'
25820 cls : 'btn-group roo-upload-cropbox-trash',
25825 cls : 'btn btn-default',
25826 html : '<i class="fa fa-trash"></i>'
25832 cls : 'btn-group roo-upload-cropbox-rotate-right',
25833 action : 'rotate-right',
25837 cls : 'btn btn-default',
25838 html : '<i class="fa fa-repeat"></i>'
25846 cls : 'btn-group roo-upload-cropbox-rotate-left',
25847 action : 'rotate-left',
25851 cls : 'btn btn-default',
25852 html : '<i class="fa fa-undo"></i>'
25858 cls : 'btn-group roo-upload-cropbox-rotate-right',
25859 action : 'rotate-right',
25863 cls : 'btn btn-default',
25864 html : '<i class="fa fa-repeat"></i>'
25877 * @class Roo.bootstrap.DocumentManager
25878 * @extends Roo.bootstrap.Component
25879 * Bootstrap DocumentManager class
25880 * @cfg {String} paramName default 'imageUpload'
25881 * @cfg {String} method default POST
25882 * @cfg {String} url action url
25883 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25884 * @cfg {Boolean} multiple multiple upload default true
25885 * @cfg {Number} thumbSize default 300
25886 * @cfg {String} fieldLabel
25887 * @cfg {Number} labelWidth default 4
25888 * @cfg {String} labelAlign (left|top) default left
25889 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25892 * Create a new DocumentManager
25893 * @param {Object} config The config object
25896 Roo.bootstrap.DocumentManager = function(config){
25897 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25902 * Fire when initial the DocumentManager
25903 * @param {Roo.bootstrap.DocumentManager} this
25908 * inspect selected file
25909 * @param {Roo.bootstrap.DocumentManager} this
25910 * @param {File} file
25915 * Fire when xhr load exception
25916 * @param {Roo.bootstrap.DocumentManager} this
25917 * @param {XMLHttpRequest} xhr
25919 "exception" : true,
25922 * prepare the form data
25923 * @param {Roo.bootstrap.DocumentManager} this
25924 * @param {Object} formData
25929 * Fire when remove the file
25930 * @param {Roo.bootstrap.DocumentManager} this
25931 * @param {Object} file
25936 * Fire after refresh the file
25937 * @param {Roo.bootstrap.DocumentManager} this
25942 * Fire after click the image
25943 * @param {Roo.bootstrap.DocumentManager} this
25944 * @param {Object} file
25949 * Fire when upload a image and editable set to true
25950 * @param {Roo.bootstrap.DocumentManager} this
25951 * @param {Object} file
25955 * @event beforeselectfile
25956 * Fire before select file
25957 * @param {Roo.bootstrap.DocumentManager} this
25959 "beforeselectfile" : true,
25962 * Fire before process file
25963 * @param {Roo.bootstrap.DocumentManager} this
25964 * @param {Object} file
25971 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25980 paramName : 'imageUpload',
25983 labelAlign : 'left',
25987 getAutoCreate : function()
25989 var managerWidget = {
25991 cls : 'roo-document-manager',
25995 cls : 'roo-document-manager-selector',
26000 cls : 'roo-document-manager-uploader',
26004 cls : 'roo-document-manager-upload-btn',
26005 html : '<i class="fa fa-plus"></i>'
26016 cls : 'column col-md-12',
26021 if(this.fieldLabel.length){
26026 cls : 'column col-md-12',
26027 html : this.fieldLabel
26031 cls : 'column col-md-12',
26036 if(this.labelAlign == 'left'){
26040 cls : 'column col-md-' + this.labelWidth,
26041 html : this.fieldLabel
26045 cls : 'column col-md-' + (12 - this.labelWidth),
26055 cls : 'row clearfix',
26063 initEvents : function()
26065 this.managerEl = this.el.select('.roo-document-manager', true).first();
26066 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26068 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26069 this.selectorEl.hide();
26072 this.selectorEl.attr('multiple', 'multiple');
26075 this.selectorEl.on('change', this.onFileSelected, this);
26077 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26078 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26080 this.uploader.on('click', this.onUploaderClick, this);
26082 this.renderProgressDialog();
26086 window.addEventListener("resize", function() { _this.refresh(); } );
26088 this.fireEvent('initial', this);
26091 renderProgressDialog : function()
26095 this.progressDialog = new Roo.bootstrap.Modal({
26096 cls : 'roo-document-manager-progress-dialog',
26097 allow_close : false,
26107 btnclick : function() {
26108 _this.uploadCancel();
26114 this.progressDialog.render(Roo.get(document.body));
26116 this.progress = new Roo.bootstrap.Progress({
26117 cls : 'roo-document-manager-progress',
26122 this.progress.render(this.progressDialog.getChildContainer());
26124 this.progressBar = new Roo.bootstrap.ProgressBar({
26125 cls : 'roo-document-manager-progress-bar',
26128 aria_valuemax : 12,
26132 this.progressBar.render(this.progress.getChildContainer());
26135 onUploaderClick : function(e)
26137 e.preventDefault();
26139 if(this.fireEvent('beforeselectfile', this) != false){
26140 this.selectorEl.dom.click();
26145 onFileSelected : function(e)
26147 e.preventDefault();
26149 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26153 Roo.each(this.selectorEl.dom.files, function(file){
26154 if(this.fireEvent('inspect', this, file) != false){
26155 this.files.push(file);
26165 this.selectorEl.dom.value = '';
26167 if(!this.files.length){
26171 if(this.boxes > 0 && this.files.length > this.boxes){
26172 this.files = this.files.slice(0, this.boxes);
26175 this.uploader.show();
26177 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26178 this.uploader.hide();
26187 Roo.each(this.files, function(file){
26189 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26190 var f = this.renderPreview(file);
26195 if(file.type.indexOf('image') != -1){
26196 this.delegates.push(
26198 _this.process(file);
26199 }).createDelegate(this)
26207 _this.process(file);
26208 }).createDelegate(this)
26213 this.files = files;
26215 this.delegates = this.delegates.concat(docs);
26217 if(!this.delegates.length){
26222 this.progressBar.aria_valuemax = this.delegates.length;
26229 arrange : function()
26231 if(!this.delegates.length){
26232 this.progressDialog.hide();
26237 var delegate = this.delegates.shift();
26239 this.progressDialog.show();
26241 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26243 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26248 refresh : function()
26250 this.uploader.show();
26252 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26253 this.uploader.hide();
26256 Roo.isTouch ? this.closable(false) : this.closable(true);
26258 this.fireEvent('refresh', this);
26261 onRemove : function(e, el, o)
26263 e.preventDefault();
26265 this.fireEvent('remove', this, o);
26269 remove : function(o)
26273 Roo.each(this.files, function(file){
26274 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26283 this.files = files;
26290 Roo.each(this.files, function(file){
26295 file.target.remove();
26304 onClick : function(e, el, o)
26306 e.preventDefault();
26308 this.fireEvent('click', this, o);
26312 closable : function(closable)
26314 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26316 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26328 xhrOnLoad : function(xhr)
26330 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26334 if (xhr.readyState !== 4) {
26336 this.fireEvent('exception', this, xhr);
26340 var response = Roo.decode(xhr.responseText);
26342 if(!response.success){
26344 this.fireEvent('exception', this, xhr);
26348 var file = this.renderPreview(response.data);
26350 this.files.push(file);
26356 xhrOnError : function()
26358 Roo.log('xhr on error');
26360 var response = Roo.decode(xhr.responseText);
26367 process : function(file)
26369 if(this.fireEvent('process', this, file) !== false){
26370 if(this.editable && file.type.indexOf('image') != -1){
26371 this.fireEvent('edit', this, file);
26375 this.uploadStart(file, false);
26382 uploadStart : function(file, crop)
26384 this.xhr = new XMLHttpRequest();
26386 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26391 file.xhr = this.xhr;
26393 this.managerEl.createChild({
26395 cls : 'roo-document-manager-loading',
26399 tooltip : file.name,
26400 cls : 'roo-document-manager-thumb',
26401 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26407 this.xhr.open(this.method, this.url, true);
26410 "Accept": "application/json",
26411 "Cache-Control": "no-cache",
26412 "X-Requested-With": "XMLHttpRequest"
26415 for (var headerName in headers) {
26416 var headerValue = headers[headerName];
26418 this.xhr.setRequestHeader(headerName, headerValue);
26424 this.xhr.onload = function()
26426 _this.xhrOnLoad(_this.xhr);
26429 this.xhr.onerror = function()
26431 _this.xhrOnError(_this.xhr);
26434 var formData = new FormData();
26436 formData.append('returnHTML', 'NO');
26439 formData.append('crop', crop);
26442 formData.append(this.paramName, file, file.name);
26444 if(this.fireEvent('prepare', this, formData) != false){
26445 this.xhr.send(formData);
26449 uploadCancel : function()
26453 this.delegates = [];
26455 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26462 renderPreview : function(file)
26464 if(typeof(file.target) != 'undefined' && file.target){
26468 var previewEl = this.managerEl.createChild({
26470 cls : 'roo-document-manager-preview',
26474 tooltip : file.filename,
26475 cls : 'roo-document-manager-thumb',
26476 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26481 html : '<i class="fa fa-times-circle"></i>'
26486 var close = previewEl.select('button.close', true).first();
26488 close.on('click', this.onRemove, this, file);
26490 file.target = previewEl;
26492 var image = previewEl.select('img', true).first();
26496 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26498 image.on('click', this.onClick, this, file);
26504 onPreviewLoad : function(file, image)
26506 if(typeof(file.target) == 'undefined' || !file.target){
26510 var width = image.dom.naturalWidth || image.dom.width;
26511 var height = image.dom.naturalHeight || image.dom.height;
26513 if(width > height){
26514 file.target.addClass('wide');
26518 file.target.addClass('tall');
26523 uploadFromSource : function(file, crop)
26525 this.xhr = new XMLHttpRequest();
26527 this.managerEl.createChild({
26529 cls : 'roo-document-manager-loading',
26533 tooltip : file.name,
26534 cls : 'roo-document-manager-thumb',
26535 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26541 this.xhr.open(this.method, this.url, true);
26544 "Accept": "application/json",
26545 "Cache-Control": "no-cache",
26546 "X-Requested-With": "XMLHttpRequest"
26549 for (var headerName in headers) {
26550 var headerValue = headers[headerName];
26552 this.xhr.setRequestHeader(headerName, headerValue);
26558 this.xhr.onload = function()
26560 _this.xhrOnLoad(_this.xhr);
26563 this.xhr.onerror = function()
26565 _this.xhrOnError(_this.xhr);
26568 var formData = new FormData();
26570 formData.append('returnHTML', 'NO');
26572 formData.append('crop', crop);
26574 if(typeof(file.filename) != 'undefined'){
26575 formData.append('filename', file.filename);
26578 if(typeof(file.mimetype) != 'undefined'){
26579 formData.append('mimetype', file.mimetype);
26582 if(this.fireEvent('prepare', this, formData) != false){
26583 this.xhr.send(formData);
26593 * @class Roo.bootstrap.DocumentViewer
26594 * @extends Roo.bootstrap.Component
26595 * Bootstrap DocumentViewer class
26598 * Create a new DocumentViewer
26599 * @param {Object} config The config object
26602 Roo.bootstrap.DocumentViewer = function(config){
26603 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26608 * Fire after initEvent
26609 * @param {Roo.bootstrap.DocumentViewer} this
26615 * @param {Roo.bootstrap.DocumentViewer} this
26620 * Fire after trash button
26621 * @param {Roo.bootstrap.DocumentViewer} this
26628 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26630 getAutoCreate : function()
26634 cls : 'roo-document-viewer',
26638 cls : 'roo-document-viewer-body',
26642 cls : 'roo-document-viewer-thumb',
26646 cls : 'roo-document-viewer-image'
26654 cls : 'roo-document-viewer-footer',
26657 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26665 cls : 'btn btn-default roo-document-viewer-trash',
26666 html : '<i class="fa fa-trash"></i>'
26679 initEvents : function()
26682 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26683 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26685 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26686 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26688 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26689 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26691 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26692 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26694 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26695 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26697 this.bodyEl.on('click', this.onClick, this);
26699 this.trashBtn.on('click', this.onTrash, this);
26703 initial : function()
26705 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26708 this.fireEvent('initial', this);
26712 onClick : function(e)
26714 e.preventDefault();
26716 this.fireEvent('click', this);
26719 onTrash : function(e)
26721 e.preventDefault();
26723 this.fireEvent('trash', this);
26735 * @class Roo.bootstrap.NavProgressBar
26736 * @extends Roo.bootstrap.Component
26737 * Bootstrap NavProgressBar class
26740 * Create a new nav progress bar
26741 * @param {Object} config The config object
26744 Roo.bootstrap.NavProgressBar = function(config){
26745 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26747 this.bullets = this.bullets || [];
26749 // Roo.bootstrap.NavProgressBar.register(this);
26753 * Fires when the active item changes
26754 * @param {Roo.bootstrap.NavProgressBar} this
26755 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26756 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26763 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26768 getAutoCreate : function()
26770 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26774 cls : 'roo-navigation-bar-group',
26778 cls : 'roo-navigation-top-bar'
26782 cls : 'roo-navigation-bullets-bar',
26786 cls : 'roo-navigation-bar'
26793 cls : 'roo-navigation-bottom-bar'
26803 initEvents: function()
26808 onRender : function(ct, position)
26810 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26812 if(this.bullets.length){
26813 Roo.each(this.bullets, function(b){
26822 addItem : function(cfg)
26824 var item = new Roo.bootstrap.NavProgressItem(cfg);
26826 item.parentId = this.id;
26827 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26830 var top = new Roo.bootstrap.Element({
26832 cls : 'roo-navigation-bar-text'
26835 var bottom = new Roo.bootstrap.Element({
26837 cls : 'roo-navigation-bar-text'
26840 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26841 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26843 var topText = new Roo.bootstrap.Element({
26845 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26848 var bottomText = new Roo.bootstrap.Element({
26850 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26853 topText.onRender(top.el, null);
26854 bottomText.onRender(bottom.el, null);
26857 item.bottomEl = bottom;
26860 this.barItems.push(item);
26865 getActive : function()
26867 var active = false;
26869 Roo.each(this.barItems, function(v){
26871 if (!v.isActive()) {
26883 setActiveItem : function(item)
26887 Roo.each(this.barItems, function(v){
26888 if (v.rid == item.rid) {
26892 if (v.isActive()) {
26893 v.setActive(false);
26898 item.setActive(true);
26900 this.fireEvent('changed', this, item, prev);
26903 getBarItem: function(rid)
26907 Roo.each(this.barItems, function(e) {
26908 if (e.rid != rid) {
26919 indexOfItem : function(item)
26923 Roo.each(this.barItems, function(v, i){
26925 if (v.rid != item.rid) {
26936 setActiveNext : function()
26938 var i = this.indexOfItem(this.getActive());
26940 if (i > this.barItems.length) {
26944 this.setActiveItem(this.barItems[i+1]);
26947 setActivePrev : function()
26949 var i = this.indexOfItem(this.getActive());
26955 this.setActiveItem(this.barItems[i-1]);
26958 format : function()
26960 if(!this.barItems.length){
26964 var width = 100 / this.barItems.length;
26966 Roo.each(this.barItems, function(i){
26967 i.el.setStyle('width', width + '%');
26968 i.topEl.el.setStyle('width', width + '%');
26969 i.bottomEl.el.setStyle('width', width + '%');
26978 * Nav Progress Item
26983 * @class Roo.bootstrap.NavProgressItem
26984 * @extends Roo.bootstrap.Component
26985 * Bootstrap NavProgressItem class
26986 * @cfg {String} rid the reference id
26987 * @cfg {Boolean} active (true|false) Is item active default false
26988 * @cfg {Boolean} disabled (true|false) Is item active default false
26989 * @cfg {String} html
26990 * @cfg {String} position (top|bottom) text position default bottom
26991 * @cfg {String} icon show icon instead of number
26994 * Create a new NavProgressItem
26995 * @param {Object} config The config object
26997 Roo.bootstrap.NavProgressItem = function(config){
26998 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27003 * The raw click event for the entire grid.
27004 * @param {Roo.bootstrap.NavProgressItem} this
27005 * @param {Roo.EventObject} e
27012 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27018 position : 'bottom',
27021 getAutoCreate : function()
27023 var iconCls = 'roo-navigation-bar-item-icon';
27025 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27029 cls: 'roo-navigation-bar-item',
27039 cfg.cls += ' active';
27042 cfg.cls += ' disabled';
27048 disable : function()
27050 this.setDisabled(true);
27053 enable : function()
27055 this.setDisabled(false);
27058 initEvents: function()
27060 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27062 this.iconEl.on('click', this.onClick, this);
27065 onClick : function(e)
27067 e.preventDefault();
27073 if(this.fireEvent('click', this, e) === false){
27077 this.parent().setActiveItem(this);
27080 isActive: function ()
27082 return this.active;
27085 setActive : function(state)
27087 if(this.active == state){
27091 this.active = state;
27094 this.el.addClass('active');
27098 this.el.removeClass('active');
27103 setDisabled : function(state)
27105 if(this.disabled == state){
27109 this.disabled = state;
27112 this.el.addClass('disabled');
27116 this.el.removeClass('disabled');
27119 tooltipEl : function()
27121 return this.el.select('.roo-navigation-bar-item-icon', true).first();;