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');
1973 this.el.on('touchstart' , this.onTouch, this);
1975 this.el.on('click' , this.onClick, this);
1977 this.el.on("mouseover", this.onMouseOver, this);
1978 this.el.on("mouseout", this.onMouseOut, this);
1982 findTargetItem : function(e){
1983 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1987 //Roo.log(t); Roo.log(t.id);
1989 //Roo.log(this.menuitems);
1990 return this.menuitems.get(t.id);
1992 //return this.items.get(t.menuItemId);
1998 onTouch : function(e) {
2003 onClick : function(e){
2004 Roo.log("menu.onClick");
2005 var t = this.findTargetItem(e);
2006 if(!t || t.isContainer){
2011 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2012 if(t == this.activeItem && t.shouldDeactivate(e)){
2013 this.activeItem.deactivate();
2014 delete this.activeItem;
2018 this.setActiveItem(t, true);
2026 Roo.log('pass click event');
2030 this.fireEvent("click", this, t, e);
2034 onMouseOver : function(e){
2035 var t = this.findTargetItem(e);
2038 // if(t.canActivate && !t.disabled){
2039 // this.setActiveItem(t, true);
2043 this.fireEvent("mouseover", this, e, t);
2045 isVisible : function(){
2046 return !this.hidden;
2048 onMouseOut : function(e){
2049 var t = this.findTargetItem(e);
2052 // if(t == this.activeItem && t.shouldDeactivate(e)){
2053 // this.activeItem.deactivate();
2054 // delete this.activeItem;
2057 this.fireEvent("mouseout", this, e, t);
2062 * Displays this menu relative to another element
2063 * @param {String/HTMLElement/Roo.Element} element The element to align to
2064 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065 * the element (defaults to this.defaultAlign)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 show : function(el, pos, parentMenu){
2069 this.parentMenu = parentMenu;
2073 this.fireEvent("beforeshow", this);
2074 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2077 * Displays this menu at a specific xy position
2078 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2081 showAt : function(xy, parentMenu, /* private: */_e){
2082 this.parentMenu = parentMenu;
2087 this.fireEvent("beforeshow", this);
2088 //xy = this.el.adjustForConstraints(xy);
2092 this.hideMenuItems();
2093 this.hidden = false;
2094 this.triggerEl.addClass('open');
2096 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2102 this.fireEvent("show", this);
2108 this.doFocus.defer(50, this);
2112 doFocus : function(){
2114 this.focusEl.focus();
2119 * Hides this menu and optionally all parent menus
2120 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2122 hide : function(deep){
2124 this.hideMenuItems();
2125 if(this.el && this.isVisible()){
2126 this.fireEvent("beforehide", this);
2127 if(this.activeItem){
2128 this.activeItem.deactivate();
2129 this.activeItem = null;
2131 this.triggerEl.removeClass('open');;
2133 this.fireEvent("hide", this);
2135 if(deep === true && this.parentMenu){
2136 this.parentMenu.hide(true);
2140 onTriggerPress : function(e)
2143 Roo.log('trigger press');
2144 //Roo.log(e.getTarget());
2145 // Roo.log(this.triggerEl.dom);
2146 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2150 if (this.isVisible()) {
2155 this.show(this.triggerEl, false, false);
2164 hideMenuItems : function()
2166 //$(backdrop).remove()
2167 Roo.select('.open',true).each(function(aa) {
2169 aa.removeClass('open');
2170 //var parent = getParent($(this))
2171 //var relatedTarget = { relatedTarget: this }
2173 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174 //if (e.isDefaultPrevented()) return
2175 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2178 addxtypeChild : function (tree, cntr) {
2179 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2181 this.menuitems.add(comp);
2202 * @class Roo.bootstrap.MenuItem
2203 * @extends Roo.bootstrap.Component
2204 * Bootstrap MenuItem class
2205 * @cfg {String} html the menu label
2206 * @cfg {String} href the link
2207 * @cfg {Boolean} preventDefault (true | false) default true
2208 * @cfg {Boolean} isContainer (true | false) default false
2212 * Create a new MenuItem
2213 * @param {Object} config The config object
2217 Roo.bootstrap.MenuItem = function(config){
2218 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2223 * The raw click event for the entire grid.
2224 * @param {Roo.bootstrap.MenuItem} this
2225 * @param {Roo.EventObject} e
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2235 preventDefault: true,
2236 isContainer : false,
2238 getAutoCreate : function(){
2240 if(this.isContainer){
2243 cls: 'dropdown-menu-item'
2249 cls: 'dropdown-menu-item',
2258 if (this.parent().type == 'treeview') {
2259 cfg.cls = 'treeview-menu';
2262 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2267 initEvents: function() {
2269 //this.el.select('a').on('click', this.onClick, this);
2272 onClick : function(e)
2274 Roo.log('item on click ');
2275 //if(this.preventDefault){
2276 // e.preventDefault();
2278 //this.parent().hideMenuItems();
2280 this.fireEvent('click', this, e);
2299 * @class Roo.bootstrap.MenuSeparator
2300 * @extends Roo.bootstrap.Component
2301 * Bootstrap MenuSeparator class
2304 * Create a new MenuItem
2305 * @param {Object} config The config object
2309 Roo.bootstrap.MenuSeparator = function(config){
2310 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2315 getAutoCreate : function(){
2334 * @class Roo.bootstrap.Modal
2335 * @extends Roo.bootstrap.Component
2336 * Bootstrap Modal class
2337 * @cfg {String} title Title of dialog
2338 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2340 * @cfg {Boolean} specificTitle default false
2341 * @cfg {Array} buttons Array of buttons or standard button set..
2342 * @cfg {String} buttonPosition (left|right|center) default right
2343 * @cfg {Boolean} animate default true
2344 * @cfg {Boolean} allow_close default true
2347 * Create a new Modal Dialog
2348 * @param {Object} config The config object
2351 Roo.bootstrap.Modal = function(config){
2352 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2357 * The raw btnclick event for the button
2358 * @param {Roo.EventObject} e
2362 this.buttons = this.buttons || [];
2365 this.tmpl = Roo.factory(this.tmpl);
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2372 title : 'test dialog',
2382 specificTitle: false,
2384 buttonPosition: 'right',
2398 onRender : function(ct, position)
2400 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2403 var cfg = Roo.apply({}, this.getAutoCreate());
2406 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2408 //if (!cfg.name.length) {
2412 cfg.cls += ' ' + this.cls;
2415 cfg.style = this.style;
2417 this.el = Roo.get(document.body).createChild(cfg, position);
2419 //var type = this.el.dom.type;
2424 if(this.tabIndex !== undefined){
2425 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2429 this.bodyEl = this.el.select('.modal-body',true).first();
2430 this.closeEl = this.el.select('.modal-header .close', true).first();
2431 this.footerEl = this.el.select('.modal-footer',true).first();
2432 this.titleEl = this.el.select('.modal-title',true).first();
2436 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437 this.maskEl.enableDisplayMode("block");
2439 //this.el.addClass("x-dlg-modal");
2441 if (this.buttons.length) {
2442 Roo.each(this.buttons, function(bb) {
2443 var b = Roo.apply({}, bb);
2444 b.xns = b.xns || Roo.bootstrap;
2445 b.xtype = b.xtype || 'Button';
2446 if (typeof(b.listeners) == 'undefined') {
2447 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2450 var btn = Roo.factory(b);
2452 btn.onRender(this.el.select('.modal-footer div').first());
2456 // render the children.
2459 if(typeof(this.items) != 'undefined'){
2460 var items = this.items;
2463 for(var i =0;i < items.length;i++) {
2464 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2468 this.items = nitems;
2470 // where are these used - they used to be body/close/footer
2474 //this.el.addClass([this.fieldClass, this.cls]);
2478 getAutoCreate : function(){
2483 html : this.html || ''
2488 cls : 'modal-title',
2492 if(this.specificTitle){
2498 if (this.allow_close) {
2509 style : 'display: none',
2512 cls: "modal-dialog",
2515 cls : "modal-content",
2518 cls : 'modal-header',
2523 cls : 'modal-footer',
2527 cls: 'btn-' + this.buttonPosition
2544 modal.cls += ' fade';
2550 getChildContainer : function() {
2555 getButtonContainer : function() {
2556 return this.el.select('.modal-footer div',true).first();
2559 initEvents : function()
2561 if (this.allow_close) {
2562 this.closeEl.on('click', this.hide, this);
2567 window.addEventListener("resize", function() { _this.resize(); } );
2573 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2578 if (!this.rendered) {
2582 this.el.setStyle('display', 'block');
2586 (function(){ _this.el.addClass('in'); }).defer(50);
2588 this.el.addClass('in');
2591 // not sure how we can show data in here..
2593 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2596 Roo.get(document.body).addClass("x-body-masked");
2597 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2599 this.el.setStyle('zIndex', '10001');
2601 this.fireEvent('show', this);
2608 Roo.get(document.body).removeClass("x-body-masked");
2609 this.el.removeClass('in');
2613 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2615 this.el.setStyle('display', 'none');
2618 this.fireEvent('hide', this);
2621 addButton : function(str, cb)
2625 var b = Roo.apply({}, { html : str } );
2626 b.xns = b.xns || Roo.bootstrap;
2627 b.xtype = b.xtype || 'Button';
2628 if (typeof(b.listeners) == 'undefined') {
2629 b.listeners = { click : cb.createDelegate(this) };
2632 var btn = Roo.factory(b);
2634 btn.onRender(this.el.select('.modal-footer div').first());
2640 setDefaultButton : function(btn)
2642 //this.el.select('.modal-footer').()
2644 resizeTo: function(w,h)
2648 setContentSize : function(w, h)
2652 onButtonClick: function(btn,e)
2655 this.fireEvent('btnclick', btn.name, e);
2658 * Set the title of the Dialog
2659 * @param {String} str new Title
2661 setTitle: function(str) {
2662 this.titleEl.dom.innerHTML = str;
2665 * Set the body of the Dialog
2666 * @param {String} str new Title
2668 setBody: function(str) {
2669 this.bodyEl.dom.innerHTML = str;
2672 * Set the body of the Dialog using the template
2673 * @param {Obj} data - apply this data to the template and replace the body contents.
2675 applyBody: function(obj)
2678 Roo.log("Error - using apply Body without a template");
2681 this.tmpl.overwrite(this.bodyEl, obj);
2687 Roo.apply(Roo.bootstrap.Modal, {
2689 * Button config that displays a single OK button
2698 * Button config that displays Yes and No buttons
2714 * Button config that displays OK and Cancel buttons
2729 * Button config that displays Yes, No and Cancel buttons
2752 * messagebox - can be used as a replace
2756 * @class Roo.MessageBox
2757 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2766 // process text value...
2770 // Show a dialog using config options:
2772 title:'Save Changes?',
2773 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774 buttons: Roo.Msg.YESNOCANCEL,
2781 Roo.bootstrap.MessageBox = function(){
2782 var dlg, opt, mask, waitTimer;
2783 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784 var buttons, activeTextEl, bwidth;
2788 var handleButton = function(button){
2790 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2794 var handleHide = function(){
2796 dlg.el.removeClass(opt.cls);
2799 // Roo.TaskMgr.stop(waitTimer);
2800 // waitTimer = null;
2805 var updateButtons = function(b){
2808 buttons["ok"].hide();
2809 buttons["cancel"].hide();
2810 buttons["yes"].hide();
2811 buttons["no"].hide();
2812 //dlg.footer.dom.style.display = 'none';
2815 dlg.footerEl.dom.style.display = '';
2816 for(var k in buttons){
2817 if(typeof buttons[k] != "function"){
2820 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821 width += buttons[k].el.getWidth()+15;
2831 var handleEsc = function(d, k, e){
2832 if(opt && opt.closable !== false){
2842 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843 * @return {Roo.BasicDialog} The BasicDialog element
2845 getDialog : function(){
2847 dlg = new Roo.bootstrap.Modal( {
2850 //constraintoviewport:false,
2852 //collapsible : false,
2857 //buttonAlign:"center",
2858 closeClick : function(){
2859 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2862 handleButton("cancel");
2867 dlg.on("hide", handleHide);
2869 //dlg.addKeyListener(27, handleEsc);
2871 this.buttons = buttons;
2872 var bt = this.buttonText;
2873 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2878 bodyEl = dlg.bodyEl.createChild({
2880 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881 '<textarea class="roo-mb-textarea"></textarea>' +
2882 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2884 msgEl = bodyEl.dom.firstChild;
2885 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886 textboxEl.enableDisplayMode();
2887 textboxEl.addKeyListener([10,13], function(){
2888 if(dlg.isVisible() && opt && opt.buttons){
2891 }else if(opt.buttons.yes){
2892 handleButton("yes");
2896 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897 textareaEl.enableDisplayMode();
2898 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899 progressEl.enableDisplayMode();
2900 var pf = progressEl.dom.firstChild;
2902 pp = Roo.get(pf.firstChild);
2903 pp.setHeight(pf.offsetHeight);
2911 * Updates the message box body text
2912 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913 * the XHTML-compliant non-breaking space character '&#160;')
2914 * @return {Roo.MessageBox} This message box
2916 updateText : function(text){
2917 if(!dlg.isVisible() && !opt.width){
2918 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2920 msgEl.innerHTML = text || ' ';
2922 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2925 Math.min(opt.width || cw , this.maxWidth),
2926 Math.max(opt.minWidth || this.minWidth, bwidth)
2929 activeTextEl.setWidth(w);
2931 if(dlg.isVisible()){
2932 dlg.fixedcenter = false;
2934 // to big, make it scroll. = But as usual stupid IE does not support
2937 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2941 bodyEl.dom.style.height = '';
2942 bodyEl.dom.style.overflowY = '';
2945 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2947 bodyEl.dom.style.overflowX = '';
2950 dlg.setContentSize(w, bodyEl.getHeight());
2951 if(dlg.isVisible()){
2952 dlg.fixedcenter = true;
2958 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2959 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962 * @return {Roo.MessageBox} This message box
2964 updateProgress : function(value, text){
2966 this.updateText(text);
2968 if (pp) { // weird bug on my firefox - for some reason this is not defined
2969 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2975 * Returns true if the message box is currently displayed
2976 * @return {Boolean} True if the message box is visible, else false
2978 isVisible : function(){
2979 return dlg && dlg.isVisible();
2983 * Hides the message box if it is displayed
2986 if(this.isVisible()){
2992 * Displays a new message box, or reinitializes an existing message box, based on the config options
2993 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994 * The following config object properties are supported:
2996 Property Type Description
2997 ---------- --------------- ------------------------------------------------------------------------------------
2998 animEl String/Element An id or Element from which the message box should animate as it opens and
2999 closes (defaults to undefined)
3000 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable Boolean False to hide the top-right close button (defaults to true). Note that
3003 progress and wait dialogs will ignore this property and always hide the
3004 close button as they can only be closed programmatically.
3005 cls String A custom CSS class to apply to the message box element
3006 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3007 displayed (defaults to 75)
3008 fn Function A callback function to execute after closing the dialog. The arguments to the
3009 function will be btn (the name of the button that was clicked, if applicable,
3010 e.g. "ok"), and text (the value of the active text field, if applicable).
3011 Progress and wait dialogs will ignore this option since they do not respond to
3012 user actions and can only be closed programmatically, so any required function
3013 should be called by the same code after it closes the dialog.
3014 icon String A CSS class that provides a background image to be used as an icon for
3015 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3017 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3018 modal Boolean False to allow user interaction with the page while the message box is
3019 displayed (defaults to true)
3020 msg String A string that will replace the existing message box body text (defaults
3021 to the XHTML-compliant non-breaking space character ' ')
3022 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3023 progress Boolean True to display a progress bar (defaults to false)
3024 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3027 title String The title text
3028 value String The string value to set into the active textbox element if displayed
3029 wait Boolean True to display a progress bar (defaults to false)
3030 width Number The width of the dialog in pixels
3037 msg: 'Please enter your address:',
3039 buttons: Roo.MessageBox.OKCANCEL,
3042 animEl: 'addAddressBtn'
3045 * @param {Object} config Configuration options
3046 * @return {Roo.MessageBox} This message box
3048 show : function(options)
3051 // this causes nightmares if you show one dialog after another
3052 // especially on callbacks..
3054 if(this.isVisible()){
3057 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3059 Roo.log("New Dialog Message:" + options.msg )
3060 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3064 var d = this.getDialog();
3066 d.setTitle(opt.title || " ");
3067 d.closeEl.setDisplayed(opt.closable !== false);
3068 activeTextEl = textboxEl;
3069 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3074 textareaEl.setHeight(typeof opt.multiline == "number" ?
3075 opt.multiline : this.defaultTextHeight);
3076 activeTextEl = textareaEl;
3085 progressEl.setDisplayed(opt.progress === true);
3086 this.updateProgress(0);
3087 activeTextEl.dom.value = opt.value || "";
3089 dlg.setDefaultButton(activeTextEl);
3091 var bs = opt.buttons;
3095 }else if(bs && bs.yes){
3096 db = buttons["yes"];
3098 dlg.setDefaultButton(db);
3100 bwidth = updateButtons(opt.buttons);
3101 this.updateText(opt.msg);
3103 d.el.addClass(opt.cls);
3105 d.proxyDrag = opt.proxyDrag === true;
3106 d.modal = opt.modal !== false;
3107 d.mask = opt.modal !== false ? mask : false;
3109 // force it to the end of the z-index stack so it gets a cursor in FF
3110 document.body.appendChild(dlg.el.dom);
3111 d.animateTarget = null;
3112 d.show(options.animEl);
3118 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3119 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120 * and closing the message box when the process is complete.
3121 * @param {String} title The title bar text
3122 * @param {String} msg The message box body text
3123 * @return {Roo.MessageBox} This message box
3125 progress : function(title, msg){
3132 minWidth: this.minProgressWidth,
3139 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140 * If a callback function is passed it will be called after the user clicks the button, and the
3141 * id of the button that was clicked will be passed as the only parameter to the callback
3142 * (could also be the top-right close button).
3143 * @param {String} title The title bar text
3144 * @param {String} msg The message box body text
3145 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146 * @param {Object} scope (optional) The scope of the callback function
3147 * @return {Roo.MessageBox} This message box
3149 alert : function(title, msg, fn, scope){
3162 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3163 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164 * You are responsible for closing the message box when the process is complete.
3165 * @param {String} msg The message box body text
3166 * @param {String} title (optional) The title bar text
3167 * @return {Roo.MessageBox} This message box
3169 wait : function(msg, title){
3180 waitTimer = Roo.TaskMgr.start({
3182 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3190 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193 * @param {String} title The title bar text
3194 * @param {String} msg The message box body text
3195 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196 * @param {Object} scope (optional) The scope of the callback function
3197 * @return {Roo.MessageBox} This message box
3199 confirm : function(title, msg, fn, scope){
3203 buttons: this.YESNO,
3212 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3214 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215 * (could also be the top-right close button) and the text that was entered will be passed as the two
3216 * parameters to the callback.
3217 * @param {String} title The title bar text
3218 * @param {String} msg The message box body text
3219 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220 * @param {Object} scope (optional) The scope of the callback function
3221 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223 * @return {Roo.MessageBox} This message box
3225 prompt : function(title, msg, fn, scope, multiline){
3229 buttons: this.OKCANCEL,
3234 multiline: multiline,
3241 * Button config that displays a single OK button
3246 * Button config that displays Yes and No buttons
3249 YESNO : {yes:true, no:true},
3251 * Button config that displays OK and Cancel buttons
3254 OKCANCEL : {ok:true, cancel:true},
3256 * Button config that displays Yes, No and Cancel buttons
3259 YESNOCANCEL : {yes:true, no:true, cancel:true},
3262 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3265 defaultTextHeight : 75,
3267 * The maximum width in pixels of the message box (defaults to 600)
3272 * The minimum width in pixels of the message box (defaults to 100)
3277 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3278 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3281 minProgressWidth : 250,
3283 * An object containing the default button text strings that can be overriden for localized language support.
3284 * Supported properties are: ok, cancel, yes and no.
3285 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3298 * Shorthand for {@link Roo.MessageBox}
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3310 * @class Roo.bootstrap.Navbar
3311 * @extends Roo.bootstrap.Component
3312 * Bootstrap Navbar class
3315 * Create a new Navbar
3316 * @param {Object} config The config object
3320 Roo.bootstrap.Navbar = function(config){
3321 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3334 getAutoCreate : function(){
3337 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3341 initEvents :function ()
3343 //Roo.log(this.el.select('.navbar-toggle',true));
3344 this.el.select('.navbar-toggle',true).on('click', function() {
3345 // Roo.log('click');
3346 this.el.select('.navbar-collapse',true).toggleClass('in');
3354 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3356 var size = this.el.getSize();
3357 this.maskEl.setSize(size.width, size.height);
3358 this.maskEl.enableDisplayMode("block");
3367 getChildContainer : function()
3369 if (this.el.select('.collapse').getCount()) {
3370 return this.el.select('.collapse',true).first();
3403 * @class Roo.bootstrap.NavSimplebar
3404 * @extends Roo.bootstrap.Navbar
3405 * Bootstrap Sidebar class
3407 * @cfg {Boolean} inverse is inverted color
3409 * @cfg {String} type (nav | pills | tabs)
3410 * @cfg {Boolean} arrangement stacked | justified
3411 * @cfg {String} align (left | right) alignment
3413 * @cfg {Boolean} main (true|false) main nav bar? default false
3414 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3416 * @cfg {String} tag (header|footer|nav|div) default is nav
3422 * Create a new Sidebar
3423 * @param {Object} config The config object
3427 Roo.bootstrap.NavSimplebar = function(config){
3428 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3447 getAutoCreate : function(){
3451 tag : this.tag || 'div',
3464 this.type = this.type || 'nav';
3465 if (['tabs','pills'].indexOf(this.type)!==-1) {
3466 cfg.cn[0].cls += ' nav-' + this.type
3470 if (this.type!=='nav') {
3471 Roo.log('nav type must be nav/tabs/pills')
3473 cfg.cn[0].cls += ' navbar-nav'
3479 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480 cfg.cn[0].cls += ' nav-' + this.arrangement;
3484 if (this.align === 'right') {
3485 cfg.cn[0].cls += ' navbar-right';
3489 cfg.cls += ' navbar-inverse';
3516 * @class Roo.bootstrap.NavHeaderbar
3517 * @extends Roo.bootstrap.NavSimplebar
3518 * Bootstrap Sidebar class
3520 * @cfg {String} brand what is brand
3521 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522 * @cfg {String} brand_href href of the brand
3523 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3524 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3529 * Create a new Sidebar
3530 * @param {Object} config The config object
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3546 desktopCenter : false,
3549 getAutoCreate : function(){
3552 tag: this.nav || 'nav',
3559 if (this.desktopCenter) {
3560 cn.push({cls : 'container', cn : []});
3567 cls: 'navbar-header',
3572 cls: 'navbar-toggle',
3573 'data-toggle': 'collapse',
3578 html: 'Toggle navigation'
3600 cls: 'collapse navbar-collapse',
3604 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3606 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607 cfg.cls += ' navbar-' + this.position;
3609 // tag can override this..
3611 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3614 if (this.brand !== '') {
3617 href: this.brand_href ? this.brand_href : '#',
3618 cls: 'navbar-brand',
3626 cfg.cls += ' main-nav';
3634 getHeaderChildContainer : function()
3636 if (this.el.select('.navbar-header').getCount()) {
3637 return this.el.select('.navbar-header',true).first();
3640 return this.getChildContainer();
3644 initEvents : function()
3646 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3648 if (this.autohide) {
3653 Roo.get(document).on('scroll',function(e) {
3654 var ns = Roo.get(document).getScroll().top;
3655 var os = prevScroll;
3659 ft.removeClass('slideDown');
3660 ft.addClass('slideUp');
3663 ft.removeClass('slideUp');
3664 ft.addClass('slideDown');
3685 * @class Roo.bootstrap.NavSidebar
3686 * @extends Roo.bootstrap.Navbar
3687 * Bootstrap Sidebar class
3690 * Create a new Sidebar
3691 * @param {Object} config The config object
3695 Roo.bootstrap.NavSidebar = function(config){
3696 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3701 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3703 getAutoCreate : function(){
3708 cls: 'sidebar sidebar-nav'
3730 * @class Roo.bootstrap.NavGroup
3731 * @extends Roo.bootstrap.Component
3732 * Bootstrap NavGroup class
3733 * @cfg {String} align (left|right)
3734 * @cfg {Boolean} inverse
3735 * @cfg {String} type (nav|pills|tab) default nav
3736 * @cfg {String} navId - reference Id for navbar.
3740 * Create a new nav group
3741 * @param {Object} config The config object
3744 Roo.bootstrap.NavGroup = function(config){
3745 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3748 Roo.bootstrap.NavGroup.register(this);
3752 * Fires when the active item changes
3753 * @param {Roo.bootstrap.NavGroup} this
3754 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3773 getAutoCreate : function()
3775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3782 if (['tabs','pills'].indexOf(this.type)!==-1) {
3783 cfg.cls += ' nav-' + this.type
3785 if (this.type!=='nav') {
3786 Roo.log('nav type must be nav/tabs/pills')
3788 cfg.cls += ' navbar-nav'
3791 if (this.parent().sidebar) {
3794 cls: 'dashboard-menu sidebar-menu'
3800 if (this.form === true) {
3806 if (this.align === 'right') {
3807 cfg.cls += ' navbar-right';
3809 cfg.cls += ' navbar-left';
3813 if (this.align === 'right') {
3814 cfg.cls += ' navbar-right';
3818 cfg.cls += ' navbar-inverse';
3826 * sets the active Navigation item
3827 * @param {Roo.bootstrap.NavItem} the new current navitem
3829 setActiveItem : function(item)
3832 Roo.each(this.navItems, function(v){
3837 v.setActive(false, true);
3844 item.setActive(true, true);
3845 this.fireEvent('changed', this, item, prev);
3850 * gets the active Navigation item
3851 * @return {Roo.bootstrap.NavItem} the current navitem
3853 getActive : function()
3857 Roo.each(this.navItems, function(v){
3868 indexOfNav : function()
3872 Roo.each(this.navItems, function(v,i){
3883 * adds a Navigation item
3884 * @param {Roo.bootstrap.NavItem} the navitem to add
3886 addItem : function(cfg)
3888 var cn = new Roo.bootstrap.NavItem(cfg);
3890 cn.parentId = this.id;
3891 cn.onRender(this.el, null);
3895 * register a Navigation item
3896 * @param {Roo.bootstrap.NavItem} the navitem to add
3898 register : function(item)
3900 this.navItems.push( item);
3901 item.navId = this.navId;
3906 * clear all the Navigation item
3909 clearAll : function()
3912 this.el.dom.innerHTML = '';
3915 getNavItem: function(tabId)
3918 Roo.each(this.navItems, function(e) {
3919 if (e.tabId == tabId) {
3929 setActiveNext : function()
3931 var i = this.indexOfNav(this.getActive());
3932 if (i > this.navItems.length) {
3935 this.setActiveItem(this.navItems[i+1]);
3937 setActivePrev : function()
3939 var i = this.indexOfNav(this.getActive());
3943 this.setActiveItem(this.navItems[i-1]);
3945 clearWasActive : function(except) {
3946 Roo.each(this.navItems, function(e) {
3947 if (e.tabId != except.tabId && e.was_active) {
3948 e.was_active = false;
3955 getWasActive : function ()
3958 Roo.each(this.navItems, function(e) {
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3977 * register a Navigation Group
3978 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3980 register : function(navgrp)
3982 this.groups[navgrp.navId] = navgrp;
3986 * fetch a Navigation Group based on the navigation ID
3987 * @param {string} the navgroup to add
3988 * @returns {Roo.bootstrap.NavGroup} the navgroup
3990 get: function(navId) {
3991 if (typeof(this.groups[navId]) == 'undefined') {
3993 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3995 return this.groups[navId] ;
4010 * @class Roo.bootstrap.NavItem
4011 * @extends Roo.bootstrap.Component
4012 * Bootstrap Navbar.NavItem class
4013 * @cfg {String} href link to
4014 * @cfg {String} html content of button
4015 * @cfg {String} badge text inside badge
4016 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017 * @cfg {String} glyphicon name of glyphicon
4018 * @cfg {String} icon name of font awesome icon
4019 * @cfg {Boolean} active Is item active
4020 * @cfg {Boolean} disabled Is item disabled
4022 * @cfg {Boolean} preventDefault (true | false) default false
4023 * @cfg {String} tabId the tab that this item activates.
4024 * @cfg {String} tagtype (a|span) render as a href or span?
4025 * @cfg {Boolean} animateRef (true|false) link to element default false
4028 * Create a new Navbar Item
4029 * @param {Object} config The config object
4031 Roo.bootstrap.NavItem = function(config){
4032 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4037 * The raw click event for the entire grid.
4038 * @param {Roo.EventObject} e
4043 * Fires when the active item active state changes
4044 * @param {Roo.bootstrap.NavItem} this
4045 * @param {boolean} state the new state
4051 * Fires when scroll to element
4052 * @param {Roo.bootstrap.NavItem} this
4053 * @param {Object} options
4054 * @param {Roo.EventObject} e
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4070 preventDefault : false,
4077 getAutoCreate : function(){
4085 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4087 if (this.disabled) {
4088 cfg.cls += ' disabled';
4091 if (this.href || this.html || this.glyphicon || this.icon) {
4095 href : this.href || "#",
4096 html: this.html || ''
4101 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4104 if(this.glyphicon) {
4105 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4110 cfg.cn[0].html += " <span class='caret'></span>";
4114 if (this.badge !== '') {
4116 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4124 initEvents: function()
4126 if (typeof (this.menu) != 'undefined') {
4127 this.menu.parentType = this.xtype;
4128 this.menu.triggerEl = this.el;
4129 this.menu = this.addxtype(Roo.apply({}, this.menu));
4132 this.el.select('a',true).on('click', this.onClick, this);
4134 if(this.tagtype == 'span'){
4135 this.el.select('span',true).on('click', this.onClick, this);
4138 // at this point parent should be available..
4139 this.parent().register(this);
4142 onClick : function(e)
4145 this.preventDefault ||
4152 if (this.disabled) {
4156 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4157 if (tg && tg.transition) {
4158 Roo.log("waiting for the transitionend");
4164 //Roo.log("fire event clicked");
4165 if(this.fireEvent('click', this, e) === false){
4169 if(this.tagtype == 'span'){
4173 //Roo.log(this.href);
4174 var ael = this.el.select('a',true).first();
4177 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4178 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4179 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4180 return; // ignore... - it's a 'hash' to another page.
4184 this.scrollToElement(e);
4188 var p = this.parent();
4190 if (['tabs','pills'].indexOf(p.type)!==-1) {
4191 if (typeof(p.setActiveItem) !== 'undefined') {
4192 p.setActiveItem(this);
4196 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4197 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4198 // remove the collapsed menu expand...
4199 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4203 isActive: function () {
4206 setActive : function(state, fire, is_was_active)
4208 if (this.active && !state && this.navId) {
4209 this.was_active = true;
4210 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4212 nv.clearWasActive(this);
4216 this.active = state;
4219 this.el.removeClass('active');
4220 } else if (!this.el.hasClass('active')) {
4221 this.el.addClass('active');
4224 this.fireEvent('changed', this, state);
4227 // show a panel if it's registered and related..
4229 if (!this.navId || !this.tabId || !state || is_was_active) {
4233 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4237 var pan = tg.getPanelByName(this.tabId);
4241 // if we can not flip to new panel - go back to old nav highlight..
4242 if (false == tg.showPanel(pan)) {
4243 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4245 var onav = nv.getWasActive();
4247 onav.setActive(true, false, true);
4256 // this should not be here...
4257 setDisabled : function(state)
4259 this.disabled = state;
4261 this.el.removeClass('disabled');
4262 } else if (!this.el.hasClass('disabled')) {
4263 this.el.addClass('disabled');
4269 * Fetch the element to display the tooltip on.
4270 * @return {Roo.Element} defaults to this.el
4272 tooltipEl : function()
4274 return this.el.select('' + this.tagtype + '', true).first();
4277 scrollToElement : function(e)
4279 var c = document.body;
4282 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4284 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4285 c = document.documentElement;
4288 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4294 var o = target.calcOffsetsTo(c);
4301 this.fireEvent('scrollto', this, options, e);
4303 Roo.get(c).scrollTo('top', options.value, true);
4316 * <span> icon </span>
4317 * <span> text </span>
4318 * <span>badge </span>
4322 * @class Roo.bootstrap.NavSidebarItem
4323 * @extends Roo.bootstrap.NavItem
4324 * Bootstrap Navbar.NavSidebarItem class
4325 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4327 * Create a new Navbar Button
4328 * @param {Object} config The config object
4330 Roo.bootstrap.NavSidebarItem = function(config){
4331 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4336 * The raw click event for the entire grid.
4337 * @param {Roo.EventObject} e
4342 * Fires when the active item active state changes
4343 * @param {Roo.bootstrap.NavSidebarItem} this
4344 * @param {boolean} state the new state
4352 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4354 badgeWeight : 'default',
4356 getAutoCreate : function(){
4361 href : this.href || '#',
4373 html : this.html || ''
4378 cfg.cls += ' active';
4381 if (this.disabled) {
4382 cfg.cls += ' disabled';
4386 if (this.glyphicon || this.icon) {
4387 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4388 a.cn.push({ tag : 'i', cls : c }) ;
4393 if (this.badge !== '') {
4395 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4399 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4400 a.cls += 'dropdown-toggle treeview' ;
4411 initEvents : function()
4413 this.el.on('click', this.onClick, this);
4416 if(this.badge !== ''){
4418 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4423 onClick : function(e)
4430 if(this.preventDefault){
4434 this.fireEvent('click', this);
4437 disable : function()
4439 this.setDisabled(true);
4444 this.setDisabled(false);
4447 setDisabled : function(state)
4449 if(this.disabled == state){
4453 this.disabled = state;
4456 this.el.addClass('disabled');
4460 this.el.removeClass('disabled');
4465 setActive : function(state)
4467 if(this.active == state){
4471 this.active = state;
4474 this.el.addClass('active');
4478 this.el.removeClass('active');
4483 isActive: function ()
4488 setBadge : function(str)
4494 this.badgeEl.dom.innerHTML = str;
4511 * @class Roo.bootstrap.Row
4512 * @extends Roo.bootstrap.Component
4513 * Bootstrap Row class (contains columns...)
4517 * @param {Object} config The config object
4520 Roo.bootstrap.Row = function(config){
4521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4526 getAutoCreate : function(){
4545 * @class Roo.bootstrap.Element
4546 * @extends Roo.bootstrap.Component
4547 * Bootstrap Element class
4548 * @cfg {String} html contents of the element
4549 * @cfg {String} tag tag of the element
4550 * @cfg {String} cls class of the element
4551 * @cfg {Boolean} preventDefault (true|false) default false
4552 * @cfg {Boolean} clickable (true|false) default false
4555 * Create a new Element
4556 * @param {Object} config The config object
4559 Roo.bootstrap.Element = function(config){
4560 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4566 * When a element is chick
4567 * @param {Roo.bootstrap.Element} this
4568 * @param {Roo.EventObject} e
4574 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4579 preventDefault: false,
4582 getAutoCreate : function(){
4593 initEvents: function()
4595 Roo.bootstrap.Element.superclass.initEvents.call(this);
4598 this.el.on('click', this.onClick, this);
4603 onClick : function(e)
4605 if(this.preventDefault){
4609 this.fireEvent('click', this, e);
4612 getValue : function()
4614 return this.el.dom.innerHTML;
4617 setValue : function(value)
4619 this.el.dom.innerHTML = value;
4634 * @class Roo.bootstrap.Pagination
4635 * @extends Roo.bootstrap.Component
4636 * Bootstrap Pagination class
4637 * @cfg {String} size xs | sm | md | lg
4638 * @cfg {Boolean} inverse false | true
4641 * Create a new Pagination
4642 * @param {Object} config The config object
4645 Roo.bootstrap.Pagination = function(config){
4646 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4649 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4655 getAutoCreate : function(){
4661 cfg.cls += ' inverse';
4667 cfg.cls += " " + this.cls;
4685 * @class Roo.bootstrap.PaginationItem
4686 * @extends Roo.bootstrap.Component
4687 * Bootstrap PaginationItem class
4688 * @cfg {String} html text
4689 * @cfg {String} href the link
4690 * @cfg {Boolean} preventDefault (true | false) default true
4691 * @cfg {Boolean} active (true | false) default false
4692 * @cfg {Boolean} disabled default false
4696 * Create a new PaginationItem
4697 * @param {Object} config The config object
4701 Roo.bootstrap.PaginationItem = function(config){
4702 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4707 * The raw click event for the entire grid.
4708 * @param {Roo.EventObject} e
4714 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4718 preventDefault: true,
4723 getAutoCreate : function(){
4729 href : this.href ? this.href : '#',
4730 html : this.html ? this.html : ''
4740 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4744 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4750 initEvents: function() {
4752 this.el.on('click', this.onClick, this);
4755 onClick : function(e)
4757 Roo.log('PaginationItem on click ');
4758 if(this.preventDefault){
4766 this.fireEvent('click', this, e);
4782 * @class Roo.bootstrap.Slider
4783 * @extends Roo.bootstrap.Component
4784 * Bootstrap Slider class
4787 * Create a new Slider
4788 * @param {Object} config The config object
4791 Roo.bootstrap.Slider = function(config){
4792 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4795 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4797 getAutoCreate : function(){
4801 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4805 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4817 * Ext JS Library 1.1.1
4818 * Copyright(c) 2006-2007, Ext JS, LLC.
4820 * Originally Released Under LGPL - original licence link has changed is not relivant.
4823 * <script type="text/javascript">
4828 * @class Roo.grid.ColumnModel
4829 * @extends Roo.util.Observable
4830 * This is the default implementation of a ColumnModel used by the Grid. It defines
4831 * the columns in the grid.
4834 var colModel = new Roo.grid.ColumnModel([
4835 {header: "Ticker", width: 60, sortable: true, locked: true},
4836 {header: "Company Name", width: 150, sortable: true},
4837 {header: "Market Cap.", width: 100, sortable: true},
4838 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4839 {header: "Employees", width: 100, sortable: true, resizable: false}
4844 * The config options listed for this class are options which may appear in each
4845 * individual column definition.
4846 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4848 * @param {Object} config An Array of column config objects. See this class's
4849 * config objects for details.
4851 Roo.grid.ColumnModel = function(config){
4853 * The config passed into the constructor
4855 this.config = config;
4858 // if no id, create one
4859 // if the column does not have a dataIndex mapping,
4860 // map it to the order it is in the config
4861 for(var i = 0, len = config.length; i < len; i++){
4863 if(typeof c.dataIndex == "undefined"){
4866 if(typeof c.renderer == "string"){
4867 c.renderer = Roo.util.Format[c.renderer];
4869 if(typeof c.id == "undefined"){
4872 if(c.editor && c.editor.xtype){
4873 c.editor = Roo.factory(c.editor, Roo.grid);
4875 if(c.editor && c.editor.isFormField){
4876 c.editor = new Roo.grid.GridEditor(c.editor);
4878 this.lookup[c.id] = c;
4882 * The width of columns which have no width specified (defaults to 100)
4885 this.defaultWidth = 100;
4888 * Default sortable of columns which have no sortable specified (defaults to false)
4891 this.defaultSortable = false;
4895 * @event widthchange
4896 * Fires when the width of a column changes.
4897 * @param {ColumnModel} this
4898 * @param {Number} columnIndex The column index
4899 * @param {Number} newWidth The new width
4901 "widthchange": true,
4903 * @event headerchange
4904 * Fires when the text of a header changes.
4905 * @param {ColumnModel} this
4906 * @param {Number} columnIndex The column index
4907 * @param {Number} newText The new header text
4909 "headerchange": true,
4911 * @event hiddenchange
4912 * Fires when a column is hidden or "unhidden".
4913 * @param {ColumnModel} this
4914 * @param {Number} columnIndex The column index
4915 * @param {Boolean} hidden true if hidden, false otherwise
4917 "hiddenchange": true,
4919 * @event columnmoved
4920 * Fires when a column is moved.
4921 * @param {ColumnModel} this
4922 * @param {Number} oldIndex
4923 * @param {Number} newIndex
4925 "columnmoved" : true,
4927 * @event columlockchange
4928 * Fires when a column's locked state is changed
4929 * @param {ColumnModel} this
4930 * @param {Number} colIndex
4931 * @param {Boolean} locked true if locked
4933 "columnlockchange" : true
4935 Roo.grid.ColumnModel.superclass.constructor.call(this);
4937 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4939 * @cfg {String} header The header text to display in the Grid view.
4942 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4943 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4944 * specified, the column's index is used as an index into the Record's data Array.
4947 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4948 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4951 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4952 * Defaults to the value of the {@link #defaultSortable} property.
4953 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4956 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4959 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4962 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4965 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4968 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4969 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4970 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4971 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4974 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4977 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4980 * @cfg {String} cursor (Optional)
4983 * @cfg {String} tooltip (Optional)
4986 * Returns the id of the column at the specified index.
4987 * @param {Number} index The column index
4988 * @return {String} the id
4990 getColumnId : function(index){
4991 return this.config[index].id;
4995 * Returns the column for a specified id.
4996 * @param {String} id The column id
4997 * @return {Object} the column
4999 getColumnById : function(id){
5000 return this.lookup[id];
5005 * Returns the column for a specified dataIndex.
5006 * @param {String} dataIndex The column dataIndex
5007 * @return {Object|Boolean} the column or false if not found
5009 getColumnByDataIndex: function(dataIndex){
5010 var index = this.findColumnIndex(dataIndex);
5011 return index > -1 ? this.config[index] : false;
5015 * Returns the index for a specified column id.
5016 * @param {String} id The column id
5017 * @return {Number} the index, or -1 if not found
5019 getIndexById : function(id){
5020 for(var i = 0, len = this.config.length; i < len; i++){
5021 if(this.config[i].id == id){
5029 * Returns the index for a specified column dataIndex.
5030 * @param {String} dataIndex The column dataIndex
5031 * @return {Number} the index, or -1 if not found
5034 findColumnIndex : function(dataIndex){
5035 for(var i = 0, len = this.config.length; i < len; i++){
5036 if(this.config[i].dataIndex == dataIndex){
5044 moveColumn : function(oldIndex, newIndex){
5045 var c = this.config[oldIndex];
5046 this.config.splice(oldIndex, 1);
5047 this.config.splice(newIndex, 0, c);
5048 this.dataMap = null;
5049 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5052 isLocked : function(colIndex){
5053 return this.config[colIndex].locked === true;
5056 setLocked : function(colIndex, value, suppressEvent){
5057 if(this.isLocked(colIndex) == value){
5060 this.config[colIndex].locked = value;
5062 this.fireEvent("columnlockchange", this, colIndex, value);
5066 getTotalLockedWidth : function(){
5068 for(var i = 0; i < this.config.length; i++){
5069 if(this.isLocked(i) && !this.isHidden(i)){
5070 this.totalWidth += this.getColumnWidth(i);
5076 getLockedCount : function(){
5077 for(var i = 0, len = this.config.length; i < len; i++){
5078 if(!this.isLocked(i)){
5085 * Returns the number of columns.
5088 getColumnCount : function(visibleOnly){
5089 if(visibleOnly === true){
5091 for(var i = 0, len = this.config.length; i < len; i++){
5092 if(!this.isHidden(i)){
5098 return this.config.length;
5102 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5103 * @param {Function} fn
5104 * @param {Object} scope (optional)
5105 * @return {Array} result
5107 getColumnsBy : function(fn, scope){
5109 for(var i = 0, len = this.config.length; i < len; i++){
5110 var c = this.config[i];
5111 if(fn.call(scope||this, c, i) === true){
5119 * Returns true if the specified column is sortable.
5120 * @param {Number} col The column index
5123 isSortable : function(col){
5124 if(typeof this.config[col].sortable == "undefined"){
5125 return this.defaultSortable;
5127 return this.config[col].sortable;
5131 * Returns the rendering (formatting) function defined for the column.
5132 * @param {Number} col The column index.
5133 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5135 getRenderer : function(col){
5136 if(!this.config[col].renderer){
5137 return Roo.grid.ColumnModel.defaultRenderer;
5139 return this.config[col].renderer;
5143 * Sets the rendering (formatting) function for a column.
5144 * @param {Number} col The column index
5145 * @param {Function} fn The function to use to process the cell's raw data
5146 * to return HTML markup for the grid view. The render function is called with
5147 * the following parameters:<ul>
5148 * <li>Data value.</li>
5149 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5150 * <li>css A CSS style string to apply to the table cell.</li>
5151 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5152 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5153 * <li>Row index</li>
5154 * <li>Column index</li>
5155 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5157 setRenderer : function(col, fn){
5158 this.config[col].renderer = fn;
5162 * Returns the width for the specified column.
5163 * @param {Number} col The column index
5166 getColumnWidth : function(col){
5167 return this.config[col].width * 1 || this.defaultWidth;
5171 * Sets the width for a column.
5172 * @param {Number} col The column index
5173 * @param {Number} width The new width
5175 setColumnWidth : function(col, width, suppressEvent){
5176 this.config[col].width = width;
5177 this.totalWidth = null;
5179 this.fireEvent("widthchange", this, col, width);
5184 * Returns the total width of all columns.
5185 * @param {Boolean} includeHidden True to include hidden column widths
5188 getTotalWidth : function(includeHidden){
5189 if(!this.totalWidth){
5190 this.totalWidth = 0;
5191 for(var i = 0, len = this.config.length; i < len; i++){
5192 if(includeHidden || !this.isHidden(i)){
5193 this.totalWidth += this.getColumnWidth(i);
5197 return this.totalWidth;
5201 * Returns the header for the specified column.
5202 * @param {Number} col The column index
5205 getColumnHeader : function(col){
5206 return this.config[col].header;
5210 * Sets the header for a column.
5211 * @param {Number} col The column index
5212 * @param {String} header The new header
5214 setColumnHeader : function(col, header){
5215 this.config[col].header = header;
5216 this.fireEvent("headerchange", this, col, header);
5220 * Returns the tooltip for the specified column.
5221 * @param {Number} col The column index
5224 getColumnTooltip : function(col){
5225 return this.config[col].tooltip;
5228 * Sets the tooltip for a column.
5229 * @param {Number} col The column index
5230 * @param {String} tooltip The new tooltip
5232 setColumnTooltip : function(col, tooltip){
5233 this.config[col].tooltip = tooltip;
5237 * Returns the dataIndex for the specified column.
5238 * @param {Number} col The column index
5241 getDataIndex : function(col){
5242 return this.config[col].dataIndex;
5246 * Sets the dataIndex for a column.
5247 * @param {Number} col The column index
5248 * @param {Number} dataIndex The new dataIndex
5250 setDataIndex : function(col, dataIndex){
5251 this.config[col].dataIndex = dataIndex;
5257 * Returns true if the cell is editable.
5258 * @param {Number} colIndex The column index
5259 * @param {Number} rowIndex The row index
5262 isCellEditable : function(colIndex, rowIndex){
5263 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5267 * Returns the editor defined for the cell/column.
5268 * return false or null to disable editing.
5269 * @param {Number} colIndex The column index
5270 * @param {Number} rowIndex The row index
5273 getCellEditor : function(colIndex, rowIndex){
5274 return this.config[colIndex].editor;
5278 * Sets if a column is editable.
5279 * @param {Number} col The column index
5280 * @param {Boolean} editable True if the column is editable
5282 setEditable : function(col, editable){
5283 this.config[col].editable = editable;
5288 * Returns true if the column is hidden.
5289 * @param {Number} colIndex The column index
5292 isHidden : function(colIndex){
5293 return this.config[colIndex].hidden;
5298 * Returns true if the column width cannot be changed
5300 isFixed : function(colIndex){
5301 return this.config[colIndex].fixed;
5305 * Returns true if the column can be resized
5308 isResizable : function(colIndex){
5309 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5312 * Sets if a column is hidden.
5313 * @param {Number} colIndex The column index
5314 * @param {Boolean} hidden True if the column is hidden
5316 setHidden : function(colIndex, hidden){
5317 this.config[colIndex].hidden = hidden;
5318 this.totalWidth = null;
5319 this.fireEvent("hiddenchange", this, colIndex, hidden);
5323 * Sets the editor for a column.
5324 * @param {Number} col The column index
5325 * @param {Object} editor The editor object
5327 setEditor : function(col, editor){
5328 this.config[col].editor = editor;
5332 Roo.grid.ColumnModel.defaultRenderer = function(value){
5333 if(typeof value == "string" && value.length < 1){
5339 // Alias for backwards compatibility
5340 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5343 * Ext JS Library 1.1.1
5344 * Copyright(c) 2006-2007, Ext JS, LLC.
5346 * Originally Released Under LGPL - original licence link has changed is not relivant.
5349 * <script type="text/javascript">
5353 * @class Roo.LoadMask
5354 * A simple utility class for generically masking elements while loading data. If the element being masked has
5355 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5356 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5357 * element's UpdateManager load indicator and will be destroyed after the initial load.
5359 * Create a new LoadMask
5360 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5361 * @param {Object} config The config object
5363 Roo.LoadMask = function(el, config){
5364 this.el = Roo.get(el);
5365 Roo.apply(this, config);
5367 this.store.on('beforeload', this.onBeforeLoad, this);
5368 this.store.on('load', this.onLoad, this);
5369 this.store.on('loadexception', this.onLoadException, this);
5370 this.removeMask = false;
5372 var um = this.el.getUpdateManager();
5373 um.showLoadIndicator = false; // disable the default indicator
5374 um.on('beforeupdate', this.onBeforeLoad, this);
5375 um.on('update', this.onLoad, this);
5376 um.on('failure', this.onLoad, this);
5377 this.removeMask = true;
5381 Roo.LoadMask.prototype = {
5383 * @cfg {Boolean} removeMask
5384 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5385 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5389 * The text to display in a centered loading message box (defaults to 'Loading...')
5393 * @cfg {String} msgCls
5394 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5396 msgCls : 'x-mask-loading',
5399 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5405 * Disables the mask to prevent it from being displayed
5407 disable : function(){
5408 this.disabled = true;
5412 * Enables the mask so that it can be displayed
5414 enable : function(){
5415 this.disabled = false;
5418 onLoadException : function()
5422 if (typeof(arguments[3]) != 'undefined') {
5423 Roo.MessageBox.alert("Error loading",arguments[3]);
5427 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5428 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5437 this.el.unmask(this.removeMask);
5442 this.el.unmask(this.removeMask);
5446 onBeforeLoad : function(){
5448 this.el.mask(this.msg, this.msgCls);
5453 destroy : function(){
5455 this.store.un('beforeload', this.onBeforeLoad, this);
5456 this.store.un('load', this.onLoad, this);
5457 this.store.un('loadexception', this.onLoadException, this);
5459 var um = this.el.getUpdateManager();
5460 um.un('beforeupdate', this.onBeforeLoad, this);
5461 um.un('update', this.onLoad, this);
5462 um.un('failure', this.onLoad, this);
5473 * @class Roo.bootstrap.Table
5474 * @extends Roo.bootstrap.Component
5475 * Bootstrap Table class
5476 * @cfg {String} cls table class
5477 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5478 * @cfg {String} bgcolor Specifies the background color for a table
5479 * @cfg {Number} border Specifies whether the table cells should have borders or not
5480 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5481 * @cfg {Number} cellspacing Specifies the space between cells
5482 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5483 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5484 * @cfg {String} sortable Specifies that the table should be sortable
5485 * @cfg {String} summary Specifies a summary of the content of a table
5486 * @cfg {Number} width Specifies the width of a table
5487 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5489 * @cfg {boolean} striped Should the rows be alternative striped
5490 * @cfg {boolean} bordered Add borders to the table
5491 * @cfg {boolean} hover Add hover highlighting
5492 * @cfg {boolean} condensed Format condensed
5493 * @cfg {boolean} responsive Format condensed
5494 * @cfg {Boolean} loadMask (true|false) default false
5495 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5496 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5497 * @cfg {Boolean} rowSelection (true|false) default false
5498 * @cfg {Boolean} cellSelection (true|false) default false
5499 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5503 * Create a new Table
5504 * @param {Object} config The config object
5507 Roo.bootstrap.Table = function(config){
5508 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5511 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5512 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5513 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5514 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5518 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5519 this.sm = this.selModel;
5520 this.sm.xmodule = this.xmodule || false;
5522 if (this.cm && typeof(this.cm.config) == 'undefined') {
5523 this.colModel = new Roo.grid.ColumnModel(this.cm);
5524 this.cm = this.colModel;
5525 this.cm.xmodule = this.xmodule || false;
5528 this.store= Roo.factory(this.store, Roo.data);
5529 this.ds = this.store;
5530 this.ds.xmodule = this.xmodule || false;
5533 if (this.footer && this.store) {
5534 this.footer.dataSource = this.ds;
5535 this.footer = Roo.factory(this.footer);
5542 * Fires when a cell is clicked
5543 * @param {Roo.bootstrap.Table} this
5544 * @param {Roo.Element} el
5545 * @param {Number} rowIndex
5546 * @param {Number} columnIndex
5547 * @param {Roo.EventObject} e
5551 * @event celldblclick
5552 * Fires when a cell is double clicked
5553 * @param {Roo.bootstrap.Table} this
5554 * @param {Roo.Element} el
5555 * @param {Number} rowIndex
5556 * @param {Number} columnIndex
5557 * @param {Roo.EventObject} e
5559 "celldblclick" : true,
5562 * Fires when a row is clicked
5563 * @param {Roo.bootstrap.Table} this
5564 * @param {Roo.Element} el
5565 * @param {Number} rowIndex
5566 * @param {Roo.EventObject} e
5570 * @event rowdblclick
5571 * Fires when a row is double clicked
5572 * @param {Roo.bootstrap.Table} this
5573 * @param {Roo.Element} el
5574 * @param {Number} rowIndex
5575 * @param {Roo.EventObject} e
5577 "rowdblclick" : true,
5580 * Fires when a mouseover occur
5581 * @param {Roo.bootstrap.Table} this
5582 * @param {Roo.Element} el
5583 * @param {Number} rowIndex
5584 * @param {Number} columnIndex
5585 * @param {Roo.EventObject} e
5590 * Fires when a mouseout occur
5591 * @param {Roo.bootstrap.Table} this
5592 * @param {Roo.Element} el
5593 * @param {Number} rowIndex
5594 * @param {Number} columnIndex
5595 * @param {Roo.EventObject} e
5600 * Fires when a row is rendered, so you can change add a style to it.
5601 * @param {Roo.bootstrap.Table} this
5602 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5606 * @event rowsrendered
5607 * Fires when all the rows have been rendered
5608 * @param {Roo.bootstrap.Table} this
5610 'rowsrendered' : true
5615 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5640 rowSelection : false,
5641 cellSelection : false,
5644 // Roo.Element - the tbody
5647 getAutoCreate : function(){
5648 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5657 cfg.cls += ' table-striped';
5661 cfg.cls += ' table-hover';
5663 if (this.bordered) {
5664 cfg.cls += ' table-bordered';
5666 if (this.condensed) {
5667 cfg.cls += ' table-condensed';
5669 if (this.responsive) {
5670 cfg.cls += ' table-responsive';
5674 cfg.cls+= ' ' +this.cls;
5677 // this lot should be simplifed...
5680 cfg.align=this.align;
5683 cfg.bgcolor=this.bgcolor;
5686 cfg.border=this.border;
5688 if (this.cellpadding) {
5689 cfg.cellpadding=this.cellpadding;
5691 if (this.cellspacing) {
5692 cfg.cellspacing=this.cellspacing;
5695 cfg.frame=this.frame;
5698 cfg.rules=this.rules;
5700 if (this.sortable) {
5701 cfg.sortable=this.sortable;
5704 cfg.summary=this.summary;
5707 cfg.width=this.width;
5710 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5713 if(this.store || this.cm){
5714 if(this.headerShow){
5715 cfg.cn.push(this.renderHeader());
5718 cfg.cn.push(this.renderBody());
5720 if(this.footerShow){
5721 cfg.cn.push(this.renderFooter());
5724 cfg.cls+= ' TableGrid';
5727 return { cn : [ cfg ] };
5730 initEvents : function()
5732 if(!this.store || !this.cm){
5736 //Roo.log('initEvents with ds!!!!');
5738 this.mainBody = this.el.select('tbody', true).first();
5743 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5744 e.on('click', _this.sort, _this);
5747 this.el.on("click", this.onClick, this);
5748 this.el.on("dblclick", this.onDblClick, this);
5750 // why is this done????? = it breaks dialogs??
5751 //this.parent().el.setStyle('position', 'relative');
5755 this.footer.parentId = this.id;
5756 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5759 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5761 this.store.on('load', this.onLoad, this);
5762 this.store.on('beforeload', this.onBeforeLoad, this);
5763 this.store.on('update', this.onUpdate, this);
5764 this.store.on('add', this.onAdd, this);
5768 onMouseover : function(e, el)
5770 var cell = Roo.get(el);
5776 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5777 cell = cell.findParent('td', false, true);
5780 var row = cell.findParent('tr', false, true);
5781 var cellIndex = cell.dom.cellIndex;
5782 var rowIndex = row.dom.rowIndex - 1; // start from 0
5784 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5788 onMouseout : function(e, el)
5790 var cell = Roo.get(el);
5796 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5797 cell = cell.findParent('td', false, true);
5800 var row = cell.findParent('tr', false, true);
5801 var cellIndex = cell.dom.cellIndex;
5802 var rowIndex = row.dom.rowIndex - 1; // start from 0
5804 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5808 onClick : function(e, el)
5810 var cell = Roo.get(el);
5812 if(!cell || (!this.cellSelection && !this.rowSelection)){
5816 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5817 cell = cell.findParent('td', false, true);
5820 if(!cell || typeof(cell) == 'undefined'){
5824 var row = cell.findParent('tr', false, true);
5826 if(!row || typeof(row) == 'undefined'){
5830 var cellIndex = cell.dom.cellIndex;
5831 var rowIndex = this.getRowIndex(row);
5833 // why??? - should these not be based on SelectionModel?
5834 if(this.cellSelection){
5835 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5838 if(this.rowSelection){
5839 this.fireEvent('rowclick', this, row, rowIndex, e);
5845 onDblClick : function(e,el)
5847 var cell = Roo.get(el);
5849 if(!cell || (!this.CellSelection && !this.RowSelection)){
5853 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5854 cell = cell.findParent('td', false, true);
5857 if(!cell || typeof(cell) == 'undefined'){
5861 var row = cell.findParent('tr', false, true);
5863 if(!row || typeof(row) == 'undefined'){
5867 var cellIndex = cell.dom.cellIndex;
5868 var rowIndex = this.getRowIndex(row);
5870 if(this.CellSelection){
5871 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5874 if(this.RowSelection){
5875 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5879 sort : function(e,el)
5881 var col = Roo.get(el);
5883 if(!col.hasClass('sortable')){
5887 var sort = col.attr('sort');
5890 if(col.hasClass('glyphicon-arrow-up')){
5894 this.store.sortInfo = {field : sort, direction : dir};
5897 Roo.log("calling footer first");
5898 this.footer.onClick('first');
5901 this.store.load({ params : { start : 0 } });
5905 renderHeader : function()
5914 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5916 var config = cm.config[i];
5921 html: cm.getColumnHeader(i)
5926 if(typeof(config.lgHeader) != 'undefined'){
5927 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5930 if(typeof(config.mdHeader) != 'undefined'){
5931 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5934 if(typeof(config.smHeader) != 'undefined'){
5935 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5938 if(typeof(config.xsHeader) != 'undefined'){
5939 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5946 if(typeof(config.tooltip) != 'undefined'){
5947 c.tooltip = config.tooltip;
5950 if(typeof(config.colspan) != 'undefined'){
5951 c.colspan = config.colspan;
5954 if(typeof(config.hidden) != 'undefined' && config.hidden){
5955 c.style += ' display:none;';
5958 if(typeof(config.dataIndex) != 'undefined'){
5959 c.sort = config.dataIndex;
5962 if(typeof(config.sortable) != 'undefined' && config.sortable){
5966 if(typeof(config.align) != 'undefined' && config.align.length){
5967 c.style += ' text-align:' + config.align + ';';
5970 if(typeof(config.width) != 'undefined'){
5971 c.style += ' width:' + config.width + 'px;';
5974 if(typeof(config.cls) != 'undefined'){
5975 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5984 renderBody : function()
5994 colspan : this.cm.getColumnCount()
6004 renderFooter : function()
6014 colspan : this.cm.getColumnCount()
6028 Roo.log('ds onload');
6033 var ds = this.store;
6035 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6036 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6038 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6039 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6042 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6043 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6047 var tbody = this.mainBody;
6049 if(ds.getCount() > 0){
6050 ds.data.each(function(d,rowIndex){
6051 var row = this.renderRow(cm, ds, rowIndex);
6053 tbody.createChild(row);
6057 if(row.cellObjects.length){
6058 Roo.each(row.cellObjects, function(r){
6059 _this.renderCellObject(r);
6066 Roo.each(this.el.select('tbody td', true).elements, function(e){
6067 e.on('mouseover', _this.onMouseover, _this);
6070 Roo.each(this.el.select('tbody td', true).elements, function(e){
6071 e.on('mouseout', _this.onMouseout, _this);
6073 this.fireEvent('rowsrendered', this);
6074 //if(this.loadMask){
6075 // this.maskEl.hide();
6080 onUpdate : function(ds,record)
6082 this.refreshRow(record);
6085 onRemove : function(ds, record, index, isUpdate){
6086 if(isUpdate !== true){
6087 this.fireEvent("beforerowremoved", this, index, record);
6089 var bt = this.mainBody.dom;
6091 var rows = this.el.select('tbody > tr', true).elements;
6093 if(typeof(rows[index]) != 'undefined'){
6094 bt.removeChild(rows[index].dom);
6097 // if(bt.rows[index]){
6098 // bt.removeChild(bt.rows[index]);
6101 if(isUpdate !== true){
6102 //this.stripeRows(index);
6103 //this.syncRowHeights(index, index);
6105 this.fireEvent("rowremoved", this, index, record);
6109 onAdd : function(ds, records, rowIndex)
6111 //Roo.log('on Add called');
6112 // - note this does not handle multiple adding very well..
6113 var bt = this.mainBody.dom;
6114 for (var i =0 ; i < records.length;i++) {
6115 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6116 //Roo.log(records[i]);
6117 //Roo.log(this.store.getAt(rowIndex+i));
6118 this.insertRow(this.store, rowIndex + i, false);
6125 refreshRow : function(record){
6126 var ds = this.store, index;
6127 if(typeof record == 'number'){
6129 record = ds.getAt(index);
6131 index = ds.indexOf(record);
6133 this.insertRow(ds, index, true);
6134 this.onRemove(ds, record, index+1, true);
6135 //this.syncRowHeights(index, index);
6137 this.fireEvent("rowupdated", this, index, record);
6140 insertRow : function(dm, rowIndex, isUpdate){
6143 this.fireEvent("beforerowsinserted", this, rowIndex);
6145 //var s = this.getScrollState();
6146 var row = this.renderRow(this.cm, this.store, rowIndex);
6147 // insert before rowIndex..
6148 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6152 if(row.cellObjects.length){
6153 Roo.each(row.cellObjects, function(r){
6154 _this.renderCellObject(r);
6159 this.fireEvent("rowsinserted", this, rowIndex);
6160 //this.syncRowHeights(firstRow, lastRow);
6161 //this.stripeRows(firstRow);
6168 getRowDom : function(rowIndex)
6170 var rows = this.el.select('tbody > tr', true).elements;
6172 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6175 // returns the object tree for a tr..
6178 renderRow : function(cm, ds, rowIndex)
6181 var d = ds.getAt(rowIndex);
6188 var cellObjects = [];
6190 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6191 var config = cm.config[i];
6193 var renderer = cm.getRenderer(i);
6197 if(typeof(renderer) !== 'undefined'){
6198 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6200 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6201 // and are rendered into the cells after the row is rendered - using the id for the element.
6203 if(typeof(value) === 'object'){
6213 rowIndex : rowIndex,
6218 this.fireEvent('rowclass', this, rowcfg);
6222 cls : rowcfg.rowClass,
6224 html: (typeof(value) === 'object') ? '' : value
6231 if(typeof(config.colspan) != 'undefined'){
6232 td.colspan = config.colspan;
6235 if(typeof(config.hidden) != 'undefined' && config.hidden){
6236 td.style += ' display:none;';
6239 if(typeof(config.align) != 'undefined' && config.align.length){
6240 td.style += ' text-align:' + config.align + ';';
6243 if(typeof(config.width) != 'undefined'){
6244 td.style += ' width:' + config.width + 'px;';
6247 if(typeof(config.cursor) != 'undefined'){
6248 td.style += ' cursor:' + config.cursor + ';';
6251 if(typeof(config.cls) != 'undefined'){
6252 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6259 row.cellObjects = cellObjects;
6267 onBeforeLoad : function()
6269 //Roo.log('ds onBeforeLoad');
6273 //if(this.loadMask){
6274 // this.maskEl.show();
6282 this.el.select('tbody', true).first().dom.innerHTML = '';
6285 * Show or hide a row.
6286 * @param {Number} rowIndex to show or hide
6287 * @param {Boolean} state hide
6289 setRowVisibility : function(rowIndex, state)
6291 var bt = this.mainBody.dom;
6293 var rows = this.el.select('tbody > tr', true).elements;
6295 if(typeof(rows[rowIndex]) == 'undefined'){
6298 rows[rowIndex].dom.style.display = state ? '' : 'none';
6302 getSelectionModel : function(){
6304 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6306 return this.selModel;
6309 * Render the Roo.bootstrap object from renderder
6311 renderCellObject : function(r)
6315 var t = r.cfg.render(r.container);
6318 Roo.each(r.cfg.cn, function(c){
6320 container: t.getChildContainer(),
6323 _this.renderCellObject(child);
6328 getRowIndex : function(row)
6332 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6355 * @class Roo.bootstrap.TableCell
6356 * @extends Roo.bootstrap.Component
6357 * Bootstrap TableCell class
6358 * @cfg {String} html cell contain text
6359 * @cfg {String} cls cell class
6360 * @cfg {String} tag cell tag (td|th) default td
6361 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6362 * @cfg {String} align Aligns the content in a cell
6363 * @cfg {String} axis Categorizes cells
6364 * @cfg {String} bgcolor Specifies the background color of a cell
6365 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6366 * @cfg {Number} colspan Specifies the number of columns a cell should span
6367 * @cfg {String} headers Specifies one or more header cells a cell is related to
6368 * @cfg {Number} height Sets the height of a cell
6369 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6370 * @cfg {Number} rowspan Sets the number of rows a cell should span
6371 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6372 * @cfg {String} valign Vertical aligns the content in a cell
6373 * @cfg {Number} width Specifies the width of a cell
6376 * Create a new TableCell
6377 * @param {Object} config The config object
6380 Roo.bootstrap.TableCell = function(config){
6381 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6384 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6404 getAutoCreate : function(){
6405 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6425 cfg.align=this.align
6431 cfg.bgcolor=this.bgcolor
6434 cfg.charoff=this.charoff
6437 cfg.colspan=this.colspan
6440 cfg.headers=this.headers
6443 cfg.height=this.height
6446 cfg.nowrap=this.nowrap
6449 cfg.rowspan=this.rowspan
6452 cfg.scope=this.scope
6455 cfg.valign=this.valign
6458 cfg.width=this.width
6477 * @class Roo.bootstrap.TableRow
6478 * @extends Roo.bootstrap.Component
6479 * Bootstrap TableRow class
6480 * @cfg {String} cls row class
6481 * @cfg {String} align Aligns the content in a table row
6482 * @cfg {String} bgcolor Specifies a background color for a table row
6483 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6484 * @cfg {String} valign Vertical aligns the content in a table row
6487 * Create a new TableRow
6488 * @param {Object} config The config object
6491 Roo.bootstrap.TableRow = function(config){
6492 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6495 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6503 getAutoCreate : function(){
6504 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6514 cfg.align = this.align;
6517 cfg.bgcolor = this.bgcolor;
6520 cfg.charoff = this.charoff;
6523 cfg.valign = this.valign;
6541 * @class Roo.bootstrap.TableBody
6542 * @extends Roo.bootstrap.Component
6543 * Bootstrap TableBody class
6544 * @cfg {String} cls element class
6545 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6546 * @cfg {String} align Aligns the content inside the element
6547 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6548 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6551 * Create a new TableBody
6552 * @param {Object} config The config object
6555 Roo.bootstrap.TableBody = function(config){
6556 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6559 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6567 getAutoCreate : function(){
6568 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6582 cfg.align = this.align;
6585 cfg.charoff = this.charoff;
6588 cfg.valign = this.valign;
6595 // initEvents : function()
6602 // this.store = Roo.factory(this.store, Roo.data);
6603 // this.store.on('load', this.onLoad, this);
6605 // this.store.load();
6609 // onLoad: function ()
6611 // this.fireEvent('load', this);
6621 * Ext JS Library 1.1.1
6622 * Copyright(c) 2006-2007, Ext JS, LLC.
6624 * Originally Released Under LGPL - original licence link has changed is not relivant.
6627 * <script type="text/javascript">
6630 // as we use this in bootstrap.
6631 Roo.namespace('Roo.form');
6633 * @class Roo.form.Action
6634 * Internal Class used to handle form actions
6636 * @param {Roo.form.BasicForm} el The form element or its id
6637 * @param {Object} config Configuration options
6642 // define the action interface
6643 Roo.form.Action = function(form, options){
6645 this.options = options || {};
6648 * Client Validation Failed
6651 Roo.form.Action.CLIENT_INVALID = 'client';
6653 * Server Validation Failed
6656 Roo.form.Action.SERVER_INVALID = 'server';
6658 * Connect to Server Failed
6661 Roo.form.Action.CONNECT_FAILURE = 'connect';
6663 * Reading Data from Server Failed
6666 Roo.form.Action.LOAD_FAILURE = 'load';
6668 Roo.form.Action.prototype = {
6670 failureType : undefined,
6671 response : undefined,
6675 run : function(options){
6680 success : function(response){
6685 handleResponse : function(response){
6689 // default connection failure
6690 failure : function(response){
6692 this.response = response;
6693 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6694 this.form.afterAction(this, false);
6697 processResponse : function(response){
6698 this.response = response;
6699 if(!response.responseText){
6702 this.result = this.handleResponse(response);
6706 // utility functions used internally
6707 getUrl : function(appendParams){
6708 var url = this.options.url || this.form.url || this.form.el.dom.action;
6710 var p = this.getParams();
6712 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6718 getMethod : function(){
6719 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6722 getParams : function(){
6723 var bp = this.form.baseParams;
6724 var p = this.options.params;
6726 if(typeof p == "object"){
6727 p = Roo.urlEncode(Roo.applyIf(p, bp));
6728 }else if(typeof p == 'string' && bp){
6729 p += '&' + Roo.urlEncode(bp);
6732 p = Roo.urlEncode(bp);
6737 createCallback : function(){
6739 success: this.success,
6740 failure: this.failure,
6742 timeout: (this.form.timeout*1000),
6743 upload: this.form.fileUpload ? this.success : undefined
6748 Roo.form.Action.Submit = function(form, options){
6749 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6752 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6755 haveProgress : false,
6756 uploadComplete : false,
6758 // uploadProgress indicator.
6759 uploadProgress : function()
6761 if (!this.form.progressUrl) {
6765 if (!this.haveProgress) {
6766 Roo.MessageBox.progress("Uploading", "Uploading");
6768 if (this.uploadComplete) {
6769 Roo.MessageBox.hide();
6773 this.haveProgress = true;
6775 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6777 var c = new Roo.data.Connection();
6779 url : this.form.progressUrl,
6784 success : function(req){
6785 //console.log(data);
6789 rdata = Roo.decode(req.responseText)
6791 Roo.log("Invalid data from server..");
6795 if (!rdata || !rdata.success) {
6797 Roo.MessageBox.alert(Roo.encode(rdata));
6800 var data = rdata.data;
6802 if (this.uploadComplete) {
6803 Roo.MessageBox.hide();
6808 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6809 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6812 this.uploadProgress.defer(2000,this);
6815 failure: function(data) {
6816 Roo.log('progress url failed ');
6827 // run get Values on the form, so it syncs any secondary forms.
6828 this.form.getValues();
6830 var o = this.options;
6831 var method = this.getMethod();
6832 var isPost = method == 'POST';
6833 if(o.clientValidation === false || this.form.isValid()){
6835 if (this.form.progressUrl) {
6836 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6837 (new Date() * 1) + '' + Math.random());
6842 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6843 form:this.form.el.dom,
6844 url:this.getUrl(!isPost),
6846 params:isPost ? this.getParams() : null,
6847 isUpload: this.form.fileUpload
6850 this.uploadProgress();
6852 }else if (o.clientValidation !== false){ // client validation failed
6853 this.failureType = Roo.form.Action.CLIENT_INVALID;
6854 this.form.afterAction(this, false);
6858 success : function(response)
6860 this.uploadComplete= true;
6861 if (this.haveProgress) {
6862 Roo.MessageBox.hide();
6866 var result = this.processResponse(response);
6867 if(result === true || result.success){
6868 this.form.afterAction(this, true);
6872 this.form.markInvalid(result.errors);
6873 this.failureType = Roo.form.Action.SERVER_INVALID;
6875 this.form.afterAction(this, false);
6877 failure : function(response)
6879 this.uploadComplete= true;
6880 if (this.haveProgress) {
6881 Roo.MessageBox.hide();
6884 this.response = response;
6885 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6886 this.form.afterAction(this, false);
6889 handleResponse : function(response){
6890 if(this.form.errorReader){
6891 var rs = this.form.errorReader.read(response);
6894 for(var i = 0, len = rs.records.length; i < len; i++) {
6895 var r = rs.records[i];
6899 if(errors.length < 1){
6903 success : rs.success,
6909 ret = Roo.decode(response.responseText);
6913 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6923 Roo.form.Action.Load = function(form, options){
6924 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6925 this.reader = this.form.reader;
6928 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6933 Roo.Ajax.request(Roo.apply(
6934 this.createCallback(), {
6935 method:this.getMethod(),
6936 url:this.getUrl(false),
6937 params:this.getParams()
6941 success : function(response){
6943 var result = this.processResponse(response);
6944 if(result === true || !result.success || !result.data){
6945 this.failureType = Roo.form.Action.LOAD_FAILURE;
6946 this.form.afterAction(this, false);
6949 this.form.clearInvalid();
6950 this.form.setValues(result.data);
6951 this.form.afterAction(this, true);
6954 handleResponse : function(response){
6955 if(this.form.reader){
6956 var rs = this.form.reader.read(response);
6957 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6959 success : rs.success,
6963 return Roo.decode(response.responseText);
6967 Roo.form.Action.ACTION_TYPES = {
6968 'load' : Roo.form.Action.Load,
6969 'submit' : Roo.form.Action.Submit
6978 * @class Roo.bootstrap.Form
6979 * @extends Roo.bootstrap.Component
6980 * Bootstrap Form class
6981 * @cfg {String} method GET | POST (default POST)
6982 * @cfg {String} labelAlign top | left (default top)
6983 * @cfg {String} align left | right - for navbars
6984 * @cfg {Boolean} loadMask load mask when submit (default true)
6989 * @param {Object} config The config object
6993 Roo.bootstrap.Form = function(config){
6994 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6997 * @event clientvalidation
6998 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6999 * @param {Form} this
7000 * @param {Boolean} valid true if the form has passed client-side validation
7002 clientvalidation: true,
7004 * @event beforeaction
7005 * Fires before any action is performed. Return false to cancel the action.
7006 * @param {Form} this
7007 * @param {Action} action The action to be performed
7011 * @event actionfailed
7012 * Fires when an action fails.
7013 * @param {Form} this
7014 * @param {Action} action The action that failed
7016 actionfailed : true,
7018 * @event actioncomplete
7019 * Fires when an action is completed.
7020 * @param {Form} this
7021 * @param {Action} action The action that completed
7023 actioncomplete : true
7028 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7031 * @cfg {String} method
7032 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7037 * The URL to use for form actions if one isn't supplied in the action options.
7040 * @cfg {Boolean} fileUpload
7041 * Set to true if this form is a file upload.
7045 * @cfg {Object} baseParams
7046 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7050 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7054 * @cfg {Sting} align (left|right) for navbar forms
7059 activeAction : null,
7062 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7063 * element by passing it or its id or mask the form itself by passing in true.
7066 waitMsgTarget : false,
7070 getAutoCreate : function(){
7074 method : this.method || 'POST',
7075 id : this.id || Roo.id(),
7078 if (this.parent().xtype.match(/^Nav/)) {
7079 cfg.cls = 'navbar-form navbar-' + this.align;
7083 if (this.labelAlign == 'left' ) {
7084 cfg.cls += ' form-horizontal';
7090 initEvents : function()
7092 this.el.on('submit', this.onSubmit, this);
7093 // this was added as random key presses on the form where triggering form submit.
7094 this.el.on('keypress', function(e) {
7095 if (e.getCharCode() != 13) {
7098 // we might need to allow it for textareas.. and some other items.
7099 // check e.getTarget().
7101 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7105 Roo.log("keypress blocked");
7113 onSubmit : function(e){
7118 * Returns true if client-side validation on the form is successful.
7121 isValid : function(){
7122 var items = this.getItems();
7124 items.each(function(f){
7133 * Returns true if any fields in this form have changed since their original load.
7136 isDirty : function(){
7138 var items = this.getItems();
7139 items.each(function(f){
7149 * Performs a predefined action (submit or load) or custom actions you define on this form.
7150 * @param {String} actionName The name of the action type
7151 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7152 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7153 * accept other config options):
7155 Property Type Description
7156 ---------------- --------------- ----------------------------------------------------------------------------------
7157 url String The url for the action (defaults to the form's url)
7158 method String The form method to use (defaults to the form's method, or POST if not defined)
7159 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7160 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7161 validate the form on the client (defaults to false)
7163 * @return {BasicForm} this
7165 doAction : function(action, options){
7166 if(typeof action == 'string'){
7167 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7169 if(this.fireEvent('beforeaction', this, action) !== false){
7170 this.beforeAction(action);
7171 action.run.defer(100, action);
7177 beforeAction : function(action){
7178 var o = action.options;
7181 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7183 // not really supported yet.. ??
7185 //if(this.waitMsgTarget === true){
7186 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7187 //}else if(this.waitMsgTarget){
7188 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7189 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7191 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7197 afterAction : function(action, success){
7198 this.activeAction = null;
7199 var o = action.options;
7201 //if(this.waitMsgTarget === true){
7203 //}else if(this.waitMsgTarget){
7204 // this.waitMsgTarget.unmask();
7206 // Roo.MessageBox.updateProgress(1);
7207 // Roo.MessageBox.hide();
7214 Roo.callback(o.success, o.scope, [this, action]);
7215 this.fireEvent('actioncomplete', this, action);
7219 // failure condition..
7220 // we have a scenario where updates need confirming.
7221 // eg. if a locking scenario exists..
7222 // we look for { errors : { needs_confirm : true }} in the response.
7224 (typeof(action.result) != 'undefined') &&
7225 (typeof(action.result.errors) != 'undefined') &&
7226 (typeof(action.result.errors.needs_confirm) != 'undefined')
7229 Roo.log("not supported yet");
7232 Roo.MessageBox.confirm(
7233 "Change requires confirmation",
7234 action.result.errorMsg,
7239 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7249 Roo.callback(o.failure, o.scope, [this, action]);
7250 // show an error message if no failed handler is set..
7251 if (!this.hasListener('actionfailed')) {
7252 Roo.log("need to add dialog support");
7254 Roo.MessageBox.alert("Error",
7255 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7256 action.result.errorMsg :
7257 "Saving Failed, please check your entries or try again"
7262 this.fireEvent('actionfailed', this, action);
7267 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7268 * @param {String} id The value to search for
7271 findField : function(id){
7272 var items = this.getItems();
7273 var field = items.get(id);
7275 items.each(function(f){
7276 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7283 return field || null;
7286 * Mark fields in this form invalid in bulk.
7287 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7288 * @return {BasicForm} this
7290 markInvalid : function(errors){
7291 if(errors instanceof Array){
7292 for(var i = 0, len = errors.length; i < len; i++){
7293 var fieldError = errors[i];
7294 var f = this.findField(fieldError.id);
7296 f.markInvalid(fieldError.msg);
7302 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7303 field.markInvalid(errors[id]);
7307 //Roo.each(this.childForms || [], function (f) {
7308 // f.markInvalid(errors);
7315 * Set values for fields in this form in bulk.
7316 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7317 * @return {BasicForm} this
7319 setValues : function(values){
7320 if(values instanceof Array){ // array of objects
7321 for(var i = 0, len = values.length; i < len; i++){
7323 var f = this.findField(v.id);
7325 f.setValue(v.value);
7326 if(this.trackResetOnLoad){
7327 f.originalValue = f.getValue();
7331 }else{ // object hash
7334 if(typeof values[id] != 'function' && (field = this.findField(id))){
7336 if (field.setFromData &&
7338 field.displayField &&
7339 // combos' with local stores can
7340 // be queried via setValue()
7341 // to set their value..
7342 (field.store && !field.store.isLocal)
7346 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7347 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7348 field.setFromData(sd);
7351 field.setValue(values[id]);
7355 if(this.trackResetOnLoad){
7356 field.originalValue = field.getValue();
7362 //Roo.each(this.childForms || [], function (f) {
7363 // f.setValues(values);
7370 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7371 * they are returned as an array.
7372 * @param {Boolean} asString
7375 getValues : function(asString){
7376 //if (this.childForms) {
7377 // copy values from the child forms
7378 // Roo.each(this.childForms, function (f) {
7379 // this.setValues(f.getValues());
7385 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7386 if(asString === true){
7389 return Roo.urlDecode(fs);
7393 * Returns the fields in this form as an object with key/value pairs.
7394 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7397 getFieldValues : function(with_hidden)
7399 var items = this.getItems();
7401 items.each(function(f){
7405 var v = f.getValue();
7406 if (f.inputType =='radio') {
7407 if (typeof(ret[f.getName()]) == 'undefined') {
7408 ret[f.getName()] = ''; // empty..
7411 if (!f.el.dom.checked) {
7419 // not sure if this supported any more..
7420 if ((typeof(v) == 'object') && f.getRawValue) {
7421 v = f.getRawValue() ; // dates..
7423 // combo boxes where name != hiddenName...
7424 if (f.name != f.getName()) {
7425 ret[f.name] = f.getRawValue();
7427 ret[f.getName()] = v;
7434 * Clears all invalid messages in this form.
7435 * @return {BasicForm} this
7437 clearInvalid : function(){
7438 var items = this.getItems();
7440 items.each(function(f){
7451 * @return {BasicForm} this
7454 var items = this.getItems();
7455 items.each(function(f){
7459 Roo.each(this.childForms || [], function (f) {
7466 getItems : function()
7468 var r=new Roo.util.MixedCollection(false, function(o){
7469 return o.id || (o.id = Roo.id());
7471 var iter = function(el) {
7478 Roo.each(el.items,function(e) {
7498 * Ext JS Library 1.1.1
7499 * Copyright(c) 2006-2007, Ext JS, LLC.
7501 * Originally Released Under LGPL - original licence link has changed is not relivant.
7504 * <script type="text/javascript">
7507 * @class Roo.form.VTypes
7508 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7511 Roo.form.VTypes = function(){
7512 // closure these in so they are only created once.
7513 var alpha = /^[a-zA-Z_]+$/;
7514 var alphanum = /^[a-zA-Z0-9_]+$/;
7515 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7516 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7518 // All these messages and functions are configurable
7521 * The function used to validate email addresses
7522 * @param {String} value The email address
7524 'email' : function(v){
7525 return email.test(v);
7528 * The error text to display when the email validation function returns false
7531 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7533 * The keystroke filter mask to be applied on email input
7536 'emailMask' : /[a-z0-9_\.\-@]/i,
7539 * The function used to validate URLs
7540 * @param {String} value The URL
7542 'url' : function(v){
7546 * The error text to display when the url validation function returns false
7549 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7552 * The function used to validate alpha values
7553 * @param {String} value The value
7555 'alpha' : function(v){
7556 return alpha.test(v);
7559 * The error text to display when the alpha validation function returns false
7562 'alphaText' : 'This field should only contain letters and _',
7564 * The keystroke filter mask to be applied on alpha input
7567 'alphaMask' : /[a-z_]/i,
7570 * The function used to validate alphanumeric values
7571 * @param {String} value The value
7573 'alphanum' : function(v){
7574 return alphanum.test(v);
7577 * The error text to display when the alphanumeric validation function returns false
7580 'alphanumText' : 'This field should only contain letters, numbers and _',
7582 * The keystroke filter mask to be applied on alphanumeric input
7585 'alphanumMask' : /[a-z0-9_]/i
7595 * @class Roo.bootstrap.Input
7596 * @extends Roo.bootstrap.Component
7597 * Bootstrap Input class
7598 * @cfg {Boolean} disabled is it disabled
7599 * @cfg {String} fieldLabel - the label associated
7600 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7601 * @cfg {String} name name of the input
7602 * @cfg {string} fieldLabel - the label associated
7603 * @cfg {string} inputType - input / file submit ...
7604 * @cfg {string} placeholder - placeholder to put in text.
7605 * @cfg {string} before - input group add on before
7606 * @cfg {string} after - input group add on after
7607 * @cfg {string} size - (lg|sm) or leave empty..
7608 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7609 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7610 * @cfg {Number} md colspan out of 12 for computer-sized screens
7611 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7612 * @cfg {string} value default value of the input
7613 * @cfg {Number} labelWidth set the width of label (0-12)
7614 * @cfg {String} labelAlign (top|left)
7615 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7616 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7618 * @cfg {String} align (left|center|right) Default left
7619 * @cfg {Boolean} forceFeedback (true|false) Default false
7625 * Create a new Input
7626 * @param {Object} config The config object
7629 Roo.bootstrap.Input = function(config){
7630 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7635 * Fires when this field receives input focus.
7636 * @param {Roo.form.Field} this
7641 * Fires when this field loses input focus.
7642 * @param {Roo.form.Field} this
7647 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7648 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7649 * @param {Roo.form.Field} this
7650 * @param {Roo.EventObject} e The event object
7655 * Fires just before the field blurs if the field value has changed.
7656 * @param {Roo.form.Field} this
7657 * @param {Mixed} newValue The new value
7658 * @param {Mixed} oldValue The original value
7663 * Fires after the field has been marked as invalid.
7664 * @param {Roo.form.Field} this
7665 * @param {String} msg The validation message
7670 * Fires after the field has been validated with no errors.
7671 * @param {Roo.form.Field} this
7676 * Fires after the key up
7677 * @param {Roo.form.Field} this
7678 * @param {Roo.EventObject} e The event Object
7684 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7686 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7687 automatic validation (defaults to "keyup").
7689 validationEvent : "keyup",
7691 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7693 validateOnBlur : true,
7695 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7697 validationDelay : 250,
7699 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7701 focusClass : "x-form-focus", // not needed???
7705 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7707 invalidClass : "has-warning",
7710 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7712 validClass : "has-success",
7715 * @cfg {Boolean} hasFeedback (true|false) default true
7720 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7722 invalidFeedbackClass : "glyphicon-warning-sign",
7725 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7727 validFeedbackClass : "glyphicon-ok",
7730 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7732 selectOnFocus : false,
7735 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7739 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7744 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7746 disableKeyFilter : false,
7749 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7753 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7757 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7759 blankText : "This field is required",
7762 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7766 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7768 maxLength : Number.MAX_VALUE,
7770 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7772 minLengthText : "The minimum length for this field is {0}",
7774 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7776 maxLengthText : "The maximum length for this field is {0}",
7780 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7781 * If available, this function will be called only after the basic validators all return true, and will be passed the
7782 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7786 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7787 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7788 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7792 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7796 autocomplete: false,
7815 formatedValue : false,
7816 forceFeedback : false,
7818 parentLabelAlign : function()
7821 while (parent.parent()) {
7822 parent = parent.parent();
7823 if (typeof(parent.labelAlign) !='undefined') {
7824 return parent.labelAlign;
7831 getAutoCreate : function(){
7833 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7839 if(this.inputType != 'hidden'){
7840 cfg.cls = 'form-group' //input-group
7846 type : this.inputType,
7848 cls : 'form-control',
7849 placeholder : this.placeholder || '',
7850 autocomplete : this.autocomplete || 'new-password'
7855 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7858 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7859 input.maxLength = this.maxLength;
7862 if (this.disabled) {
7863 input.disabled=true;
7866 if (this.readOnly) {
7867 input.readonly=true;
7871 input.name = this.name;
7874 input.cls += ' input-' + this.size;
7877 ['xs','sm','md','lg'].map(function(size){
7878 if (settings[size]) {
7879 cfg.cls += ' col-' + size + '-' + settings[size];
7883 var inputblock = input;
7887 cls: 'glyphicon form-control-feedback'
7890 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7893 cls : 'has-feedback',
7901 if (this.before || this.after) {
7904 cls : 'input-group',
7908 if (this.before && typeof(this.before) == 'string') {
7910 inputblock.cn.push({
7912 cls : 'roo-input-before input-group-addon',
7916 if (this.before && typeof(this.before) == 'object') {
7917 this.before = Roo.factory(this.before);
7918 Roo.log(this.before);
7919 inputblock.cn.push({
7921 cls : 'roo-input-before input-group-' +
7922 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7926 inputblock.cn.push(input);
7928 if (this.after && typeof(this.after) == 'string') {
7929 inputblock.cn.push({
7931 cls : 'roo-input-after input-group-addon',
7935 if (this.after && typeof(this.after) == 'object') {
7936 this.after = Roo.factory(this.after);
7937 Roo.log(this.after);
7938 inputblock.cn.push({
7940 cls : 'roo-input-after input-group-' +
7941 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7945 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7946 inputblock.cls += ' has-feedback';
7947 inputblock.cn.push(feedback);
7951 if (align ==='left' && this.fieldLabel.length) {
7952 Roo.log("left and has label");
7958 cls : 'control-label col-sm-' + this.labelWidth,
7959 html : this.fieldLabel
7963 cls : "col-sm-" + (12 - this.labelWidth),
7970 } else if ( this.fieldLabel.length) {
7976 //cls : 'input-group-addon',
7977 html : this.fieldLabel
7987 Roo.log(" no label && no align");
7996 Roo.log('input-parentType: ' + this.parentType);
7998 if (this.parentType === 'Navbar' && this.parent().bar) {
7999 cfg.cls += ' navbar-form';
8007 * return the real input element.
8009 inputEl: function ()
8011 return this.el.select('input.form-control',true).first();
8014 tooltipEl : function()
8016 return this.inputEl();
8019 setDisabled : function(v)
8021 var i = this.inputEl().dom;
8023 i.removeAttribute('disabled');
8027 i.setAttribute('disabled','true');
8029 initEvents : function()
8032 this.inputEl().on("keydown" , this.fireKey, this);
8033 this.inputEl().on("focus", this.onFocus, this);
8034 this.inputEl().on("blur", this.onBlur, this);
8036 this.inputEl().relayEvent('keyup', this);
8038 // reference to original value for reset
8039 this.originalValue = this.getValue();
8040 //Roo.form.TextField.superclass.initEvents.call(this);
8041 if(this.validationEvent == 'keyup'){
8042 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8043 this.inputEl().on('keyup', this.filterValidation, this);
8045 else if(this.validationEvent !== false){
8046 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8049 if(this.selectOnFocus){
8050 this.on("focus", this.preFocus, this);
8053 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8054 this.inputEl().on("keypress", this.filterKeys, this);
8057 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8058 this.el.on("click", this.autoSize, this);
8061 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8062 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8065 if (typeof(this.before) == 'object') {
8066 this.before.render(this.el.select('.roo-input-before',true).first());
8068 if (typeof(this.after) == 'object') {
8069 this.after.render(this.el.select('.roo-input-after',true).first());
8074 filterValidation : function(e){
8075 if(!e.isNavKeyPress()){
8076 this.validationTask.delay(this.validationDelay);
8080 * Validates the field value
8081 * @return {Boolean} True if the value is valid, else false
8083 validate : function(){
8084 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8085 if(this.disabled || this.validateValue(this.getRawValue())){
8096 * Validates a value according to the field's validation rules and marks the field as invalid
8097 * if the validation fails
8098 * @param {Mixed} value The value to validate
8099 * @return {Boolean} True if the value is valid, else false
8101 validateValue : function(value){
8102 if(value.length < 1) { // if it's blank
8103 if(this.allowBlank){
8109 if(value.length < this.minLength){
8112 if(value.length > this.maxLength){
8116 var vt = Roo.form.VTypes;
8117 if(!vt[this.vtype](value, this)){
8121 if(typeof this.validator == "function"){
8122 var msg = this.validator(value);
8128 if(this.regex && !this.regex.test(value)){
8138 fireKey : function(e){
8139 //Roo.log('field ' + e.getKey());
8140 if(e.isNavKeyPress()){
8141 this.fireEvent("specialkey", this, e);
8144 focus : function (selectText){
8146 this.inputEl().focus();
8147 if(selectText === true){
8148 this.inputEl().dom.select();
8154 onFocus : function(){
8155 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8156 // this.el.addClass(this.focusClass);
8159 this.hasFocus = true;
8160 this.startValue = this.getValue();
8161 this.fireEvent("focus", this);
8165 beforeBlur : Roo.emptyFn,
8169 onBlur : function(){
8171 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8172 //this.el.removeClass(this.focusClass);
8174 this.hasFocus = false;
8175 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8178 var v = this.getValue();
8179 if(String(v) !== String(this.startValue)){
8180 this.fireEvent('change', this, v, this.startValue);
8182 this.fireEvent("blur", this);
8186 * Resets the current field value to the originally loaded value and clears any validation messages
8189 this.setValue(this.originalValue);
8193 * Returns the name of the field
8194 * @return {Mixed} name The name field
8196 getName: function(){
8200 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8201 * @return {Mixed} value The field value
8203 getValue : function(){
8205 var v = this.inputEl().getValue();
8210 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8211 * @return {Mixed} value The field value
8213 getRawValue : function(){
8214 var v = this.inputEl().getValue();
8220 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8221 * @param {Mixed} value The value to set
8223 setRawValue : function(v){
8224 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8227 selectText : function(start, end){
8228 var v = this.getRawValue();
8230 start = start === undefined ? 0 : start;
8231 end = end === undefined ? v.length : end;
8232 var d = this.inputEl().dom;
8233 if(d.setSelectionRange){
8234 d.setSelectionRange(start, end);
8235 }else if(d.createTextRange){
8236 var range = d.createTextRange();
8237 range.moveStart("character", start);
8238 range.moveEnd("character", v.length-end);
8245 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8246 * @param {Mixed} value The value to set
8248 setValue : function(v){
8251 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8257 processValue : function(value){
8258 if(this.stripCharsRe){
8259 var newValue = value.replace(this.stripCharsRe, '');
8260 if(newValue !== value){
8261 this.setRawValue(newValue);
8268 preFocus : function(){
8270 if(this.selectOnFocus){
8271 this.inputEl().dom.select();
8274 filterKeys : function(e){
8276 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8279 var c = e.getCharCode(), cc = String.fromCharCode(c);
8280 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8283 if(!this.maskRe.test(cc)){
8288 * Clear any invalid styles/messages for this field
8290 clearInvalid : function(){
8292 if(!this.el || this.preventMark){ // not rendered
8295 this.el.removeClass(this.invalidClass);
8297 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8299 var feedback = this.el.select('.form-control-feedback', true).first();
8302 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8307 this.fireEvent('valid', this);
8311 * Mark this field as valid
8313 markValid : function(){
8314 if(!this.el || this.preventMark){ // not rendered
8318 this.el.removeClass([this.invalidClass, this.validClass]);
8320 var feedback = this.el.select('.form-control-feedback', true).first();
8323 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8326 if(this.disabled || this.allowBlank){
8330 this.el.addClass(this.validClass);
8332 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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]);
8338 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8343 this.fireEvent('valid', this);
8347 * Mark this field as invalid
8348 * @param {String} msg The validation message
8350 markInvalid : function(msg)
8352 if(!this.el || this.preventMark){ // not rendered
8356 this.el.removeClass([this.invalidClass, this.validClass]);
8358 var feedback = this.el.select('.form-control-feedback', true).first();
8361 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8364 if(this.disabled || this.allowBlank){
8368 this.el.addClass(this.invalidClass);
8370 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8372 var feedback = this.el.select('.form-control-feedback', true).first();
8375 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8377 if(this.getValue().length || this.forceFeedback){
8378 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8385 this.fireEvent('invalid', this, msg);
8388 SafariOnKeyDown : function(event)
8390 // this is a workaround for a password hang bug on chrome/ webkit.
8392 var isSelectAll = false;
8394 if(this.inputEl().dom.selectionEnd > 0){
8395 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8397 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8398 event.preventDefault();
8403 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8405 event.preventDefault();
8406 // this is very hacky as keydown always get's upper case.
8408 var cc = String.fromCharCode(event.getCharCode());
8409 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8413 adjustWidth : function(tag, w){
8414 tag = tag.toLowerCase();
8415 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8416 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8420 if(tag == 'textarea'){
8423 }else if(Roo.isOpera){
8427 if(tag == 'textarea'){
8446 * @class Roo.bootstrap.TextArea
8447 * @extends Roo.bootstrap.Input
8448 * Bootstrap TextArea class
8449 * @cfg {Number} cols Specifies the visible width of a text area
8450 * @cfg {Number} rows Specifies the visible number of lines in a text area
8451 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8452 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8453 * @cfg {string} html text
8456 * Create a new TextArea
8457 * @param {Object} config The config object
8460 Roo.bootstrap.TextArea = function(config){
8461 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8465 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8475 getAutoCreate : function(){
8477 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8488 value : this.value || '',
8489 html: this.html || '',
8490 cls : 'form-control',
8491 placeholder : this.placeholder || ''
8495 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8496 input.maxLength = this.maxLength;
8500 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8504 input.cols = this.cols;
8507 if (this.readOnly) {
8508 input.readonly = true;
8512 input.name = this.name;
8516 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8520 ['xs','sm','md','lg'].map(function(size){
8521 if (settings[size]) {
8522 cfg.cls += ' col-' + size + '-' + settings[size];
8526 var inputblock = input;
8528 if(this.hasFeedback && !this.allowBlank){
8532 cls: 'glyphicon form-control-feedback'
8536 cls : 'has-feedback',
8545 if (this.before || this.after) {
8548 cls : 'input-group',
8552 inputblock.cn.push({
8554 cls : 'input-group-addon',
8559 inputblock.cn.push(input);
8561 if(this.hasFeedback && !this.allowBlank){
8562 inputblock.cls += ' has-feedback';
8563 inputblock.cn.push(feedback);
8567 inputblock.cn.push({
8569 cls : 'input-group-addon',
8576 if (align ==='left' && this.fieldLabel.length) {
8577 Roo.log("left and has label");
8583 cls : 'control-label col-sm-' + this.labelWidth,
8584 html : this.fieldLabel
8588 cls : "col-sm-" + (12 - this.labelWidth),
8595 } else if ( this.fieldLabel.length) {
8601 //cls : 'input-group-addon',
8602 html : this.fieldLabel
8612 Roo.log(" no label && no align");
8622 if (this.disabled) {
8623 input.disabled=true;
8630 * return the real textarea element.
8632 inputEl: function ()
8634 return this.el.select('textarea.form-control',true).first();
8642 * trigger field - base class for combo..
8647 * @class Roo.bootstrap.TriggerField
8648 * @extends Roo.bootstrap.Input
8649 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8650 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8651 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8652 * for which you can provide a custom implementation. For example:
8654 var trigger = new Roo.bootstrap.TriggerField();
8655 trigger.onTriggerClick = myTriggerFn;
8656 trigger.applyTo('my-field');
8659 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8660 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8661 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8662 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8663 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8666 * Create a new TriggerField.
8667 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8668 * to the base TextField)
8670 Roo.bootstrap.TriggerField = function(config){
8671 this.mimicing = false;
8672 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8675 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8677 * @cfg {String} triggerClass A CSS class to apply to the trigger
8680 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8685 * @cfg {Boolean} removable (true|false) special filter default false
8689 /** @cfg {Boolean} grow @hide */
8690 /** @cfg {Number} growMin @hide */
8691 /** @cfg {Number} growMax @hide */
8697 autoSize: Roo.emptyFn,
8704 actionMode : 'wrap',
8709 getAutoCreate : function(){
8711 var align = this.labelAlign || this.parentLabelAlign();
8716 cls: 'form-group' //input-group
8723 type : this.inputType,
8724 cls : 'form-control',
8725 autocomplete: 'new-password',
8726 placeholder : this.placeholder || ''
8730 input.name = this.name;
8733 input.cls += ' input-' + this.size;
8736 if (this.disabled) {
8737 input.disabled=true;
8740 var inputblock = input;
8742 if(this.hasFeedback && !this.allowBlank){
8746 cls: 'glyphicon form-control-feedback'
8749 if(this.removable && !this.editable && !this.tickable){
8751 cls : 'has-feedback',
8757 cls : 'roo-combo-removable-btn close'
8764 cls : 'has-feedback',
8773 if(this.removable && !this.editable && !this.tickable){
8775 cls : 'roo-removable',
8781 cls : 'roo-combo-removable-btn close'
8788 if (this.before || this.after) {
8791 cls : 'input-group',
8795 inputblock.cn.push({
8797 cls : 'input-group-addon',
8802 inputblock.cn.push(input);
8804 if(this.hasFeedback && !this.allowBlank){
8805 inputblock.cls += ' has-feedback';
8806 inputblock.cn.push(feedback);
8810 inputblock.cn.push({
8812 cls : 'input-group-addon',
8825 cls: 'form-hidden-field'
8833 Roo.log('multiple');
8841 cls: 'form-hidden-field'
8845 cls: 'select2-choices',
8849 cls: 'select2-search-field',
8862 cls: 'select2-container input-group',
8867 // cls: 'typeahead typeahead-long dropdown-menu',
8868 // style: 'display:none'
8873 if(!this.multiple && this.showToggleBtn){
8879 if (this.caret != false) {
8882 cls: 'fa fa-' + this.caret
8889 cls : 'input-group-addon btn dropdown-toggle',
8894 cls: 'combobox-clear',
8908 combobox.cls += ' select2-container-multi';
8911 if (align ==='left' && this.fieldLabel.length) {
8913 Roo.log("left and has label");
8919 cls : 'control-label col-sm-' + this.labelWidth,
8920 html : this.fieldLabel
8924 cls : "col-sm-" + (12 - this.labelWidth),
8931 } else if ( this.fieldLabel.length) {
8937 //cls : 'input-group-addon',
8938 html : this.fieldLabel
8948 Roo.log(" no label && no align");
8955 ['xs','sm','md','lg'].map(function(size){
8956 if (settings[size]) {
8957 cfg.cls += ' col-' + size + '-' + settings[size];
8968 onResize : function(w, h){
8969 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8970 // if(typeof w == 'number'){
8971 // var x = w - this.trigger.getWidth();
8972 // this.inputEl().setWidth(this.adjustWidth('input', x));
8973 // this.trigger.setStyle('left', x+'px');
8978 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8981 getResizeEl : function(){
8982 return this.inputEl();
8986 getPositionEl : function(){
8987 return this.inputEl();
8991 alignErrorIcon : function(){
8992 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8996 initEvents : function(){
9000 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9001 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9002 if(!this.multiple && this.showToggleBtn){
9003 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9004 if(this.hideTrigger){
9005 this.trigger.setDisplayed(false);
9007 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9011 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9014 if(this.removable && !this.editable && !this.tickable){
9015 var close = this.closeTriggerEl();
9018 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9019 close.on('click', this.removeBtnClick, this, close);
9023 //this.trigger.addClassOnOver('x-form-trigger-over');
9024 //this.trigger.addClassOnClick('x-form-trigger-click');
9027 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9031 closeTriggerEl : function()
9033 var close = this.el.select('.roo-combo-removable-btn', true).first();
9034 return close ? close : false;
9037 removeBtnClick : function(e, h, el)
9041 if(this.fireEvent("remove", this) !== false){
9046 createList : function()
9048 this.list = Roo.get(document.body).createChild({
9050 cls: 'typeahead typeahead-long dropdown-menu',
9051 style: 'display:none'
9054 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9059 initTrigger : function(){
9064 onDestroy : function(){
9066 this.trigger.removeAllListeners();
9067 // this.trigger.remove();
9070 // this.wrap.remove();
9072 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9076 onFocus : function(){
9077 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9080 this.wrap.addClass('x-trigger-wrap-focus');
9081 this.mimicing = true;
9082 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9083 if(this.monitorTab){
9084 this.el.on("keydown", this.checkTab, this);
9091 checkTab : function(e){
9092 if(e.getKey() == e.TAB){
9098 onBlur : function(){
9103 mimicBlur : function(e, t){
9105 if(!this.wrap.contains(t) && this.validateBlur()){
9112 triggerBlur : function(){
9113 this.mimicing = false;
9114 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9115 if(this.monitorTab){
9116 this.el.un("keydown", this.checkTab, this);
9118 //this.wrap.removeClass('x-trigger-wrap-focus');
9119 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9123 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9124 validateBlur : function(e, t){
9129 onDisable : function(){
9130 this.inputEl().dom.disabled = true;
9131 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9133 // this.wrap.addClass('x-item-disabled');
9138 onEnable : function(){
9139 this.inputEl().dom.disabled = false;
9140 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9142 // this.el.removeClass('x-item-disabled');
9147 onShow : function(){
9148 var ae = this.getActionEl();
9151 ae.dom.style.display = '';
9152 ae.dom.style.visibility = 'visible';
9158 onHide : function(){
9159 var ae = this.getActionEl();
9160 ae.dom.style.display = 'none';
9164 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9165 * by an implementing function.
9167 * @param {EventObject} e
9169 onTriggerClick : Roo.emptyFn
9173 * Ext JS Library 1.1.1
9174 * Copyright(c) 2006-2007, Ext JS, LLC.
9176 * Originally Released Under LGPL - original licence link has changed is not relivant.
9179 * <script type="text/javascript">
9184 * @class Roo.data.SortTypes
9186 * Defines the default sorting (casting?) comparison functions used when sorting data.
9188 Roo.data.SortTypes = {
9190 * Default sort that does nothing
9191 * @param {Mixed} s The value being converted
9192 * @return {Mixed} The comparison value
9199 * The regular expression used to strip tags
9203 stripTagsRE : /<\/?[^>]+>/gi,
9206 * Strips all HTML tags to sort on text only
9207 * @param {Mixed} s The value being converted
9208 * @return {String} The comparison value
9210 asText : function(s){
9211 return String(s).replace(this.stripTagsRE, "");
9215 * Strips all HTML tags to sort on text only - Case insensitive
9216 * @param {Mixed} s The value being converted
9217 * @return {String} The comparison value
9219 asUCText : function(s){
9220 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9224 * Case insensitive string
9225 * @param {Mixed} s The value being converted
9226 * @return {String} The comparison value
9228 asUCString : function(s) {
9229 return String(s).toUpperCase();
9234 * @param {Mixed} s The value being converted
9235 * @return {Number} The comparison value
9237 asDate : function(s) {
9241 if(s instanceof Date){
9244 return Date.parse(String(s));
9249 * @param {Mixed} s The value being converted
9250 * @return {Float} The comparison value
9252 asFloat : function(s) {
9253 var val = parseFloat(String(s).replace(/,/g, ""));
9254 if(isNaN(val)) val = 0;
9260 * @param {Mixed} s The value being converted
9261 * @return {Number} The comparison value
9263 asInt : function(s) {
9264 var val = parseInt(String(s).replace(/,/g, ""));
9265 if(isNaN(val)) val = 0;
9270 * Ext JS Library 1.1.1
9271 * Copyright(c) 2006-2007, Ext JS, LLC.
9273 * Originally Released Under LGPL - original licence link has changed is not relivant.
9276 * <script type="text/javascript">
9280 * @class Roo.data.Record
9281 * Instances of this class encapsulate both record <em>definition</em> information, and record
9282 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9283 * to access Records cached in an {@link Roo.data.Store} object.<br>
9285 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9286 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9289 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9291 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9292 * {@link #create}. The parameters are the same.
9293 * @param {Array} data An associative Array of data values keyed by the field name.
9294 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9295 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9296 * not specified an integer id is generated.
9298 Roo.data.Record = function(data, id){
9299 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9304 * Generate a constructor for a specific record layout.
9305 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9306 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9307 * Each field definition object may contain the following properties: <ul>
9308 * <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,
9309 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9310 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9311 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9312 * is being used, then this is a string containing the javascript expression to reference the data relative to
9313 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9314 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9315 * this may be omitted.</p></li>
9316 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9317 * <ul><li>auto (Default, implies no conversion)</li>
9322 * <li>date</li></ul></p></li>
9323 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9324 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9325 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9326 * by the Reader into an object that will be stored in the Record. It is passed the
9327 * following parameters:<ul>
9328 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9330 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9332 * <br>usage:<br><pre><code>
9333 var TopicRecord = Roo.data.Record.create(
9334 {name: 'title', mapping: 'topic_title'},
9335 {name: 'author', mapping: 'username'},
9336 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9337 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9338 {name: 'lastPoster', mapping: 'user2'},
9339 {name: 'excerpt', mapping: 'post_text'}
9342 var myNewRecord = new TopicRecord({
9343 title: 'Do my job please',
9346 lastPost: new Date(),
9347 lastPoster: 'Animal',
9348 excerpt: 'No way dude!'
9350 myStore.add(myNewRecord);
9355 Roo.data.Record.create = function(o){
9357 f.superclass.constructor.apply(this, arguments);
9359 Roo.extend(f, Roo.data.Record);
9360 var p = f.prototype;
9361 p.fields = new Roo.util.MixedCollection(false, function(field){
9364 for(var i = 0, len = o.length; i < len; i++){
9365 p.fields.add(new Roo.data.Field(o[i]));
9367 f.getField = function(name){
9368 return p.fields.get(name);
9373 Roo.data.Record.AUTO_ID = 1000;
9374 Roo.data.Record.EDIT = 'edit';
9375 Roo.data.Record.REJECT = 'reject';
9376 Roo.data.Record.COMMIT = 'commit';
9378 Roo.data.Record.prototype = {
9380 * Readonly flag - true if this record has been modified.
9389 join : function(store){
9394 * Set the named field to the specified value.
9395 * @param {String} name The name of the field to set.
9396 * @param {Object} value The value to set the field to.
9398 set : function(name, value){
9399 if(this.data[name] == value){
9406 if(typeof this.modified[name] == 'undefined'){
9407 this.modified[name] = this.data[name];
9409 this.data[name] = value;
9410 if(!this.editing && this.store){
9411 this.store.afterEdit(this);
9416 * Get the value of the named field.
9417 * @param {String} name The name of the field to get the value of.
9418 * @return {Object} The value of the field.
9420 get : function(name){
9421 return this.data[name];
9425 beginEdit : function(){
9426 this.editing = true;
9431 cancelEdit : function(){
9432 this.editing = false;
9433 delete this.modified;
9437 endEdit : function(){
9438 this.editing = false;
9439 if(this.dirty && this.store){
9440 this.store.afterEdit(this);
9445 * Usually called by the {@link Roo.data.Store} which owns the Record.
9446 * Rejects all changes made to the Record since either creation, or the last commit operation.
9447 * Modified fields are reverted to their original values.
9449 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9450 * of reject operations.
9452 reject : function(){
9453 var m = this.modified;
9455 if(typeof m[n] != "function"){
9456 this.data[n] = m[n];
9460 delete this.modified;
9461 this.editing = false;
9463 this.store.afterReject(this);
9468 * Usually called by the {@link Roo.data.Store} which owns the Record.
9469 * Commits all changes made to the Record since either creation, or the last commit operation.
9471 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9472 * of commit operations.
9474 commit : function(){
9476 delete this.modified;
9477 this.editing = false;
9479 this.store.afterCommit(this);
9484 hasError : function(){
9485 return this.error != null;
9489 clearError : function(){
9494 * Creates a copy of this record.
9495 * @param {String} id (optional) A new record id if you don't want to use this record's id
9498 copy : function(newId) {
9499 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9503 * Ext JS Library 1.1.1
9504 * Copyright(c) 2006-2007, Ext JS, LLC.
9506 * Originally Released Under LGPL - original licence link has changed is not relivant.
9509 * <script type="text/javascript">
9515 * @class Roo.data.Store
9516 * @extends Roo.util.Observable
9517 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9518 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9520 * 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
9521 * has no knowledge of the format of the data returned by the Proxy.<br>
9523 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9524 * instances from the data object. These records are cached and made available through accessor functions.
9526 * Creates a new Store.
9527 * @param {Object} config A config object containing the objects needed for the Store to access data,
9528 * and read the data into Records.
9530 Roo.data.Store = function(config){
9531 this.data = new Roo.util.MixedCollection(false);
9532 this.data.getKey = function(o){
9535 this.baseParams = {};
9542 "multisort" : "_multisort"
9545 if(config && config.data){
9546 this.inlineData = config.data;
9550 Roo.apply(this, config);
9552 if(this.reader){ // reader passed
9553 this.reader = Roo.factory(this.reader, Roo.data);
9554 this.reader.xmodule = this.xmodule || false;
9555 if(!this.recordType){
9556 this.recordType = this.reader.recordType;
9558 if(this.reader.onMetaChange){
9559 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9563 if(this.recordType){
9564 this.fields = this.recordType.prototype.fields;
9570 * @event datachanged
9571 * Fires when the data cache has changed, and a widget which is using this Store
9572 * as a Record cache should refresh its view.
9573 * @param {Store} this
9578 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9579 * @param {Store} this
9580 * @param {Object} meta The JSON metadata
9585 * Fires when Records have been added to the Store
9586 * @param {Store} this
9587 * @param {Roo.data.Record[]} records The array of Records added
9588 * @param {Number} index The index at which the record(s) were added
9593 * Fires when a Record has been removed from the Store
9594 * @param {Store} this
9595 * @param {Roo.data.Record} record The Record that was removed
9596 * @param {Number} index The index at which the record was removed
9601 * Fires when a Record has been updated
9602 * @param {Store} this
9603 * @param {Roo.data.Record} record The Record that was updated
9604 * @param {String} operation The update operation being performed. Value may be one of:
9606 Roo.data.Record.EDIT
9607 Roo.data.Record.REJECT
9608 Roo.data.Record.COMMIT
9614 * Fires when the data cache has been cleared.
9615 * @param {Store} this
9620 * Fires before a request is made for a new data object. If the beforeload handler returns false
9621 * the load action will be canceled.
9622 * @param {Store} this
9623 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9627 * @event beforeloadadd
9628 * Fires after a new set of Records has been loaded.
9629 * @param {Store} this
9630 * @param {Roo.data.Record[]} records The Records that were loaded
9631 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9633 beforeloadadd : true,
9636 * Fires after a new set of Records has been loaded, before they are added to the store.
9637 * @param {Store} this
9638 * @param {Roo.data.Record[]} records The Records that were loaded
9639 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9640 * @params {Object} return from reader
9644 * @event loadexception
9645 * Fires if an exception occurs in the Proxy during loading.
9646 * Called with the signature of the Proxy's "loadexception" event.
9647 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9650 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9651 * @param {Object} load options
9652 * @param {Object} jsonData from your request (normally this contains the Exception)
9654 loadexception : true
9658 this.proxy = Roo.factory(this.proxy, Roo.data);
9659 this.proxy.xmodule = this.xmodule || false;
9660 this.relayEvents(this.proxy, ["loadexception"]);
9662 this.sortToggle = {};
9663 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9665 Roo.data.Store.superclass.constructor.call(this);
9667 if(this.inlineData){
9668 this.loadData(this.inlineData);
9669 delete this.inlineData;
9673 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9675 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9676 * without a remote query - used by combo/forms at present.
9680 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9683 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9686 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9687 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9690 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9691 * on any HTTP request
9694 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9697 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9701 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9702 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9707 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9708 * loaded or when a record is removed. (defaults to false).
9710 pruneModifiedRecords : false,
9716 * Add Records to the Store and fires the add event.
9717 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9719 add : function(records){
9720 records = [].concat(records);
9721 for(var i = 0, len = records.length; i < len; i++){
9722 records[i].join(this);
9724 var index = this.data.length;
9725 this.data.addAll(records);
9726 this.fireEvent("add", this, records, index);
9730 * Remove a Record from the Store and fires the remove event.
9731 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9733 remove : function(record){
9734 var index = this.data.indexOf(record);
9735 this.data.removeAt(index);
9736 if(this.pruneModifiedRecords){
9737 this.modified.remove(record);
9739 this.fireEvent("remove", this, record, index);
9743 * Remove all Records from the Store and fires the clear event.
9745 removeAll : function(){
9747 if(this.pruneModifiedRecords){
9750 this.fireEvent("clear", this);
9754 * Inserts Records to the Store at the given index and fires the add event.
9755 * @param {Number} index The start index at which to insert the passed Records.
9756 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9758 insert : function(index, records){
9759 records = [].concat(records);
9760 for(var i = 0, len = records.length; i < len; i++){
9761 this.data.insert(index, records[i]);
9762 records[i].join(this);
9764 this.fireEvent("add", this, records, index);
9768 * Get the index within the cache of the passed Record.
9769 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9770 * @return {Number} The index of the passed Record. Returns -1 if not found.
9772 indexOf : function(record){
9773 return this.data.indexOf(record);
9777 * Get the index within the cache of the Record with the passed id.
9778 * @param {String} id The id of the Record to find.
9779 * @return {Number} The index of the Record. Returns -1 if not found.
9781 indexOfId : function(id){
9782 return this.data.indexOfKey(id);
9786 * Get the Record with the specified id.
9787 * @param {String} id The id of the Record to find.
9788 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9790 getById : function(id){
9791 return this.data.key(id);
9795 * Get the Record at the specified index.
9796 * @param {Number} index The index of the Record to find.
9797 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9799 getAt : function(index){
9800 return this.data.itemAt(index);
9804 * Returns a range of Records between specified indices.
9805 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9806 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9807 * @return {Roo.data.Record[]} An array of Records
9809 getRange : function(start, end){
9810 return this.data.getRange(start, end);
9814 storeOptions : function(o){
9815 o = Roo.apply({}, o);
9818 this.lastOptions = o;
9822 * Loads the Record cache from the configured Proxy using the configured Reader.
9824 * If using remote paging, then the first load call must specify the <em>start</em>
9825 * and <em>limit</em> properties in the options.params property to establish the initial
9826 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9828 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9829 * and this call will return before the new data has been loaded. Perform any post-processing
9830 * in a callback function, or in a "load" event handler.</strong>
9832 * @param {Object} options An object containing properties which control loading options:<ul>
9833 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9834 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9835 * passed the following arguments:<ul>
9836 * <li>r : Roo.data.Record[]</li>
9837 * <li>options: Options object from the load call</li>
9838 * <li>success: Boolean success indicator</li></ul></li>
9839 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9840 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9843 load : function(options){
9844 options = options || {};
9845 if(this.fireEvent("beforeload", this, options) !== false){
9846 this.storeOptions(options);
9847 var p = Roo.apply(options.params || {}, this.baseParams);
9848 // if meta was not loaded from remote source.. try requesting it.
9849 if (!this.reader.metaFromRemote) {
9852 if(this.sortInfo && this.remoteSort){
9853 var pn = this.paramNames;
9854 p[pn["sort"]] = this.sortInfo.field;
9855 p[pn["dir"]] = this.sortInfo.direction;
9857 if (this.multiSort) {
9858 var pn = this.paramNames;
9859 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9862 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9867 * Reloads the Record cache from the configured Proxy using the configured Reader and
9868 * the options from the last load operation performed.
9869 * @param {Object} options (optional) An object containing properties which may override the options
9870 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9871 * the most recently used options are reused).
9873 reload : function(options){
9874 this.load(Roo.applyIf(options||{}, this.lastOptions));
9878 // Called as a callback by the Reader during a load operation.
9879 loadRecords : function(o, options, success){
9880 if(!o || success === false){
9881 if(success !== false){
9882 this.fireEvent("load", this, [], options, o);
9884 if(options.callback){
9885 options.callback.call(options.scope || this, [], options, false);
9889 // if data returned failure - throw an exception.
9890 if (o.success === false) {
9891 // show a message if no listener is registered.
9892 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9893 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9895 // loadmask wil be hooked into this..
9896 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9899 var r = o.records, t = o.totalRecords || r.length;
9901 this.fireEvent("beforeloadadd", this, r, options, o);
9903 if(!options || options.add !== true){
9904 if(this.pruneModifiedRecords){
9907 for(var i = 0, len = r.length; i < len; i++){
9911 this.data = this.snapshot;
9912 delete this.snapshot;
9915 this.data.addAll(r);
9916 this.totalLength = t;
9918 this.fireEvent("datachanged", this);
9920 this.totalLength = Math.max(t, this.data.length+r.length);
9923 this.fireEvent("load", this, r, options, o);
9924 if(options.callback){
9925 options.callback.call(options.scope || this, r, options, true);
9931 * Loads data from a passed data block. A Reader which understands the format of the data
9932 * must have been configured in the constructor.
9933 * @param {Object} data The data block from which to read the Records. The format of the data expected
9934 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9935 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9937 loadData : function(o, append){
9938 var r = this.reader.readRecords(o);
9939 this.loadRecords(r, {add: append}, true);
9943 * Gets the number of cached records.
9945 * <em>If using paging, this may not be the total size of the dataset. If the data object
9946 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9947 * the data set size</em>
9949 getCount : function(){
9950 return this.data.length || 0;
9954 * Gets the total number of records in the dataset as returned by the server.
9956 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9957 * the dataset size</em>
9959 getTotalCount : function(){
9960 return this.totalLength || 0;
9964 * Returns the sort state of the Store as an object with two properties:
9966 field {String} The name of the field by which the Records are sorted
9967 direction {String} The sort order, "ASC" or "DESC"
9970 getSortState : function(){
9971 return this.sortInfo;
9975 applySort : function(){
9976 if(this.sortInfo && !this.remoteSort){
9977 var s = this.sortInfo, f = s.field;
9978 var st = this.fields.get(f).sortType;
9979 var fn = function(r1, r2){
9980 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9981 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9983 this.data.sort(s.direction, fn);
9984 if(this.snapshot && this.snapshot != this.data){
9985 this.snapshot.sort(s.direction, fn);
9991 * Sets the default sort column and order to be used by the next load operation.
9992 * @param {String} fieldName The name of the field to sort by.
9993 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9995 setDefaultSort : function(field, dir){
9996 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10000 * Sort the Records.
10001 * If remote sorting is used, the sort is performed on the server, and the cache is
10002 * reloaded. If local sorting is used, the cache is sorted internally.
10003 * @param {String} fieldName The name of the field to sort by.
10004 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10006 sort : function(fieldName, dir){
10007 var f = this.fields.get(fieldName);
10009 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10011 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10012 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10017 this.sortToggle[f.name] = dir;
10018 this.sortInfo = {field: f.name, direction: dir};
10019 if(!this.remoteSort){
10021 this.fireEvent("datachanged", this);
10023 this.load(this.lastOptions);
10028 * Calls the specified function for each of the Records in the cache.
10029 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10030 * Returning <em>false</em> aborts and exits the iteration.
10031 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10033 each : function(fn, scope){
10034 this.data.each(fn, scope);
10038 * Gets all records modified since the last commit. Modified records are persisted across load operations
10039 * (e.g., during paging).
10040 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10042 getModifiedRecords : function(){
10043 return this.modified;
10047 createFilterFn : function(property, value, anyMatch){
10048 if(!value.exec){ // not a regex
10049 value = String(value);
10050 if(value.length == 0){
10053 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10055 return function(r){
10056 return value.test(r.data[property]);
10061 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10062 * @param {String} property A field on your records
10063 * @param {Number} start The record index to start at (defaults to 0)
10064 * @param {Number} end The last record index to include (defaults to length - 1)
10065 * @return {Number} The sum
10067 sum : function(property, start, end){
10068 var rs = this.data.items, v = 0;
10069 start = start || 0;
10070 end = (end || end === 0) ? end : rs.length-1;
10072 for(var i = start; i <= end; i++){
10073 v += (rs[i].data[property] || 0);
10079 * Filter the records by a specified property.
10080 * @param {String} field A field on your records
10081 * @param {String/RegExp} value Either a string that the field
10082 * should start with or a RegExp to test against the field
10083 * @param {Boolean} anyMatch True to match any part not just the beginning
10085 filter : function(property, value, anyMatch){
10086 var fn = this.createFilterFn(property, value, anyMatch);
10087 return fn ? this.filterBy(fn) : this.clearFilter();
10091 * Filter by a function. The specified function will be called with each
10092 * record in this data source. If the function returns true the record is included,
10093 * otherwise it is filtered.
10094 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10095 * @param {Object} scope (optional) The scope of the function (defaults to this)
10097 filterBy : function(fn, scope){
10098 this.snapshot = this.snapshot || this.data;
10099 this.data = this.queryBy(fn, scope||this);
10100 this.fireEvent("datachanged", this);
10104 * Query the records by a specified property.
10105 * @param {String} field A field on your records
10106 * @param {String/RegExp} value Either a string that the field
10107 * should start with or a RegExp to test against the field
10108 * @param {Boolean} anyMatch True to match any part not just the beginning
10109 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10111 query : function(property, value, anyMatch){
10112 var fn = this.createFilterFn(property, value, anyMatch);
10113 return fn ? this.queryBy(fn) : this.data.clone();
10117 * Query by a function. The specified function will be called with each
10118 * record in this data source. If the function returns true the record is included
10120 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10121 * @param {Object} scope (optional) The scope of the function (defaults to this)
10122 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10124 queryBy : function(fn, scope){
10125 var data = this.snapshot || this.data;
10126 return data.filterBy(fn, scope||this);
10130 * Collects unique values for a particular dataIndex from this store.
10131 * @param {String} dataIndex The property to collect
10132 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10133 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10134 * @return {Array} An array of the unique values
10136 collect : function(dataIndex, allowNull, bypassFilter){
10137 var d = (bypassFilter === true && this.snapshot) ?
10138 this.snapshot.items : this.data.items;
10139 var v, sv, r = [], l = {};
10140 for(var i = 0, len = d.length; i < len; i++){
10141 v = d[i].data[dataIndex];
10143 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10152 * Revert to a view of the Record cache with no filtering applied.
10153 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10155 clearFilter : function(suppressEvent){
10156 if(this.snapshot && this.snapshot != this.data){
10157 this.data = this.snapshot;
10158 delete this.snapshot;
10159 if(suppressEvent !== true){
10160 this.fireEvent("datachanged", this);
10166 afterEdit : function(record){
10167 if(this.modified.indexOf(record) == -1){
10168 this.modified.push(record);
10170 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10174 afterReject : function(record){
10175 this.modified.remove(record);
10176 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10180 afterCommit : function(record){
10181 this.modified.remove(record);
10182 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10186 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10187 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10189 commitChanges : function(){
10190 var m = this.modified.slice(0);
10191 this.modified = [];
10192 for(var i = 0, len = m.length; i < len; i++){
10198 * Cancel outstanding changes on all changed records.
10200 rejectChanges : function(){
10201 var m = this.modified.slice(0);
10202 this.modified = [];
10203 for(var i = 0, len = m.length; i < len; i++){
10208 onMetaChange : function(meta, rtype, o){
10209 this.recordType = rtype;
10210 this.fields = rtype.prototype.fields;
10211 delete this.snapshot;
10212 this.sortInfo = meta.sortInfo || this.sortInfo;
10213 this.modified = [];
10214 this.fireEvent('metachange', this, this.reader.meta);
10217 moveIndex : function(data, type)
10219 var index = this.indexOf(data);
10221 var newIndex = index + type;
10225 this.insert(newIndex, data);
10230 * Ext JS Library 1.1.1
10231 * Copyright(c) 2006-2007, Ext JS, LLC.
10233 * Originally Released Under LGPL - original licence link has changed is not relivant.
10236 * <script type="text/javascript">
10240 * @class Roo.data.SimpleStore
10241 * @extends Roo.data.Store
10242 * Small helper class to make creating Stores from Array data easier.
10243 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10244 * @cfg {Array} fields An array of field definition objects, or field name strings.
10245 * @cfg {Array} data The multi-dimensional array of data
10247 * @param {Object} config
10249 Roo.data.SimpleStore = function(config){
10250 Roo.data.SimpleStore.superclass.constructor.call(this, {
10252 reader: new Roo.data.ArrayReader({
10255 Roo.data.Record.create(config.fields)
10257 proxy : new Roo.data.MemoryProxy(config.data)
10261 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10263 * Ext JS Library 1.1.1
10264 * Copyright(c) 2006-2007, Ext JS, LLC.
10266 * Originally Released Under LGPL - original licence link has changed is not relivant.
10269 * <script type="text/javascript">
10274 * @extends Roo.data.Store
10275 * @class Roo.data.JsonStore
10276 * Small helper class to make creating Stores for JSON data easier. <br/>
10278 var store = new Roo.data.JsonStore({
10279 url: 'get-images.php',
10281 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10284 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10285 * JsonReader and HttpProxy (unless inline data is provided).</b>
10286 * @cfg {Array} fields An array of field definition objects, or field name strings.
10288 * @param {Object} config
10290 Roo.data.JsonStore = function(c){
10291 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10292 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10293 reader: new Roo.data.JsonReader(c, c.fields)
10296 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10298 * Ext JS Library 1.1.1
10299 * Copyright(c) 2006-2007, Ext JS, LLC.
10301 * Originally Released Under LGPL - original licence link has changed is not relivant.
10304 * <script type="text/javascript">
10308 Roo.data.Field = function(config){
10309 if(typeof config == "string"){
10310 config = {name: config};
10312 Roo.apply(this, config);
10315 this.type = "auto";
10318 var st = Roo.data.SortTypes;
10319 // named sortTypes are supported, here we look them up
10320 if(typeof this.sortType == "string"){
10321 this.sortType = st[this.sortType];
10324 // set default sortType for strings and dates
10325 if(!this.sortType){
10328 this.sortType = st.asUCString;
10331 this.sortType = st.asDate;
10334 this.sortType = st.none;
10339 var stripRe = /[\$,%]/g;
10341 // prebuilt conversion function for this field, instead of
10342 // switching every time we're reading a value
10344 var cv, dateFormat = this.dateFormat;
10349 cv = function(v){ return v; };
10352 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10356 return v !== undefined && v !== null && v !== '' ?
10357 parseInt(String(v).replace(stripRe, ""), 10) : '';
10362 return v !== undefined && v !== null && v !== '' ?
10363 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10368 cv = function(v){ return v === true || v === "true" || v == 1; };
10375 if(v instanceof Date){
10379 if(dateFormat == "timestamp"){
10380 return new Date(v*1000);
10382 return Date.parseDate(v, dateFormat);
10384 var parsed = Date.parse(v);
10385 return parsed ? new Date(parsed) : null;
10394 Roo.data.Field.prototype = {
10402 * Ext JS Library 1.1.1
10403 * Copyright(c) 2006-2007, Ext JS, LLC.
10405 * Originally Released Under LGPL - original licence link has changed is not relivant.
10408 * <script type="text/javascript">
10411 // Base class for reading structured data from a data source. This class is intended to be
10412 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10415 * @class Roo.data.DataReader
10416 * Base class for reading structured data from a data source. This class is intended to be
10417 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10420 Roo.data.DataReader = function(meta, recordType){
10424 this.recordType = recordType instanceof Array ?
10425 Roo.data.Record.create(recordType) : recordType;
10428 Roo.data.DataReader.prototype = {
10430 * Create an empty record
10431 * @param {Object} data (optional) - overlay some values
10432 * @return {Roo.data.Record} record created.
10434 newRow : function(d) {
10436 this.recordType.prototype.fields.each(function(c) {
10438 case 'int' : da[c.name] = 0; break;
10439 case 'date' : da[c.name] = new Date(); break;
10440 case 'float' : da[c.name] = 0.0; break;
10441 case 'boolean' : da[c.name] = false; break;
10442 default : da[c.name] = ""; break;
10446 return new this.recordType(Roo.apply(da, d));
10451 * Ext JS Library 1.1.1
10452 * Copyright(c) 2006-2007, Ext JS, LLC.
10454 * Originally Released Under LGPL - original licence link has changed is not relivant.
10457 * <script type="text/javascript">
10461 * @class Roo.data.DataProxy
10462 * @extends Roo.data.Observable
10463 * This class is an abstract base class for implementations which provide retrieval of
10464 * unformatted data objects.<br>
10466 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10467 * (of the appropriate type which knows how to parse the data object) to provide a block of
10468 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10470 * Custom implementations must implement the load method as described in
10471 * {@link Roo.data.HttpProxy#load}.
10473 Roo.data.DataProxy = function(){
10476 * @event beforeload
10477 * Fires before a network request is made to retrieve a data object.
10478 * @param {Object} This DataProxy object.
10479 * @param {Object} params The params parameter to the load function.
10484 * Fires before the load method's callback is called.
10485 * @param {Object} This DataProxy object.
10486 * @param {Object} o The data object.
10487 * @param {Object} arg The callback argument object passed to the load function.
10491 * @event loadexception
10492 * Fires if an Exception occurs during data retrieval.
10493 * @param {Object} This DataProxy object.
10494 * @param {Object} o The data object.
10495 * @param {Object} arg The callback argument object passed to the load function.
10496 * @param {Object} e The Exception.
10498 loadexception : true
10500 Roo.data.DataProxy.superclass.constructor.call(this);
10503 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10506 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10510 * Ext JS Library 1.1.1
10511 * Copyright(c) 2006-2007, Ext JS, LLC.
10513 * Originally Released Under LGPL - original licence link has changed is not relivant.
10516 * <script type="text/javascript">
10519 * @class Roo.data.MemoryProxy
10520 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10521 * to the Reader when its load method is called.
10523 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10525 Roo.data.MemoryProxy = function(data){
10529 Roo.data.MemoryProxy.superclass.constructor.call(this);
10533 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10535 * Load data from the requested source (in this case an in-memory
10536 * data object passed to the constructor), read the data object into
10537 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10538 * process that block using the passed callback.
10539 * @param {Object} params This parameter is not used by the MemoryProxy class.
10540 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10541 * object into a block of Roo.data.Records.
10542 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10543 * The function must be passed <ul>
10544 * <li>The Record block object</li>
10545 * <li>The "arg" argument from the load function</li>
10546 * <li>A boolean success indicator</li>
10548 * @param {Object} scope The scope in which to call the callback
10549 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10551 load : function(params, reader, callback, scope, arg){
10552 params = params || {};
10555 result = reader.readRecords(this.data);
10557 this.fireEvent("loadexception", this, arg, null, e);
10558 callback.call(scope, null, arg, false);
10561 callback.call(scope, result, arg, true);
10565 update : function(params, records){
10570 * Ext JS Library 1.1.1
10571 * Copyright(c) 2006-2007, Ext JS, LLC.
10573 * Originally Released Under LGPL - original licence link has changed is not relivant.
10576 * <script type="text/javascript">
10579 * @class Roo.data.HttpProxy
10580 * @extends Roo.data.DataProxy
10581 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10582 * configured to reference a certain URL.<br><br>
10584 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10585 * from which the running page was served.<br><br>
10587 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10589 * Be aware that to enable the browser to parse an XML document, the server must set
10590 * the Content-Type header in the HTTP response to "text/xml".
10592 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10593 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10594 * will be used to make the request.
10596 Roo.data.HttpProxy = function(conn){
10597 Roo.data.HttpProxy.superclass.constructor.call(this);
10598 // is conn a conn config or a real conn?
10600 this.useAjax = !conn || !conn.events;
10604 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10605 // thse are take from connection...
10608 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10611 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10612 * extra parameters to each request made by this object. (defaults to undefined)
10615 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10616 * to each request made by this object. (defaults to undefined)
10619 * @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)
10622 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10625 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10631 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10635 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10636 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10637 * a finer-grained basis than the DataProxy events.
10639 getConnection : function(){
10640 return this.useAjax ? Roo.Ajax : this.conn;
10644 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10645 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10646 * process that block using the passed callback.
10647 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10648 * for the request to the remote server.
10649 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10650 * object into a block of Roo.data.Records.
10651 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10652 * The function must be passed <ul>
10653 * <li>The Record block object</li>
10654 * <li>The "arg" argument from the load function</li>
10655 * <li>A boolean success indicator</li>
10657 * @param {Object} scope The scope in which to call the callback
10658 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10660 load : function(params, reader, callback, scope, arg){
10661 if(this.fireEvent("beforeload", this, params) !== false){
10663 params : params || {},
10665 callback : callback,
10670 callback : this.loadResponse,
10674 Roo.applyIf(o, this.conn);
10675 if(this.activeRequest){
10676 Roo.Ajax.abort(this.activeRequest);
10678 this.activeRequest = Roo.Ajax.request(o);
10680 this.conn.request(o);
10683 callback.call(scope||this, null, arg, false);
10688 loadResponse : function(o, success, response){
10689 delete this.activeRequest;
10691 this.fireEvent("loadexception", this, o, response);
10692 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10697 result = o.reader.read(response);
10699 this.fireEvent("loadexception", this, o, response, e);
10700 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10704 this.fireEvent("load", this, o, o.request.arg);
10705 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10709 update : function(dataSet){
10714 updateResponse : function(dataSet){
10719 * Ext JS Library 1.1.1
10720 * Copyright(c) 2006-2007, Ext JS, LLC.
10722 * Originally Released Under LGPL - original licence link has changed is not relivant.
10725 * <script type="text/javascript">
10729 * @class Roo.data.ScriptTagProxy
10730 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10731 * other than the originating domain of the running page.<br><br>
10733 * <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
10734 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10736 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10737 * source code that is used as the source inside a <script> tag.<br><br>
10739 * In order for the browser to process the returned data, the server must wrap the data object
10740 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10741 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10742 * depending on whether the callback name was passed:
10745 boolean scriptTag = false;
10746 String cb = request.getParameter("callback");
10749 response.setContentType("text/javascript");
10751 response.setContentType("application/x-json");
10753 Writer out = response.getWriter();
10755 out.write(cb + "(");
10757 out.print(dataBlock.toJsonString());
10764 * @param {Object} config A configuration object.
10766 Roo.data.ScriptTagProxy = function(config){
10767 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10768 Roo.apply(this, config);
10769 this.head = document.getElementsByTagName("head")[0];
10772 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10774 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10776 * @cfg {String} url The URL from which to request the data object.
10779 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10783 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10784 * the server the name of the callback function set up by the load call to process the returned data object.
10785 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10786 * javascript output which calls this named function passing the data object as its only parameter.
10788 callbackParam : "callback",
10790 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10791 * name to the request.
10796 * Load data from the configured URL, read the data object into
10797 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10798 * process that block using the passed callback.
10799 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10800 * for the request to the remote server.
10801 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10802 * object into a block of Roo.data.Records.
10803 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10804 * The function must be passed <ul>
10805 * <li>The Record block object</li>
10806 * <li>The "arg" argument from the load function</li>
10807 * <li>A boolean success indicator</li>
10809 * @param {Object} scope The scope in which to call the callback
10810 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10812 load : function(params, reader, callback, scope, arg){
10813 if(this.fireEvent("beforeload", this, params) !== false){
10815 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10817 var url = this.url;
10818 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10820 url += "&_dc=" + (new Date().getTime());
10822 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10825 cb : "stcCallback"+transId,
10826 scriptId : "stcScript"+transId,
10830 callback : callback,
10836 window[trans.cb] = function(o){
10837 conn.handleResponse(o, trans);
10840 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10842 if(this.autoAbort !== false){
10846 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10848 var script = document.createElement("script");
10849 script.setAttribute("src", url);
10850 script.setAttribute("type", "text/javascript");
10851 script.setAttribute("id", trans.scriptId);
10852 this.head.appendChild(script);
10854 this.trans = trans;
10856 callback.call(scope||this, null, arg, false);
10861 isLoading : function(){
10862 return this.trans ? true : false;
10866 * Abort the current server request.
10868 abort : function(){
10869 if(this.isLoading()){
10870 this.destroyTrans(this.trans);
10875 destroyTrans : function(trans, isLoaded){
10876 this.head.removeChild(document.getElementById(trans.scriptId));
10877 clearTimeout(trans.timeoutId);
10879 window[trans.cb] = undefined;
10881 delete window[trans.cb];
10884 // if hasn't been loaded, wait for load to remove it to prevent script error
10885 window[trans.cb] = function(){
10886 window[trans.cb] = undefined;
10888 delete window[trans.cb];
10895 handleResponse : function(o, trans){
10896 this.trans = false;
10897 this.destroyTrans(trans, true);
10900 result = trans.reader.readRecords(o);
10902 this.fireEvent("loadexception", this, o, trans.arg, e);
10903 trans.callback.call(trans.scope||window, null, trans.arg, false);
10906 this.fireEvent("load", this, o, trans.arg);
10907 trans.callback.call(trans.scope||window, result, trans.arg, true);
10911 handleFailure : function(trans){
10912 this.trans = false;
10913 this.destroyTrans(trans, false);
10914 this.fireEvent("loadexception", this, null, trans.arg);
10915 trans.callback.call(trans.scope||window, null, trans.arg, false);
10919 * Ext JS Library 1.1.1
10920 * Copyright(c) 2006-2007, Ext JS, LLC.
10922 * Originally Released Under LGPL - original licence link has changed is not relivant.
10925 * <script type="text/javascript">
10929 * @class Roo.data.JsonReader
10930 * @extends Roo.data.DataReader
10931 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10932 * based on mappings in a provided Roo.data.Record constructor.
10934 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10935 * in the reply previously.
10940 var RecordDef = Roo.data.Record.create([
10941 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10942 {name: 'occupation'} // This field will use "occupation" as the mapping.
10944 var myReader = new Roo.data.JsonReader({
10945 totalProperty: "results", // The property which contains the total dataset size (optional)
10946 root: "rows", // The property which contains an Array of row objects
10947 id: "id" // The property within each row object that provides an ID for the record (optional)
10951 * This would consume a JSON file like this:
10953 { 'results': 2, 'rows': [
10954 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10955 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10958 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10959 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10960 * paged from the remote server.
10961 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10962 * @cfg {String} root name of the property which contains the Array of row objects.
10963 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10964 * @cfg {Array} fields Array of field definition objects
10966 * Create a new JsonReader
10967 * @param {Object} meta Metadata configuration options
10968 * @param {Object} recordType Either an Array of field definition objects,
10969 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10971 Roo.data.JsonReader = function(meta, recordType){
10974 // set some defaults:
10975 Roo.applyIf(meta, {
10976 totalProperty: 'total',
10977 successProperty : 'success',
10982 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10984 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10987 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10988 * Used by Store query builder to append _requestMeta to params.
10991 metaFromRemote : false,
10993 * This method is only used by a DataProxy which has retrieved data from a remote server.
10994 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10995 * @return {Object} data A data block which is used by an Roo.data.Store object as
10996 * a cache of Roo.data.Records.
10998 read : function(response){
10999 var json = response.responseText;
11001 var o = /* eval:var:o */ eval("("+json+")");
11003 throw {message: "JsonReader.read: Json object not found"};
11009 this.metaFromRemote = true;
11010 this.meta = o.metaData;
11011 this.recordType = Roo.data.Record.create(o.metaData.fields);
11012 this.onMetaChange(this.meta, this.recordType, o);
11014 return this.readRecords(o);
11017 // private function a store will implement
11018 onMetaChange : function(meta, recordType, o){
11025 simpleAccess: function(obj, subsc) {
11032 getJsonAccessor: function(){
11034 return function(expr) {
11036 return(re.test(expr))
11037 ? new Function("obj", "return obj." + expr)
11042 return Roo.emptyFn;
11047 * Create a data block containing Roo.data.Records from an XML document.
11048 * @param {Object} o An object which contains an Array of row objects in the property specified
11049 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11050 * which contains the total size of the dataset.
11051 * @return {Object} data A data block which is used by an Roo.data.Store object as
11052 * a cache of Roo.data.Records.
11054 readRecords : function(o){
11056 * After any data loads, the raw JSON data is available for further custom processing.
11060 var s = this.meta, Record = this.recordType,
11061 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11063 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11065 if(s.totalProperty) {
11066 this.getTotal = this.getJsonAccessor(s.totalProperty);
11068 if(s.successProperty) {
11069 this.getSuccess = this.getJsonAccessor(s.successProperty);
11071 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11073 var g = this.getJsonAccessor(s.id);
11074 this.getId = function(rec) {
11076 return (r === undefined || r === "") ? null : r;
11079 this.getId = function(){return null;};
11082 for(var jj = 0; jj < fl; jj++){
11084 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11085 this.ef[jj] = this.getJsonAccessor(map);
11089 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11090 if(s.totalProperty){
11091 var vt = parseInt(this.getTotal(o), 10);
11096 if(s.successProperty){
11097 var vs = this.getSuccess(o);
11098 if(vs === false || vs === 'false'){
11103 for(var i = 0; i < c; i++){
11106 var id = this.getId(n);
11107 for(var j = 0; j < fl; j++){
11109 var v = this.ef[j](n);
11111 Roo.log('missing convert for ' + f.name);
11115 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11117 var record = new Record(values, id);
11119 records[i] = record;
11125 totalRecords : totalRecords
11130 * Ext JS Library 1.1.1
11131 * Copyright(c) 2006-2007, Ext JS, LLC.
11133 * Originally Released Under LGPL - original licence link has changed is not relivant.
11136 * <script type="text/javascript">
11140 * @class Roo.data.ArrayReader
11141 * @extends Roo.data.DataReader
11142 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11143 * Each element of that Array represents a row of data fields. The
11144 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11145 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11149 var RecordDef = Roo.data.Record.create([
11150 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11151 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11153 var myReader = new Roo.data.ArrayReader({
11154 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11158 * This would consume an Array like this:
11160 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11162 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11164 * Create a new JsonReader
11165 * @param {Object} meta Metadata configuration options.
11166 * @param {Object} recordType Either an Array of field definition objects
11167 * as specified to {@link Roo.data.Record#create},
11168 * or an {@link Roo.data.Record} object
11169 * created using {@link Roo.data.Record#create}.
11171 Roo.data.ArrayReader = function(meta, recordType){
11172 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11175 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11177 * Create a data block containing Roo.data.Records from an XML document.
11178 * @param {Object} o An Array of row objects which represents the dataset.
11179 * @return {Object} data A data block which is used by an Roo.data.Store object as
11180 * a cache of Roo.data.Records.
11182 readRecords : function(o){
11183 var sid = this.meta ? this.meta.id : null;
11184 var recordType = this.recordType, fields = recordType.prototype.fields;
11187 for(var i = 0; i < root.length; i++){
11190 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11191 for(var j = 0, jlen = fields.length; j < jlen; j++){
11192 var f = fields.items[j];
11193 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11194 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11196 values[f.name] = v;
11198 var record = new recordType(values, id);
11200 records[records.length] = record;
11204 totalRecords : records.length
11213 * @class Roo.bootstrap.ComboBox
11214 * @extends Roo.bootstrap.TriggerField
11215 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11216 * @cfg {Boolean} append (true|false) default false
11217 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11218 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11219 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11220 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11221 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11222 * @cfg {Boolean} animate default true
11223 * @cfg {Boolean} emptyResultText only for touch device
11224 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11226 * Create a new ComboBox.
11227 * @param {Object} config Configuration options
11229 Roo.bootstrap.ComboBox = function(config){
11230 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11234 * Fires when the dropdown list is expanded
11235 * @param {Roo.bootstrap.ComboBox} combo This combo box
11240 * Fires when the dropdown list is collapsed
11241 * @param {Roo.bootstrap.ComboBox} combo This combo box
11245 * @event beforeselect
11246 * Fires before a list item is selected. Return false to cancel the selection.
11247 * @param {Roo.bootstrap.ComboBox} combo This combo box
11248 * @param {Roo.data.Record} record The data record returned from the underlying store
11249 * @param {Number} index The index of the selected item in the dropdown list
11251 'beforeselect' : true,
11254 * Fires when a list item is selected
11255 * @param {Roo.bootstrap.ComboBox} combo This combo box
11256 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11257 * @param {Number} index The index of the selected item in the dropdown list
11261 * @event beforequery
11262 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11263 * The event object passed has these properties:
11264 * @param {Roo.bootstrap.ComboBox} combo This combo box
11265 * @param {String} query The query
11266 * @param {Boolean} forceAll true to force "all" query
11267 * @param {Boolean} cancel true to cancel the query
11268 * @param {Object} e The query event object
11270 'beforequery': true,
11273 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11274 * @param {Roo.bootstrap.ComboBox} combo This combo box
11279 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11280 * @param {Roo.bootstrap.ComboBox} combo This combo box
11281 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11286 * Fires when the remove value from the combobox array
11287 * @param {Roo.bootstrap.ComboBox} combo This combo box
11291 * @event specialfilter
11292 * Fires when specialfilter
11293 * @param {Roo.bootstrap.ComboBox} combo This combo box
11295 'specialfilter' : true
11300 this.tickItems = [];
11302 this.selectedIndex = -1;
11303 if(this.mode == 'local'){
11304 if(config.queryDelay === undefined){
11305 this.queryDelay = 10;
11307 if(config.minChars === undefined){
11313 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11316 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11317 * rendering into an Roo.Editor, defaults to false)
11320 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11321 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11324 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11327 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11328 * the dropdown list (defaults to undefined, with no header element)
11332 * @cfg {String/Roo.Template} tpl The template to use to render the output
11336 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11338 listWidth: undefined,
11340 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11341 * mode = 'remote' or 'text' if mode = 'local')
11343 displayField: undefined,
11346 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11347 * mode = 'remote' or 'value' if mode = 'local').
11348 * Note: use of a valueField requires the user make a selection
11349 * in order for a value to be mapped.
11351 valueField: undefined,
11355 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11356 * field's data value (defaults to the underlying DOM element's name)
11358 hiddenName: undefined,
11360 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11364 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11366 selectedClass: 'active',
11369 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11373 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11374 * anchor positions (defaults to 'tl-bl')
11376 listAlign: 'tl-bl?',
11378 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11382 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11383 * query specified by the allQuery config option (defaults to 'query')
11385 triggerAction: 'query',
11387 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11388 * (defaults to 4, does not apply if editable = false)
11392 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11393 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11397 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11398 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11402 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11403 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11407 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11408 * when editable = true (defaults to false)
11410 selectOnFocus:false,
11412 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11414 queryParam: 'query',
11416 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11417 * when mode = 'remote' (defaults to 'Loading...')
11419 loadingText: 'Loading...',
11421 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11425 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11429 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11430 * traditional select (defaults to true)
11434 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11438 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11442 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11443 * listWidth has a higher value)
11447 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11448 * allow the user to set arbitrary text into the field (defaults to false)
11450 forceSelection:false,
11452 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11453 * if typeAhead = true (defaults to 250)
11455 typeAheadDelay : 250,
11457 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11458 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11460 valueNotFoundText : undefined,
11462 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11464 blockFocus : false,
11467 * @cfg {Boolean} disableClear Disable showing of clear button.
11469 disableClear : false,
11471 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11473 alwaysQuery : false,
11476 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11481 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11483 invalidClass : "has-warning",
11486 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11488 validClass : "has-success",
11491 * @cfg {Boolean} specialFilter (true|false) special filter default false
11493 specialFilter : false,
11496 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11498 mobileTouchView : true,
11510 btnPosition : 'right',
11511 triggerList : true,
11512 showToggleBtn : true,
11514 emptyResultText: 'Empty',
11515 triggerText : 'Select',
11517 // element that contains real text value.. (when hidden is used..)
11519 getAutoCreate : function()
11527 if(Roo.isTouch && this.mobileTouchView){
11528 cfg = this.getAutoCreateTouchView();
11535 if(!this.tickable){
11536 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11541 * ComboBox with tickable selections
11544 var align = this.labelAlign || this.parentLabelAlign();
11547 cls : 'form-group roo-combobox-tickable' //input-group
11552 cls : 'tickable-buttons',
11557 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11558 html : this.triggerText
11564 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11571 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11578 buttons.cn.unshift({
11580 cls: 'select2-search-field-input'
11586 Roo.each(buttons.cn, function(c){
11588 c.cls += ' btn-' + _this.size;
11591 if (_this.disabled) {
11602 cls: 'form-hidden-field'
11606 cls: 'select2-choices',
11610 cls: 'select2-search-field',
11622 cls: 'select2-container input-group select2-container-multi',
11627 // cls: 'typeahead typeahead-long dropdown-menu',
11628 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11633 if(this.hasFeedback && !this.allowBlank){
11637 cls: 'glyphicon form-control-feedback'
11640 combobox.cn.push(feedback);
11643 if (align ==='left' && this.fieldLabel.length) {
11645 Roo.log("left and has label");
11651 cls : 'control-label col-sm-' + this.labelWidth,
11652 html : this.fieldLabel
11656 cls : "col-sm-" + (12 - this.labelWidth),
11663 } else if ( this.fieldLabel.length) {
11669 //cls : 'input-group-addon',
11670 html : this.fieldLabel
11680 Roo.log(" no label && no align");
11687 ['xs','sm','md','lg'].map(function(size){
11688 if (settings[size]) {
11689 cfg.cls += ' col-' + size + '-' + settings[size];
11697 _initEventsCalled : false,
11700 initEvents: function()
11703 if (this._initEventsCalled) { // as we call render... prevent looping...
11706 this._initEventsCalled = true;
11709 throw "can not find store for combo";
11712 this.store = Roo.factory(this.store, Roo.data);
11714 // if we are building from html. then this element is so complex, that we can not really
11715 // use the rendered HTML.
11716 // so we have to trash and replace the previous code.
11717 if (Roo.XComponent.build_from_html) {
11719 // remove this element....
11720 var e = this.el.dom, k=0;
11721 while (e ) { e = e.previousSibling; ++k;}
11726 this.rendered = false;
11728 this.render(this.parent().getChildContainer(true), k);
11739 if(Roo.isTouch && this.mobileTouchView){
11740 this.initTouchView();
11745 this.initTickableEvents();
11749 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11751 if(this.hiddenName){
11753 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11755 this.hiddenField.dom.value =
11756 this.hiddenValue !== undefined ? this.hiddenValue :
11757 this.value !== undefined ? this.value : '';
11759 // prevent input submission
11760 this.el.dom.removeAttribute('name');
11761 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11766 // this.el.dom.setAttribute('autocomplete', 'off');
11769 var cls = 'x-combo-list';
11771 //this.list = new Roo.Layer({
11772 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11778 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11779 _this.list.setWidth(lw);
11782 this.list.on('mouseover', this.onViewOver, this);
11783 this.list.on('mousemove', this.onViewMove, this);
11785 this.list.on('scroll', this.onViewScroll, this);
11788 this.list.swallowEvent('mousewheel');
11789 this.assetHeight = 0;
11792 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11793 this.assetHeight += this.header.getHeight();
11796 this.innerList = this.list.createChild({cls:cls+'-inner'});
11797 this.innerList.on('mouseover', this.onViewOver, this);
11798 this.innerList.on('mousemove', this.onViewMove, this);
11799 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11801 if(this.allowBlank && !this.pageSize && !this.disableClear){
11802 this.footer = this.list.createChild({cls:cls+'-ft'});
11803 this.pageTb = new Roo.Toolbar(this.footer);
11807 this.footer = this.list.createChild({cls:cls+'-ft'});
11808 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11809 {pageSize: this.pageSize});
11813 if (this.pageTb && this.allowBlank && !this.disableClear) {
11815 this.pageTb.add(new Roo.Toolbar.Fill(), {
11816 cls: 'x-btn-icon x-btn-clear',
11818 handler: function()
11821 _this.clearValue();
11822 _this.onSelect(false, -1);
11827 this.assetHeight += this.footer.getHeight();
11832 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11835 this.view = new Roo.View(this.list, this.tpl, {
11836 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11838 //this.view.wrapEl.setDisplayed(false);
11839 this.view.on('click', this.onViewClick, this);
11843 this.store.on('beforeload', this.onBeforeLoad, this);
11844 this.store.on('load', this.onLoad, this);
11845 this.store.on('loadexception', this.onLoadException, this);
11847 if(this.resizable){
11848 this.resizer = new Roo.Resizable(this.list, {
11849 pinned:true, handles:'se'
11851 this.resizer.on('resize', function(r, w, h){
11852 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11853 this.listWidth = w;
11854 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11855 this.restrictHeight();
11857 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11860 if(!this.editable){
11861 this.editable = true;
11862 this.setEditable(false);
11867 if (typeof(this.events.add.listeners) != 'undefined') {
11869 this.addicon = this.wrap.createChild(
11870 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11872 this.addicon.on('click', function(e) {
11873 this.fireEvent('add', this);
11876 if (typeof(this.events.edit.listeners) != 'undefined') {
11878 this.editicon = this.wrap.createChild(
11879 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11880 if (this.addicon) {
11881 this.editicon.setStyle('margin-left', '40px');
11883 this.editicon.on('click', function(e) {
11885 // we fire even if inothing is selected..
11886 this.fireEvent('edit', this, this.lastData );
11892 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11893 "up" : function(e){
11894 this.inKeyMode = true;
11898 "down" : function(e){
11899 if(!this.isExpanded()){
11900 this.onTriggerClick();
11902 this.inKeyMode = true;
11907 "enter" : function(e){
11908 // this.onViewClick();
11912 if(this.fireEvent("specialkey", this, e)){
11913 this.onViewClick(false);
11919 "esc" : function(e){
11923 "tab" : function(e){
11926 if(this.fireEvent("specialkey", this, e)){
11927 this.onViewClick(false);
11935 doRelay : function(foo, bar, hname){
11936 if(hname == 'down' || this.scope.isExpanded()){
11937 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11946 this.queryDelay = Math.max(this.queryDelay || 10,
11947 this.mode == 'local' ? 10 : 250);
11950 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11952 if(this.typeAhead){
11953 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11955 if(this.editable !== false){
11956 this.inputEl().on("keyup", this.onKeyUp, this);
11958 if(this.forceSelection){
11959 this.inputEl().on('blur', this.doForce, this);
11963 this.choices = this.el.select('ul.select2-choices', true).first();
11964 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11968 initTickableEvents: function()
11972 if(this.hiddenName){
11974 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11976 this.hiddenField.dom.value =
11977 this.hiddenValue !== undefined ? this.hiddenValue :
11978 this.value !== undefined ? this.value : '';
11980 // prevent input submission
11981 this.el.dom.removeAttribute('name');
11982 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11987 // this.list = this.el.select('ul.dropdown-menu',true).first();
11989 this.choices = this.el.select('ul.select2-choices', true).first();
11990 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11991 if(this.triggerList){
11992 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11995 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11996 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11998 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11999 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12001 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12002 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12004 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12005 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12006 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12009 this.cancelBtn.hide();
12014 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12015 _this.list.setWidth(lw);
12018 this.list.on('mouseover', this.onViewOver, this);
12019 this.list.on('mousemove', this.onViewMove, this);
12021 this.list.on('scroll', this.onViewScroll, this);
12024 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>';
12027 this.view = new Roo.View(this.list, this.tpl, {
12028 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12031 //this.view.wrapEl.setDisplayed(false);
12032 this.view.on('click', this.onViewClick, this);
12036 this.store.on('beforeload', this.onBeforeLoad, this);
12037 this.store.on('load', this.onLoad, this);
12038 this.store.on('loadexception', this.onLoadException, this);
12041 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12042 "up" : function(e){
12043 this.inKeyMode = true;
12047 "down" : function(e){
12048 this.inKeyMode = true;
12052 "enter" : function(e){
12053 if(this.fireEvent("specialkey", this, e)){
12054 this.onViewClick(false);
12060 "esc" : function(e){
12061 this.onTickableFooterButtonClick(e, false, false);
12064 "tab" : function(e){
12065 this.fireEvent("specialkey", this, e);
12067 this.onTickableFooterButtonClick(e, false, false);
12074 doRelay : function(e, fn, key){
12075 if(this.scope.isExpanded()){
12076 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12085 this.queryDelay = Math.max(this.queryDelay || 10,
12086 this.mode == 'local' ? 10 : 250);
12089 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12091 if(this.typeAhead){
12092 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12095 if(this.editable !== false){
12096 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12101 onDestroy : function(){
12103 this.view.setStore(null);
12104 this.view.el.removeAllListeners();
12105 this.view.el.remove();
12106 this.view.purgeListeners();
12109 this.list.dom.innerHTML = '';
12113 this.store.un('beforeload', this.onBeforeLoad, this);
12114 this.store.un('load', this.onLoad, this);
12115 this.store.un('loadexception', this.onLoadException, this);
12117 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12121 fireKey : function(e){
12122 if(e.isNavKeyPress() && !this.list.isVisible()){
12123 this.fireEvent("specialkey", this, e);
12128 onResize: function(w, h){
12129 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12131 // if(typeof w != 'number'){
12132 // // we do not handle it!?!?
12135 // var tw = this.trigger.getWidth();
12136 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12137 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12139 // this.inputEl().setWidth( this.adjustWidth('input', x));
12141 // //this.trigger.setStyle('left', x+'px');
12143 // if(this.list && this.listWidth === undefined){
12144 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12145 // this.list.setWidth(lw);
12146 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12154 * Allow or prevent the user from directly editing the field text. If false is passed,
12155 * the user will only be able to select from the items defined in the dropdown list. This method
12156 * is the runtime equivalent of setting the 'editable' config option at config time.
12157 * @param {Boolean} value True to allow the user to directly edit the field text
12159 setEditable : function(value){
12160 if(value == this.editable){
12163 this.editable = value;
12165 this.inputEl().dom.setAttribute('readOnly', true);
12166 this.inputEl().on('mousedown', this.onTriggerClick, this);
12167 this.inputEl().addClass('x-combo-noedit');
12169 this.inputEl().dom.setAttribute('readOnly', false);
12170 this.inputEl().un('mousedown', this.onTriggerClick, this);
12171 this.inputEl().removeClass('x-combo-noedit');
12177 onBeforeLoad : function(combo,opts){
12178 if(!this.hasFocus){
12182 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12184 this.restrictHeight();
12185 this.selectedIndex = -1;
12189 onLoad : function(){
12191 this.hasQuery = false;
12193 if(!this.hasFocus){
12197 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12198 this.loading.hide();
12201 if(this.store.getCount() > 0){
12203 this.restrictHeight();
12204 if(this.lastQuery == this.allQuery){
12205 if(this.editable && !this.tickable){
12206 this.inputEl().dom.select();
12210 !this.selectByValue(this.value, true) &&
12213 !this.store.lastOptions ||
12214 typeof(this.store.lastOptions.add) == 'undefined' ||
12215 this.store.lastOptions.add != true
12218 this.select(0, true);
12221 if(this.autoFocus){
12224 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12225 this.taTask.delay(this.typeAheadDelay);
12229 this.onEmptyResults();
12235 onLoadException : function()
12237 this.hasQuery = false;
12239 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12240 this.loading.hide();
12243 if(this.tickable && this.editable){
12249 Roo.log(this.store.reader.jsonData);
12250 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12252 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12258 onTypeAhead : function(){
12259 if(this.store.getCount() > 0){
12260 var r = this.store.getAt(0);
12261 var newValue = r.data[this.displayField];
12262 var len = newValue.length;
12263 var selStart = this.getRawValue().length;
12265 if(selStart != len){
12266 this.setRawValue(newValue);
12267 this.selectText(selStart, newValue.length);
12273 onSelect : function(record, index){
12275 if(this.fireEvent('beforeselect', this, record, index) !== false){
12277 this.setFromData(index > -1 ? record.data : false);
12280 this.fireEvent('select', this, record, index);
12285 * Returns the currently selected field value or empty string if no value is set.
12286 * @return {String} value The selected value
12288 getValue : function(){
12291 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12294 if(this.valueField){
12295 return typeof this.value != 'undefined' ? this.value : '';
12297 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12302 * Clears any text/value currently set in the field
12304 clearValue : function(){
12305 if(this.hiddenField){
12306 this.hiddenField.dom.value = '';
12309 this.setRawValue('');
12310 this.lastSelectionText = '';
12311 this.lastData = false;
12313 var close = this.closeTriggerEl();
12322 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12323 * will be displayed in the field. If the value does not match the data value of an existing item,
12324 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12325 * Otherwise the field will be blank (although the value will still be set).
12326 * @param {String} value The value to match
12328 setValue : function(v){
12335 if(this.valueField){
12336 var r = this.findRecord(this.valueField, v);
12338 text = r.data[this.displayField];
12339 }else if(this.valueNotFoundText !== undefined){
12340 text = this.valueNotFoundText;
12343 this.lastSelectionText = text;
12344 if(this.hiddenField){
12345 this.hiddenField.dom.value = v;
12347 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12350 var close = this.closeTriggerEl();
12353 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12357 * @property {Object} the last set data for the element
12362 * Sets the value of the field based on a object which is related to the record format for the store.
12363 * @param {Object} value the value to set as. or false on reset?
12365 setFromData : function(o){
12372 var dv = ''; // display value
12373 var vv = ''; // value value..
12375 if (this.displayField) {
12376 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12378 // this is an error condition!!!
12379 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12382 if(this.valueField){
12383 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12386 var close = this.closeTriggerEl();
12389 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12392 if(this.hiddenField){
12393 this.hiddenField.dom.value = vv;
12395 this.lastSelectionText = dv;
12396 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12400 // no hidden field.. - we store the value in 'value', but still display
12401 // display field!!!!
12402 this.lastSelectionText = dv;
12403 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12410 reset : function(){
12411 // overridden so that last data is reset..
12418 this.setValue(this.originalValue);
12419 this.clearInvalid();
12420 this.lastData = false;
12422 this.view.clearSelections();
12426 findRecord : function(prop, value){
12428 if(this.store.getCount() > 0){
12429 this.store.each(function(r){
12430 if(r.data[prop] == value){
12440 getName: function()
12442 // returns hidden if it's set..
12443 if (!this.rendered) {return ''};
12444 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12448 onViewMove : function(e, t){
12449 this.inKeyMode = false;
12453 onViewOver : function(e, t){
12454 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12457 var item = this.view.findItemFromChild(t);
12460 var index = this.view.indexOf(item);
12461 this.select(index, false);
12466 onViewClick : function(view, doFocus, el, e)
12468 var index = this.view.getSelectedIndexes()[0];
12470 var r = this.store.getAt(index);
12474 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12481 Roo.each(this.tickItems, function(v,k){
12483 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12484 _this.tickItems.splice(k, 1);
12486 if(typeof(e) == 'undefined' && view == false){
12487 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12499 this.tickItems.push(r.data);
12501 if(typeof(e) == 'undefined' && view == false){
12502 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12509 this.onSelect(r, index);
12511 if(doFocus !== false && !this.blockFocus){
12512 this.inputEl().focus();
12517 restrictHeight : function(){
12518 //this.innerList.dom.style.height = '';
12519 //var inner = this.innerList.dom;
12520 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12521 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12522 //this.list.beginUpdate();
12523 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12524 this.list.alignTo(this.inputEl(), this.listAlign);
12525 this.list.alignTo(this.inputEl(), this.listAlign);
12526 //this.list.endUpdate();
12530 onEmptyResults : function(){
12532 if(this.tickable && this.editable){
12533 this.restrictHeight();
12541 * Returns true if the dropdown list is expanded, else false.
12543 isExpanded : function(){
12544 return this.list.isVisible();
12548 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12549 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12550 * @param {String} value The data value of the item to select
12551 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12552 * selected item if it is not currently in view (defaults to true)
12553 * @return {Boolean} True if the value matched an item in the list, else false
12555 selectByValue : function(v, scrollIntoView){
12556 if(v !== undefined && v !== null){
12557 var r = this.findRecord(this.valueField || this.displayField, v);
12559 this.select(this.store.indexOf(r), scrollIntoView);
12567 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12568 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12569 * @param {Number} index The zero-based index of the list item to select
12570 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12571 * selected item if it is not currently in view (defaults to true)
12573 select : function(index, scrollIntoView){
12574 this.selectedIndex = index;
12575 this.view.select(index);
12576 if(scrollIntoView !== false){
12577 var el = this.view.getNode(index);
12579 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12582 this.list.scrollChildIntoView(el, false);
12588 selectNext : function(){
12589 var ct = this.store.getCount();
12591 if(this.selectedIndex == -1){
12593 }else if(this.selectedIndex < ct-1){
12594 this.select(this.selectedIndex+1);
12600 selectPrev : function(){
12601 var ct = this.store.getCount();
12603 if(this.selectedIndex == -1){
12605 }else if(this.selectedIndex != 0){
12606 this.select(this.selectedIndex-1);
12612 onKeyUp : function(e){
12613 if(this.editable !== false && !e.isSpecialKey()){
12614 this.lastKey = e.getKey();
12615 this.dqTask.delay(this.queryDelay);
12620 validateBlur : function(){
12621 return !this.list || !this.list.isVisible();
12625 initQuery : function(){
12627 var v = this.getRawValue();
12629 if(this.tickable && this.editable){
12630 v = this.tickableInputEl().getValue();
12637 doForce : function(){
12638 if(this.inputEl().dom.value.length > 0){
12639 this.inputEl().dom.value =
12640 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12646 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12647 * query allowing the query action to be canceled if needed.
12648 * @param {String} query The SQL query to execute
12649 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12650 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12651 * saved in the current store (defaults to false)
12653 doQuery : function(q, forceAll){
12655 if(q === undefined || q === null){
12660 forceAll: forceAll,
12664 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12669 forceAll = qe.forceAll;
12670 if(forceAll === true || (q.length >= this.minChars)){
12672 this.hasQuery = true;
12674 if(this.lastQuery != q || this.alwaysQuery){
12675 this.lastQuery = q;
12676 if(this.mode == 'local'){
12677 this.selectedIndex = -1;
12679 this.store.clearFilter();
12682 if(this.specialFilter){
12683 this.fireEvent('specialfilter', this);
12688 this.store.filter(this.displayField, q);
12691 this.store.fireEvent("datachanged", this.store);
12698 this.store.baseParams[this.queryParam] = q;
12700 var options = {params : this.getParams(q)};
12703 options.add = true;
12704 options.params.start = this.page * this.pageSize;
12707 this.store.load(options);
12710 * this code will make the page width larger, at the beginning, the list not align correctly,
12711 * we should expand the list on onLoad
12712 * so command out it
12717 this.selectedIndex = -1;
12722 this.loadNext = false;
12726 getParams : function(q){
12728 //p[this.queryParam] = q;
12732 p.limit = this.pageSize;
12738 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12740 collapse : function(){
12741 if(!this.isExpanded()){
12748 this.hasFocus = false;
12750 this.cancelBtn.hide();
12751 this.trigger.show();
12754 this.tickableInputEl().dom.value = '';
12755 this.tickableInputEl().blur();
12760 Roo.get(document).un('mousedown', this.collapseIf, this);
12761 Roo.get(document).un('mousewheel', this.collapseIf, this);
12762 if (!this.editable) {
12763 Roo.get(document).un('keydown', this.listKeyPress, this);
12765 this.fireEvent('collapse', this);
12769 collapseIf : function(e){
12770 var in_combo = e.within(this.el);
12771 var in_list = e.within(this.list);
12772 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12774 if (in_combo || in_list || is_list) {
12775 //e.stopPropagation();
12780 this.onTickableFooterButtonClick(e, false, false);
12788 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12790 expand : function(){
12792 if(this.isExpanded() || !this.hasFocus){
12796 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12797 this.list.setWidth(lw);
12804 this.restrictHeight();
12808 this.tickItems = Roo.apply([], this.item);
12811 this.cancelBtn.show();
12812 this.trigger.hide();
12815 this.tickableInputEl().focus();
12820 Roo.get(document).on('mousedown', this.collapseIf, this);
12821 Roo.get(document).on('mousewheel', this.collapseIf, this);
12822 if (!this.editable) {
12823 Roo.get(document).on('keydown', this.listKeyPress, this);
12826 this.fireEvent('expand', this);
12830 // Implements the default empty TriggerField.onTriggerClick function
12831 onTriggerClick : function(e)
12833 Roo.log('trigger click');
12835 if(this.disabled || !this.triggerList){
12840 this.loadNext = false;
12842 if(this.isExpanded()){
12844 if (!this.blockFocus) {
12845 this.inputEl().focus();
12849 this.hasFocus = true;
12850 if(this.triggerAction == 'all') {
12851 this.doQuery(this.allQuery, true);
12853 this.doQuery(this.getRawValue());
12855 if (!this.blockFocus) {
12856 this.inputEl().focus();
12861 onTickableTriggerClick : function(e)
12868 this.loadNext = false;
12869 this.hasFocus = true;
12871 if(this.triggerAction == 'all') {
12872 this.doQuery(this.allQuery, true);
12874 this.doQuery(this.getRawValue());
12878 onSearchFieldClick : function(e)
12880 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12881 this.onTickableFooterButtonClick(e, false, false);
12885 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12890 this.loadNext = false;
12891 this.hasFocus = true;
12893 if(this.triggerAction == 'all') {
12894 this.doQuery(this.allQuery, true);
12896 this.doQuery(this.getRawValue());
12900 listKeyPress : function(e)
12902 //Roo.log('listkeypress');
12903 // scroll to first matching element based on key pres..
12904 if (e.isSpecialKey()) {
12907 var k = String.fromCharCode(e.getKey()).toUpperCase();
12910 var csel = this.view.getSelectedNodes();
12911 var cselitem = false;
12913 var ix = this.view.indexOf(csel[0]);
12914 cselitem = this.store.getAt(ix);
12915 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12921 this.store.each(function(v) {
12923 // start at existing selection.
12924 if (cselitem.id == v.id) {
12930 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12931 match = this.store.indexOf(v);
12937 if (match === false) {
12938 return true; // no more action?
12941 this.view.select(match);
12942 var sn = Roo.get(this.view.getSelectedNodes()[0])
12943 sn.scrollIntoView(sn.dom.parentNode, false);
12946 onViewScroll : function(e, t){
12948 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){
12952 this.hasQuery = true;
12954 this.loading = this.list.select('.loading', true).first();
12956 if(this.loading === null){
12957 this.list.createChild({
12959 cls: 'loading select2-more-results select2-active',
12960 html: 'Loading more results...'
12963 this.loading = this.list.select('.loading', true).first();
12965 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12967 this.loading.hide();
12970 this.loading.show();
12975 this.loadNext = true;
12977 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12982 addItem : function(o)
12984 var dv = ''; // display value
12986 if (this.displayField) {
12987 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12989 // this is an error condition!!!
12990 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12997 var choice = this.choices.createChild({
12999 cls: 'select2-search-choice',
13008 cls: 'select2-search-choice-close',
13013 }, this.searchField);
13015 var close = choice.select('a.select2-search-choice-close', true).first()
13017 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13025 this.inputEl().dom.value = '';
13030 onRemoveItem : function(e, _self, o)
13032 e.preventDefault();
13034 this.lastItem = Roo.apply([], this.item);
13036 var index = this.item.indexOf(o.data) * 1;
13039 Roo.log('not this item?!');
13043 this.item.splice(index, 1);
13048 this.fireEvent('remove', this, e);
13054 syncValue : function()
13056 if(!this.item.length){
13063 Roo.each(this.item, function(i){
13064 if(_this.valueField){
13065 value.push(i[_this.valueField]);
13072 this.value = value.join(',');
13074 if(this.hiddenField){
13075 this.hiddenField.dom.value = this.value;
13078 this.store.fireEvent("datachanged", this.store);
13081 clearItem : function()
13083 if(!this.multiple){
13089 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13097 if(this.tickable && !Roo.isTouch){
13098 this.view.refresh();
13102 inputEl: function ()
13104 if(Roo.isTouch && this.mobileTouchView){
13105 return this.el.select('input.form-control',true).first();
13109 return this.searchField;
13112 return this.el.select('input.form-control',true).first();
13116 onTickableFooterButtonClick : function(e, btn, el)
13118 e.preventDefault();
13120 this.lastItem = Roo.apply([], this.item);
13122 if(btn && btn.name == 'cancel'){
13123 this.tickItems = Roo.apply([], this.item);
13132 Roo.each(this.tickItems, function(o){
13140 validate : function()
13142 var v = this.getRawValue();
13145 v = this.getValue();
13148 if(this.disabled || this.allowBlank || v.length){
13153 this.markInvalid();
13157 tickableInputEl : function()
13159 if(!this.tickable || !this.editable){
13160 return this.inputEl();
13163 return this.inputEl().select('.select2-search-field-input', true).first();
13167 getAutoCreateTouchView : function()
13172 cls: 'form-group' //input-group
13178 type : this.inputType,
13179 cls : 'form-control x-combo-noedit',
13180 autocomplete: 'new-password',
13181 placeholder : this.placeholder || '',
13186 input.name = this.name;
13190 input.cls += ' input-' + this.size;
13193 if (this.disabled) {
13194 input.disabled = true;
13205 inputblock.cls += ' input-group';
13207 inputblock.cn.unshift({
13209 cls : 'input-group-addon',
13214 if(this.removable && !this.multiple){
13215 inputblock.cls += ' roo-removable';
13217 inputblock.cn.push({
13220 cls : 'roo-combo-removable-btn close'
13224 if(this.hasFeedback && !this.allowBlank){
13226 inputblock.cls += ' has-feedback';
13228 inputblock.cn.push({
13230 cls: 'glyphicon form-control-feedback'
13237 inputblock.cls += (this.before) ? '' : ' input-group';
13239 inputblock.cn.push({
13241 cls : 'input-group-addon',
13252 cls: 'form-hidden-field'
13266 cls: 'form-hidden-field'
13270 cls: 'select2-choices',
13274 cls: 'select2-search-field',
13287 cls: 'select2-container input-group',
13294 combobox.cls += ' select2-container-multi';
13297 var align = this.labelAlign || this.parentLabelAlign();
13301 if(this.fieldLabel.length){
13303 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13304 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13309 cls : 'control-label ' + lw,
13310 html : this.fieldLabel
13322 var settings = this;
13324 ['xs','sm','md','lg'].map(function(size){
13325 if (settings[size]) {
13326 cfg.cls += ' col-' + size + '-' + settings[size];
13333 initTouchView : function()
13335 this.renderTouchView();
13337 this.touchViewEl.on('scroll', function(){
13338 this.el.dom.scrollTop = 0;
13341 this.inputEl().on("click", this.showTouchView, this);
13342 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13343 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13345 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13347 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13348 this.store.on('load', this.onTouchViewLoad, this);
13349 this.store.on('loadexception', this.onTouchViewLoadException, this);
13351 if(this.hiddenName){
13353 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13355 this.hiddenField.dom.value =
13356 this.hiddenValue !== undefined ? this.hiddenValue :
13357 this.value !== undefined ? this.value : '';
13359 this.el.dom.removeAttribute('name');
13360 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13364 this.choices = this.el.select('ul.select2-choices', true).first();
13365 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13368 if(this.removable && !this.multiple){
13369 var close = this.closeTriggerEl();
13371 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13372 close.on('click', this.removeBtnClick, this, close);
13381 renderTouchView : function()
13383 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13384 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13386 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13387 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13389 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13390 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13391 this.touchViewBodyEl.setStyle('overflow', 'auto');
13393 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13394 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13396 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13397 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13401 showTouchView : function()
13403 this.touchViewHeaderEl.hide();
13405 if(this.fieldLabel.length){
13406 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13407 this.touchViewHeaderEl.show();
13410 this.touchViewEl.show();
13412 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13413 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13415 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13417 if(this.fieldLabel.length){
13418 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13421 this.touchViewBodyEl.setHeight(bodyHeight);
13425 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13427 this.touchViewEl.addClass('in');
13430 this.doTouchViewQuery();
13434 hideTouchView : function()
13436 this.touchViewEl.removeClass('in');
13440 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13442 this.touchViewEl.setStyle('display', 'none');
13447 setTouchViewValue : function()
13454 Roo.each(this.tickItems, function(o){
13459 this.hideTouchView();
13462 doTouchViewQuery : function()
13471 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13475 if(!this.alwaysQuery || this.mode == 'local'){
13476 this.onTouchViewLoad();
13483 onTouchViewBeforeLoad : function(combo,opts)
13489 onTouchViewLoad : function()
13491 if(this.store.getCount() < 1){
13492 this.onTouchViewEmptyResults();
13496 this.clearTouchView();
13498 var rawValue = this.getRawValue();
13500 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13502 this.tickItems = [];
13504 this.store.data.each(function(d, rowIndex){
13505 var row = this.touchViewListGroup.createChild(template);
13507 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13508 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13511 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13512 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13515 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13516 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13517 this.tickItems.push(d.data);
13520 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13524 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13526 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13528 if(this.fieldLabel.length){
13529 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13532 var listHeight = this.touchViewListGroup.getHeight();
13536 if(firstChecked && listHeight > bodyHeight){
13537 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13542 onTouchViewLoadException : function()
13544 this.hideTouchView();
13547 onTouchViewEmptyResults : function()
13549 this.clearTouchView();
13551 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13553 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13557 clearTouchView : function()
13559 this.touchViewListGroup.dom.innerHTML = '';
13562 onTouchViewClick : function(e, el, o)
13564 e.preventDefault();
13567 var rowIndex = o.rowIndex;
13569 var r = this.store.getAt(rowIndex);
13571 if(!this.multiple){
13572 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13573 c.dom.removeAttribute('checked');
13576 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13578 this.setFromData(r.data);
13580 var close = this.closeTriggerEl();
13586 this.hideTouchView();
13588 this.fireEvent('select', this, r, rowIndex);
13593 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13594 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13595 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13599 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13600 this.addItem(r.data);
13601 this.tickItems.push(r.data);
13607 * @cfg {Boolean} grow
13611 * @cfg {Number} growMin
13615 * @cfg {Number} growMax
13624 Roo.apply(Roo.bootstrap.ComboBox, {
13628 cls: 'modal-header',
13650 cls: 'list-group-item',
13654 cls: 'roo-combobox-list-group-item-value'
13658 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13672 listItemCheckbox : {
13674 cls: 'list-group-item',
13678 cls: 'roo-combobox-list-group-item-value'
13682 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13698 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13703 cls: 'modal-footer',
13711 cls: 'col-xs-6 text-left',
13714 cls: 'btn btn-danger roo-touch-view-cancel',
13720 cls: 'col-xs-6 text-right',
13723 cls: 'btn btn-success roo-touch-view-ok',
13734 Roo.apply(Roo.bootstrap.ComboBox, {
13736 touchViewTemplate : {
13738 cls: 'modal fade roo-combobox-touch-view',
13742 cls: 'modal-dialog',
13746 cls: 'modal-content',
13748 Roo.bootstrap.ComboBox.header,
13749 Roo.bootstrap.ComboBox.body,
13750 Roo.bootstrap.ComboBox.footer
13759 * Ext JS Library 1.1.1
13760 * Copyright(c) 2006-2007, Ext JS, LLC.
13762 * Originally Released Under LGPL - original licence link has changed is not relivant.
13765 * <script type="text/javascript">
13770 * @extends Roo.util.Observable
13771 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13772 * This class also supports single and multi selection modes. <br>
13773 * Create a data model bound view:
13775 var store = new Roo.data.Store(...);
13777 var view = new Roo.View({
13779 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13781 singleSelect: true,
13782 selectedClass: "ydataview-selected",
13786 // listen for node click?
13787 view.on("click", function(vw, index, node, e){
13788 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13792 dataModel.load("foobar.xml");
13794 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13796 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13797 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13799 * Note: old style constructor is still suported (container, template, config)
13802 * Create a new View
13803 * @param {Object} config The config object
13806 Roo.View = function(config, depreciated_tpl, depreciated_config){
13808 this.parent = false;
13810 if (typeof(depreciated_tpl) == 'undefined') {
13811 // new way.. - universal constructor.
13812 Roo.apply(this, config);
13813 this.el = Roo.get(this.el);
13816 this.el = Roo.get(config);
13817 this.tpl = depreciated_tpl;
13818 Roo.apply(this, depreciated_config);
13820 this.wrapEl = this.el.wrap().wrap();
13821 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13824 if(typeof(this.tpl) == "string"){
13825 this.tpl = new Roo.Template(this.tpl);
13827 // support xtype ctors..
13828 this.tpl = new Roo.factory(this.tpl, Roo);
13832 this.tpl.compile();
13837 * @event beforeclick
13838 * Fires before a click is processed. Returns false to cancel the default action.
13839 * @param {Roo.View} this
13840 * @param {Number} index The index of the target node
13841 * @param {HTMLElement} node The target node
13842 * @param {Roo.EventObject} e The raw event object
13844 "beforeclick" : true,
13847 * Fires when a template node is clicked.
13848 * @param {Roo.View} this
13849 * @param {Number} index The index of the target node
13850 * @param {HTMLElement} node The target node
13851 * @param {Roo.EventObject} e The raw event object
13856 * Fires when a template node is double clicked.
13857 * @param {Roo.View} this
13858 * @param {Number} index The index of the target node
13859 * @param {HTMLElement} node The target node
13860 * @param {Roo.EventObject} e The raw event object
13864 * @event contextmenu
13865 * Fires when a template node is right clicked.
13866 * @param {Roo.View} this
13867 * @param {Number} index The index of the target node
13868 * @param {HTMLElement} node The target node
13869 * @param {Roo.EventObject} e The raw event object
13871 "contextmenu" : true,
13873 * @event selectionchange
13874 * Fires when the selected nodes change.
13875 * @param {Roo.View} this
13876 * @param {Array} selections Array of the selected nodes
13878 "selectionchange" : true,
13881 * @event beforeselect
13882 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13883 * @param {Roo.View} this
13884 * @param {HTMLElement} node The node to be selected
13885 * @param {Array} selections Array of currently selected nodes
13887 "beforeselect" : true,
13889 * @event preparedata
13890 * Fires on every row to render, to allow you to change the data.
13891 * @param {Roo.View} this
13892 * @param {Object} data to be rendered (change this)
13894 "preparedata" : true
13902 "click": this.onClick,
13903 "dblclick": this.onDblClick,
13904 "contextmenu": this.onContextMenu,
13908 this.selections = [];
13910 this.cmp = new Roo.CompositeElementLite([]);
13912 this.store = Roo.factory(this.store, Roo.data);
13913 this.setStore(this.store, true);
13916 if ( this.footer && this.footer.xtype) {
13918 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13920 this.footer.dataSource = this.store;
13921 this.footer.container = fctr;
13922 this.footer = Roo.factory(this.footer, Roo);
13923 fctr.insertFirst(this.el);
13925 // this is a bit insane - as the paging toolbar seems to detach the el..
13926 // dom.parentNode.parentNode.parentNode
13927 // they get detached?
13931 Roo.View.superclass.constructor.call(this);
13936 Roo.extend(Roo.View, Roo.util.Observable, {
13939 * @cfg {Roo.data.Store} store Data store to load data from.
13944 * @cfg {String|Roo.Element} el The container element.
13949 * @cfg {String|Roo.Template} tpl The template used by this View
13953 * @cfg {String} dataName the named area of the template to use as the data area
13954 * Works with domtemplates roo-name="name"
13958 * @cfg {String} selectedClass The css class to add to selected nodes
13960 selectedClass : "x-view-selected",
13962 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13967 * @cfg {String} text to display on mask (default Loading)
13971 * @cfg {Boolean} multiSelect Allow multiple selection
13973 multiSelect : false,
13975 * @cfg {Boolean} singleSelect Allow single selection
13977 singleSelect: false,
13980 * @cfg {Boolean} toggleSelect - selecting
13982 toggleSelect : false,
13985 * @cfg {Boolean} tickable - selecting
13990 * Returns the element this view is bound to.
13991 * @return {Roo.Element}
13993 getEl : function(){
13994 return this.wrapEl;
14000 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14002 refresh : function(){
14003 //Roo.log('refresh');
14006 // if we are using something like 'domtemplate', then
14007 // the what gets used is:
14008 // t.applySubtemplate(NAME, data, wrapping data..)
14009 // the outer template then get' applied with
14010 // the store 'extra data'
14011 // and the body get's added to the
14012 // roo-name="data" node?
14013 // <span class='roo-tpl-{name}'></span> ?????
14017 this.clearSelections();
14018 this.el.update("");
14020 var records = this.store.getRange();
14021 if(records.length < 1) {
14023 // is this valid?? = should it render a template??
14025 this.el.update(this.emptyText);
14029 if (this.dataName) {
14030 this.el.update(t.apply(this.store.meta)); //????
14031 el = this.el.child('.roo-tpl-' + this.dataName);
14034 for(var i = 0, len = records.length; i < len; i++){
14035 var data = this.prepareData(records[i].data, i, records[i]);
14036 this.fireEvent("preparedata", this, data, i, records[i]);
14038 var d = Roo.apply({}, data);
14041 Roo.apply(d, {'roo-id' : Roo.id()});
14045 Roo.each(this.parent.item, function(item){
14046 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14049 Roo.apply(d, {'roo-data-checked' : 'checked'});
14053 html[html.length] = Roo.util.Format.trim(
14055 t.applySubtemplate(this.dataName, d, this.store.meta) :
14062 el.update(html.join(""));
14063 this.nodes = el.dom.childNodes;
14064 this.updateIndexes(0);
14069 * Function to override to reformat the data that is sent to
14070 * the template for each node.
14071 * DEPRICATED - use the preparedata event handler.
14072 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14073 * a JSON object for an UpdateManager bound view).
14075 prepareData : function(data, index, record)
14077 this.fireEvent("preparedata", this, data, index, record);
14081 onUpdate : function(ds, record){
14082 // Roo.log('on update');
14083 this.clearSelections();
14084 var index = this.store.indexOf(record);
14085 var n = this.nodes[index];
14086 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14087 n.parentNode.removeChild(n);
14088 this.updateIndexes(index, index);
14094 onAdd : function(ds, records, index)
14096 //Roo.log(['on Add', ds, records, index] );
14097 this.clearSelections();
14098 if(this.nodes.length == 0){
14102 var n = this.nodes[index];
14103 for(var i = 0, len = records.length; i < len; i++){
14104 var d = this.prepareData(records[i].data, i, records[i]);
14106 this.tpl.insertBefore(n, d);
14109 this.tpl.append(this.el, d);
14112 this.updateIndexes(index);
14115 onRemove : function(ds, record, index){
14116 // Roo.log('onRemove');
14117 this.clearSelections();
14118 var el = this.dataName ?
14119 this.el.child('.roo-tpl-' + this.dataName) :
14122 el.dom.removeChild(this.nodes[index]);
14123 this.updateIndexes(index);
14127 * Refresh an individual node.
14128 * @param {Number} index
14130 refreshNode : function(index){
14131 this.onUpdate(this.store, this.store.getAt(index));
14134 updateIndexes : function(startIndex, endIndex){
14135 var ns = this.nodes;
14136 startIndex = startIndex || 0;
14137 endIndex = endIndex || ns.length - 1;
14138 for(var i = startIndex; i <= endIndex; i++){
14139 ns[i].nodeIndex = i;
14144 * Changes the data store this view uses and refresh the view.
14145 * @param {Store} store
14147 setStore : function(store, initial){
14148 if(!initial && this.store){
14149 this.store.un("datachanged", this.refresh);
14150 this.store.un("add", this.onAdd);
14151 this.store.un("remove", this.onRemove);
14152 this.store.un("update", this.onUpdate);
14153 this.store.un("clear", this.refresh);
14154 this.store.un("beforeload", this.onBeforeLoad);
14155 this.store.un("load", this.onLoad);
14156 this.store.un("loadexception", this.onLoad);
14160 store.on("datachanged", this.refresh, this);
14161 store.on("add", this.onAdd, this);
14162 store.on("remove", this.onRemove, this);
14163 store.on("update", this.onUpdate, this);
14164 store.on("clear", this.refresh, this);
14165 store.on("beforeload", this.onBeforeLoad, this);
14166 store.on("load", this.onLoad, this);
14167 store.on("loadexception", this.onLoad, this);
14175 * onbeforeLoad - masks the loading area.
14178 onBeforeLoad : function(store,opts)
14180 //Roo.log('onBeforeLoad');
14182 this.el.update("");
14184 this.el.mask(this.mask ? this.mask : "Loading" );
14186 onLoad : function ()
14193 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14194 * @param {HTMLElement} node
14195 * @return {HTMLElement} The template node
14197 findItemFromChild : function(node){
14198 var el = this.dataName ?
14199 this.el.child('.roo-tpl-' + this.dataName,true) :
14202 if(!node || node.parentNode == el){
14205 var p = node.parentNode;
14206 while(p && p != el){
14207 if(p.parentNode == el){
14216 onClick : function(e){
14217 var item = this.findItemFromChild(e.getTarget());
14219 var index = this.indexOf(item);
14220 if(this.onItemClick(item, index, e) !== false){
14221 this.fireEvent("click", this, index, item, e);
14224 this.clearSelections();
14229 onContextMenu : function(e){
14230 var item = this.findItemFromChild(e.getTarget());
14232 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14237 onDblClick : function(e){
14238 var item = this.findItemFromChild(e.getTarget());
14240 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14244 onItemClick : function(item, index, e)
14246 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14249 if (this.toggleSelect) {
14250 var m = this.isSelected(item) ? 'unselect' : 'select';
14253 _t[m](item, true, false);
14256 if(this.multiSelect || this.singleSelect){
14257 if(this.multiSelect && e.shiftKey && this.lastSelection){
14258 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14260 this.select(item, this.multiSelect && e.ctrlKey);
14261 this.lastSelection = item;
14264 if(!this.tickable){
14265 e.preventDefault();
14273 * Get the number of selected nodes.
14276 getSelectionCount : function(){
14277 return this.selections.length;
14281 * Get the currently selected nodes.
14282 * @return {Array} An array of HTMLElements
14284 getSelectedNodes : function(){
14285 return this.selections;
14289 * Get the indexes of the selected nodes.
14292 getSelectedIndexes : function(){
14293 var indexes = [], s = this.selections;
14294 for(var i = 0, len = s.length; i < len; i++){
14295 indexes.push(s[i].nodeIndex);
14301 * Clear all selections
14302 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14304 clearSelections : function(suppressEvent){
14305 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14306 this.cmp.elements = this.selections;
14307 this.cmp.removeClass(this.selectedClass);
14308 this.selections = [];
14309 if(!suppressEvent){
14310 this.fireEvent("selectionchange", this, this.selections);
14316 * Returns true if the passed node is selected
14317 * @param {HTMLElement/Number} node The node or node index
14318 * @return {Boolean}
14320 isSelected : function(node){
14321 var s = this.selections;
14325 node = this.getNode(node);
14326 return s.indexOf(node) !== -1;
14331 * @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
14332 * @param {Boolean} keepExisting (optional) true to keep existing selections
14333 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14335 select : function(nodeInfo, keepExisting, suppressEvent){
14336 if(nodeInfo instanceof Array){
14338 this.clearSelections(true);
14340 for(var i = 0, len = nodeInfo.length; i < len; i++){
14341 this.select(nodeInfo[i], true, true);
14345 var node = this.getNode(nodeInfo);
14346 if(!node || this.isSelected(node)){
14347 return; // already selected.
14350 this.clearSelections(true);
14353 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14354 Roo.fly(node).addClass(this.selectedClass);
14355 this.selections.push(node);
14356 if(!suppressEvent){
14357 this.fireEvent("selectionchange", this, this.selections);
14365 * @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
14366 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14367 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14369 unselect : function(nodeInfo, keepExisting, suppressEvent)
14371 if(nodeInfo instanceof Array){
14372 Roo.each(this.selections, function(s) {
14373 this.unselect(s, nodeInfo);
14377 var node = this.getNode(nodeInfo);
14378 if(!node || !this.isSelected(node)){
14379 //Roo.log("not selected");
14380 return; // not selected.
14384 Roo.each(this.selections, function(s) {
14386 Roo.fly(node).removeClass(this.selectedClass);
14393 this.selections= ns;
14394 this.fireEvent("selectionchange", this, this.selections);
14398 * Gets a template node.
14399 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14400 * @return {HTMLElement} The node or null if it wasn't found
14402 getNode : function(nodeInfo){
14403 if(typeof nodeInfo == "string"){
14404 return document.getElementById(nodeInfo);
14405 }else if(typeof nodeInfo == "number"){
14406 return this.nodes[nodeInfo];
14412 * Gets a range template nodes.
14413 * @param {Number} startIndex
14414 * @param {Number} endIndex
14415 * @return {Array} An array of nodes
14417 getNodes : function(start, end){
14418 var ns = this.nodes;
14419 start = start || 0;
14420 end = typeof end == "undefined" ? ns.length - 1 : end;
14423 for(var i = start; i <= end; i++){
14427 for(var i = start; i >= end; i--){
14435 * Finds the index of the passed node
14436 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14437 * @return {Number} The index of the node or -1
14439 indexOf : function(node){
14440 node = this.getNode(node);
14441 if(typeof node.nodeIndex == "number"){
14442 return node.nodeIndex;
14444 var ns = this.nodes;
14445 for(var i = 0, len = ns.length; i < len; i++){
14456 * based on jquery fullcalendar
14460 Roo.bootstrap = Roo.bootstrap || {};
14462 * @class Roo.bootstrap.Calendar
14463 * @extends Roo.bootstrap.Component
14464 * Bootstrap Calendar class
14465 * @cfg {Boolean} loadMask (true|false) default false
14466 * @cfg {Object} header generate the user specific header of the calendar, default false
14469 * Create a new Container
14470 * @param {Object} config The config object
14475 Roo.bootstrap.Calendar = function(config){
14476 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14480 * Fires when a date is selected
14481 * @param {DatePicker} this
14482 * @param {Date} date The selected date
14486 * @event monthchange
14487 * Fires when the displayed month changes
14488 * @param {DatePicker} this
14489 * @param {Date} date The selected month
14491 'monthchange': true,
14493 * @event evententer
14494 * Fires when mouse over an event
14495 * @param {Calendar} this
14496 * @param {event} Event
14498 'evententer': true,
14500 * @event eventleave
14501 * Fires when the mouse leaves an
14502 * @param {Calendar} this
14505 'eventleave': true,
14507 * @event eventclick
14508 * Fires when the mouse click an
14509 * @param {Calendar} this
14518 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14521 * @cfg {Number} startDay
14522 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14530 getAutoCreate : function(){
14533 var fc_button = function(name, corner, style, content ) {
14534 return Roo.apply({},{
14536 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14538 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14541 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14552 style : 'width:100%',
14559 cls : 'fc-header-left',
14561 fc_button('prev', 'left', 'arrow', '‹' ),
14562 fc_button('next', 'right', 'arrow', '›' ),
14563 { tag: 'span', cls: 'fc-header-space' },
14564 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14572 cls : 'fc-header-center',
14576 cls: 'fc-header-title',
14579 html : 'month / year'
14587 cls : 'fc-header-right',
14589 /* fc_button('month', 'left', '', 'month' ),
14590 fc_button('week', '', '', 'week' ),
14591 fc_button('day', 'right', '', 'day' )
14603 header = this.header;
14606 var cal_heads = function() {
14608 // fixme - handle this.
14610 for (var i =0; i < Date.dayNames.length; i++) {
14611 var d = Date.dayNames[i];
14614 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14615 html : d.substring(0,3)
14619 ret[0].cls += ' fc-first';
14620 ret[6].cls += ' fc-last';
14623 var cal_cell = function(n) {
14626 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14631 cls: 'fc-day-number',
14635 cls: 'fc-day-content',
14639 style: 'position: relative;' // height: 17px;
14651 var cal_rows = function() {
14654 for (var r = 0; r < 6; r++) {
14661 for (var i =0; i < Date.dayNames.length; i++) {
14662 var d = Date.dayNames[i];
14663 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14666 row.cn[0].cls+=' fc-first';
14667 row.cn[0].cn[0].style = 'min-height:90px';
14668 row.cn[6].cls+=' fc-last';
14672 ret[0].cls += ' fc-first';
14673 ret[4].cls += ' fc-prev-last';
14674 ret[5].cls += ' fc-last';
14681 cls: 'fc-border-separate',
14682 style : 'width:100%',
14690 cls : 'fc-first fc-last',
14708 cls : 'fc-content',
14709 style : "position: relative;",
14712 cls : 'fc-view fc-view-month fc-grid',
14713 style : 'position: relative',
14714 unselectable : 'on',
14717 cls : 'fc-event-container',
14718 style : 'position:absolute;z-index:8;top:0;left:0;'
14736 initEvents : function()
14739 throw "can not find store for calendar";
14745 style: "text-align:center",
14749 style: "background-color:white;width:50%;margin:250 auto",
14753 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14764 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14766 var size = this.el.select('.fc-content', true).first().getSize();
14767 this.maskEl.setSize(size.width, size.height);
14768 this.maskEl.enableDisplayMode("block");
14769 if(!this.loadMask){
14770 this.maskEl.hide();
14773 this.store = Roo.factory(this.store, Roo.data);
14774 this.store.on('load', this.onLoad, this);
14775 this.store.on('beforeload', this.onBeforeLoad, this);
14779 this.cells = this.el.select('.fc-day',true);
14780 //Roo.log(this.cells);
14781 this.textNodes = this.el.query('.fc-day-number');
14782 this.cells.addClassOnOver('fc-state-hover');
14784 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14785 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14786 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14787 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14789 this.on('monthchange', this.onMonthChange, this);
14791 this.update(new Date().clearTime());
14794 resize : function() {
14795 var sz = this.el.getSize();
14797 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14798 this.el.select('.fc-day-content div',true).setHeight(34);
14803 showPrevMonth : function(e){
14804 this.update(this.activeDate.add("mo", -1));
14806 showToday : function(e){
14807 this.update(new Date().clearTime());
14810 showNextMonth : function(e){
14811 this.update(this.activeDate.add("mo", 1));
14815 showPrevYear : function(){
14816 this.update(this.activeDate.add("y", -1));
14820 showNextYear : function(){
14821 this.update(this.activeDate.add("y", 1));
14826 update : function(date)
14828 var vd = this.activeDate;
14829 this.activeDate = date;
14830 // if(vd && this.el){
14831 // var t = date.getTime();
14832 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14833 // Roo.log('using add remove');
14835 // this.fireEvent('monthchange', this, date);
14837 // this.cells.removeClass("fc-state-highlight");
14838 // this.cells.each(function(c){
14839 // if(c.dateValue == t){
14840 // c.addClass("fc-state-highlight");
14841 // setTimeout(function(){
14842 // try{c.dom.firstChild.focus();}catch(e){}
14852 var days = date.getDaysInMonth();
14854 var firstOfMonth = date.getFirstDateOfMonth();
14855 var startingPos = firstOfMonth.getDay()-this.startDay;
14857 if(startingPos < this.startDay){
14861 var pm = date.add(Date.MONTH, -1);
14862 var prevStart = pm.getDaysInMonth()-startingPos;
14864 this.cells = this.el.select('.fc-day',true);
14865 this.textNodes = this.el.query('.fc-day-number');
14866 this.cells.addClassOnOver('fc-state-hover');
14868 var cells = this.cells.elements;
14869 var textEls = this.textNodes;
14871 Roo.each(cells, function(cell){
14872 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14875 days += startingPos;
14877 // convert everything to numbers so it's fast
14878 var day = 86400000;
14879 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14882 //Roo.log(prevStart);
14884 var today = new Date().clearTime().getTime();
14885 var sel = date.clearTime().getTime();
14886 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14887 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14888 var ddMatch = this.disabledDatesRE;
14889 var ddText = this.disabledDatesText;
14890 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14891 var ddaysText = this.disabledDaysText;
14892 var format = this.format;
14894 var setCellClass = function(cal, cell){
14898 //Roo.log('set Cell Class');
14900 var t = d.getTime();
14904 cell.dateValue = t;
14906 cell.className += " fc-today";
14907 cell.className += " fc-state-highlight";
14908 cell.title = cal.todayText;
14911 // disable highlight in other month..
14912 //cell.className += " fc-state-highlight";
14917 cell.className = " fc-state-disabled";
14918 cell.title = cal.minText;
14922 cell.className = " fc-state-disabled";
14923 cell.title = cal.maxText;
14927 if(ddays.indexOf(d.getDay()) != -1){
14928 cell.title = ddaysText;
14929 cell.className = " fc-state-disabled";
14932 if(ddMatch && format){
14933 var fvalue = d.dateFormat(format);
14934 if(ddMatch.test(fvalue)){
14935 cell.title = ddText.replace("%0", fvalue);
14936 cell.className = " fc-state-disabled";
14940 if (!cell.initialClassName) {
14941 cell.initialClassName = cell.dom.className;
14944 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14949 for(; i < startingPos; i++) {
14950 textEls[i].innerHTML = (++prevStart);
14951 d.setDate(d.getDate()+1);
14953 cells[i].className = "fc-past fc-other-month";
14954 setCellClass(this, cells[i]);
14959 for(; i < days; i++){
14960 intDay = i - startingPos + 1;
14961 textEls[i].innerHTML = (intDay);
14962 d.setDate(d.getDate()+1);
14964 cells[i].className = ''; // "x-date-active";
14965 setCellClass(this, cells[i]);
14969 for(; i < 42; i++) {
14970 textEls[i].innerHTML = (++extraDays);
14971 d.setDate(d.getDate()+1);
14973 cells[i].className = "fc-future fc-other-month";
14974 setCellClass(this, cells[i]);
14977 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14979 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14981 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14982 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14984 if(totalRows != 6){
14985 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14986 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14989 this.fireEvent('monthchange', this, date);
14993 if(!this.internalRender){
14994 var main = this.el.dom.firstChild;
14995 var w = main.offsetWidth;
14996 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14997 Roo.fly(main).setWidth(w);
14998 this.internalRender = true;
14999 // opera does not respect the auto grow header center column
15000 // then, after it gets a width opera refuses to recalculate
15001 // without a second pass
15002 if(Roo.isOpera && !this.secondPass){
15003 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15004 this.secondPass = true;
15005 this.update.defer(10, this, [date]);
15012 findCell : function(dt) {
15013 dt = dt.clearTime().getTime();
15015 this.cells.each(function(c){
15016 //Roo.log("check " +c.dateValue + '?=' + dt);
15017 if(c.dateValue == dt){
15027 findCells : function(ev) {
15028 var s = ev.start.clone().clearTime().getTime();
15030 var e= ev.end.clone().clearTime().getTime();
15033 this.cells.each(function(c){
15034 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15036 if(c.dateValue > e){
15039 if(c.dateValue < s){
15048 // findBestRow: function(cells)
15052 // for (var i =0 ; i < cells.length;i++) {
15053 // ret = Math.max(cells[i].rows || 0,ret);
15060 addItem : function(ev)
15062 // look for vertical location slot in
15063 var cells = this.findCells(ev);
15065 // ev.row = this.findBestRow(cells);
15067 // work out the location.
15071 for(var i =0; i < cells.length; i++) {
15073 cells[i].row = cells[0].row;
15076 cells[i].row = cells[i].row + 1;
15086 if (crow.start.getY() == cells[i].getY()) {
15088 crow.end = cells[i];
15105 cells[0].events.push(ev);
15107 this.calevents.push(ev);
15110 clearEvents: function() {
15112 if(!this.calevents){
15116 Roo.each(this.cells.elements, function(c){
15122 Roo.each(this.calevents, function(e) {
15123 Roo.each(e.els, function(el) {
15124 el.un('mouseenter' ,this.onEventEnter, this);
15125 el.un('mouseleave' ,this.onEventLeave, this);
15130 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15136 renderEvents: function()
15140 this.cells.each(function(c) {
15149 if(c.row != c.events.length){
15150 r = 4 - (4 - (c.row - c.events.length));
15153 c.events = ev.slice(0, r);
15154 c.more = ev.slice(r);
15156 if(c.more.length && c.more.length == 1){
15157 c.events.push(c.more.pop());
15160 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15164 this.cells.each(function(c) {
15166 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15169 for (var e = 0; e < c.events.length; e++){
15170 var ev = c.events[e];
15171 var rows = ev.rows;
15173 for(var i = 0; i < rows.length; i++) {
15175 // how many rows should it span..
15178 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15179 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15181 unselectable : "on",
15184 cls: 'fc-event-inner',
15188 // cls: 'fc-event-time',
15189 // html : cells.length > 1 ? '' : ev.time
15193 cls: 'fc-event-title',
15194 html : String.format('{0}', ev.title)
15201 cls: 'ui-resizable-handle ui-resizable-e',
15202 html : '  '
15209 cfg.cls += ' fc-event-start';
15211 if ((i+1) == rows.length) {
15212 cfg.cls += ' fc-event-end';
15215 var ctr = _this.el.select('.fc-event-container',true).first();
15216 var cg = ctr.createChild(cfg);
15218 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15219 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15221 var r = (c.more.length) ? 1 : 0;
15222 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15223 cg.setWidth(ebox.right - sbox.x -2);
15225 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15226 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15227 cg.on('click', _this.onEventClick, _this, ev);
15238 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15239 style : 'position: absolute',
15240 unselectable : "on",
15243 cls: 'fc-event-inner',
15247 cls: 'fc-event-title',
15255 cls: 'ui-resizable-handle ui-resizable-e',
15256 html : '  '
15262 var ctr = _this.el.select('.fc-event-container',true).first();
15263 var cg = ctr.createChild(cfg);
15265 var sbox = c.select('.fc-day-content',true).first().getBox();
15266 var ebox = c.select('.fc-day-content',true).first().getBox();
15268 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15269 cg.setWidth(ebox.right - sbox.x -2);
15271 cg.on('click', _this.onMoreEventClick, _this, c.more);
15281 onEventEnter: function (e, el,event,d) {
15282 this.fireEvent('evententer', this, el, event);
15285 onEventLeave: function (e, el,event,d) {
15286 this.fireEvent('eventleave', this, el, event);
15289 onEventClick: function (e, el,event,d) {
15290 this.fireEvent('eventclick', this, el, event);
15293 onMonthChange: function () {
15297 onMoreEventClick: function(e, el, more)
15301 this.calpopover.placement = 'right';
15302 this.calpopover.setTitle('More');
15304 this.calpopover.setContent('');
15306 var ctr = this.calpopover.el.select('.popover-content', true).first();
15308 Roo.each(more, function(m){
15310 cls : 'fc-event-hori fc-event-draggable',
15313 var cg = ctr.createChild(cfg);
15315 cg.on('click', _this.onEventClick, _this, m);
15318 this.calpopover.show(el);
15323 onLoad: function ()
15325 this.calevents = [];
15328 if(this.store.getCount() > 0){
15329 this.store.data.each(function(d){
15332 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15333 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15334 time : d.data.start_time,
15335 title : d.data.title,
15336 description : d.data.description,
15337 venue : d.data.venue
15342 this.renderEvents();
15344 if(this.calevents.length && this.loadMask){
15345 this.maskEl.hide();
15349 onBeforeLoad: function()
15351 this.clearEvents();
15353 this.maskEl.show();
15367 * @class Roo.bootstrap.Popover
15368 * @extends Roo.bootstrap.Component
15369 * Bootstrap Popover class
15370 * @cfg {String} html contents of the popover (or false to use children..)
15371 * @cfg {String} title of popover (or false to hide)
15372 * @cfg {String} placement how it is placed
15373 * @cfg {String} trigger click || hover (or false to trigger manually)
15374 * @cfg {String} over what (parent or false to trigger manually.)
15375 * @cfg {Number} delay - delay before showing
15378 * Create a new Popover
15379 * @param {Object} config The config object
15382 Roo.bootstrap.Popover = function(config){
15383 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15386 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15388 title: 'Fill in a title',
15391 placement : 'right',
15392 trigger : 'hover', // hover
15398 can_build_overlaid : false,
15400 getChildContainer : function()
15402 return this.el.select('.popover-content',true).first();
15405 getAutoCreate : function(){
15406 Roo.log('make popover?');
15408 cls : 'popover roo-dynamic',
15409 style: 'display:block',
15415 cls : 'popover-inner',
15419 cls: 'popover-title',
15423 cls : 'popover-content',
15434 setTitle: function(str)
15437 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15439 setContent: function(str)
15442 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15444 // as it get's added to the bottom of the page.
15445 onRender : function(ct, position)
15447 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15449 var cfg = Roo.apply({}, this.getAutoCreate());
15453 cfg.cls += ' ' + this.cls;
15456 cfg.style = this.style;
15458 Roo.log("adding to ")
15459 this.el = Roo.get(document.body).createChild(cfg, position);
15465 initEvents : function()
15467 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15468 this.el.enableDisplayMode('block');
15470 if (this.over === false) {
15473 if (this.triggers === false) {
15476 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15477 var triggers = this.trigger ? this.trigger.split(' ') : [];
15478 Roo.each(triggers, function(trigger) {
15480 if (trigger == 'click') {
15481 on_el.on('click', this.toggle, this);
15482 } else if (trigger != 'manual') {
15483 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15484 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15486 on_el.on(eventIn ,this.enter, this);
15487 on_el.on(eventOut, this.leave, this);
15498 toggle : function () {
15499 this.hoverState == 'in' ? this.leave() : this.enter();
15502 enter : function () {
15505 clearTimeout(this.timeout);
15507 this.hoverState = 'in';
15509 if (!this.delay || !this.delay.show) {
15514 this.timeout = setTimeout(function () {
15515 if (_t.hoverState == 'in') {
15518 }, this.delay.show)
15520 leave : function() {
15521 clearTimeout(this.timeout);
15523 this.hoverState = 'out';
15525 if (!this.delay || !this.delay.hide) {
15530 this.timeout = setTimeout(function () {
15531 if (_t.hoverState == 'out') {
15534 }, this.delay.hide)
15537 show : function (on_el)
15540 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15543 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15544 if (this.html !== false) {
15545 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15547 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15548 if (!this.title.length) {
15549 this.el.select('.popover-title',true).hide();
15552 var placement = typeof this.placement == 'function' ?
15553 this.placement.call(this, this.el, on_el) :
15556 var autoToken = /\s?auto?\s?/i;
15557 var autoPlace = autoToken.test(placement);
15559 placement = placement.replace(autoToken, '') || 'top';
15563 //this.el.setXY([0,0]);
15565 this.el.dom.style.display='block';
15566 this.el.addClass(placement);
15568 //this.el.appendTo(on_el);
15570 var p = this.getPosition();
15571 var box = this.el.getBox();
15576 var align = Roo.bootstrap.Popover.alignment[placement];
15577 this.el.alignTo(on_el, align[0],align[1]);
15578 //var arrow = this.el.select('.arrow',true).first();
15579 //arrow.set(align[2],
15581 this.el.addClass('in');
15584 if (this.el.hasClass('fade')) {
15591 this.el.setXY([0,0]);
15592 this.el.removeClass('in');
15594 this.hoverState = null;
15600 Roo.bootstrap.Popover.alignment = {
15601 'left' : ['r-l', [-10,0], 'right'],
15602 'right' : ['l-r', [10,0], 'left'],
15603 'bottom' : ['t-b', [0,10], 'top'],
15604 'top' : [ 'b-t', [0,-10], 'bottom']
15615 * @class Roo.bootstrap.Progress
15616 * @extends Roo.bootstrap.Component
15617 * Bootstrap Progress class
15618 * @cfg {Boolean} striped striped of the progress bar
15619 * @cfg {Boolean} active animated of the progress bar
15623 * Create a new Progress
15624 * @param {Object} config The config object
15627 Roo.bootstrap.Progress = function(config){
15628 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15631 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15636 getAutoCreate : function(){
15644 cfg.cls += ' progress-striped';
15648 cfg.cls += ' active';
15667 * @class Roo.bootstrap.ProgressBar
15668 * @extends Roo.bootstrap.Component
15669 * Bootstrap ProgressBar class
15670 * @cfg {Number} aria_valuenow aria-value now
15671 * @cfg {Number} aria_valuemin aria-value min
15672 * @cfg {Number} aria_valuemax aria-value max
15673 * @cfg {String} label label for the progress bar
15674 * @cfg {String} panel (success | info | warning | danger )
15675 * @cfg {String} role role of the progress bar
15676 * @cfg {String} sr_only text
15680 * Create a new ProgressBar
15681 * @param {Object} config The config object
15684 Roo.bootstrap.ProgressBar = function(config){
15685 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15688 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15692 aria_valuemax : 100,
15698 getAutoCreate : function()
15703 cls: 'progress-bar',
15704 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15716 cfg.role = this.role;
15719 if(this.aria_valuenow){
15720 cfg['aria-valuenow'] = this.aria_valuenow;
15723 if(this.aria_valuemin){
15724 cfg['aria-valuemin'] = this.aria_valuemin;
15727 if(this.aria_valuemax){
15728 cfg['aria-valuemax'] = this.aria_valuemax;
15731 if(this.label && !this.sr_only){
15732 cfg.html = this.label;
15736 cfg.cls += ' progress-bar-' + this.panel;
15742 update : function(aria_valuenow)
15744 this.aria_valuenow = aria_valuenow;
15746 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15761 * @class Roo.bootstrap.TabGroup
15762 * @extends Roo.bootstrap.Column
15763 * Bootstrap Column class
15764 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15765 * @cfg {Boolean} carousel true to make the group behave like a carousel
15766 * @cfg {Number} bullets show the panel pointer.. default 0
15767 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15768 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15769 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15772 * Create a new TabGroup
15773 * @param {Object} config The config object
15776 Roo.bootstrap.TabGroup = function(config){
15777 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15779 this.navId = Roo.id();
15782 Roo.bootstrap.TabGroup.register(this);
15786 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15789 transition : false,
15794 slideOnTouch : false,
15796 getAutoCreate : function()
15798 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15800 cfg.cls += ' tab-content';
15802 Roo.log('get auto create...............');
15804 if (this.carousel) {
15805 cfg.cls += ' carousel slide';
15808 cls : 'carousel-inner'
15811 if(this.bullets > 0 && !Roo.isTouch){
15814 cls : 'carousel-bullets',
15818 if(this.bullets_cls){
15819 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15822 for (var i = 0; i < this.bullets; i++){
15824 cls : 'bullet bullet-' + i
15832 cfg.cn[0].cn = bullets;
15839 initEvents: function()
15841 Roo.log('-------- init events on tab group ---------');
15843 if(this.bullets > 0 && !Roo.isTouch){
15849 if(Roo.isTouch && this.slideOnTouch){
15850 this.el.on("touchstart", this.onTouchStart, this);
15853 if(this.autoslide){
15856 this.slideFn = window.setInterval(function() {
15857 _this.showPanelNext();
15863 onTouchStart : function(e, el, o)
15865 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15869 this.showPanelNext();
15872 getChildContainer : function()
15874 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15878 * register a Navigation item
15879 * @param {Roo.bootstrap.NavItem} the navitem to add
15881 register : function(item)
15883 this.tabs.push( item);
15884 item.navId = this.navId; // not really needed..
15888 getActivePanel : function()
15891 Roo.each(this.tabs, function(t) {
15901 getPanelByName : function(n)
15904 Roo.each(this.tabs, function(t) {
15905 if (t.tabId == n) {
15913 indexOfPanel : function(p)
15916 Roo.each(this.tabs, function(t,i) {
15917 if (t.tabId == p.tabId) {
15926 * show a specific panel
15927 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15928 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15930 showPanel : function (pan)
15932 if(this.transition){
15933 Roo.log("waiting for the transitionend");
15937 if (typeof(pan) == 'number') {
15938 pan = this.tabs[pan];
15940 if (typeof(pan) == 'string') {
15941 pan = this.getPanelByName(pan);
15943 if (pan.tabId == this.getActivePanel().tabId) {
15946 var cur = this.getActivePanel();
15948 if (false === cur.fireEvent('beforedeactivate')) {
15952 if(this.bullets > 0 && !Roo.isTouch){
15953 this.setActiveBullet(this.indexOfPanel(pan));
15956 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15958 this.transition = true;
15959 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15960 var lr = dir == 'next' ? 'left' : 'right';
15961 pan.el.addClass(dir); // or prev
15962 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15963 cur.el.addClass(lr); // or right
15964 pan.el.addClass(lr);
15967 cur.el.on('transitionend', function() {
15968 Roo.log("trans end?");
15970 pan.el.removeClass([lr,dir]);
15971 pan.setActive(true);
15973 cur.el.removeClass([lr]);
15974 cur.setActive(false);
15976 _this.transition = false;
15978 }, this, { single: true } );
15983 cur.setActive(false);
15984 pan.setActive(true);
15989 showPanelNext : function()
15991 var i = this.indexOfPanel(this.getActivePanel());
15993 if (i >= this.tabs.length - 1 && !this.autoslide) {
15997 if (i >= this.tabs.length - 1 && this.autoslide) {
16001 this.showPanel(this.tabs[i+1]);
16004 showPanelPrev : function()
16006 var i = this.indexOfPanel(this.getActivePanel());
16008 if (i < 1 && !this.autoslide) {
16012 if (i < 1 && this.autoslide) {
16013 i = this.tabs.length;
16016 this.showPanel(this.tabs[i-1]);
16019 initBullet : function()
16027 for (var i = 0; i < this.bullets; i++){
16028 var bullet = this.el.select('.bullet-' + i, true).first();
16034 bullet.on('click', (function(e, el, o, ii, t){
16036 e.preventDefault();
16038 _this.showPanel(ii);
16040 if(_this.autoslide && _this.slideFn){
16041 clearInterval(_this.slideFn);
16042 _this.slideFn = window.setInterval(function() {
16043 _this.showPanelNext();
16047 }).createDelegate(this, [i, bullet], true));
16051 setActiveBullet : function(i)
16057 Roo.each(this.el.select('.bullet', true).elements, function(el){
16058 el.removeClass('selected');
16061 var bullet = this.el.select('.bullet-' + i, true).first();
16067 bullet.addClass('selected');
16078 Roo.apply(Roo.bootstrap.TabGroup, {
16082 * register a Navigation Group
16083 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16085 register : function(navgrp)
16087 this.groups[navgrp.navId] = navgrp;
16091 * fetch a Navigation Group based on the navigation ID
16092 * if one does not exist , it will get created.
16093 * @param {string} the navgroup to add
16094 * @returns {Roo.bootstrap.NavGroup} the navgroup
16096 get: function(navId) {
16097 if (typeof(this.groups[navId]) == 'undefined') {
16098 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16100 return this.groups[navId] ;
16115 * @class Roo.bootstrap.TabPanel
16116 * @extends Roo.bootstrap.Component
16117 * Bootstrap TabPanel class
16118 * @cfg {Boolean} active panel active
16119 * @cfg {String} html panel content
16120 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16121 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16125 * Create a new TabPanel
16126 * @param {Object} config The config object
16129 Roo.bootstrap.TabPanel = function(config){
16130 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16134 * Fires when the active status changes
16135 * @param {Roo.bootstrap.TabPanel} this
16136 * @param {Boolean} state the new state
16141 * @event beforedeactivate
16142 * Fires before a tab is de-activated - can be used to do validation on a form.
16143 * @param {Roo.bootstrap.TabPanel} this
16144 * @return {Boolean} false if there is an error
16147 'beforedeactivate': true
16150 this.tabId = this.tabId || Roo.id();
16154 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16161 getAutoCreate : function(){
16164 // item is needed for carousel - not sure if it has any effect otherwise
16165 cls: 'tab-pane item',
16166 html: this.html || ''
16170 cfg.cls += ' active';
16174 cfg.tabId = this.tabId;
16181 initEvents: function()
16183 Roo.log('-------- init events on tab panel ---------');
16185 var p = this.parent();
16186 this.navId = this.navId || p.navId;
16188 if (typeof(this.navId) != 'undefined') {
16189 // not really needed.. but just in case.. parent should be a NavGroup.
16190 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16191 Roo.log(['register', tg, this]);
16194 var i = tg.tabs.length - 1;
16196 if(this.active && tg.bullets > 0 && i < tg.bullets){
16197 tg.setActiveBullet(i);
16204 onRender : function(ct, position)
16206 // Roo.log("Call onRender: " + this.xtype);
16208 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16216 setActive: function(state)
16218 Roo.log("panel - set active " + this.tabId + "=" + state);
16220 this.active = state;
16222 this.el.removeClass('active');
16224 } else if (!this.el.hasClass('active')) {
16225 this.el.addClass('active');
16228 this.fireEvent('changed', this, state);
16245 * @class Roo.bootstrap.DateField
16246 * @extends Roo.bootstrap.Input
16247 * Bootstrap DateField class
16248 * @cfg {Number} weekStart default 0
16249 * @cfg {String} viewMode default empty, (months|years)
16250 * @cfg {String} minViewMode default empty, (months|years)
16251 * @cfg {Number} startDate default -Infinity
16252 * @cfg {Number} endDate default Infinity
16253 * @cfg {Boolean} todayHighlight default false
16254 * @cfg {Boolean} todayBtn default false
16255 * @cfg {Boolean} calendarWeeks default false
16256 * @cfg {Object} daysOfWeekDisabled default empty
16257 * @cfg {Boolean} singleMode default false (true | false)
16259 * @cfg {Boolean} keyboardNavigation default true
16260 * @cfg {String} language default en
16263 * Create a new DateField
16264 * @param {Object} config The config object
16267 Roo.bootstrap.DateField = function(config){
16268 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16272 * Fires when this field show.
16273 * @param {Roo.bootstrap.DateField} this
16274 * @param {Mixed} date The date value
16279 * Fires when this field hide.
16280 * @param {Roo.bootstrap.DateField} this
16281 * @param {Mixed} date The date value
16286 * Fires when select a date.
16287 * @param {Roo.bootstrap.DateField} this
16288 * @param {Mixed} date The date value
16294 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16297 * @cfg {String} format
16298 * The default date format string which can be overriden for localization support. The format must be
16299 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16303 * @cfg {String} altFormats
16304 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16305 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16307 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16315 todayHighlight : false,
16321 keyboardNavigation: true,
16323 calendarWeeks: false,
16325 startDate: -Infinity,
16329 daysOfWeekDisabled: [],
16333 singleMode : false,
16335 UTCDate: function()
16337 return new Date(Date.UTC.apply(Date, arguments));
16340 UTCToday: function()
16342 var today = new Date();
16343 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16346 getDate: function() {
16347 var d = this.getUTCDate();
16348 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16351 getUTCDate: function() {
16355 setDate: function(d) {
16356 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16359 setUTCDate: function(d) {
16361 this.setValue(this.formatDate(this.date));
16364 onRender: function(ct, position)
16367 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16369 this.language = this.language || 'en';
16370 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16371 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16373 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16374 this.format = this.format || 'm/d/y';
16375 this.isInline = false;
16376 this.isInput = true;
16377 this.component = this.el.select('.add-on', true).first() || false;
16378 this.component = (this.component && this.component.length === 0) ? false : this.component;
16379 this.hasInput = this.component && this.inputEL().length;
16381 if (typeof(this.minViewMode === 'string')) {
16382 switch (this.minViewMode) {
16384 this.minViewMode = 1;
16387 this.minViewMode = 2;
16390 this.minViewMode = 0;
16395 if (typeof(this.viewMode === 'string')) {
16396 switch (this.viewMode) {
16409 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16411 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16413 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16415 this.picker().on('mousedown', this.onMousedown, this);
16416 this.picker().on('click', this.onClick, this);
16418 this.picker().addClass('datepicker-dropdown');
16420 this.startViewMode = this.viewMode;
16422 if(this.singleMode){
16423 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16424 v.setVisibilityMode(Roo.Element.DISPLAY)
16428 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16429 v.setStyle('width', '189px');
16433 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16434 if(!this.calendarWeeks){
16439 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16440 v.attr('colspan', function(i, val){
16441 return parseInt(val) + 1;
16446 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16448 this.setStartDate(this.startDate);
16449 this.setEndDate(this.endDate);
16451 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16458 if(this.isInline) {
16463 picker : function()
16465 return this.pickerEl;
16466 // return this.el.select('.datepicker', true).first();
16469 fillDow: function()
16471 var dowCnt = this.weekStart;
16480 if(this.calendarWeeks){
16488 while (dowCnt < this.weekStart + 7) {
16492 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16496 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16499 fillMonths: function()
16502 var months = this.picker().select('>.datepicker-months td', true).first();
16504 months.dom.innerHTML = '';
16510 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16513 months.createChild(month);
16520 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;
16522 if (this.date < this.startDate) {
16523 this.viewDate = new Date(this.startDate);
16524 } else if (this.date > this.endDate) {
16525 this.viewDate = new Date(this.endDate);
16527 this.viewDate = new Date(this.date);
16535 var d = new Date(this.viewDate),
16536 year = d.getUTCFullYear(),
16537 month = d.getUTCMonth(),
16538 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16539 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16540 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16541 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16542 currentDate = this.date && this.date.valueOf(),
16543 today = this.UTCToday();
16545 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16547 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16549 // this.picker.select('>tfoot th.today').
16550 // .text(dates[this.language].today)
16551 // .toggle(this.todayBtn !== false);
16553 this.updateNavArrows();
16556 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16558 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16560 prevMonth.setUTCDate(day);
16562 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16564 var nextMonth = new Date(prevMonth);
16566 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16568 nextMonth = nextMonth.valueOf();
16570 var fillMonths = false;
16572 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16574 while(prevMonth.valueOf() < nextMonth) {
16577 if (prevMonth.getUTCDay() === this.weekStart) {
16579 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16587 if(this.calendarWeeks){
16588 // ISO 8601: First week contains first thursday.
16589 // ISO also states week starts on Monday, but we can be more abstract here.
16591 // Start of current week: based on weekstart/current date
16592 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16593 // Thursday of this week
16594 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16595 // First Thursday of year, year from thursday
16596 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16597 // Calendar week: ms between thursdays, div ms per day, div 7 days
16598 calWeek = (th - yth) / 864e5 / 7 + 1;
16600 fillMonths.cn.push({
16608 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16610 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16613 if (this.todayHighlight &&
16614 prevMonth.getUTCFullYear() == today.getFullYear() &&
16615 prevMonth.getUTCMonth() == today.getMonth() &&
16616 prevMonth.getUTCDate() == today.getDate()) {
16617 clsName += ' today';
16620 if (currentDate && prevMonth.valueOf() === currentDate) {
16621 clsName += ' active';
16624 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16625 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16626 clsName += ' disabled';
16629 fillMonths.cn.push({
16631 cls: 'day ' + clsName,
16632 html: prevMonth.getDate()
16635 prevMonth.setDate(prevMonth.getDate()+1);
16638 var currentYear = this.date && this.date.getUTCFullYear();
16639 var currentMonth = this.date && this.date.getUTCMonth();
16641 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16643 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16644 v.removeClass('active');
16646 if(currentYear === year && k === currentMonth){
16647 v.addClass('active');
16650 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16651 v.addClass('disabled');
16657 year = parseInt(year/10, 10) * 10;
16659 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16661 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16664 for (var i = -1; i < 11; i++) {
16665 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16667 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16675 showMode: function(dir)
16678 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16681 Roo.each(this.picker().select('>div',true).elements, function(v){
16682 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16685 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16690 if(this.isInline) return;
16692 this.picker().removeClass(['bottom', 'top']);
16694 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16696 * place to the top of element!
16700 this.picker().addClass('top');
16701 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16706 this.picker().addClass('bottom');
16708 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16711 parseDate : function(value)
16713 if(!value || value instanceof Date){
16716 var v = Date.parseDate(value, this.format);
16717 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16718 v = Date.parseDate(value, 'Y-m-d');
16720 if(!v && this.altFormats){
16721 if(!this.altFormatsArray){
16722 this.altFormatsArray = this.altFormats.split("|");
16724 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16725 v = Date.parseDate(value, this.altFormatsArray[i]);
16731 formatDate : function(date, fmt)
16733 return (!date || !(date instanceof Date)) ?
16734 date : date.dateFormat(fmt || this.format);
16737 onFocus : function()
16739 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16743 onBlur : function()
16745 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16747 var d = this.inputEl().getValue();
16756 this.picker().show();
16760 this.fireEvent('show', this, this.date);
16765 if(this.isInline) return;
16766 this.picker().hide();
16767 this.viewMode = this.startViewMode;
16770 this.fireEvent('hide', this, this.date);
16774 onMousedown: function(e)
16776 e.stopPropagation();
16777 e.preventDefault();
16782 Roo.bootstrap.DateField.superclass.keyup.call(this);
16786 setValue: function(v)
16789 // v can be a string or a date..
16792 var d = new Date(this.parseDate(v) ).clearTime();
16794 if(isNaN(d.getTime())){
16795 this.date = this.viewDate = '';
16796 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16800 v = this.formatDate(d);
16802 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16804 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16808 this.fireEvent('select', this, this.date);
16812 getValue: function()
16814 return this.formatDate(this.date);
16817 fireKey: function(e)
16819 if (!this.picker().isVisible()){
16820 if (e.keyCode == 27) // allow escape to hide and re-show picker
16825 var dateChanged = false,
16827 newDate, newViewDate;
16832 e.preventDefault();
16836 if (!this.keyboardNavigation) break;
16837 dir = e.keyCode == 37 ? -1 : 1;
16840 newDate = this.moveYear(this.date, dir);
16841 newViewDate = this.moveYear(this.viewDate, dir);
16842 } else if (e.shiftKey){
16843 newDate = this.moveMonth(this.date, dir);
16844 newViewDate = this.moveMonth(this.viewDate, dir);
16846 newDate = new Date(this.date);
16847 newDate.setUTCDate(this.date.getUTCDate() + dir);
16848 newViewDate = new Date(this.viewDate);
16849 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16851 if (this.dateWithinRange(newDate)){
16852 this.date = newDate;
16853 this.viewDate = newViewDate;
16854 this.setValue(this.formatDate(this.date));
16856 e.preventDefault();
16857 dateChanged = true;
16862 if (!this.keyboardNavigation) break;
16863 dir = e.keyCode == 38 ? -1 : 1;
16865 newDate = this.moveYear(this.date, dir);
16866 newViewDate = this.moveYear(this.viewDate, dir);
16867 } else if (e.shiftKey){
16868 newDate = this.moveMonth(this.date, dir);
16869 newViewDate = this.moveMonth(this.viewDate, dir);
16871 newDate = new Date(this.date);
16872 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16873 newViewDate = new Date(this.viewDate);
16874 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16876 if (this.dateWithinRange(newDate)){
16877 this.date = newDate;
16878 this.viewDate = newViewDate;
16879 this.setValue(this.formatDate(this.date));
16881 e.preventDefault();
16882 dateChanged = true;
16886 this.setValue(this.formatDate(this.date));
16888 e.preventDefault();
16891 this.setValue(this.formatDate(this.date));
16905 onClick: function(e)
16907 e.stopPropagation();
16908 e.preventDefault();
16910 var target = e.getTarget();
16912 if(target.nodeName.toLowerCase() === 'i'){
16913 target = Roo.get(target).dom.parentNode;
16916 var nodeName = target.nodeName;
16917 var className = target.className;
16918 var html = target.innerHTML;
16919 //Roo.log(nodeName);
16921 switch(nodeName.toLowerCase()) {
16923 switch(className) {
16929 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16930 switch(this.viewMode){
16932 this.viewDate = this.moveMonth(this.viewDate, dir);
16936 this.viewDate = this.moveYear(this.viewDate, dir);
16942 var date = new Date();
16943 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16945 this.setValue(this.formatDate(this.date));
16952 if (className.indexOf('disabled') < 0) {
16953 this.viewDate.setUTCDate(1);
16954 if (className.indexOf('month') > -1) {
16955 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16957 var year = parseInt(html, 10) || 0;
16958 this.viewDate.setUTCFullYear(year);
16962 if(this.singleMode){
16963 this.setValue(this.formatDate(this.viewDate));
16974 //Roo.log(className);
16975 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16976 var day = parseInt(html, 10) || 1;
16977 var year = this.viewDate.getUTCFullYear(),
16978 month = this.viewDate.getUTCMonth();
16980 if (className.indexOf('old') > -1) {
16987 } else if (className.indexOf('new') > -1) {
16995 //Roo.log([year,month,day]);
16996 this.date = this.UTCDate(year, month, day,0,0,0,0);
16997 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16999 //Roo.log(this.formatDate(this.date));
17000 this.setValue(this.formatDate(this.date));
17007 setStartDate: function(startDate)
17009 this.startDate = startDate || -Infinity;
17010 if (this.startDate !== -Infinity) {
17011 this.startDate = this.parseDate(this.startDate);
17014 this.updateNavArrows();
17017 setEndDate: function(endDate)
17019 this.endDate = endDate || Infinity;
17020 if (this.endDate !== Infinity) {
17021 this.endDate = this.parseDate(this.endDate);
17024 this.updateNavArrows();
17027 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17029 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17030 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17031 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17033 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17034 return parseInt(d, 10);
17037 this.updateNavArrows();
17040 updateNavArrows: function()
17042 if(this.singleMode){
17046 var d = new Date(this.viewDate),
17047 year = d.getUTCFullYear(),
17048 month = d.getUTCMonth();
17050 Roo.each(this.picker().select('.prev', true).elements, function(v){
17052 switch (this.viewMode) {
17055 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17061 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17068 Roo.each(this.picker().select('.next', true).elements, function(v){
17070 switch (this.viewMode) {
17073 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17079 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17087 moveMonth: function(date, dir)
17089 if (!dir) return date;
17090 var new_date = new Date(date.valueOf()),
17091 day = new_date.getUTCDate(),
17092 month = new_date.getUTCMonth(),
17093 mag = Math.abs(dir),
17095 dir = dir > 0 ? 1 : -1;
17098 // If going back one month, make sure month is not current month
17099 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17101 return new_date.getUTCMonth() == month;
17103 // If going forward one month, make sure month is as expected
17104 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17106 return new_date.getUTCMonth() != new_month;
17108 new_month = month + dir;
17109 new_date.setUTCMonth(new_month);
17110 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17111 if (new_month < 0 || new_month > 11)
17112 new_month = (new_month + 12) % 12;
17114 // For magnitudes >1, move one month at a time...
17115 for (var i=0; i<mag; i++)
17116 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17117 new_date = this.moveMonth(new_date, dir);
17118 // ...then reset the day, keeping it in the new month
17119 new_month = new_date.getUTCMonth();
17120 new_date.setUTCDate(day);
17122 return new_month != new_date.getUTCMonth();
17125 // Common date-resetting loop -- if date is beyond end of month, make it
17128 new_date.setUTCDate(--day);
17129 new_date.setUTCMonth(new_month);
17134 moveYear: function(date, dir)
17136 return this.moveMonth(date, dir*12);
17139 dateWithinRange: function(date)
17141 return date >= this.startDate && date <= this.endDate;
17147 this.picker().remove();
17152 Roo.apply(Roo.bootstrap.DateField, {
17163 html: '<i class="fa fa-arrow-left"/>'
17173 html: '<i class="fa fa-arrow-right"/>'
17215 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17216 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17217 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17218 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17219 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17232 navFnc: 'FullYear',
17237 navFnc: 'FullYear',
17242 Roo.apply(Roo.bootstrap.DateField, {
17246 cls: 'datepicker dropdown-menu roo-dynamic',
17250 cls: 'datepicker-days',
17254 cls: 'table-condensed',
17256 Roo.bootstrap.DateField.head,
17260 Roo.bootstrap.DateField.footer
17267 cls: 'datepicker-months',
17271 cls: 'table-condensed',
17273 Roo.bootstrap.DateField.head,
17274 Roo.bootstrap.DateField.content,
17275 Roo.bootstrap.DateField.footer
17282 cls: 'datepicker-years',
17286 cls: 'table-condensed',
17288 Roo.bootstrap.DateField.head,
17289 Roo.bootstrap.DateField.content,
17290 Roo.bootstrap.DateField.footer
17309 * @class Roo.bootstrap.TimeField
17310 * @extends Roo.bootstrap.Input
17311 * Bootstrap DateField class
17315 * Create a new TimeField
17316 * @param {Object} config The config object
17319 Roo.bootstrap.TimeField = function(config){
17320 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17324 * Fires when this field show.
17325 * @param {Roo.bootstrap.DateField} thisthis
17326 * @param {Mixed} date The date value
17331 * Fires when this field hide.
17332 * @param {Roo.bootstrap.DateField} this
17333 * @param {Mixed} date The date value
17338 * Fires when select a date.
17339 * @param {Roo.bootstrap.DateField} this
17340 * @param {Mixed} date The date value
17346 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17349 * @cfg {String} format
17350 * The default time format string which can be overriden for localization support. The format must be
17351 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17355 onRender: function(ct, position)
17358 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17360 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17362 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17364 this.pop = this.picker().select('>.datepicker-time',true).first();
17365 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17367 this.picker().on('mousedown', this.onMousedown, this);
17368 this.picker().on('click', this.onClick, this);
17370 this.picker().addClass('datepicker-dropdown');
17375 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17376 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17377 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17378 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17379 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17380 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17384 fireKey: function(e){
17385 if (!this.picker().isVisible()){
17386 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17392 e.preventDefault();
17400 this.onTogglePeriod();
17403 this.onIncrementMinutes();
17406 this.onDecrementMinutes();
17415 onClick: function(e) {
17416 e.stopPropagation();
17417 e.preventDefault();
17420 picker : function()
17422 return this.el.select('.datepicker', true).first();
17425 fillTime: function()
17427 var time = this.pop.select('tbody', true).first();
17429 time.dom.innerHTML = '';
17444 cls: 'hours-up glyphicon glyphicon-chevron-up'
17464 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17485 cls: 'timepicker-hour',
17500 cls: 'timepicker-minute',
17515 cls: 'btn btn-primary period',
17537 cls: 'hours-down glyphicon glyphicon-chevron-down'
17557 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17575 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17582 var hours = this.time.getHours();
17583 var minutes = this.time.getMinutes();
17596 hours = hours - 12;
17600 hours = '0' + hours;
17604 minutes = '0' + minutes;
17607 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17608 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17609 this.pop.select('button', true).first().dom.innerHTML = period;
17615 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17617 var cls = ['bottom'];
17619 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17626 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17631 this.picker().addClass(cls.join('-'));
17635 Roo.each(cls, function(c){
17637 _this.picker().setTop(_this.inputEl().getHeight());
17641 _this.picker().setTop(0 - _this.picker().getHeight());
17646 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17650 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17657 onFocus : function()
17659 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17663 onBlur : function()
17665 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17671 this.picker().show();
17676 this.fireEvent('show', this, this.date);
17681 this.picker().hide();
17684 this.fireEvent('hide', this, this.date);
17687 setTime : function()
17690 this.setValue(this.time.format(this.format));
17692 this.fireEvent('select', this, this.date);
17697 onMousedown: function(e){
17698 e.stopPropagation();
17699 e.preventDefault();
17702 onIncrementHours: function()
17704 Roo.log('onIncrementHours');
17705 this.time = this.time.add(Date.HOUR, 1);
17710 onDecrementHours: function()
17712 Roo.log('onDecrementHours');
17713 this.time = this.time.add(Date.HOUR, -1);
17717 onIncrementMinutes: function()
17719 Roo.log('onIncrementMinutes');
17720 this.time = this.time.add(Date.MINUTE, 1);
17724 onDecrementMinutes: function()
17726 Roo.log('onDecrementMinutes');
17727 this.time = this.time.add(Date.MINUTE, -1);
17731 onTogglePeriod: function()
17733 Roo.log('onTogglePeriod');
17734 this.time = this.time.add(Date.HOUR, 12);
17741 Roo.apply(Roo.bootstrap.TimeField, {
17771 cls: 'btn btn-info ok',
17783 Roo.apply(Roo.bootstrap.TimeField, {
17787 cls: 'datepicker dropdown-menu',
17791 cls: 'datepicker-time',
17795 cls: 'table-condensed',
17797 Roo.bootstrap.TimeField.content,
17798 Roo.bootstrap.TimeField.footer
17817 * @class Roo.bootstrap.MonthField
17818 * @extends Roo.bootstrap.Input
17819 * Bootstrap MonthField class
17821 * @cfg {String} language default en
17824 * Create a new MonthField
17825 * @param {Object} config The config object
17828 Roo.bootstrap.MonthField = function(config){
17829 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17834 * Fires when this field show.
17835 * @param {Roo.bootstrap.MonthField} this
17836 * @param {Mixed} date The date value
17841 * Fires when this field hide.
17842 * @param {Roo.bootstrap.MonthField} this
17843 * @param {Mixed} date The date value
17848 * Fires when select a date.
17849 * @param {Roo.bootstrap.MonthField} this
17850 * @param {String} oldvalue The old value
17851 * @param {String} newvalue The new value
17857 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17859 onRender: function(ct, position)
17862 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17864 this.language = this.language || 'en';
17865 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17866 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17868 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17869 this.isInline = false;
17870 this.isInput = true;
17871 this.component = this.el.select('.add-on', true).first() || false;
17872 this.component = (this.component && this.component.length === 0) ? false : this.component;
17873 this.hasInput = this.component && this.inputEL().length;
17875 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17877 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17879 this.picker().on('mousedown', this.onMousedown, this);
17880 this.picker().on('click', this.onClick, this);
17882 this.picker().addClass('datepicker-dropdown');
17884 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17885 v.setStyle('width', '189px');
17892 if(this.isInline) {
17898 setValue: function(v, suppressEvent)
17900 var o = this.getValue();
17902 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17906 if(suppressEvent !== true){
17907 this.fireEvent('select', this, o, v);
17912 getValue: function()
17917 onClick: function(e)
17919 e.stopPropagation();
17920 e.preventDefault();
17922 var target = e.getTarget();
17924 if(target.nodeName.toLowerCase() === 'i'){
17925 target = Roo.get(target).dom.parentNode;
17928 var nodeName = target.nodeName;
17929 var className = target.className;
17930 var html = target.innerHTML;
17932 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17936 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17938 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17944 picker : function()
17946 return this.pickerEl;
17949 fillMonths: function()
17952 var months = this.picker().select('>.datepicker-months td', true).first();
17954 months.dom.innerHTML = '';
17960 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17963 months.createChild(month);
17972 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17973 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17976 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17977 e.removeClass('active');
17979 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17980 e.addClass('active');
17987 if(this.isInline) return;
17989 this.picker().removeClass(['bottom', 'top']);
17991 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17993 * place to the top of element!
17997 this.picker().addClass('top');
17998 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18003 this.picker().addClass('bottom');
18005 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18008 onFocus : function()
18010 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18014 onBlur : function()
18016 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18018 var d = this.inputEl().getValue();
18027 this.picker().show();
18028 this.picker().select('>.datepicker-months', true).first().show();
18032 this.fireEvent('show', this, this.date);
18037 if(this.isInline) return;
18038 this.picker().hide();
18039 this.fireEvent('hide', this, this.date);
18043 onMousedown: function(e)
18045 e.stopPropagation();
18046 e.preventDefault();
18051 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18055 fireKey: function(e)
18057 if (!this.picker().isVisible()){
18058 if (e.keyCode == 27) // allow escape to hide and re-show picker
18068 e.preventDefault();
18072 dir = e.keyCode == 37 ? -1 : 1;
18074 this.vIndex = this.vIndex + dir;
18076 if(this.vIndex < 0){
18080 if(this.vIndex > 11){
18084 if(isNaN(this.vIndex)){
18088 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18094 dir = e.keyCode == 38 ? -1 : 1;
18096 this.vIndex = this.vIndex + dir * 4;
18098 if(this.vIndex < 0){
18102 if(this.vIndex > 11){
18106 if(isNaN(this.vIndex)){
18110 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18115 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18116 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18120 e.preventDefault();
18123 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18124 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18140 this.picker().remove();
18145 Roo.apply(Roo.bootstrap.MonthField, {
18164 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18165 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18170 Roo.apply(Roo.bootstrap.MonthField, {
18174 cls: 'datepicker dropdown-menu roo-dynamic',
18178 cls: 'datepicker-months',
18182 cls: 'table-condensed',
18184 Roo.bootstrap.DateField.content
18204 * @class Roo.bootstrap.CheckBox
18205 * @extends Roo.bootstrap.Input
18206 * Bootstrap CheckBox class
18208 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18209 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18210 * @cfg {String} boxLabel The text that appears beside the checkbox
18211 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18212 * @cfg {Boolean} checked initnal the element
18213 * @cfg {Boolean} inline inline the element (default false)
18214 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18217 * Create a new CheckBox
18218 * @param {Object} config The config object
18221 Roo.bootstrap.CheckBox = function(config){
18222 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18227 * Fires when the element is checked or unchecked.
18228 * @param {Roo.bootstrap.CheckBox} this This input
18229 * @param {Boolean} checked The new checked value
18236 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18238 inputType: 'checkbox',
18246 getAutoCreate : function()
18248 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18254 cfg.cls = 'form-group ' + this.inputType; //input-group
18257 cfg.cls += ' ' + this.inputType + '-inline';
18263 type : this.inputType,
18264 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18265 cls : 'roo-' + this.inputType, //'form-box',
18266 placeholder : this.placeholder || ''
18270 if (this.weight) { // Validity check?
18271 cfg.cls += " " + this.inputType + "-" + this.weight;
18274 if (this.disabled) {
18275 input.disabled=true;
18279 input.checked = this.checked;
18283 input.name = this.name;
18287 input.cls += ' input-' + this.size;
18292 ['xs','sm','md','lg'].map(function(size){
18293 if (settings[size]) {
18294 cfg.cls += ' col-' + size + '-' + settings[size];
18298 var inputblock = input;
18300 if (this.before || this.after) {
18303 cls : 'input-group',
18308 inputblock.cn.push({
18310 cls : 'input-group-addon',
18315 inputblock.cn.push(input);
18318 inputblock.cn.push({
18320 cls : 'input-group-addon',
18327 if (align ==='left' && this.fieldLabel.length) {
18328 Roo.log("left and has label");
18334 cls : 'control-label col-md-' + this.labelWidth,
18335 html : this.fieldLabel
18339 cls : "col-md-" + (12 - this.labelWidth),
18346 } else if ( this.fieldLabel.length) {
18351 tag: this.boxLabel ? 'span' : 'label',
18353 cls: 'control-label box-input-label',
18354 //cls : 'input-group-addon',
18355 html : this.fieldLabel
18365 Roo.log(" no label && no align");
18366 cfg.cn = [ inputblock ] ;
18371 var boxLabelCfg = {
18373 //'for': id, // box label is handled by onclick - so no for...
18375 html: this.boxLabel
18379 boxLabelCfg.tooltip = this.tooltip;
18382 cfg.cn.push(boxLabelCfg);
18392 * return the real input element.
18394 inputEl: function ()
18396 return this.el.select('input.roo-' + this.inputType,true).first();
18399 labelEl: function()
18401 return this.el.select('label.control-label',true).first();
18403 /* depricated... */
18407 return this.labelEl();
18410 initEvents : function()
18412 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18414 this.inputEl().on('click', this.onClick, this);
18416 if (this.boxLabel) {
18417 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18420 this.startValue = this.getValue();
18423 Roo.bootstrap.CheckBox.register(this);
18427 onClick : function()
18429 this.setChecked(!this.checked);
18432 setChecked : function(state,suppressEvent)
18434 this.startValue = this.getValue();
18436 if(this.inputType == 'radio'){
18438 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18439 e.dom.checked = false;
18442 this.inputEl().dom.checked = true;
18444 this.inputEl().dom.value = this.inputValue;
18446 if(suppressEvent !== true){
18447 this.fireEvent('check', this, true);
18455 this.checked = state;
18457 this.inputEl().dom.checked = state;
18459 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18461 if(suppressEvent !== true){
18462 this.fireEvent('check', this, state);
18468 getValue : function()
18470 if(this.inputType == 'radio'){
18471 return this.getGroupValue();
18474 return this.inputEl().getValue();
18478 getGroupValue : function()
18480 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18484 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18487 setValue : function(v,suppressEvent)
18489 if(this.inputType == 'radio'){
18490 this.setGroupValue(v, suppressEvent);
18494 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18499 setGroupValue : function(v, suppressEvent)
18501 this.startValue = this.getValue();
18503 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18504 e.dom.checked = false;
18506 if(e.dom.value == v){
18507 e.dom.checked = true;
18511 if(suppressEvent !== true){
18512 this.fireEvent('check', this, true);
18520 validate : function()
18524 (this.inputType == 'radio' && this.validateRadio()) ||
18525 (this.inputType == 'checkbox' && this.validateCheckbox())
18531 this.markInvalid();
18535 validateRadio : function()
18539 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18540 if(!e.dom.checked){
18552 validateCheckbox : function()
18555 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18558 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18566 for(var i in group){
18571 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18578 * Mark this field as valid
18580 markValid : function()
18582 if(this.allowBlank){
18588 this.fireEvent('valid', this);
18590 if(this.inputType == 'radio'){
18591 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18592 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18593 e.findParent('.form-group', false, true).addClass(_this.validClass);
18600 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18601 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18605 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18611 for(var i in group){
18612 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18613 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18618 * Mark this field as invalid
18619 * @param {String} msg The validation message
18621 markInvalid : function(msg)
18623 if(this.allowBlank){
18629 this.fireEvent('invalid', this, msg);
18631 if(this.inputType == 'radio'){
18632 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18633 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18634 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18641 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18642 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18646 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18652 for(var i in group){
18653 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18654 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18661 Roo.apply(Roo.bootstrap.CheckBox, {
18666 * register a CheckBox Group
18667 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18669 register : function(checkbox)
18671 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18672 this.groups[checkbox.groupId] = {};
18675 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18679 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18683 * fetch a CheckBox Group based on the group ID
18684 * @param {string} the group ID
18685 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18687 get: function(groupId) {
18688 if (typeof(this.groups[groupId]) == 'undefined') {
18692 return this.groups[groupId] ;
18704 *<div class="radio">
18706 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18707 Option one is this and that—be sure to include why it's great
18714 *<label class="radio-inline">fieldLabel</label>
18715 *<label class="radio-inline">
18716 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18724 * @class Roo.bootstrap.Radio
18725 * @extends Roo.bootstrap.CheckBox
18726 * Bootstrap Radio class
18729 * Create a new Radio
18730 * @param {Object} config The config object
18733 Roo.bootstrap.Radio = function(config){
18734 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18738 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18740 inputType: 'radio',
18744 getAutoCreate : function()
18746 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18747 align = align || 'left'; // default...
18754 tag : this.inline ? 'span' : 'div',
18759 var inline = this.inline ? ' radio-inline' : '';
18763 // does not need for, as we wrap the input with it..
18765 cls : 'control-label box-label' + inline,
18768 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18772 //cls : 'control-label' + inline,
18773 html : this.fieldLabel,
18774 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18783 type : this.inputType,
18784 //value : (!this.checked) ? this.valueOff : this.inputValue,
18785 value : this.inputValue,
18787 placeholder : this.placeholder || '' // ?? needed????
18790 if (this.weight) { // Validity check?
18791 input.cls += " radio-" + this.weight;
18793 if (this.disabled) {
18794 input.disabled=true;
18798 input.checked = this.checked;
18802 input.name = this.name;
18806 input.cls += ' input-' + this.size;
18809 //?? can span's inline have a width??
18812 ['xs','sm','md','lg'].map(function(size){
18813 if (settings[size]) {
18814 cfg.cls += ' col-' + size + '-' + settings[size];
18818 var inputblock = input;
18820 if (this.before || this.after) {
18823 cls : 'input-group',
18828 inputblock.cn.push({
18830 cls : 'input-group-addon',
18834 inputblock.cn.push(input);
18836 inputblock.cn.push({
18838 cls : 'input-group-addon',
18846 if (this.fieldLabel && this.fieldLabel.length) {
18847 cfg.cn.push(fieldLabel);
18850 // normal bootstrap puts the input inside the label.
18851 // however with our styled version - it has to go after the input.
18853 //lbl.cn.push(inputblock);
18857 cls: 'radio' + inline,
18864 cfg.cn.push( lblwrap);
18869 html: this.boxLabel
18878 initEvents : function()
18880 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18882 this.inputEl().on('click', this.onClick, this);
18883 if (this.boxLabel) {
18884 Roo.log('find label')
18885 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18890 inputEl: function ()
18892 return this.el.select('input.roo-radio',true).first();
18894 onClick : function()
18897 this.setChecked(true);
18900 setChecked : function(state,suppressEvent)
18903 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18904 v.dom.checked = false;
18907 Roo.log(this.inputEl().dom);
18908 this.checked = state;
18909 this.inputEl().dom.checked = state;
18911 if(suppressEvent !== true){
18912 this.fireEvent('check', this, state);
18915 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18919 getGroupValue : function()
18922 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18923 if(v.dom.checked == true){
18924 value = v.dom.value;
18932 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18933 * @return {Mixed} value The field value
18935 getValue : function(){
18936 return this.getGroupValue();
18942 //<script type="text/javascript">
18945 * Based Ext JS Library 1.1.1
18946 * Copyright(c) 2006-2007, Ext JS, LLC.
18952 * @class Roo.HtmlEditorCore
18953 * @extends Roo.Component
18954 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18956 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18959 Roo.HtmlEditorCore = function(config){
18962 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18967 * @event initialize
18968 * Fires when the editor is fully initialized (including the iframe)
18969 * @param {Roo.HtmlEditorCore} this
18974 * Fires when the editor is first receives the focus. Any insertion must wait
18975 * until after this event.
18976 * @param {Roo.HtmlEditorCore} this
18980 * @event beforesync
18981 * Fires before the textarea is updated with content from the editor iframe. Return false
18982 * to cancel the sync.
18983 * @param {Roo.HtmlEditorCore} this
18984 * @param {String} html
18988 * @event beforepush
18989 * Fires before the iframe editor is updated with content from the textarea. Return false
18990 * to cancel the push.
18991 * @param {Roo.HtmlEditorCore} this
18992 * @param {String} html
18997 * Fires when the textarea is updated with content from the editor iframe.
18998 * @param {Roo.HtmlEditorCore} this
18999 * @param {String} html
19004 * Fires when the iframe editor is updated with content from the textarea.
19005 * @param {Roo.HtmlEditorCore} this
19006 * @param {String} html
19011 * @event editorevent
19012 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19013 * @param {Roo.HtmlEditorCore} this
19019 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19021 // defaults : white / black...
19022 this.applyBlacklists();
19029 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19033 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19039 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19044 * @cfg {Number} height (in pixels)
19048 * @cfg {Number} width (in pixels)
19053 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19056 stylesheets: false,
19061 // private properties
19062 validationEvent : false,
19064 initialized : false,
19066 sourceEditMode : false,
19067 onFocus : Roo.emptyFn,
19069 hideMode:'offsets',
19073 // blacklist + whitelisted elements..
19080 * Protected method that will not generally be called directly. It
19081 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19082 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19084 getDocMarkup : function(){
19088 // inherit styels from page...??
19089 if (this.stylesheets === false) {
19091 Roo.get(document.head).select('style').each(function(node) {
19092 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19095 Roo.get(document.head).select('link').each(function(node) {
19096 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19099 } else if (!this.stylesheets.length) {
19101 st = '<style type="text/css">' +
19102 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19108 st += '<style type="text/css">' +
19109 'IMG { cursor: pointer } ' +
19113 return '<html><head>' + st +
19114 //<style type="text/css">' +
19115 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19117 ' </head><body class="roo-htmleditor-body"></body></html>';
19121 onRender : function(ct, position)
19124 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19125 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19128 this.el.dom.style.border = '0 none';
19129 this.el.dom.setAttribute('tabIndex', -1);
19130 this.el.addClass('x-hidden hide');
19134 if(Roo.isIE){ // fix IE 1px bogus margin
19135 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19139 this.frameId = Roo.id();
19143 var iframe = this.owner.wrap.createChild({
19145 cls: 'form-control', // bootstrap..
19147 name: this.frameId,
19148 frameBorder : 'no',
19149 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19154 this.iframe = iframe.dom;
19156 this.assignDocWin();
19158 this.doc.designMode = 'on';
19161 this.doc.write(this.getDocMarkup());
19165 var task = { // must defer to wait for browser to be ready
19167 //console.log("run task?" + this.doc.readyState);
19168 this.assignDocWin();
19169 if(this.doc.body || this.doc.readyState == 'complete'){
19171 this.doc.designMode="on";
19175 Roo.TaskMgr.stop(task);
19176 this.initEditor.defer(10, this);
19183 Roo.TaskMgr.start(task);
19188 onResize : function(w, h)
19190 Roo.log('resize: ' +w + ',' + h );
19191 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19195 if(typeof w == 'number'){
19197 this.iframe.style.width = w + 'px';
19199 if(typeof h == 'number'){
19201 this.iframe.style.height = h + 'px';
19203 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19210 * Toggles the editor between standard and source edit mode.
19211 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19213 toggleSourceEdit : function(sourceEditMode){
19215 this.sourceEditMode = sourceEditMode === true;
19217 if(this.sourceEditMode){
19219 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19222 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19223 //this.iframe.className = '';
19226 //this.setSize(this.owner.wrap.getSize());
19227 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19234 * Protected method that will not generally be called directly. If you need/want
19235 * custom HTML cleanup, this is the method you should override.
19236 * @param {String} html The HTML to be cleaned
19237 * return {String} The cleaned HTML
19239 cleanHtml : function(html){
19240 html = String(html);
19241 if(html.length > 5){
19242 if(Roo.isSafari){ // strip safari nonsense
19243 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19246 if(html == ' '){
19253 * HTML Editor -> Textarea
19254 * Protected method that will not generally be called directly. Syncs the contents
19255 * of the editor iframe with the textarea.
19257 syncValue : function(){
19258 if(this.initialized){
19259 var bd = (this.doc.body || this.doc.documentElement);
19260 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19261 var html = bd.innerHTML;
19263 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19264 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19266 html = '<div style="'+m[0]+'">' + html + '</div>';
19269 html = this.cleanHtml(html);
19270 // fix up the special chars.. normaly like back quotes in word...
19271 // however we do not want to do this with chinese..
19272 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19273 var cc = b.charCodeAt();
19275 (cc >= 0x4E00 && cc < 0xA000 ) ||
19276 (cc >= 0x3400 && cc < 0x4E00 ) ||
19277 (cc >= 0xf900 && cc < 0xfb00 )
19283 if(this.owner.fireEvent('beforesync', this, html) !== false){
19284 this.el.dom.value = html;
19285 this.owner.fireEvent('sync', this, html);
19291 * Protected method that will not generally be called directly. Pushes the value of the textarea
19292 * into the iframe editor.
19294 pushValue : function(){
19295 if(this.initialized){
19296 var v = this.el.dom.value.trim();
19298 // if(v.length < 1){
19302 if(this.owner.fireEvent('beforepush', this, v) !== false){
19303 var d = (this.doc.body || this.doc.documentElement);
19305 this.cleanUpPaste();
19306 this.el.dom.value = d.innerHTML;
19307 this.owner.fireEvent('push', this, v);
19313 deferFocus : function(){
19314 this.focus.defer(10, this);
19318 focus : function(){
19319 if(this.win && !this.sourceEditMode){
19326 assignDocWin: function()
19328 var iframe = this.iframe;
19331 this.doc = iframe.contentWindow.document;
19332 this.win = iframe.contentWindow;
19334 // if (!Roo.get(this.frameId)) {
19337 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19338 // this.win = Roo.get(this.frameId).dom.contentWindow;
19340 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19344 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19345 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19350 initEditor : function(){
19351 //console.log("INIT EDITOR");
19352 this.assignDocWin();
19356 this.doc.designMode="on";
19358 this.doc.write(this.getDocMarkup());
19361 var dbody = (this.doc.body || this.doc.documentElement);
19362 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19363 // this copies styles from the containing element into thsi one..
19364 // not sure why we need all of this..
19365 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19367 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19368 //ss['background-attachment'] = 'fixed'; // w3c
19369 dbody.bgProperties = 'fixed'; // ie
19370 //Roo.DomHelper.applyStyles(dbody, ss);
19371 Roo.EventManager.on(this.doc, {
19372 //'mousedown': this.onEditorEvent,
19373 'mouseup': this.onEditorEvent,
19374 'dblclick': this.onEditorEvent,
19375 'click': this.onEditorEvent,
19376 'keyup': this.onEditorEvent,
19381 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19383 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19384 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19386 this.initialized = true;
19388 this.owner.fireEvent('initialize', this);
19393 onDestroy : function(){
19399 //for (var i =0; i < this.toolbars.length;i++) {
19400 // // fixme - ask toolbars for heights?
19401 // this.toolbars[i].onDestroy();
19404 //this.wrap.dom.innerHTML = '';
19405 //this.wrap.remove();
19410 onFirstFocus : function(){
19412 this.assignDocWin();
19415 this.activated = true;
19418 if(Roo.isGecko){ // prevent silly gecko errors
19420 var s = this.win.getSelection();
19421 if(!s.focusNode || s.focusNode.nodeType != 3){
19422 var r = s.getRangeAt(0);
19423 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19428 this.execCmd('useCSS', true);
19429 this.execCmd('styleWithCSS', false);
19432 this.owner.fireEvent('activate', this);
19436 adjustFont: function(btn){
19437 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19438 //if(Roo.isSafari){ // safari
19441 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19442 if(Roo.isSafari){ // safari
19443 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19444 v = (v < 10) ? 10 : v;
19445 v = (v > 48) ? 48 : v;
19446 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19451 v = Math.max(1, v+adjust);
19453 this.execCmd('FontSize', v );
19456 onEditorEvent : function(e)
19458 this.owner.fireEvent('editorevent', this, e);
19459 // this.updateToolbar();
19460 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19463 insertTag : function(tg)
19465 // could be a bit smarter... -> wrap the current selected tRoo..
19466 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19468 range = this.createRange(this.getSelection());
19469 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19470 wrappingNode.appendChild(range.extractContents());
19471 range.insertNode(wrappingNode);
19478 this.execCmd("formatblock", tg);
19482 insertText : function(txt)
19486 var range = this.createRange();
19487 range.deleteContents();
19488 //alert(Sender.getAttribute('label'));
19490 range.insertNode(this.doc.createTextNode(txt));
19496 * Executes a Midas editor command on the editor document and performs necessary focus and
19497 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19498 * @param {String} cmd The Midas command
19499 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19501 relayCmd : function(cmd, value){
19503 this.execCmd(cmd, value);
19504 this.owner.fireEvent('editorevent', this);
19505 //this.updateToolbar();
19506 this.owner.deferFocus();
19510 * Executes a Midas editor command directly on the editor document.
19511 * For visual commands, you should use {@link #relayCmd} instead.
19512 * <b>This should only be called after the editor is initialized.</b>
19513 * @param {String} cmd The Midas command
19514 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19516 execCmd : function(cmd, value){
19517 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19524 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19526 * @param {String} text | dom node..
19528 insertAtCursor : function(text)
19533 if(!this.activated){
19539 var r = this.doc.selection.createRange();
19550 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19554 // from jquery ui (MIT licenced)
19556 var win = this.win;
19558 if (win.getSelection && win.getSelection().getRangeAt) {
19559 range = win.getSelection().getRangeAt(0);
19560 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19561 range.insertNode(node);
19562 } else if (win.document.selection && win.document.selection.createRange) {
19563 // no firefox support
19564 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19565 win.document.selection.createRange().pasteHTML(txt);
19567 // no firefox support
19568 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19569 this.execCmd('InsertHTML', txt);
19578 mozKeyPress : function(e){
19580 var c = e.getCharCode(), cmd;
19583 c = String.fromCharCode(c).toLowerCase();
19597 this.cleanUpPaste.defer(100, this);
19605 e.preventDefault();
19613 fixKeys : function(){ // load time branching for fastest keydown performance
19615 return function(e){
19616 var k = e.getKey(), r;
19619 r = this.doc.selection.createRange();
19622 r.pasteHTML('    ');
19629 r = this.doc.selection.createRange();
19631 var target = r.parentElement();
19632 if(!target || target.tagName.toLowerCase() != 'li'){
19634 r.pasteHTML('<br />');
19640 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19641 this.cleanUpPaste.defer(100, this);
19647 }else if(Roo.isOpera){
19648 return function(e){
19649 var k = e.getKey();
19653 this.execCmd('InsertHTML','    ');
19656 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19657 this.cleanUpPaste.defer(100, this);
19662 }else if(Roo.isSafari){
19663 return function(e){
19664 var k = e.getKey();
19668 this.execCmd('InsertText','\t');
19672 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19673 this.cleanUpPaste.defer(100, this);
19681 getAllAncestors: function()
19683 var p = this.getSelectedNode();
19686 a.push(p); // push blank onto stack..
19687 p = this.getParentElement();
19691 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19695 a.push(this.doc.body);
19699 lastSelNode : false,
19702 getSelection : function()
19704 this.assignDocWin();
19705 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19708 getSelectedNode: function()
19710 // this may only work on Gecko!!!
19712 // should we cache this!!!!
19717 var range = this.createRange(this.getSelection()).cloneRange();
19720 var parent = range.parentElement();
19722 var testRange = range.duplicate();
19723 testRange.moveToElementText(parent);
19724 if (testRange.inRange(range)) {
19727 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19730 parent = parent.parentElement;
19735 // is ancestor a text element.
19736 var ac = range.commonAncestorContainer;
19737 if (ac.nodeType == 3) {
19738 ac = ac.parentNode;
19741 var ar = ac.childNodes;
19744 var other_nodes = [];
19745 var has_other_nodes = false;
19746 for (var i=0;i<ar.length;i++) {
19747 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19750 // fullly contained node.
19752 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19757 // probably selected..
19758 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19759 other_nodes.push(ar[i]);
19763 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19768 has_other_nodes = true;
19770 if (!nodes.length && other_nodes.length) {
19771 nodes= other_nodes;
19773 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19779 createRange: function(sel)
19781 // this has strange effects when using with
19782 // top toolbar - not sure if it's a great idea.
19783 //this.editor.contentWindow.focus();
19784 if (typeof sel != "undefined") {
19786 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19788 return this.doc.createRange();
19791 return this.doc.createRange();
19794 getParentElement: function()
19797 this.assignDocWin();
19798 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19800 var range = this.createRange(sel);
19803 var p = range.commonAncestorContainer;
19804 while (p.nodeType == 3) { // text node
19815 * Range intersection.. the hard stuff...
19819 * [ -- selected range --- ]
19823 * if end is before start or hits it. fail.
19824 * if start is after end or hits it fail.
19826 * if either hits (but other is outside. - then it's not
19832 // @see http://www.thismuchiknow.co.uk/?p=64.
19833 rangeIntersectsNode : function(range, node)
19835 var nodeRange = node.ownerDocument.createRange();
19837 nodeRange.selectNode(node);
19839 nodeRange.selectNodeContents(node);
19842 var rangeStartRange = range.cloneRange();
19843 rangeStartRange.collapse(true);
19845 var rangeEndRange = range.cloneRange();
19846 rangeEndRange.collapse(false);
19848 var nodeStartRange = nodeRange.cloneRange();
19849 nodeStartRange.collapse(true);
19851 var nodeEndRange = nodeRange.cloneRange();
19852 nodeEndRange.collapse(false);
19854 return rangeStartRange.compareBoundaryPoints(
19855 Range.START_TO_START, nodeEndRange) == -1 &&
19856 rangeEndRange.compareBoundaryPoints(
19857 Range.START_TO_START, nodeStartRange) == 1;
19861 rangeCompareNode : function(range, node)
19863 var nodeRange = node.ownerDocument.createRange();
19865 nodeRange.selectNode(node);
19867 nodeRange.selectNodeContents(node);
19871 range.collapse(true);
19873 nodeRange.collapse(true);
19875 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19876 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19878 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19880 var nodeIsBefore = ss == 1;
19881 var nodeIsAfter = ee == -1;
19883 if (nodeIsBefore && nodeIsAfter)
19885 if (!nodeIsBefore && nodeIsAfter)
19886 return 1; //right trailed.
19888 if (nodeIsBefore && !nodeIsAfter)
19889 return 2; // left trailed.
19894 // private? - in a new class?
19895 cleanUpPaste : function()
19897 // cleans up the whole document..
19898 Roo.log('cleanuppaste');
19900 this.cleanUpChildren(this.doc.body);
19901 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19902 if (clean != this.doc.body.innerHTML) {
19903 this.doc.body.innerHTML = clean;
19908 cleanWordChars : function(input) {// change the chars to hex code
19909 var he = Roo.HtmlEditorCore;
19911 var output = input;
19912 Roo.each(he.swapCodes, function(sw) {
19913 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19915 output = output.replace(swapper, sw[1]);
19922 cleanUpChildren : function (n)
19924 if (!n.childNodes.length) {
19927 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19928 this.cleanUpChild(n.childNodes[i]);
19935 cleanUpChild : function (node)
19938 //console.log(node);
19939 if (node.nodeName == "#text") {
19940 // clean up silly Windows -- stuff?
19943 if (node.nodeName == "#comment") {
19944 node.parentNode.removeChild(node);
19945 // clean up silly Windows -- stuff?
19948 var lcname = node.tagName.toLowerCase();
19949 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19950 // whitelist of tags..
19952 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19954 node.parentNode.removeChild(node);
19959 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19961 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19962 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19964 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19965 // remove_keep_children = true;
19968 if (remove_keep_children) {
19969 this.cleanUpChildren(node);
19970 // inserts everything just before this node...
19971 while (node.childNodes.length) {
19972 var cn = node.childNodes[0];
19973 node.removeChild(cn);
19974 node.parentNode.insertBefore(cn, node);
19976 node.parentNode.removeChild(node);
19980 if (!node.attributes || !node.attributes.length) {
19981 this.cleanUpChildren(node);
19985 function cleanAttr(n,v)
19988 if (v.match(/^\./) || v.match(/^\//)) {
19991 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19994 if (v.match(/^#/)) {
19997 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19998 node.removeAttribute(n);
20002 var cwhite = this.cwhite;
20003 var cblack = this.cblack;
20005 function cleanStyle(n,v)
20007 if (v.match(/expression/)) { //XSS?? should we even bother..
20008 node.removeAttribute(n);
20012 var parts = v.split(/;/);
20015 Roo.each(parts, function(p) {
20016 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20020 var l = p.split(':').shift().replace(/\s+/g,'');
20021 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20023 if ( cwhite.length && cblack.indexOf(l) > -1) {
20024 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20025 //node.removeAttribute(n);
20029 // only allow 'c whitelisted system attributes'
20030 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20031 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20032 //node.removeAttribute(n);
20042 if (clean.length) {
20043 node.setAttribute(n, clean.join(';'));
20045 node.removeAttribute(n);
20051 for (var i = node.attributes.length-1; i > -1 ; i--) {
20052 var a = node.attributes[i];
20055 if (a.name.toLowerCase().substr(0,2)=='on') {
20056 node.removeAttribute(a.name);
20059 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20060 node.removeAttribute(a.name);
20063 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20064 cleanAttr(a.name,a.value); // fixme..
20067 if (a.name == 'style') {
20068 cleanStyle(a.name,a.value);
20071 /// clean up MS crap..
20072 // tecnically this should be a list of valid class'es..
20075 if (a.name == 'class') {
20076 if (a.value.match(/^Mso/)) {
20077 node.className = '';
20080 if (a.value.match(/body/)) {
20081 node.className = '';
20092 this.cleanUpChildren(node);
20098 * Clean up MS wordisms...
20100 cleanWord : function(node)
20105 this.cleanWord(this.doc.body);
20108 if (node.nodeName == "#text") {
20109 // clean up silly Windows -- stuff?
20112 if (node.nodeName == "#comment") {
20113 node.parentNode.removeChild(node);
20114 // clean up silly Windows -- stuff?
20118 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20119 node.parentNode.removeChild(node);
20123 // remove - but keep children..
20124 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20125 while (node.childNodes.length) {
20126 var cn = node.childNodes[0];
20127 node.removeChild(cn);
20128 node.parentNode.insertBefore(cn, node);
20130 node.parentNode.removeChild(node);
20131 this.iterateChildren(node, this.cleanWord);
20135 if (node.className.length) {
20137 var cn = node.className.split(/\W+/);
20139 Roo.each(cn, function(cls) {
20140 if (cls.match(/Mso[a-zA-Z]+/)) {
20145 node.className = cna.length ? cna.join(' ') : '';
20147 node.removeAttribute("class");
20151 if (node.hasAttribute("lang")) {
20152 node.removeAttribute("lang");
20155 if (node.hasAttribute("style")) {
20157 var styles = node.getAttribute("style").split(";");
20159 Roo.each(styles, function(s) {
20160 if (!s.match(/:/)) {
20163 var kv = s.split(":");
20164 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20167 // what ever is left... we allow.
20170 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20171 if (!nstyle.length) {
20172 node.removeAttribute('style');
20175 this.iterateChildren(node, this.cleanWord);
20181 * iterateChildren of a Node, calling fn each time, using this as the scole..
20182 * @param {DomNode} node node to iterate children of.
20183 * @param {Function} fn method of this class to call on each item.
20185 iterateChildren : function(node, fn)
20187 if (!node.childNodes.length) {
20190 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20191 fn.call(this, node.childNodes[i])
20197 * cleanTableWidths.
20199 * Quite often pasting from word etc.. results in tables with column and widths.
20200 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20203 cleanTableWidths : function(node)
20208 this.cleanTableWidths(this.doc.body);
20213 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20216 Roo.log(node.tagName);
20217 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20218 this.iterateChildren(node, this.cleanTableWidths);
20221 if (node.hasAttribute('width')) {
20222 node.removeAttribute('width');
20226 if (node.hasAttribute("style")) {
20229 var styles = node.getAttribute("style").split(";");
20231 Roo.each(styles, function(s) {
20232 if (!s.match(/:/)) {
20235 var kv = s.split(":");
20236 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20239 // what ever is left... we allow.
20242 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20243 if (!nstyle.length) {
20244 node.removeAttribute('style');
20248 this.iterateChildren(node, this.cleanTableWidths);
20256 domToHTML : function(currentElement, depth, nopadtext) {
20258 depth = depth || 0;
20259 nopadtext = nopadtext || false;
20261 if (!currentElement) {
20262 return this.domToHTML(this.doc.body);
20265 //Roo.log(currentElement);
20267 var allText = false;
20268 var nodeName = currentElement.nodeName;
20269 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20271 if (nodeName == '#text') {
20273 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20278 if (nodeName != 'BODY') {
20281 // Prints the node tagName, such as <A>, <IMG>, etc
20284 for(i = 0; i < currentElement.attributes.length;i++) {
20286 var aname = currentElement.attributes.item(i).name;
20287 if (!currentElement.attributes.item(i).value.length) {
20290 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20293 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20302 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20305 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20310 // Traverse the tree
20312 var currentElementChild = currentElement.childNodes.item(i);
20313 var allText = true;
20314 var innerHTML = '';
20316 while (currentElementChild) {
20317 // Formatting code (indent the tree so it looks nice on the screen)
20318 var nopad = nopadtext;
20319 if (lastnode == 'SPAN') {
20323 if (currentElementChild.nodeName == '#text') {
20324 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20325 toadd = nopadtext ? toadd : toadd.trim();
20326 if (!nopad && toadd.length > 80) {
20327 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20329 innerHTML += toadd;
20332 currentElementChild = currentElement.childNodes.item(i);
20338 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20340 // Recursively traverse the tree structure of the child node
20341 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20342 lastnode = currentElementChild.nodeName;
20344 currentElementChild=currentElement.childNodes.item(i);
20350 // The remaining code is mostly for formatting the tree
20351 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20356 ret+= "</"+tagName+">";
20362 applyBlacklists : function()
20364 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20365 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20369 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20370 if (b.indexOf(tag) > -1) {
20373 this.white.push(tag);
20377 Roo.each(w, function(tag) {
20378 if (b.indexOf(tag) > -1) {
20381 if (this.white.indexOf(tag) > -1) {
20384 this.white.push(tag);
20389 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20390 if (w.indexOf(tag) > -1) {
20393 this.black.push(tag);
20397 Roo.each(b, function(tag) {
20398 if (w.indexOf(tag) > -1) {
20401 if (this.black.indexOf(tag) > -1) {
20404 this.black.push(tag);
20409 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20410 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20414 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20415 if (b.indexOf(tag) > -1) {
20418 this.cwhite.push(tag);
20422 Roo.each(w, function(tag) {
20423 if (b.indexOf(tag) > -1) {
20426 if (this.cwhite.indexOf(tag) > -1) {
20429 this.cwhite.push(tag);
20434 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20435 if (w.indexOf(tag) > -1) {
20438 this.cblack.push(tag);
20442 Roo.each(b, function(tag) {
20443 if (w.indexOf(tag) > -1) {
20446 if (this.cblack.indexOf(tag) > -1) {
20449 this.cblack.push(tag);
20454 setStylesheets : function(stylesheets)
20456 if(typeof(stylesheets) == 'string'){
20457 Roo.get(this.iframe.contentDocument.head).createChild({
20459 rel : 'stylesheet',
20468 Roo.each(stylesheets, function(s) {
20473 Roo.get(_this.iframe.contentDocument.head).createChild({
20475 rel : 'stylesheet',
20484 removeStylesheets : function()
20488 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20493 // hide stuff that is not compatible
20507 * @event specialkey
20511 * @cfg {String} fieldClass @hide
20514 * @cfg {String} focusClass @hide
20517 * @cfg {String} autoCreate @hide
20520 * @cfg {String} inputType @hide
20523 * @cfg {String} invalidClass @hide
20526 * @cfg {String} invalidText @hide
20529 * @cfg {String} msgFx @hide
20532 * @cfg {String} validateOnBlur @hide
20536 Roo.HtmlEditorCore.white = [
20537 'area', 'br', 'img', 'input', 'hr', 'wbr',
20539 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20540 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20541 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20542 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20543 'table', 'ul', 'xmp',
20545 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20548 'dir', 'menu', 'ol', 'ul', 'dl',
20554 Roo.HtmlEditorCore.black = [
20555 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20557 'base', 'basefont', 'bgsound', 'blink', 'body',
20558 'frame', 'frameset', 'head', 'html', 'ilayer',
20559 'iframe', 'layer', 'link', 'meta', 'object',
20560 'script', 'style' ,'title', 'xml' // clean later..
20562 Roo.HtmlEditorCore.clean = [
20563 'script', 'style', 'title', 'xml'
20565 Roo.HtmlEditorCore.remove = [
20570 Roo.HtmlEditorCore.ablack = [
20574 Roo.HtmlEditorCore.aclean = [
20575 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20579 Roo.HtmlEditorCore.pwhite= [
20580 'http', 'https', 'mailto'
20583 // white listed style attributes.
20584 Roo.HtmlEditorCore.cwhite= [
20585 // 'text-align', /// default is to allow most things..
20591 // black listed style attributes.
20592 Roo.HtmlEditorCore.cblack= [
20593 // 'font-size' -- this can be set by the project
20597 Roo.HtmlEditorCore.swapCodes =[
20616 * @class Roo.bootstrap.HtmlEditor
20617 * @extends Roo.bootstrap.TextArea
20618 * Bootstrap HtmlEditor class
20621 * Create a new HtmlEditor
20622 * @param {Object} config The config object
20625 Roo.bootstrap.HtmlEditor = function(config){
20626 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20627 if (!this.toolbars) {
20628 this.toolbars = [];
20630 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20633 * @event initialize
20634 * Fires when the editor is fully initialized (including the iframe)
20635 * @param {HtmlEditor} this
20640 * Fires when the editor is first receives the focus. Any insertion must wait
20641 * until after this event.
20642 * @param {HtmlEditor} this
20646 * @event beforesync
20647 * Fires before the textarea is updated with content from the editor iframe. Return false
20648 * to cancel the sync.
20649 * @param {HtmlEditor} this
20650 * @param {String} html
20654 * @event beforepush
20655 * Fires before the iframe editor is updated with content from the textarea. Return false
20656 * to cancel the push.
20657 * @param {HtmlEditor} this
20658 * @param {String} html
20663 * Fires when the textarea is updated with content from the editor iframe.
20664 * @param {HtmlEditor} this
20665 * @param {String} html
20670 * Fires when the iframe editor is updated with content from the textarea.
20671 * @param {HtmlEditor} this
20672 * @param {String} html
20676 * @event editmodechange
20677 * Fires when the editor switches edit modes
20678 * @param {HtmlEditor} this
20679 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20681 editmodechange: true,
20683 * @event editorevent
20684 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20685 * @param {HtmlEditor} this
20689 * @event firstfocus
20690 * Fires when on first focus - needed by toolbars..
20691 * @param {HtmlEditor} this
20696 * Auto save the htmlEditor value as a file into Events
20697 * @param {HtmlEditor} this
20701 * @event savedpreview
20702 * preview the saved version of htmlEditor
20703 * @param {HtmlEditor} this
20710 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20714 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20719 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20724 * @cfg {Number} height (in pixels)
20728 * @cfg {Number} width (in pixels)
20733 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20736 stylesheets: false,
20741 // private properties
20742 validationEvent : false,
20744 initialized : false,
20747 onFocus : Roo.emptyFn,
20749 hideMode:'offsets',
20752 tbContainer : false,
20754 toolbarContainer :function() {
20755 return this.wrap.select('.x-html-editor-tb',true).first();
20759 * Protected method that will not generally be called directly. It
20760 * is called when the editor creates its toolbar. Override this method if you need to
20761 * add custom toolbar buttons.
20762 * @param {HtmlEditor} editor
20764 createToolbar : function(){
20766 Roo.log("create toolbars");
20768 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20769 this.toolbars[0].render(this.toolbarContainer());
20773 // if (!editor.toolbars || !editor.toolbars.length) {
20774 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20777 // for (var i =0 ; i < editor.toolbars.length;i++) {
20778 // editor.toolbars[i] = Roo.factory(
20779 // typeof(editor.toolbars[i]) == 'string' ?
20780 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20781 // Roo.bootstrap.HtmlEditor);
20782 // editor.toolbars[i].init(editor);
20788 onRender : function(ct, position)
20790 // Roo.log("Call onRender: " + this.xtype);
20792 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20794 this.wrap = this.inputEl().wrap({
20795 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20798 this.editorcore.onRender(ct, position);
20800 if (this.resizable) {
20801 this.resizeEl = new Roo.Resizable(this.wrap, {
20805 minHeight : this.height,
20806 height: this.height,
20807 handles : this.resizable,
20810 resize : function(r, w, h) {
20811 _t.onResize(w,h); // -something
20817 this.createToolbar(this);
20820 if(!this.width && this.resizable){
20821 this.setSize(this.wrap.getSize());
20823 if (this.resizeEl) {
20824 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20825 // should trigger onReize..
20831 onResize : function(w, h)
20833 Roo.log('resize: ' +w + ',' + h );
20834 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20838 if(this.inputEl() ){
20839 if(typeof w == 'number'){
20840 var aw = w - this.wrap.getFrameWidth('lr');
20841 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20844 if(typeof h == 'number'){
20845 var tbh = -11; // fixme it needs to tool bar size!
20846 for (var i =0; i < this.toolbars.length;i++) {
20847 // fixme - ask toolbars for heights?
20848 tbh += this.toolbars[i].el.getHeight();
20849 //if (this.toolbars[i].footer) {
20850 // tbh += this.toolbars[i].footer.el.getHeight();
20858 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20859 ah -= 5; // knock a few pixes off for look..
20860 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20864 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20865 this.editorcore.onResize(ew,eh);
20870 * Toggles the editor between standard and source edit mode.
20871 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20873 toggleSourceEdit : function(sourceEditMode)
20875 this.editorcore.toggleSourceEdit(sourceEditMode);
20877 if(this.editorcore.sourceEditMode){
20878 Roo.log('editor - showing textarea');
20881 // Roo.log(this.syncValue());
20883 this.inputEl().removeClass(['hide', 'x-hidden']);
20884 this.inputEl().dom.removeAttribute('tabIndex');
20885 this.inputEl().focus();
20887 Roo.log('editor - hiding textarea');
20889 // Roo.log(this.pushValue());
20892 this.inputEl().addClass(['hide', 'x-hidden']);
20893 this.inputEl().dom.setAttribute('tabIndex', -1);
20894 //this.deferFocus();
20897 if(this.resizable){
20898 this.setSize(this.wrap.getSize());
20901 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20904 // private (for BoxComponent)
20905 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20907 // private (for BoxComponent)
20908 getResizeEl : function(){
20912 // private (for BoxComponent)
20913 getPositionEl : function(){
20918 initEvents : function(){
20919 this.originalValue = this.getValue();
20923 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20926 // markInvalid : Roo.emptyFn,
20928 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20931 // clearInvalid : Roo.emptyFn,
20933 setValue : function(v){
20934 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20935 this.editorcore.pushValue();
20940 deferFocus : function(){
20941 this.focus.defer(10, this);
20945 focus : function(){
20946 this.editorcore.focus();
20952 onDestroy : function(){
20958 for (var i =0; i < this.toolbars.length;i++) {
20959 // fixme - ask toolbars for heights?
20960 this.toolbars[i].onDestroy();
20963 this.wrap.dom.innerHTML = '';
20964 this.wrap.remove();
20969 onFirstFocus : function(){
20970 //Roo.log("onFirstFocus");
20971 this.editorcore.onFirstFocus();
20972 for (var i =0; i < this.toolbars.length;i++) {
20973 this.toolbars[i].onFirstFocus();
20979 syncValue : function()
20981 this.editorcore.syncValue();
20984 pushValue : function()
20986 this.editorcore.pushValue();
20990 // hide stuff that is not compatible
21004 * @event specialkey
21008 * @cfg {String} fieldClass @hide
21011 * @cfg {String} focusClass @hide
21014 * @cfg {String} autoCreate @hide
21017 * @cfg {String} inputType @hide
21020 * @cfg {String} invalidClass @hide
21023 * @cfg {String} invalidText @hide
21026 * @cfg {String} msgFx @hide
21029 * @cfg {String} validateOnBlur @hide
21038 Roo.namespace('Roo.bootstrap.htmleditor');
21040 * @class Roo.bootstrap.HtmlEditorToolbar1
21045 new Roo.bootstrap.HtmlEditor({
21048 new Roo.bootstrap.HtmlEditorToolbar1({
21049 disable : { fonts: 1 , format: 1, ..., ... , ...],
21055 * @cfg {Object} disable List of elements to disable..
21056 * @cfg {Array} btns List of additional buttons.
21060 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21063 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21066 Roo.apply(this, config);
21068 // default disabled, based on 'good practice'..
21069 this.disable = this.disable || {};
21070 Roo.applyIf(this.disable, {
21073 specialElements : true
21075 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21077 this.editor = config.editor;
21078 this.editorcore = config.editor.editorcore;
21080 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21082 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21083 // dont call parent... till later.
21085 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21090 editorcore : false,
21095 "h1","h2","h3","h4","h5","h6",
21097 "abbr", "acronym", "address", "cite", "samp", "var",
21101 onRender : function(ct, position)
21103 // Roo.log("Call onRender: " + this.xtype);
21105 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21107 this.el.dom.style.marginBottom = '0';
21109 var editorcore = this.editorcore;
21110 var editor= this.editor;
21113 var btn = function(id,cmd , toggle, handler){
21115 var event = toggle ? 'toggle' : 'click';
21120 xns: Roo.bootstrap,
21123 enableToggle:toggle !== false,
21125 pressed : toggle ? false : null,
21128 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21129 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21138 xns: Roo.bootstrap,
21139 glyphicon : 'font',
21143 xns: Roo.bootstrap,
21147 Roo.each(this.formats, function(f) {
21148 style.menu.items.push({
21150 xns: Roo.bootstrap,
21151 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21156 editorcore.insertTag(this.tagname);
21163 children.push(style);
21166 btn('bold',false,true);
21167 btn('italic',false,true);
21168 btn('align-left', 'justifyleft',true);
21169 btn('align-center', 'justifycenter',true);
21170 btn('align-right' , 'justifyright',true);
21171 btn('link', false, false, function(btn) {
21172 //Roo.log("create link?");
21173 var url = prompt(this.createLinkText, this.defaultLinkValue);
21174 if(url && url != 'http:/'+'/'){
21175 this.editorcore.relayCmd('createlink', url);
21178 btn('list','insertunorderedlist',true);
21179 btn('pencil', false,true, function(btn){
21182 this.toggleSourceEdit(btn.pressed);
21188 xns: Roo.bootstrap,
21193 xns: Roo.bootstrap,
21198 cog.menu.items.push({
21200 xns: Roo.bootstrap,
21201 html : Clean styles,
21206 editorcore.insertTag(this.tagname);
21215 this.xtype = 'NavSimplebar';
21217 for(var i=0;i< children.length;i++) {
21219 this.buttons.add(this.addxtypeChild(children[i]));
21223 editor.on('editorevent', this.updateToolbar, this);
21225 onBtnClick : function(id)
21227 this.editorcore.relayCmd(id);
21228 this.editorcore.focus();
21232 * Protected method that will not generally be called directly. It triggers
21233 * a toolbar update by reading the markup state of the current selection in the editor.
21235 updateToolbar: function(){
21237 if(!this.editorcore.activated){
21238 this.editor.onFirstFocus(); // is this neeed?
21242 var btns = this.buttons;
21243 var doc = this.editorcore.doc;
21244 btns.get('bold').setActive(doc.queryCommandState('bold'));
21245 btns.get('italic').setActive(doc.queryCommandState('italic'));
21246 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21248 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21249 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21250 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21252 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21253 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21256 var ans = this.editorcore.getAllAncestors();
21257 if (this.formatCombo) {
21260 var store = this.formatCombo.store;
21261 this.formatCombo.setValue("");
21262 for (var i =0; i < ans.length;i++) {
21263 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21265 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21273 // hides menus... - so this cant be on a menu...
21274 Roo.bootstrap.MenuMgr.hideAll();
21276 Roo.bootstrap.MenuMgr.hideAll();
21277 //this.editorsyncValue();
21279 onFirstFocus: function() {
21280 this.buttons.each(function(item){
21284 toggleSourceEdit : function(sourceEditMode){
21287 if(sourceEditMode){
21288 Roo.log("disabling buttons");
21289 this.buttons.each( function(item){
21290 if(item.cmd != 'pencil'){
21296 Roo.log("enabling buttons");
21297 if(this.editorcore.initialized){
21298 this.buttons.each( function(item){
21304 Roo.log("calling toggole on editor");
21305 // tell the editor that it's been pressed..
21306 this.editor.toggleSourceEdit(sourceEditMode);
21316 * @class Roo.bootstrap.Table.AbstractSelectionModel
21317 * @extends Roo.util.Observable
21318 * Abstract base class for grid SelectionModels. It provides the interface that should be
21319 * implemented by descendant classes. This class should not be directly instantiated.
21322 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21323 this.locked = false;
21324 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21328 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21329 /** @ignore Called by the grid automatically. Do not call directly. */
21330 init : function(grid){
21336 * Locks the selections.
21339 this.locked = true;
21343 * Unlocks the selections.
21345 unlock : function(){
21346 this.locked = false;
21350 * Returns true if the selections are locked.
21351 * @return {Boolean}
21353 isLocked : function(){
21354 return this.locked;
21358 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21359 * @class Roo.bootstrap.Table.RowSelectionModel
21360 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21361 * It supports multiple selections and keyboard selection/navigation.
21363 * @param {Object} config
21366 Roo.bootstrap.Table.RowSelectionModel = function(config){
21367 Roo.apply(this, config);
21368 this.selections = new Roo.util.MixedCollection(false, function(o){
21373 this.lastActive = false;
21377 * @event selectionchange
21378 * Fires when the selection changes
21379 * @param {SelectionModel} this
21381 "selectionchange" : true,
21383 * @event afterselectionchange
21384 * Fires after the selection changes (eg. by key press or clicking)
21385 * @param {SelectionModel} this
21387 "afterselectionchange" : true,
21389 * @event beforerowselect
21390 * Fires when a row is selected being selected, return false to cancel.
21391 * @param {SelectionModel} this
21392 * @param {Number} rowIndex The selected index
21393 * @param {Boolean} keepExisting False if other selections will be cleared
21395 "beforerowselect" : true,
21398 * Fires when a row is selected.
21399 * @param {SelectionModel} this
21400 * @param {Number} rowIndex The selected index
21401 * @param {Roo.data.Record} r The record
21403 "rowselect" : true,
21405 * @event rowdeselect
21406 * Fires when a row is deselected.
21407 * @param {SelectionModel} this
21408 * @param {Number} rowIndex The selected index
21410 "rowdeselect" : true
21412 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21413 this.locked = false;
21416 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21418 * @cfg {Boolean} singleSelect
21419 * True to allow selection of only one row at a time (defaults to false)
21421 singleSelect : false,
21424 initEvents : function(){
21426 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21427 this.grid.on("mousedown", this.handleMouseDown, this);
21428 }else{ // allow click to work like normal
21429 this.grid.on("rowclick", this.handleDragableRowClick, this);
21432 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21433 "up" : function(e){
21435 this.selectPrevious(e.shiftKey);
21436 }else if(this.last !== false && this.lastActive !== false){
21437 var last = this.last;
21438 this.selectRange(this.last, this.lastActive-1);
21439 this.grid.getView().focusRow(this.lastActive);
21440 if(last !== false){
21444 this.selectFirstRow();
21446 this.fireEvent("afterselectionchange", this);
21448 "down" : function(e){
21450 this.selectNext(e.shiftKey);
21451 }else if(this.last !== false && this.lastActive !== false){
21452 var last = this.last;
21453 this.selectRange(this.last, this.lastActive+1);
21454 this.grid.getView().focusRow(this.lastActive);
21455 if(last !== false){
21459 this.selectFirstRow();
21461 this.fireEvent("afterselectionchange", this);
21466 var view = this.grid.view;
21467 view.on("refresh", this.onRefresh, this);
21468 view.on("rowupdated", this.onRowUpdated, this);
21469 view.on("rowremoved", this.onRemove, this);
21473 onRefresh : function(){
21474 var ds = this.grid.dataSource, i, v = this.grid.view;
21475 var s = this.selections;
21476 s.each(function(r){
21477 if((i = ds.indexOfId(r.id)) != -1){
21486 onRemove : function(v, index, r){
21487 this.selections.remove(r);
21491 onRowUpdated : function(v, index, r){
21492 if(this.isSelected(r)){
21493 v.onRowSelect(index);
21499 * @param {Array} records The records to select
21500 * @param {Boolean} keepExisting (optional) True to keep existing selections
21502 selectRecords : function(records, keepExisting){
21504 this.clearSelections();
21506 var ds = this.grid.dataSource;
21507 for(var i = 0, len = records.length; i < len; i++){
21508 this.selectRow(ds.indexOf(records[i]), true);
21513 * Gets the number of selected rows.
21516 getCount : function(){
21517 return this.selections.length;
21521 * Selects the first row in the grid.
21523 selectFirstRow : function(){
21528 * Select the last row.
21529 * @param {Boolean} keepExisting (optional) True to keep existing selections
21531 selectLastRow : function(keepExisting){
21532 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21536 * Selects the row immediately following the last selected row.
21537 * @param {Boolean} keepExisting (optional) True to keep existing selections
21539 selectNext : function(keepExisting){
21540 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21541 this.selectRow(this.last+1, keepExisting);
21542 this.grid.getView().focusRow(this.last);
21547 * Selects the row that precedes the last selected row.
21548 * @param {Boolean} keepExisting (optional) True to keep existing selections
21550 selectPrevious : function(keepExisting){
21552 this.selectRow(this.last-1, keepExisting);
21553 this.grid.getView().focusRow(this.last);
21558 * Returns the selected records
21559 * @return {Array} Array of selected records
21561 getSelections : function(){
21562 return [].concat(this.selections.items);
21566 * Returns the first selected record.
21569 getSelected : function(){
21570 return this.selections.itemAt(0);
21575 * Clears all selections.
21577 clearSelections : function(fast){
21578 if(this.locked) return;
21580 var ds = this.grid.dataSource;
21581 var s = this.selections;
21582 s.each(function(r){
21583 this.deselectRow(ds.indexOfId(r.id));
21587 this.selections.clear();
21594 * Selects all rows.
21596 selectAll : function(){
21597 if(this.locked) return;
21598 this.selections.clear();
21599 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21600 this.selectRow(i, true);
21605 * Returns True if there is a selection.
21606 * @return {Boolean}
21608 hasSelection : function(){
21609 return this.selections.length > 0;
21613 * Returns True if the specified row is selected.
21614 * @param {Number/Record} record The record or index of the record to check
21615 * @return {Boolean}
21617 isSelected : function(index){
21618 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21619 return (r && this.selections.key(r.id) ? true : false);
21623 * Returns True if the specified record id is selected.
21624 * @param {String} id The id of record to check
21625 * @return {Boolean}
21627 isIdSelected : function(id){
21628 return (this.selections.key(id) ? true : false);
21632 handleMouseDown : function(e, t){
21633 var view = this.grid.getView(), rowIndex;
21634 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21637 if(e.shiftKey && this.last !== false){
21638 var last = this.last;
21639 this.selectRange(last, rowIndex, e.ctrlKey);
21640 this.last = last; // reset the last
21641 view.focusRow(rowIndex);
21643 var isSelected = this.isSelected(rowIndex);
21644 if(e.button !== 0 && isSelected){
21645 view.focusRow(rowIndex);
21646 }else if(e.ctrlKey && isSelected){
21647 this.deselectRow(rowIndex);
21648 }else if(!isSelected){
21649 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21650 view.focusRow(rowIndex);
21653 this.fireEvent("afterselectionchange", this);
21656 handleDragableRowClick : function(grid, rowIndex, e)
21658 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21659 this.selectRow(rowIndex, false);
21660 grid.view.focusRow(rowIndex);
21661 this.fireEvent("afterselectionchange", this);
21666 * Selects multiple rows.
21667 * @param {Array} rows Array of the indexes of the row to select
21668 * @param {Boolean} keepExisting (optional) True to keep existing selections
21670 selectRows : function(rows, keepExisting){
21672 this.clearSelections();
21674 for(var i = 0, len = rows.length; i < len; i++){
21675 this.selectRow(rows[i], true);
21680 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21681 * @param {Number} startRow The index of the first row in the range
21682 * @param {Number} endRow The index of the last row in the range
21683 * @param {Boolean} keepExisting (optional) True to retain existing selections
21685 selectRange : function(startRow, endRow, keepExisting){
21686 if(this.locked) return;
21688 this.clearSelections();
21690 if(startRow <= endRow){
21691 for(var i = startRow; i <= endRow; i++){
21692 this.selectRow(i, true);
21695 for(var i = startRow; i >= endRow; i--){
21696 this.selectRow(i, true);
21702 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21703 * @param {Number} startRow The index of the first row in the range
21704 * @param {Number} endRow The index of the last row in the range
21706 deselectRange : function(startRow, endRow, preventViewNotify){
21707 if(this.locked) return;
21708 for(var i = startRow; i <= endRow; i++){
21709 this.deselectRow(i, preventViewNotify);
21715 * @param {Number} row The index of the row to select
21716 * @param {Boolean} keepExisting (optional) True to keep existing selections
21718 selectRow : function(index, keepExisting, preventViewNotify){
21719 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21720 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21721 if(!keepExisting || this.singleSelect){
21722 this.clearSelections();
21724 var r = this.grid.dataSource.getAt(index);
21725 this.selections.add(r);
21726 this.last = this.lastActive = index;
21727 if(!preventViewNotify){
21728 this.grid.getView().onRowSelect(index);
21730 this.fireEvent("rowselect", this, index, r);
21731 this.fireEvent("selectionchange", this);
21737 * @param {Number} row The index of the row to deselect
21739 deselectRow : function(index, preventViewNotify){
21740 if(this.locked) return;
21741 if(this.last == index){
21744 if(this.lastActive == index){
21745 this.lastActive = false;
21747 var r = this.grid.dataSource.getAt(index);
21748 this.selections.remove(r);
21749 if(!preventViewNotify){
21750 this.grid.getView().onRowDeselect(index);
21752 this.fireEvent("rowdeselect", this, index);
21753 this.fireEvent("selectionchange", this);
21757 restoreLast : function(){
21759 this.last = this._last;
21764 acceptsNav : function(row, col, cm){
21765 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21769 onEditorKey : function(field, e){
21770 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21775 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21777 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21779 }else if(k == e.ENTER && !e.ctrlKey){
21783 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21785 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21787 }else if(k == e.ESC){
21791 g.startEditing(newCell[0], newCell[1]);
21796 * Ext JS Library 1.1.1
21797 * Copyright(c) 2006-2007, Ext JS, LLC.
21799 * Originally Released Under LGPL - original licence link has changed is not relivant.
21802 * <script type="text/javascript">
21806 * @class Roo.bootstrap.PagingToolbar
21807 * @extends Roo.bootstrap.NavSimplebar
21808 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21810 * Create a new PagingToolbar
21811 * @param {Object} config The config object
21812 * @param {Roo.data.Store} store
21814 Roo.bootstrap.PagingToolbar = function(config)
21816 // old args format still supported... - xtype is prefered..
21817 // created from xtype...
21819 this.ds = config.dataSource;
21821 if (config.store && !this.ds) {
21822 this.store= Roo.factory(config.store, Roo.data);
21823 this.ds = this.store;
21824 this.ds.xmodule = this.xmodule || false;
21827 this.toolbarItems = [];
21828 if (config.items) {
21829 this.toolbarItems = config.items;
21832 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21837 this.bind(this.ds);
21840 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21844 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21846 * @cfg {Roo.data.Store} dataSource
21847 * The underlying data store providing the paged data
21850 * @cfg {String/HTMLElement/Element} container
21851 * container The id or element that will contain the toolbar
21854 * @cfg {Boolean} displayInfo
21855 * True to display the displayMsg (defaults to false)
21858 * @cfg {Number} pageSize
21859 * The number of records to display per page (defaults to 20)
21863 * @cfg {String} displayMsg
21864 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21866 displayMsg : 'Displaying {0} - {1} of {2}',
21868 * @cfg {String} emptyMsg
21869 * The message to display when no records are found (defaults to "No data to display")
21871 emptyMsg : 'No data to display',
21873 * Customizable piece of the default paging text (defaults to "Page")
21876 beforePageText : "Page",
21878 * Customizable piece of the default paging text (defaults to "of %0")
21881 afterPageText : "of {0}",
21883 * Customizable piece of the default paging text (defaults to "First Page")
21886 firstText : "First Page",
21888 * Customizable piece of the default paging text (defaults to "Previous Page")
21891 prevText : "Previous Page",
21893 * Customizable piece of the default paging text (defaults to "Next Page")
21896 nextText : "Next Page",
21898 * Customizable piece of the default paging text (defaults to "Last Page")
21901 lastText : "Last Page",
21903 * Customizable piece of the default paging text (defaults to "Refresh")
21906 refreshText : "Refresh",
21910 onRender : function(ct, position)
21912 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21913 this.navgroup.parentId = this.id;
21914 this.navgroup.onRender(this.el, null);
21915 // add the buttons to the navgroup
21917 if(this.displayInfo){
21918 Roo.log(this.el.select('ul.navbar-nav',true).first());
21919 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21920 this.displayEl = this.el.select('.x-paging-info', true).first();
21921 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21922 // this.displayEl = navel.el.select('span',true).first();
21928 Roo.each(_this.buttons, function(e){ // this might need to use render????
21929 Roo.factory(e).onRender(_this.el, null);
21933 Roo.each(_this.toolbarItems, function(e) {
21934 _this.navgroup.addItem(e);
21938 this.first = this.navgroup.addItem({
21939 tooltip: this.firstText,
21941 icon : 'fa fa-backward',
21943 preventDefault: true,
21944 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21947 this.prev = this.navgroup.addItem({
21948 tooltip: this.prevText,
21950 icon : 'fa fa-step-backward',
21952 preventDefault: true,
21953 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21955 //this.addSeparator();
21958 var field = this.navgroup.addItem( {
21960 cls : 'x-paging-position',
21962 html : this.beforePageText +
21963 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21964 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21967 this.field = field.el.select('input', true).first();
21968 this.field.on("keydown", this.onPagingKeydown, this);
21969 this.field.on("focus", function(){this.dom.select();});
21972 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21973 //this.field.setHeight(18);
21974 //this.addSeparator();
21975 this.next = this.navgroup.addItem({
21976 tooltip: this.nextText,
21978 html : ' <i class="fa fa-step-forward">',
21980 preventDefault: true,
21981 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21983 this.last = this.navgroup.addItem({
21984 tooltip: this.lastText,
21985 icon : 'fa fa-forward',
21988 preventDefault: true,
21989 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21991 //this.addSeparator();
21992 this.loading = this.navgroup.addItem({
21993 tooltip: this.refreshText,
21994 icon: 'fa fa-refresh',
21995 preventDefault: true,
21996 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22002 updateInfo : function(){
22003 if(this.displayEl){
22004 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22005 var msg = count == 0 ?
22009 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22011 this.displayEl.update(msg);
22016 onLoad : function(ds, r, o){
22017 this.cursor = o.params ? o.params.start : 0;
22018 var d = this.getPageData(),
22022 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22023 this.field.dom.value = ap;
22024 this.first.setDisabled(ap == 1);
22025 this.prev.setDisabled(ap == 1);
22026 this.next.setDisabled(ap == ps);
22027 this.last.setDisabled(ap == ps);
22028 this.loading.enable();
22033 getPageData : function(){
22034 var total = this.ds.getTotalCount();
22037 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22038 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22043 onLoadError : function(){
22044 this.loading.enable();
22048 onPagingKeydown : function(e){
22049 var k = e.getKey();
22050 var d = this.getPageData();
22052 var v = this.field.dom.value, pageNum;
22053 if(!v || isNaN(pageNum = parseInt(v, 10))){
22054 this.field.dom.value = d.activePage;
22057 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22058 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22061 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))
22063 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22064 this.field.dom.value = pageNum;
22065 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22068 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22070 var v = this.field.dom.value, pageNum;
22071 var increment = (e.shiftKey) ? 10 : 1;
22072 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22074 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22075 this.field.dom.value = d.activePage;
22078 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22080 this.field.dom.value = parseInt(v, 10) + increment;
22081 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22082 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22089 beforeLoad : function(){
22091 this.loading.disable();
22096 onClick : function(which){
22105 ds.load({params:{start: 0, limit: this.pageSize}});
22108 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22111 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22114 var total = ds.getTotalCount();
22115 var extra = total % this.pageSize;
22116 var lastStart = extra ? (total - extra) : total-this.pageSize;
22117 ds.load({params:{start: lastStart, limit: this.pageSize}});
22120 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22126 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22127 * @param {Roo.data.Store} store The data store to unbind
22129 unbind : function(ds){
22130 ds.un("beforeload", this.beforeLoad, this);
22131 ds.un("load", this.onLoad, this);
22132 ds.un("loadexception", this.onLoadError, this);
22133 ds.un("remove", this.updateInfo, this);
22134 ds.un("add", this.updateInfo, this);
22135 this.ds = undefined;
22139 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22140 * @param {Roo.data.Store} store The data store to bind
22142 bind : function(ds){
22143 ds.on("beforeload", this.beforeLoad, this);
22144 ds.on("load", this.onLoad, this);
22145 ds.on("loadexception", this.onLoadError, this);
22146 ds.on("remove", this.updateInfo, this);
22147 ds.on("add", this.updateInfo, this);
22158 * @class Roo.bootstrap.MessageBar
22159 * @extends Roo.bootstrap.Component
22160 * Bootstrap MessageBar class
22161 * @cfg {String} html contents of the MessageBar
22162 * @cfg {String} weight (info | success | warning | danger) default info
22163 * @cfg {String} beforeClass insert the bar before the given class
22164 * @cfg {Boolean} closable (true | false) default false
22165 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22168 * Create a new Element
22169 * @param {Object} config The config object
22172 Roo.bootstrap.MessageBar = function(config){
22173 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22176 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22182 beforeClass: 'bootstrap-sticky-wrap',
22184 getAutoCreate : function(){
22188 cls: 'alert alert-dismissable alert-' + this.weight,
22193 html: this.html || ''
22199 cfg.cls += ' alert-messages-fixed';
22213 onRender : function(ct, position)
22215 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22218 var cfg = Roo.apply({}, this.getAutoCreate());
22222 cfg.cls += ' ' + this.cls;
22225 cfg.style = this.style;
22227 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22229 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22232 this.el.select('>button.close').on('click', this.hide, this);
22238 if (!this.rendered) {
22244 this.fireEvent('show', this);
22250 if (!this.rendered) {
22256 this.fireEvent('hide', this);
22259 update : function()
22261 // var e = this.el.dom.firstChild;
22263 // if(this.closable){
22264 // e = e.nextSibling;
22267 // e.data = this.html || '';
22269 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22285 * @class Roo.bootstrap.Graph
22286 * @extends Roo.bootstrap.Component
22287 * Bootstrap Graph class
22291 @cfg {String} graphtype bar | vbar | pie
22292 @cfg {number} g_x coodinator | centre x (pie)
22293 @cfg {number} g_y coodinator | centre y (pie)
22294 @cfg {number} g_r radius (pie)
22295 @cfg {number} g_height height of the chart (respected by all elements in the set)
22296 @cfg {number} g_width width of the chart (respected by all elements in the set)
22297 @cfg {Object} title The title of the chart
22300 -opts (object) options for the chart
22302 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22303 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22305 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.
22306 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22308 o stretch (boolean)
22310 -opts (object) options for the pie
22313 o startAngle (number)
22314 o endAngle (number)
22318 * Create a new Input
22319 * @param {Object} config The config object
22322 Roo.bootstrap.Graph = function(config){
22323 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22329 * The img click event for the img.
22330 * @param {Roo.EventObject} e
22336 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22347 //g_colors: this.colors,
22354 getAutoCreate : function(){
22365 onRender : function(ct,position){
22366 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22367 this.raphael = Raphael(this.el.dom);
22369 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22370 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22371 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22372 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22374 r.text(160, 10, "Single Series Chart").attr(txtattr);
22375 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22376 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22377 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22379 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22380 r.barchart(330, 10, 300, 220, data1);
22381 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22382 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22385 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22386 // r.barchart(30, 30, 560, 250, xdata, {
22387 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22388 // axis : "0 0 1 1",
22389 // axisxlabels : xdata
22390 // //yvalues : cols,
22393 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22395 // this.load(null,xdata,{
22396 // axis : "0 0 1 1",
22397 // axisxlabels : xdata
22402 load : function(graphtype,xdata,opts){
22403 this.raphael.clear();
22405 graphtype = this.graphtype;
22410 var r = this.raphael,
22411 fin = function () {
22412 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22414 fout = function () {
22415 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22417 pfin = function() {
22418 this.sector.stop();
22419 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22422 this.label[0].stop();
22423 this.label[0].attr({ r: 7.5 });
22424 this.label[1].attr({ "font-weight": 800 });
22427 pfout = function() {
22428 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22431 this.label[0].animate({ r: 5 }, 500, "bounce");
22432 this.label[1].attr({ "font-weight": 400 });
22438 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22441 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22444 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22445 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22447 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22454 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22459 setTitle: function(o)
22464 initEvents: function() {
22467 this.el.on('click', this.onClick, this);
22471 onClick : function(e)
22473 Roo.log('img onclick');
22474 this.fireEvent('click', this, e);
22486 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22489 * @class Roo.bootstrap.dash.NumberBox
22490 * @extends Roo.bootstrap.Component
22491 * Bootstrap NumberBox class
22492 * @cfg {String} headline Box headline
22493 * @cfg {String} content Box content
22494 * @cfg {String} icon Box icon
22495 * @cfg {String} footer Footer text
22496 * @cfg {String} fhref Footer href
22499 * Create a new NumberBox
22500 * @param {Object} config The config object
22504 Roo.bootstrap.dash.NumberBox = function(config){
22505 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22509 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22518 getAutoCreate : function(){
22522 cls : 'small-box ',
22530 cls : 'roo-headline',
22531 html : this.headline
22535 cls : 'roo-content',
22536 html : this.content
22550 cls : 'ion ' + this.icon
22559 cls : 'small-box-footer',
22560 href : this.fhref || '#',
22564 cfg.cn.push(footer);
22571 onRender : function(ct,position){
22572 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22579 setHeadline: function (value)
22581 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22584 setFooter: function (value, href)
22586 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22589 this.el.select('a.small-box-footer',true).first().attr('href', href);
22594 setContent: function (value)
22596 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22599 initEvents: function()
22613 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22616 * @class Roo.bootstrap.dash.TabBox
22617 * @extends Roo.bootstrap.Component
22618 * Bootstrap TabBox class
22619 * @cfg {String} title Title of the TabBox
22620 * @cfg {String} icon Icon of the TabBox
22621 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22622 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22625 * Create a new TabBox
22626 * @param {Object} config The config object
22630 Roo.bootstrap.dash.TabBox = function(config){
22631 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22636 * When a pane is added
22637 * @param {Roo.bootstrap.dash.TabPane} pane
22641 * @event activatepane
22642 * When a pane is activated
22643 * @param {Roo.bootstrap.dash.TabPane} pane
22645 "activatepane" : true
22653 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22658 tabScrollable : false,
22660 getChildContainer : function()
22662 return this.el.select('.tab-content', true).first();
22665 getAutoCreate : function(){
22669 cls: 'pull-left header',
22677 cls: 'fa ' + this.icon
22683 cls: 'nav nav-tabs pull-right',
22689 if(this.tabScrollable){
22696 cls: 'nav nav-tabs pull-right',
22707 cls: 'nav-tabs-custom',
22712 cls: 'tab-content no-padding',
22720 initEvents : function()
22722 //Roo.log('add add pane handler');
22723 this.on('addpane', this.onAddPane, this);
22726 * Updates the box title
22727 * @param {String} html to set the title to.
22729 setTitle : function(value)
22731 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22733 onAddPane : function(pane)
22735 this.panes.push(pane);
22736 //Roo.log('addpane');
22738 // tabs are rendere left to right..
22739 if(!this.showtabs){
22743 var ctr = this.el.select('.nav-tabs', true).first();
22746 var existing = ctr.select('.nav-tab',true);
22747 var qty = existing.getCount();;
22750 var tab = ctr.createChild({
22752 cls : 'nav-tab' + (qty ? '' : ' active'),
22760 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22763 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22765 pane.el.addClass('active');
22770 onTabClick : function(ev,un,ob,pane)
22772 //Roo.log('tab - prev default');
22773 ev.preventDefault();
22776 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22777 pane.tab.addClass('active');
22778 //Roo.log(pane.title);
22779 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22780 // technically we should have a deactivate event.. but maybe add later.
22781 // and it should not de-activate the selected tab...
22782 this.fireEvent('activatepane', pane);
22783 pane.el.addClass('active');
22784 pane.fireEvent('activate');
22789 getActivePane : function()
22792 Roo.each(this.panes, function(p) {
22793 if(p.el.hasClass('active')){
22814 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22816 * @class Roo.bootstrap.TabPane
22817 * @extends Roo.bootstrap.Component
22818 * Bootstrap TabPane class
22819 * @cfg {Boolean} active (false | true) Default false
22820 * @cfg {String} title title of panel
22824 * Create a new TabPane
22825 * @param {Object} config The config object
22828 Roo.bootstrap.dash.TabPane = function(config){
22829 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22835 * When a pane is activated
22836 * @param {Roo.bootstrap.dash.TabPane} pane
22843 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22848 // the tabBox that this is attached to.
22851 getAutoCreate : function()
22859 cfg.cls += ' active';
22864 initEvents : function()
22866 //Roo.log('trigger add pane handler');
22867 this.parent().fireEvent('addpane', this)
22871 * Updates the tab title
22872 * @param {String} html to set the title to.
22874 setTitle: function(str)
22880 this.tab.select('a', true).first().dom.innerHTML = str;
22897 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22900 * @class Roo.bootstrap.menu.Menu
22901 * @extends Roo.bootstrap.Component
22902 * Bootstrap Menu class - container for Menu
22903 * @cfg {String} html Text of the menu
22904 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22905 * @cfg {String} icon Font awesome icon
22906 * @cfg {String} pos Menu align to (top | bottom) default bottom
22910 * Create a new Menu
22911 * @param {Object} config The config object
22915 Roo.bootstrap.menu.Menu = function(config){
22916 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22920 * @event beforeshow
22921 * Fires before this menu is displayed
22922 * @param {Roo.bootstrap.menu.Menu} this
22926 * @event beforehide
22927 * Fires before this menu is hidden
22928 * @param {Roo.bootstrap.menu.Menu} this
22933 * Fires after this menu is displayed
22934 * @param {Roo.bootstrap.menu.Menu} this
22939 * Fires after this menu is hidden
22940 * @param {Roo.bootstrap.menu.Menu} this
22945 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22946 * @param {Roo.bootstrap.menu.Menu} this
22947 * @param {Roo.EventObject} e
22954 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22958 weight : 'default',
22963 getChildContainer : function() {
22964 if(this.isSubMenu){
22968 return this.el.select('ul.dropdown-menu', true).first();
22971 getAutoCreate : function()
22976 cls : 'roo-menu-text',
22984 cls : 'fa ' + this.icon
22995 cls : 'dropdown-button btn btn-' + this.weight,
23000 cls : 'dropdown-toggle btn btn-' + this.weight,
23010 cls : 'dropdown-menu'
23016 if(this.pos == 'top'){
23017 cfg.cls += ' dropup';
23020 if(this.isSubMenu){
23023 cls : 'dropdown-menu'
23030 onRender : function(ct, position)
23032 this.isSubMenu = ct.hasClass('dropdown-submenu');
23034 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23037 initEvents : function()
23039 if(this.isSubMenu){
23043 this.hidden = true;
23045 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23046 this.triggerEl.on('click', this.onTriggerPress, this);
23048 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23049 this.buttonEl.on('click', this.onClick, this);
23055 if(this.isSubMenu){
23059 return this.el.select('ul.dropdown-menu', true).first();
23062 onClick : function(e)
23064 this.fireEvent("click", this, e);
23067 onTriggerPress : function(e)
23069 if (this.isVisible()) {
23076 isVisible : function(){
23077 return !this.hidden;
23082 this.fireEvent("beforeshow", this);
23084 this.hidden = false;
23085 this.el.addClass('open');
23087 Roo.get(document).on("mouseup", this.onMouseUp, this);
23089 this.fireEvent("show", this);
23096 this.fireEvent("beforehide", this);
23098 this.hidden = true;
23099 this.el.removeClass('open');
23101 Roo.get(document).un("mouseup", this.onMouseUp);
23103 this.fireEvent("hide", this);
23106 onMouseUp : function()
23120 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23123 * @class Roo.bootstrap.menu.Item
23124 * @extends Roo.bootstrap.Component
23125 * Bootstrap MenuItem class
23126 * @cfg {Boolean} submenu (true | false) default false
23127 * @cfg {String} html text of the item
23128 * @cfg {String} href the link
23129 * @cfg {Boolean} disable (true | false) default false
23130 * @cfg {Boolean} preventDefault (true | false) default true
23131 * @cfg {String} icon Font awesome icon
23132 * @cfg {String} pos Submenu align to (left | right) default right
23136 * Create a new Item
23137 * @param {Object} config The config object
23141 Roo.bootstrap.menu.Item = function(config){
23142 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23146 * Fires when the mouse is hovering over this menu
23147 * @param {Roo.bootstrap.menu.Item} this
23148 * @param {Roo.EventObject} e
23153 * Fires when the mouse exits this menu
23154 * @param {Roo.bootstrap.menu.Item} this
23155 * @param {Roo.EventObject} e
23161 * The raw click event for the entire grid.
23162 * @param {Roo.EventObject} e
23168 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23173 preventDefault: true,
23178 getAutoCreate : function()
23183 cls : 'roo-menu-item-text',
23191 cls : 'fa ' + this.icon
23200 href : this.href || '#',
23207 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23211 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23213 if(this.pos == 'left'){
23214 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23221 initEvents : function()
23223 this.el.on('mouseover', this.onMouseOver, this);
23224 this.el.on('mouseout', this.onMouseOut, this);
23226 this.el.select('a', true).first().on('click', this.onClick, this);
23230 onClick : function(e)
23232 if(this.preventDefault){
23233 e.preventDefault();
23236 this.fireEvent("click", this, e);
23239 onMouseOver : function(e)
23241 if(this.submenu && this.pos == 'left'){
23242 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23245 this.fireEvent("mouseover", this, e);
23248 onMouseOut : function(e)
23250 this.fireEvent("mouseout", this, e);
23262 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23265 * @class Roo.bootstrap.menu.Separator
23266 * @extends Roo.bootstrap.Component
23267 * Bootstrap Separator class
23270 * Create a new Separator
23271 * @param {Object} config The config object
23275 Roo.bootstrap.menu.Separator = function(config){
23276 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23279 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23281 getAutoCreate : function(){
23302 * @class Roo.bootstrap.Tooltip
23303 * Bootstrap Tooltip class
23304 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23305 * to determine which dom element triggers the tooltip.
23307 * It needs to add support for additional attributes like tooltip-position
23310 * Create a new Toolti
23311 * @param {Object} config The config object
23314 Roo.bootstrap.Tooltip = function(config){
23315 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23318 Roo.apply(Roo.bootstrap.Tooltip, {
23320 * @function init initialize tooltip monitoring.
23324 currentTip : false,
23325 currentRegion : false,
23331 Roo.get(document).on('mouseover', this.enter ,this);
23332 Roo.get(document).on('mouseout', this.leave, this);
23335 this.currentTip = new Roo.bootstrap.Tooltip();
23338 enter : function(ev)
23340 var dom = ev.getTarget();
23342 //Roo.log(['enter',dom]);
23343 var el = Roo.fly(dom);
23344 if (this.currentEl) {
23346 //Roo.log(this.currentEl);
23347 //Roo.log(this.currentEl.contains(dom));
23348 if (this.currentEl == el) {
23351 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23357 if (this.currentTip.el) {
23358 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23363 // you can not look for children, as if el is the body.. then everythign is the child..
23364 if (!el.attr('tooltip')) { //
23365 if (!el.select("[tooltip]").elements.length) {
23368 // is the mouse over this child...?
23369 bindEl = el.select("[tooltip]").first();
23370 var xy = ev.getXY();
23371 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23372 //Roo.log("not in region.");
23375 //Roo.log("child element over..");
23378 this.currentEl = bindEl;
23379 this.currentTip.bind(bindEl);
23380 this.currentRegion = Roo.lib.Region.getRegion(dom);
23381 this.currentTip.enter();
23384 leave : function(ev)
23386 var dom = ev.getTarget();
23387 //Roo.log(['leave',dom]);
23388 if (!this.currentEl) {
23393 if (dom != this.currentEl.dom) {
23396 var xy = ev.getXY();
23397 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23400 // only activate leave if mouse cursor is outside... bounding box..
23405 if (this.currentTip) {
23406 this.currentTip.leave();
23408 //Roo.log('clear currentEl');
23409 this.currentEl = false;
23414 'left' : ['r-l', [-2,0], 'right'],
23415 'right' : ['l-r', [2,0], 'left'],
23416 'bottom' : ['t-b', [0,2], 'top'],
23417 'top' : [ 'b-t', [0,-2], 'bottom']
23423 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23428 delay : null, // can be { show : 300 , hide: 500}
23432 hoverState : null, //???
23434 placement : 'bottom',
23436 getAutoCreate : function(){
23443 cls : 'tooltip-arrow'
23446 cls : 'tooltip-inner'
23453 bind : function(el)
23459 enter : function () {
23461 if (this.timeout != null) {
23462 clearTimeout(this.timeout);
23465 this.hoverState = 'in';
23466 //Roo.log("enter - show");
23467 if (!this.delay || !this.delay.show) {
23472 this.timeout = setTimeout(function () {
23473 if (_t.hoverState == 'in') {
23476 }, this.delay.show);
23480 clearTimeout(this.timeout);
23482 this.hoverState = 'out';
23483 if (!this.delay || !this.delay.hide) {
23489 this.timeout = setTimeout(function () {
23490 //Roo.log("leave - timeout");
23492 if (_t.hoverState == 'out') {
23494 Roo.bootstrap.Tooltip.currentEl = false;
23502 this.render(document.body);
23505 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23507 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23509 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23511 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23513 var placement = typeof this.placement == 'function' ?
23514 this.placement.call(this, this.el, on_el) :
23517 var autoToken = /\s?auto?\s?/i;
23518 var autoPlace = autoToken.test(placement);
23520 placement = placement.replace(autoToken, '') || 'top';
23524 //this.el.setXY([0,0]);
23526 //this.el.dom.style.display='block';
23528 //this.el.appendTo(on_el);
23530 var p = this.getPosition();
23531 var box = this.el.getBox();
23537 var align = Roo.bootstrap.Tooltip.alignment[placement];
23539 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23541 if(placement == 'top' || placement == 'bottom'){
23543 placement = 'right';
23546 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23547 placement = 'left';
23551 align = Roo.bootstrap.Tooltip.alignment[placement];
23553 this.el.alignTo(this.bindEl, align[0],align[1]);
23554 //var arrow = this.el.select('.arrow',true).first();
23555 //arrow.set(align[2],
23557 this.el.addClass(placement);
23559 this.el.addClass('in fade');
23561 this.hoverState = null;
23563 if (this.el.hasClass('fade')) {
23574 //this.el.setXY([0,0]);
23575 this.el.removeClass('in');
23591 * @class Roo.bootstrap.LocationPicker
23592 * @extends Roo.bootstrap.Component
23593 * Bootstrap LocationPicker class
23594 * @cfg {Number} latitude Position when init default 0
23595 * @cfg {Number} longitude Position when init default 0
23596 * @cfg {Number} zoom default 15
23597 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23598 * @cfg {Boolean} mapTypeControl default false
23599 * @cfg {Boolean} disableDoubleClickZoom default false
23600 * @cfg {Boolean} scrollwheel default true
23601 * @cfg {Boolean} streetViewControl default false
23602 * @cfg {Number} radius default 0
23603 * @cfg {String} locationName
23604 * @cfg {Boolean} draggable default true
23605 * @cfg {Boolean} enableAutocomplete default false
23606 * @cfg {Boolean} enableReverseGeocode default true
23607 * @cfg {String} markerTitle
23610 * Create a new LocationPicker
23611 * @param {Object} config The config object
23615 Roo.bootstrap.LocationPicker = function(config){
23617 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23622 * Fires when the picker initialized.
23623 * @param {Roo.bootstrap.LocationPicker} this
23624 * @param {Google Location} location
23628 * @event positionchanged
23629 * Fires when the picker position changed.
23630 * @param {Roo.bootstrap.LocationPicker} this
23631 * @param {Google Location} location
23633 positionchanged : true,
23636 * Fires when the map resize.
23637 * @param {Roo.bootstrap.LocationPicker} this
23642 * Fires when the map show.
23643 * @param {Roo.bootstrap.LocationPicker} this
23648 * Fires when the map hide.
23649 * @param {Roo.bootstrap.LocationPicker} this
23654 * Fires when click the map.
23655 * @param {Roo.bootstrap.LocationPicker} this
23656 * @param {Map event} e
23660 * @event mapRightClick
23661 * Fires when right click the map.
23662 * @param {Roo.bootstrap.LocationPicker} this
23663 * @param {Map event} e
23665 mapRightClick : true,
23667 * @event markerClick
23668 * Fires when click the marker.
23669 * @param {Roo.bootstrap.LocationPicker} this
23670 * @param {Map event} e
23672 markerClick : true,
23674 * @event markerRightClick
23675 * Fires when right click the marker.
23676 * @param {Roo.bootstrap.LocationPicker} this
23677 * @param {Map event} e
23679 markerRightClick : true,
23681 * @event OverlayViewDraw
23682 * Fires when OverlayView Draw
23683 * @param {Roo.bootstrap.LocationPicker} this
23685 OverlayViewDraw : true,
23687 * @event OverlayViewOnAdd
23688 * Fires when OverlayView Draw
23689 * @param {Roo.bootstrap.LocationPicker} this
23691 OverlayViewOnAdd : true,
23693 * @event OverlayViewOnRemove
23694 * Fires when OverlayView Draw
23695 * @param {Roo.bootstrap.LocationPicker} this
23697 OverlayViewOnRemove : true,
23699 * @event OverlayViewShow
23700 * Fires when OverlayView Draw
23701 * @param {Roo.bootstrap.LocationPicker} this
23702 * @param {Pixel} cpx
23704 OverlayViewShow : true,
23706 * @event OverlayViewHide
23707 * Fires when OverlayView Draw
23708 * @param {Roo.bootstrap.LocationPicker} this
23710 OverlayViewHide : true,
23712 * @event loadexception
23713 * Fires when load google lib failed.
23714 * @param {Roo.bootstrap.LocationPicker} this
23716 loadexception : true
23721 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23723 gMapContext: false,
23729 mapTypeControl: false,
23730 disableDoubleClickZoom: false,
23732 streetViewControl: false,
23736 enableAutocomplete: false,
23737 enableReverseGeocode: true,
23740 getAutoCreate: function()
23745 cls: 'roo-location-picker'
23751 initEvents: function(ct, position)
23753 if(!this.el.getWidth() || this.isApplied()){
23757 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23762 initial: function()
23764 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23765 this.fireEvent('loadexception', this);
23769 if(!this.mapTypeId){
23770 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23773 this.gMapContext = this.GMapContext();
23775 this.initOverlayView();
23777 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23781 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23782 _this.setPosition(_this.gMapContext.marker.position);
23785 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23786 _this.fireEvent('mapClick', this, event);
23790 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23791 _this.fireEvent('mapRightClick', this, event);
23795 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23796 _this.fireEvent('markerClick', this, event);
23800 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23801 _this.fireEvent('markerRightClick', this, event);
23805 this.setPosition(this.gMapContext.location);
23807 this.fireEvent('initial', this, this.gMapContext.location);
23810 initOverlayView: function()
23814 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23818 _this.fireEvent('OverlayViewDraw', _this);
23823 _this.fireEvent('OverlayViewOnAdd', _this);
23826 onRemove: function()
23828 _this.fireEvent('OverlayViewOnRemove', _this);
23831 show: function(cpx)
23833 _this.fireEvent('OverlayViewShow', _this, cpx);
23838 _this.fireEvent('OverlayViewHide', _this);
23844 fromLatLngToContainerPixel: function(event)
23846 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23849 isApplied: function()
23851 return this.getGmapContext() == false ? false : true;
23854 getGmapContext: function()
23856 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23859 GMapContext: function()
23861 var position = new google.maps.LatLng(this.latitude, this.longitude);
23863 var _map = new google.maps.Map(this.el.dom, {
23866 mapTypeId: this.mapTypeId,
23867 mapTypeControl: this.mapTypeControl,
23868 disableDoubleClickZoom: this.disableDoubleClickZoom,
23869 scrollwheel: this.scrollwheel,
23870 streetViewControl: this.streetViewControl,
23871 locationName: this.locationName,
23872 draggable: this.draggable,
23873 enableAutocomplete: this.enableAutocomplete,
23874 enableReverseGeocode: this.enableReverseGeocode
23877 var _marker = new google.maps.Marker({
23878 position: position,
23880 title: this.markerTitle,
23881 draggable: this.draggable
23888 location: position,
23889 radius: this.radius,
23890 locationName: this.locationName,
23891 addressComponents: {
23892 formatted_address: null,
23893 addressLine1: null,
23894 addressLine2: null,
23896 streetNumber: null,
23900 stateOrProvince: null
23903 domContainer: this.el.dom,
23904 geodecoder: new google.maps.Geocoder()
23908 drawCircle: function(center, radius, options)
23910 if (this.gMapContext.circle != null) {
23911 this.gMapContext.circle.setMap(null);
23915 options = Roo.apply({}, options, {
23916 strokeColor: "#0000FF",
23917 strokeOpacity: .35,
23919 fillColor: "#0000FF",
23923 options.map = this.gMapContext.map;
23924 options.radius = radius;
23925 options.center = center;
23926 this.gMapContext.circle = new google.maps.Circle(options);
23927 return this.gMapContext.circle;
23933 setPosition: function(location)
23935 this.gMapContext.location = location;
23936 this.gMapContext.marker.setPosition(location);
23937 this.gMapContext.map.panTo(location);
23938 this.drawCircle(location, this.gMapContext.radius, {});
23942 if (this.gMapContext.settings.enableReverseGeocode) {
23943 this.gMapContext.geodecoder.geocode({
23944 latLng: this.gMapContext.location
23945 }, function(results, status) {
23947 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23948 _this.gMapContext.locationName = results[0].formatted_address;
23949 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23951 _this.fireEvent('positionchanged', this, location);
23958 this.fireEvent('positionchanged', this, location);
23963 google.maps.event.trigger(this.gMapContext.map, "resize");
23965 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23967 this.fireEvent('resize', this);
23970 setPositionByLatLng: function(latitude, longitude)
23972 this.setPosition(new google.maps.LatLng(latitude, longitude));
23975 getCurrentPosition: function()
23978 latitude: this.gMapContext.location.lat(),
23979 longitude: this.gMapContext.location.lng()
23983 getAddressName: function()
23985 return this.gMapContext.locationName;
23988 getAddressComponents: function()
23990 return this.gMapContext.addressComponents;
23993 address_component_from_google_geocode: function(address_components)
23997 for (var i = 0; i < address_components.length; i++) {
23998 var component = address_components[i];
23999 if (component.types.indexOf("postal_code") >= 0) {
24000 result.postalCode = component.short_name;
24001 } else if (component.types.indexOf("street_number") >= 0) {
24002 result.streetNumber = component.short_name;
24003 } else if (component.types.indexOf("route") >= 0) {
24004 result.streetName = component.short_name;
24005 } else if (component.types.indexOf("neighborhood") >= 0) {
24006 result.city = component.short_name;
24007 } else if (component.types.indexOf("locality") >= 0) {
24008 result.city = component.short_name;
24009 } else if (component.types.indexOf("sublocality") >= 0) {
24010 result.district = component.short_name;
24011 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24012 result.stateOrProvince = component.short_name;
24013 } else if (component.types.indexOf("country") >= 0) {
24014 result.country = component.short_name;
24018 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24019 result.addressLine2 = "";
24023 setZoomLevel: function(zoom)
24025 this.gMapContext.map.setZoom(zoom);
24038 this.fireEvent('show', this);
24049 this.fireEvent('hide', this);
24054 Roo.apply(Roo.bootstrap.LocationPicker, {
24056 OverlayView : function(map, options)
24058 options = options || {};
24072 * @class Roo.bootstrap.Alert
24073 * @extends Roo.bootstrap.Component
24074 * Bootstrap Alert class
24075 * @cfg {String} title The title of alert
24076 * @cfg {String} html The content of alert
24077 * @cfg {String} weight ( success | info | warning | danger )
24078 * @cfg {String} faicon font-awesomeicon
24081 * Create a new alert
24082 * @param {Object} config The config object
24086 Roo.bootstrap.Alert = function(config){
24087 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24091 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24098 getAutoCreate : function()
24107 cls : 'roo-alert-icon'
24112 cls : 'roo-alert-title',
24117 cls : 'roo-alert-text',
24124 cfg.cn[0].cls += ' fa ' + this.faicon;
24128 cfg.cls += ' alert-' + this.weight;
24134 initEvents: function()
24136 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24139 setTitle : function(str)
24141 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24144 setText : function(str)
24146 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24149 setWeight : function(weight)
24152 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24155 this.weight = weight;
24157 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24160 setIcon : function(icon)
24163 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24166 this.faicon = icon;
24168 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24189 * @class Roo.bootstrap.UploadCropbox
24190 * @extends Roo.bootstrap.Component
24191 * Bootstrap UploadCropbox class
24192 * @cfg {String} emptyText show when image has been loaded
24193 * @cfg {String} rotateNotify show when image too small to rotate
24194 * @cfg {Number} errorTimeout default 3000
24195 * @cfg {Number} minWidth default 300
24196 * @cfg {Number} minHeight default 300
24197 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24198 * @cfg {Boolean} isDocument (true|false) default false
24199 * @cfg {String} url action url
24200 * @cfg {String} paramName default 'imageUpload'
24201 * @cfg {String} method default POST
24202 * @cfg {Boolean} loadMask (true|false) default true
24203 * @cfg {Boolean} loadingText default 'Loading...'
24206 * Create a new UploadCropbox
24207 * @param {Object} config The config object
24210 Roo.bootstrap.UploadCropbox = function(config){
24211 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24215 * @event beforeselectfile
24216 * Fire before select file
24217 * @param {Roo.bootstrap.UploadCropbox} this
24219 "beforeselectfile" : true,
24222 * Fire after initEvent
24223 * @param {Roo.bootstrap.UploadCropbox} this
24228 * Fire after initEvent
24229 * @param {Roo.bootstrap.UploadCropbox} this
24230 * @param {String} data
24235 * Fire when preparing the file data
24236 * @param {Roo.bootstrap.UploadCropbox} this
24237 * @param {Object} file
24242 * Fire when get exception
24243 * @param {Roo.bootstrap.UploadCropbox} this
24244 * @param {XMLHttpRequest} xhr
24246 "exception" : true,
24248 * @event beforeloadcanvas
24249 * Fire before load the canvas
24250 * @param {Roo.bootstrap.UploadCropbox} this
24251 * @param {String} src
24253 "beforeloadcanvas" : true,
24256 * Fire when trash image
24257 * @param {Roo.bootstrap.UploadCropbox} this
24262 * Fire when download the image
24263 * @param {Roo.bootstrap.UploadCropbox} this
24267 * @event footerbuttonclick
24268 * Fire when footerbuttonclick
24269 * @param {Roo.bootstrap.UploadCropbox} this
24270 * @param {String} type
24272 "footerbuttonclick" : true,
24276 * @param {Roo.bootstrap.UploadCropbox} this
24281 * Fire when rotate the image
24282 * @param {Roo.bootstrap.UploadCropbox} this
24283 * @param {String} pos
24288 * Fire when inspect the file
24289 * @param {Roo.bootstrap.UploadCropbox} this
24290 * @param {Object} file
24295 * Fire when xhr upload the file
24296 * @param {Roo.bootstrap.UploadCropbox} this
24297 * @param {Object} data
24302 * Fire when arrange the file data
24303 * @param {Roo.bootstrap.UploadCropbox} this
24304 * @param {Object} formData
24309 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24312 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24314 emptyText : 'Click to upload image',
24315 rotateNotify : 'Image is too small to rotate',
24316 errorTimeout : 3000,
24330 cropType : 'image/jpeg',
24332 canvasLoaded : false,
24333 isDocument : false,
24335 paramName : 'imageUpload',
24337 loadingText : 'Loading...',
24340 getAutoCreate : function()
24344 cls : 'roo-upload-cropbox',
24348 cls : 'roo-upload-cropbox-selector',
24353 cls : 'roo-upload-cropbox-body',
24354 style : 'cursor:pointer',
24358 cls : 'roo-upload-cropbox-preview'
24362 cls : 'roo-upload-cropbox-thumb'
24366 cls : 'roo-upload-cropbox-empty-notify',
24367 html : this.emptyText
24371 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24372 html : this.rotateNotify
24378 cls : 'roo-upload-cropbox-footer',
24381 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24391 onRender : function(ct, position)
24393 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24395 if (this.buttons.length) {
24397 Roo.each(this.buttons, function(bb) {
24399 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24401 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24407 this.maskEl = this.el;
24411 initEvents : function()
24413 this.urlAPI = (window.createObjectURL && window) ||
24414 (window.URL && URL.revokeObjectURL && URL) ||
24415 (window.webkitURL && webkitURL);
24417 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24418 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24420 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24421 this.selectorEl.hide();
24423 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24424 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24426 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24427 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24428 this.thumbEl.hide();
24430 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24431 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24433 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24434 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24435 this.errorEl.hide();
24437 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24438 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24439 this.footerEl.hide();
24441 this.setThumbBoxSize();
24447 this.fireEvent('initial', this);
24454 window.addEventListener("resize", function() { _this.resize(); } );
24456 this.bodyEl.on('click', this.beforeSelectFile, this);
24459 this.bodyEl.on('touchstart', this.onTouchStart, this);
24460 this.bodyEl.on('touchmove', this.onTouchMove, this);
24461 this.bodyEl.on('touchend', this.onTouchEnd, this);
24465 this.bodyEl.on('mousedown', this.onMouseDown, this);
24466 this.bodyEl.on('mousemove', this.onMouseMove, this);
24467 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24468 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24469 Roo.get(document).on('mouseup', this.onMouseUp, this);
24472 this.selectorEl.on('change', this.onFileSelected, this);
24478 this.baseScale = 1;
24480 this.baseRotate = 1;
24481 this.dragable = false;
24482 this.pinching = false;
24485 this.cropData = false;
24486 this.notifyEl.dom.innerHTML = this.emptyText;
24488 this.selectorEl.dom.value = '';
24492 resize : function()
24494 if(this.fireEvent('resize', this) != false){
24495 this.setThumbBoxPosition();
24496 this.setCanvasPosition();
24500 onFooterButtonClick : function(e, el, o, type)
24503 case 'rotate-left' :
24504 this.onRotateLeft(e);
24506 case 'rotate-right' :
24507 this.onRotateRight(e);
24510 this.beforeSelectFile(e);
24525 this.fireEvent('footerbuttonclick', this, type);
24528 beforeSelectFile : function(e)
24530 e.preventDefault();
24532 if(this.fireEvent('beforeselectfile', this) != false){
24533 this.selectorEl.dom.click();
24537 onFileSelected : function(e)
24539 e.preventDefault();
24541 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24545 var file = this.selectorEl.dom.files[0];
24547 if(this.fireEvent('inspect', this, file) != false){
24548 this.prepare(file);
24553 trash : function(e)
24555 this.fireEvent('trash', this);
24558 download : function(e)
24560 this.fireEvent('download', this);
24563 loadCanvas : function(src)
24565 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24569 this.imageEl = document.createElement('img');
24573 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24575 this.imageEl.src = src;
24579 onLoadCanvas : function()
24581 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24582 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24584 this.bodyEl.un('click', this.beforeSelectFile, this);
24586 this.notifyEl.hide();
24587 this.thumbEl.show();
24588 this.footerEl.show();
24590 this.baseRotateLevel();
24592 if(this.isDocument){
24593 this.setThumbBoxSize();
24596 this.setThumbBoxPosition();
24598 this.baseScaleLevel();
24604 this.canvasLoaded = true;
24607 this.maskEl.unmask();
24612 setCanvasPosition : function()
24614 if(!this.canvasEl){
24618 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24619 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24621 this.previewEl.setLeft(pw);
24622 this.previewEl.setTop(ph);
24626 onMouseDown : function(e)
24630 this.dragable = true;
24631 this.pinching = false;
24633 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24634 this.dragable = false;
24638 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24639 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24643 onMouseMove : function(e)
24647 if(!this.canvasLoaded){
24651 if (!this.dragable){
24655 var minX = Math.ceil(this.thumbEl.getLeft(true));
24656 var minY = Math.ceil(this.thumbEl.getTop(true));
24658 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24659 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24661 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24662 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24664 x = x - this.mouseX;
24665 y = y - this.mouseY;
24667 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24668 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24670 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24671 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24673 this.previewEl.setLeft(bgX);
24674 this.previewEl.setTop(bgY);
24676 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24677 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24680 onMouseUp : function(e)
24684 this.dragable = false;
24687 onMouseWheel : function(e)
24691 this.startScale = this.scale;
24693 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24695 if(!this.zoomable()){
24696 this.scale = this.startScale;
24705 zoomable : function()
24707 var minScale = this.thumbEl.getWidth() / this.minWidth;
24709 if(this.minWidth < this.minHeight){
24710 minScale = this.thumbEl.getHeight() / this.minHeight;
24713 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24714 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24718 (this.rotate == 0 || this.rotate == 180) &&
24720 width > this.imageEl.OriginWidth ||
24721 height > this.imageEl.OriginHeight ||
24722 (width < this.minWidth && height < this.minHeight)
24730 (this.rotate == 90 || this.rotate == 270) &&
24732 width > this.imageEl.OriginWidth ||
24733 height > this.imageEl.OriginHeight ||
24734 (width < this.minHeight && height < this.minWidth)
24741 !this.isDocument &&
24742 (this.rotate == 0 || this.rotate == 180) &&
24744 width < this.minWidth ||
24745 width > this.imageEl.OriginWidth ||
24746 height < this.minHeight ||
24747 height > this.imageEl.OriginHeight
24754 !this.isDocument &&
24755 (this.rotate == 90 || this.rotate == 270) &&
24757 width < this.minHeight ||
24758 width > this.imageEl.OriginWidth ||
24759 height < this.minWidth ||
24760 height > this.imageEl.OriginHeight
24770 onRotateLeft : function(e)
24772 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24774 var minScale = this.thumbEl.getWidth() / this.minWidth;
24776 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24777 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24779 this.startScale = this.scale;
24781 while (this.getScaleLevel() < minScale){
24783 this.scale = this.scale + 1;
24785 if(!this.zoomable()){
24790 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24791 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24796 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24803 this.scale = this.startScale;
24805 this.onRotateFail();
24810 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24812 if(this.isDocument){
24813 this.setThumbBoxSize();
24814 this.setThumbBoxPosition();
24815 this.setCanvasPosition();
24820 this.fireEvent('rotate', this, 'left');
24824 onRotateRight : function(e)
24826 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24828 var minScale = this.thumbEl.getWidth() / this.minWidth;
24830 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24831 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24833 this.startScale = this.scale;
24835 while (this.getScaleLevel() < minScale){
24837 this.scale = this.scale + 1;
24839 if(!this.zoomable()){
24844 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24845 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24850 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24857 this.scale = this.startScale;
24859 this.onRotateFail();
24864 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24866 if(this.isDocument){
24867 this.setThumbBoxSize();
24868 this.setThumbBoxPosition();
24869 this.setCanvasPosition();
24874 this.fireEvent('rotate', this, 'right');
24877 onRotateFail : function()
24879 this.errorEl.show(true);
24883 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24888 this.previewEl.dom.innerHTML = '';
24890 var canvasEl = document.createElement("canvas");
24892 var contextEl = canvasEl.getContext("2d");
24894 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24895 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24896 var center = this.imageEl.OriginWidth / 2;
24898 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24899 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24900 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24901 center = this.imageEl.OriginHeight / 2;
24904 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24906 contextEl.translate(center, center);
24907 contextEl.rotate(this.rotate * Math.PI / 180);
24909 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24911 this.canvasEl = document.createElement("canvas");
24913 this.contextEl = this.canvasEl.getContext("2d");
24915 switch (this.rotate) {
24918 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24919 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24921 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24926 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24927 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24929 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24930 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24934 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24939 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24940 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24942 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24943 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);
24947 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);
24952 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24953 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24955 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24956 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24960 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);
24967 this.previewEl.appendChild(this.canvasEl);
24969 this.setCanvasPosition();
24974 if(!this.canvasLoaded){
24978 var imageCanvas = document.createElement("canvas");
24980 var imageContext = imageCanvas.getContext("2d");
24982 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24983 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24985 var center = imageCanvas.width / 2;
24987 imageContext.translate(center, center);
24989 imageContext.rotate(this.rotate * Math.PI / 180);
24991 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24993 var canvas = document.createElement("canvas");
24995 var context = canvas.getContext("2d");
24997 canvas.width = this.minWidth;
24998 canvas.height = this.minHeight;
25000 switch (this.rotate) {
25003 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25004 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25006 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25007 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25009 var targetWidth = this.minWidth - 2 * x;
25010 var targetHeight = this.minHeight - 2 * y;
25014 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25015 scale = targetWidth / width;
25018 if(x > 0 && y == 0){
25019 scale = targetHeight / height;
25022 if(x > 0 && y > 0){
25023 scale = targetWidth / width;
25025 if(width < height){
25026 scale = targetHeight / height;
25030 context.scale(scale, scale);
25032 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25033 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25035 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25036 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25038 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25043 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25044 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25046 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25047 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25049 var targetWidth = this.minWidth - 2 * x;
25050 var targetHeight = this.minHeight - 2 * y;
25054 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25055 scale = targetWidth / width;
25058 if(x > 0 && y == 0){
25059 scale = targetHeight / height;
25062 if(x > 0 && y > 0){
25063 scale = targetWidth / width;
25065 if(width < height){
25066 scale = targetHeight / height;
25070 context.scale(scale, scale);
25072 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25073 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25075 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25076 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25078 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25080 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25085 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25086 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25088 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25089 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25091 var targetWidth = this.minWidth - 2 * x;
25092 var targetHeight = this.minHeight - 2 * y;
25096 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25097 scale = targetWidth / width;
25100 if(x > 0 && y == 0){
25101 scale = targetHeight / height;
25104 if(x > 0 && y > 0){
25105 scale = targetWidth / width;
25107 if(width < height){
25108 scale = targetHeight / height;
25112 context.scale(scale, scale);
25114 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25115 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25117 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25118 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25120 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25121 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25123 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25128 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25129 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25131 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25132 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25134 var targetWidth = this.minWidth - 2 * x;
25135 var targetHeight = this.minHeight - 2 * y;
25139 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25140 scale = targetWidth / width;
25143 if(x > 0 && y == 0){
25144 scale = targetHeight / height;
25147 if(x > 0 && y > 0){
25148 scale = targetWidth / width;
25150 if(width < height){
25151 scale = targetHeight / height;
25155 context.scale(scale, scale);
25157 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25158 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25160 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25161 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25163 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25165 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25172 this.cropData = canvas.toDataURL(this.cropType);
25174 if(this.fireEvent('crop', this, this.cropData) !== false){
25175 this.process(this.file, this.cropData);
25182 setThumbBoxSize : function()
25186 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25187 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25188 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25190 this.minWidth = width;
25191 this.minHeight = height;
25193 if(this.rotate == 90 || this.rotate == 270){
25194 this.minWidth = height;
25195 this.minHeight = width;
25200 width = Math.ceil(this.minWidth * height / this.minHeight);
25202 if(this.minWidth > this.minHeight){
25204 height = Math.ceil(this.minHeight * width / this.minWidth);
25207 this.thumbEl.setStyle({
25208 width : width + 'px',
25209 height : height + 'px'
25216 setThumbBoxPosition : function()
25218 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25219 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25221 this.thumbEl.setLeft(x);
25222 this.thumbEl.setTop(y);
25226 baseRotateLevel : function()
25228 this.baseRotate = 1;
25231 typeof(this.exif) != 'undefined' &&
25232 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25233 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25235 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25238 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25242 baseScaleLevel : function()
25246 if(this.isDocument){
25248 if(this.baseRotate == 6 || this.baseRotate == 8){
25250 height = this.thumbEl.getHeight();
25251 this.baseScale = height / this.imageEl.OriginWidth;
25253 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25254 width = this.thumbEl.getWidth();
25255 this.baseScale = width / this.imageEl.OriginHeight;
25261 height = this.thumbEl.getHeight();
25262 this.baseScale = height / this.imageEl.OriginHeight;
25264 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25265 width = this.thumbEl.getWidth();
25266 this.baseScale = width / this.imageEl.OriginWidth;
25272 if(this.baseRotate == 6 || this.baseRotate == 8){
25274 width = this.thumbEl.getHeight();
25275 this.baseScale = width / this.imageEl.OriginHeight;
25277 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25278 height = this.thumbEl.getWidth();
25279 this.baseScale = height / this.imageEl.OriginHeight;
25282 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25283 height = this.thumbEl.getWidth();
25284 this.baseScale = height / this.imageEl.OriginHeight;
25286 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25287 width = this.thumbEl.getHeight();
25288 this.baseScale = width / this.imageEl.OriginWidth;
25295 width = this.thumbEl.getWidth();
25296 this.baseScale = width / this.imageEl.OriginWidth;
25298 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25299 height = this.thumbEl.getHeight();
25300 this.baseScale = height / this.imageEl.OriginHeight;
25303 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25305 height = this.thumbEl.getHeight();
25306 this.baseScale = height / this.imageEl.OriginHeight;
25308 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25309 width = this.thumbEl.getWidth();
25310 this.baseScale = width / this.imageEl.OriginWidth;
25318 getScaleLevel : function()
25320 return this.baseScale * Math.pow(1.1, this.scale);
25323 onTouchStart : function(e)
25325 if(!this.canvasLoaded){
25326 this.beforeSelectFile(e);
25330 var touches = e.browserEvent.touches;
25336 if(touches.length == 1){
25337 this.onMouseDown(e);
25341 if(touches.length != 2){
25347 for(var i = 0, finger; finger = touches[i]; i++){
25348 coords.push(finger.pageX, finger.pageY);
25351 var x = Math.pow(coords[0] - coords[2], 2);
25352 var y = Math.pow(coords[1] - coords[3], 2);
25354 this.startDistance = Math.sqrt(x + y);
25356 this.startScale = this.scale;
25358 this.pinching = true;
25359 this.dragable = false;
25363 onTouchMove : function(e)
25365 if(!this.pinching && !this.dragable){
25369 var touches = e.browserEvent.touches;
25376 this.onMouseMove(e);
25382 for(var i = 0, finger; finger = touches[i]; i++){
25383 coords.push(finger.pageX, finger.pageY);
25386 var x = Math.pow(coords[0] - coords[2], 2);
25387 var y = Math.pow(coords[1] - coords[3], 2);
25389 this.endDistance = Math.sqrt(x + y);
25391 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25393 if(!this.zoomable()){
25394 this.scale = this.startScale;
25402 onTouchEnd : function(e)
25404 this.pinching = false;
25405 this.dragable = false;
25409 process : function(file, crop)
25412 this.maskEl.mask(this.loadingText);
25415 this.xhr = new XMLHttpRequest();
25417 file.xhr = this.xhr;
25419 this.xhr.open(this.method, this.url, true);
25422 "Accept": "application/json",
25423 "Cache-Control": "no-cache",
25424 "X-Requested-With": "XMLHttpRequest"
25427 for (var headerName in headers) {
25428 var headerValue = headers[headerName];
25430 this.xhr.setRequestHeader(headerName, headerValue);
25436 this.xhr.onload = function()
25438 _this.xhrOnLoad(_this.xhr);
25441 this.xhr.onerror = function()
25443 _this.xhrOnError(_this.xhr);
25446 var formData = new FormData();
25448 formData.append('returnHTML', 'NO');
25451 formData.append('crop', crop);
25454 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25455 formData.append(this.paramName, file, file.name);
25458 if(typeof(file.filename) != 'undefined'){
25459 formData.append('filename', file.filename);
25462 if(typeof(file.mimetype) != 'undefined'){
25463 formData.append('mimetype', file.mimetype);
25466 if(this.fireEvent('arrange', this, formData) != false){
25467 this.xhr.send(formData);
25471 xhrOnLoad : function(xhr)
25474 this.maskEl.unmask();
25477 if (xhr.readyState !== 4) {
25478 this.fireEvent('exception', this, xhr);
25482 var response = Roo.decode(xhr.responseText);
25484 if(!response.success){
25485 this.fireEvent('exception', this, xhr);
25489 var response = Roo.decode(xhr.responseText);
25491 this.fireEvent('upload', this, response);
25495 xhrOnError : function()
25498 this.maskEl.unmask();
25501 Roo.log('xhr on error');
25503 var response = Roo.decode(xhr.responseText);
25509 prepare : function(file)
25512 this.maskEl.mask(this.loadingText);
25518 if(typeof(file) === 'string'){
25519 this.loadCanvas(file);
25523 if(!file || !this.urlAPI){
25528 this.cropType = file.type;
25532 if(this.fireEvent('prepare', this, this.file) != false){
25534 var reader = new FileReader();
25536 reader.onload = function (e) {
25537 if (e.target.error) {
25538 Roo.log(e.target.error);
25542 var buffer = e.target.result,
25543 dataView = new DataView(buffer),
25545 maxOffset = dataView.byteLength - 4,
25549 if (dataView.getUint16(0) === 0xffd8) {
25550 while (offset < maxOffset) {
25551 markerBytes = dataView.getUint16(offset);
25553 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25554 markerLength = dataView.getUint16(offset + 2) + 2;
25555 if (offset + markerLength > dataView.byteLength) {
25556 Roo.log('Invalid meta data: Invalid segment size.');
25560 if(markerBytes == 0xffe1){
25561 _this.parseExifData(
25568 offset += markerLength;
25578 var url = _this.urlAPI.createObjectURL(_this.file);
25580 _this.loadCanvas(url);
25585 reader.readAsArrayBuffer(this.file);
25591 parseExifData : function(dataView, offset, length)
25593 var tiffOffset = offset + 10,
25597 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25598 // No Exif data, might be XMP data instead
25602 // Check for the ASCII code for "Exif" (0x45786966):
25603 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25604 // No Exif data, might be XMP data instead
25607 if (tiffOffset + 8 > dataView.byteLength) {
25608 Roo.log('Invalid Exif data: Invalid segment size.');
25611 // Check for the two null bytes:
25612 if (dataView.getUint16(offset + 8) !== 0x0000) {
25613 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25616 // Check the byte alignment:
25617 switch (dataView.getUint16(tiffOffset)) {
25619 littleEndian = true;
25622 littleEndian = false;
25625 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25628 // Check for the TIFF tag marker (0x002A):
25629 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25630 Roo.log('Invalid Exif data: Missing TIFF marker.');
25633 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25634 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25636 this.parseExifTags(
25639 tiffOffset + dirOffset,
25644 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25649 if (dirOffset + 6 > dataView.byteLength) {
25650 Roo.log('Invalid Exif data: Invalid directory offset.');
25653 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25654 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25655 if (dirEndOffset + 4 > dataView.byteLength) {
25656 Roo.log('Invalid Exif data: Invalid directory size.');
25659 for (i = 0; i < tagsNumber; i += 1) {
25663 dirOffset + 2 + 12 * i, // tag offset
25667 // Return the offset to the next directory:
25668 return dataView.getUint32(dirEndOffset, littleEndian);
25671 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25673 var tag = dataView.getUint16(offset, littleEndian);
25675 this.exif[tag] = this.getExifValue(
25679 dataView.getUint16(offset + 2, littleEndian), // tag type
25680 dataView.getUint32(offset + 4, littleEndian), // tag length
25685 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25687 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25696 Roo.log('Invalid Exif data: Invalid tag type.');
25700 tagSize = tagType.size * length;
25701 // Determine if the value is contained in the dataOffset bytes,
25702 // or if the value at the dataOffset is a pointer to the actual data:
25703 dataOffset = tagSize > 4 ?
25704 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25705 if (dataOffset + tagSize > dataView.byteLength) {
25706 Roo.log('Invalid Exif data: Invalid data offset.');
25709 if (length === 1) {
25710 return tagType.getValue(dataView, dataOffset, littleEndian);
25713 for (i = 0; i < length; i += 1) {
25714 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25717 if (tagType.ascii) {
25719 // Concatenate the chars:
25720 for (i = 0; i < values.length; i += 1) {
25722 // Ignore the terminating NULL byte(s):
25723 if (c === '\u0000') {
25735 Roo.apply(Roo.bootstrap.UploadCropbox, {
25737 'Orientation': 0x0112
25741 1: 0, //'top-left',
25743 3: 180, //'bottom-right',
25744 // 4: 'bottom-left',
25746 6: 90, //'right-top',
25747 // 7: 'right-bottom',
25748 8: 270 //'left-bottom'
25752 // byte, 8-bit unsigned int:
25754 getValue: function (dataView, dataOffset) {
25755 return dataView.getUint8(dataOffset);
25759 // ascii, 8-bit byte:
25761 getValue: function (dataView, dataOffset) {
25762 return String.fromCharCode(dataView.getUint8(dataOffset));
25767 // short, 16 bit int:
25769 getValue: function (dataView, dataOffset, littleEndian) {
25770 return dataView.getUint16(dataOffset, littleEndian);
25774 // long, 32 bit int:
25776 getValue: function (dataView, dataOffset, littleEndian) {
25777 return dataView.getUint32(dataOffset, littleEndian);
25781 // rational = two long values, first is numerator, second is denominator:
25783 getValue: function (dataView, dataOffset, littleEndian) {
25784 return dataView.getUint32(dataOffset, littleEndian) /
25785 dataView.getUint32(dataOffset + 4, littleEndian);
25789 // slong, 32 bit signed int:
25791 getValue: function (dataView, dataOffset, littleEndian) {
25792 return dataView.getInt32(dataOffset, littleEndian);
25796 // srational, two slongs, first is numerator, second is denominator:
25798 getValue: function (dataView, dataOffset, littleEndian) {
25799 return dataView.getInt32(dataOffset, littleEndian) /
25800 dataView.getInt32(dataOffset + 4, littleEndian);
25810 cls : 'btn-group roo-upload-cropbox-rotate-left',
25811 action : 'rotate-left',
25815 cls : 'btn btn-default',
25816 html : '<i class="fa fa-undo"></i>'
25822 cls : 'btn-group roo-upload-cropbox-picture',
25823 action : 'picture',
25827 cls : 'btn btn-default',
25828 html : '<i class="fa fa-picture-o"></i>'
25834 cls : 'btn-group roo-upload-cropbox-rotate-right',
25835 action : 'rotate-right',
25839 cls : 'btn btn-default',
25840 html : '<i class="fa fa-repeat"></i>'
25848 cls : 'btn-group roo-upload-cropbox-rotate-left',
25849 action : 'rotate-left',
25853 cls : 'btn btn-default',
25854 html : '<i class="fa fa-undo"></i>'
25860 cls : 'btn-group roo-upload-cropbox-download',
25861 action : 'download',
25865 cls : 'btn btn-default',
25866 html : '<i class="fa fa-download"></i>'
25872 cls : 'btn-group roo-upload-cropbox-crop',
25877 cls : 'btn btn-default',
25878 html : '<i class="fa fa-crop"></i>'
25884 cls : 'btn-group roo-upload-cropbox-trash',
25889 cls : 'btn btn-default',
25890 html : '<i class="fa fa-trash"></i>'
25896 cls : 'btn-group roo-upload-cropbox-rotate-right',
25897 action : 'rotate-right',
25901 cls : 'btn btn-default',
25902 html : '<i class="fa fa-repeat"></i>'
25910 cls : 'btn-group roo-upload-cropbox-rotate-left',
25911 action : 'rotate-left',
25915 cls : 'btn btn-default',
25916 html : '<i class="fa fa-undo"></i>'
25922 cls : 'btn-group roo-upload-cropbox-rotate-right',
25923 action : 'rotate-right',
25927 cls : 'btn btn-default',
25928 html : '<i class="fa fa-repeat"></i>'
25941 * @class Roo.bootstrap.DocumentManager
25942 * @extends Roo.bootstrap.Component
25943 * Bootstrap DocumentManager class
25944 * @cfg {String} paramName default 'imageUpload'
25945 * @cfg {String} method default POST
25946 * @cfg {String} url action url
25947 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25948 * @cfg {Boolean} multiple multiple upload default true
25949 * @cfg {Number} thumbSize default 300
25950 * @cfg {String} fieldLabel
25951 * @cfg {Number} labelWidth default 4
25952 * @cfg {String} labelAlign (left|top) default left
25953 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25956 * Create a new DocumentManager
25957 * @param {Object} config The config object
25960 Roo.bootstrap.DocumentManager = function(config){
25961 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25966 * Fire when initial the DocumentManager
25967 * @param {Roo.bootstrap.DocumentManager} this
25972 * inspect selected file
25973 * @param {Roo.bootstrap.DocumentManager} this
25974 * @param {File} file
25979 * Fire when xhr load exception
25980 * @param {Roo.bootstrap.DocumentManager} this
25981 * @param {XMLHttpRequest} xhr
25983 "exception" : true,
25986 * prepare the form data
25987 * @param {Roo.bootstrap.DocumentManager} this
25988 * @param {Object} formData
25993 * Fire when remove the file
25994 * @param {Roo.bootstrap.DocumentManager} this
25995 * @param {Object} file
26000 * Fire after refresh the file
26001 * @param {Roo.bootstrap.DocumentManager} this
26006 * Fire after click the image
26007 * @param {Roo.bootstrap.DocumentManager} this
26008 * @param {Object} file
26013 * Fire when upload a image and editable set to true
26014 * @param {Roo.bootstrap.DocumentManager} this
26015 * @param {Object} file
26019 * @event beforeselectfile
26020 * Fire before select file
26021 * @param {Roo.bootstrap.DocumentManager} this
26023 "beforeselectfile" : true,
26026 * Fire before process file
26027 * @param {Roo.bootstrap.DocumentManager} this
26028 * @param {Object} file
26035 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26044 paramName : 'imageUpload',
26047 labelAlign : 'left',
26051 getAutoCreate : function()
26053 var managerWidget = {
26055 cls : 'roo-document-manager',
26059 cls : 'roo-document-manager-selector',
26064 cls : 'roo-document-manager-uploader',
26068 cls : 'roo-document-manager-upload-btn',
26069 html : '<i class="fa fa-plus"></i>'
26080 cls : 'column col-md-12',
26085 if(this.fieldLabel.length){
26090 cls : 'column col-md-12',
26091 html : this.fieldLabel
26095 cls : 'column col-md-12',
26100 if(this.labelAlign == 'left'){
26104 cls : 'column col-md-' + this.labelWidth,
26105 html : this.fieldLabel
26109 cls : 'column col-md-' + (12 - this.labelWidth),
26119 cls : 'row clearfix',
26127 initEvents : function()
26129 this.managerEl = this.el.select('.roo-document-manager', true).first();
26130 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26132 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26133 this.selectorEl.hide();
26136 this.selectorEl.attr('multiple', 'multiple');
26139 this.selectorEl.on('change', this.onFileSelected, this);
26141 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26142 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26144 this.uploader.on('click', this.onUploaderClick, this);
26146 this.renderProgressDialog();
26150 window.addEventListener("resize", function() { _this.refresh(); } );
26152 this.fireEvent('initial', this);
26155 renderProgressDialog : function()
26159 this.progressDialog = new Roo.bootstrap.Modal({
26160 cls : 'roo-document-manager-progress-dialog',
26161 allow_close : false,
26171 btnclick : function() {
26172 _this.uploadCancel();
26178 this.progressDialog.render(Roo.get(document.body));
26180 this.progress = new Roo.bootstrap.Progress({
26181 cls : 'roo-document-manager-progress',
26186 this.progress.render(this.progressDialog.getChildContainer());
26188 this.progressBar = new Roo.bootstrap.ProgressBar({
26189 cls : 'roo-document-manager-progress-bar',
26192 aria_valuemax : 12,
26196 this.progressBar.render(this.progress.getChildContainer());
26199 onUploaderClick : function(e)
26201 e.preventDefault();
26203 if(this.fireEvent('beforeselectfile', this) != false){
26204 this.selectorEl.dom.click();
26209 onFileSelected : function(e)
26211 e.preventDefault();
26213 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26217 Roo.each(this.selectorEl.dom.files, function(file){
26218 if(this.fireEvent('inspect', this, file) != false){
26219 this.files.push(file);
26229 this.selectorEl.dom.value = '';
26231 if(!this.files.length){
26235 if(this.boxes > 0 && this.files.length > this.boxes){
26236 this.files = this.files.slice(0, this.boxes);
26239 this.uploader.show();
26241 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26242 this.uploader.hide();
26251 Roo.each(this.files, function(file){
26253 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26254 var f = this.renderPreview(file);
26259 if(file.type.indexOf('image') != -1){
26260 this.delegates.push(
26262 _this.process(file);
26263 }).createDelegate(this)
26271 _this.process(file);
26272 }).createDelegate(this)
26277 this.files = files;
26279 this.delegates = this.delegates.concat(docs);
26281 if(!this.delegates.length){
26286 this.progressBar.aria_valuemax = this.delegates.length;
26293 arrange : function()
26295 if(!this.delegates.length){
26296 this.progressDialog.hide();
26301 var delegate = this.delegates.shift();
26303 this.progressDialog.show();
26305 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26307 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26312 refresh : function()
26314 this.uploader.show();
26316 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26317 this.uploader.hide();
26320 Roo.isTouch ? this.closable(false) : this.closable(true);
26322 this.fireEvent('refresh', this);
26325 onRemove : function(e, el, o)
26327 e.preventDefault();
26329 this.fireEvent('remove', this, o);
26333 remove : function(o)
26337 Roo.each(this.files, function(file){
26338 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26347 this.files = files;
26354 Roo.each(this.files, function(file){
26359 file.target.remove();
26368 onClick : function(e, el, o)
26370 e.preventDefault();
26372 this.fireEvent('click', this, o);
26376 closable : function(closable)
26378 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26380 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26392 xhrOnLoad : function(xhr)
26394 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26398 if (xhr.readyState !== 4) {
26400 this.fireEvent('exception', this, xhr);
26404 var response = Roo.decode(xhr.responseText);
26406 if(!response.success){
26408 this.fireEvent('exception', this, xhr);
26412 var file = this.renderPreview(response.data);
26414 this.files.push(file);
26420 xhrOnError : function()
26422 Roo.log('xhr on error');
26424 var response = Roo.decode(xhr.responseText);
26431 process : function(file)
26433 if(this.fireEvent('process', this, file) !== false){
26434 if(this.editable && file.type.indexOf('image') != -1){
26435 this.fireEvent('edit', this, file);
26439 this.uploadStart(file, false);
26446 uploadStart : function(file, crop)
26448 this.xhr = new XMLHttpRequest();
26450 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26455 file.xhr = this.xhr;
26457 this.managerEl.createChild({
26459 cls : 'roo-document-manager-loading',
26463 tooltip : file.name,
26464 cls : 'roo-document-manager-thumb',
26465 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26471 this.xhr.open(this.method, this.url, true);
26474 "Accept": "application/json",
26475 "Cache-Control": "no-cache",
26476 "X-Requested-With": "XMLHttpRequest"
26479 for (var headerName in headers) {
26480 var headerValue = headers[headerName];
26482 this.xhr.setRequestHeader(headerName, headerValue);
26488 this.xhr.onload = function()
26490 _this.xhrOnLoad(_this.xhr);
26493 this.xhr.onerror = function()
26495 _this.xhrOnError(_this.xhr);
26498 var formData = new FormData();
26500 formData.append('returnHTML', 'NO');
26503 formData.append('crop', crop);
26506 formData.append(this.paramName, file, file.name);
26508 if(this.fireEvent('prepare', this, formData) != false){
26509 this.xhr.send(formData);
26513 uploadCancel : function()
26517 this.delegates = [];
26519 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26526 renderPreview : function(file)
26528 if(typeof(file.target) != 'undefined' && file.target){
26532 var previewEl = this.managerEl.createChild({
26534 cls : 'roo-document-manager-preview',
26538 tooltip : file.filename,
26539 cls : 'roo-document-manager-thumb',
26540 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26545 html : '<i class="fa fa-times-circle"></i>'
26550 var close = previewEl.select('button.close', true).first();
26552 close.on('click', this.onRemove, this, file);
26554 file.target = previewEl;
26556 var image = previewEl.select('img', true).first();
26560 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26562 image.on('click', this.onClick, this, file);
26568 onPreviewLoad : function(file, image)
26570 if(typeof(file.target) == 'undefined' || !file.target){
26574 var width = image.dom.naturalWidth || image.dom.width;
26575 var height = image.dom.naturalHeight || image.dom.height;
26577 if(width > height){
26578 file.target.addClass('wide');
26582 file.target.addClass('tall');
26587 uploadFromSource : function(file, crop)
26589 this.xhr = new XMLHttpRequest();
26591 this.managerEl.createChild({
26593 cls : 'roo-document-manager-loading',
26597 tooltip : file.name,
26598 cls : 'roo-document-manager-thumb',
26599 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26605 this.xhr.open(this.method, this.url, true);
26608 "Accept": "application/json",
26609 "Cache-Control": "no-cache",
26610 "X-Requested-With": "XMLHttpRequest"
26613 for (var headerName in headers) {
26614 var headerValue = headers[headerName];
26616 this.xhr.setRequestHeader(headerName, headerValue);
26622 this.xhr.onload = function()
26624 _this.xhrOnLoad(_this.xhr);
26627 this.xhr.onerror = function()
26629 _this.xhrOnError(_this.xhr);
26632 var formData = new FormData();
26634 formData.append('returnHTML', 'NO');
26636 formData.append('crop', crop);
26638 if(typeof(file.filename) != 'undefined'){
26639 formData.append('filename', file.filename);
26642 if(typeof(file.mimetype) != 'undefined'){
26643 formData.append('mimetype', file.mimetype);
26646 if(this.fireEvent('prepare', this, formData) != false){
26647 this.xhr.send(formData);
26657 * @class Roo.bootstrap.DocumentViewer
26658 * @extends Roo.bootstrap.Component
26659 * Bootstrap DocumentViewer class
26662 * Create a new DocumentViewer
26663 * @param {Object} config The config object
26666 Roo.bootstrap.DocumentViewer = function(config){
26667 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26672 * Fire after initEvent
26673 * @param {Roo.bootstrap.DocumentViewer} this
26679 * @param {Roo.bootstrap.DocumentViewer} this
26684 * Fire after trash button
26685 * @param {Roo.bootstrap.DocumentViewer} this
26692 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26694 getAutoCreate : function()
26698 cls : 'roo-document-viewer',
26702 cls : 'roo-document-viewer-body',
26706 cls : 'roo-document-viewer-thumb',
26710 cls : 'roo-document-viewer-image'
26718 cls : 'roo-document-viewer-footer',
26721 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26729 cls : 'btn btn-default roo-document-viewer-trash',
26730 html : '<i class="fa fa-trash"></i>'
26743 initEvents : function()
26746 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26747 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26749 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26750 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26752 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26753 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26755 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26756 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26758 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26759 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26761 this.bodyEl.on('click', this.onClick, this);
26763 this.trashBtn.on('click', this.onTrash, this);
26767 initial : function()
26769 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26772 this.fireEvent('initial', this);
26776 onClick : function(e)
26778 e.preventDefault();
26780 this.fireEvent('click', this);
26783 onTrash : function(e)
26785 e.preventDefault();
26787 this.fireEvent('trash', this);
26799 * @class Roo.bootstrap.NavProgressBar
26800 * @extends Roo.bootstrap.Component
26801 * Bootstrap NavProgressBar class
26804 * Create a new nav progress bar
26805 * @param {Object} config The config object
26808 Roo.bootstrap.NavProgressBar = function(config){
26809 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26811 this.bullets = this.bullets || [];
26813 // Roo.bootstrap.NavProgressBar.register(this);
26817 * Fires when the active item changes
26818 * @param {Roo.bootstrap.NavProgressBar} this
26819 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26820 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26827 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26832 getAutoCreate : function()
26834 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26838 cls : 'roo-navigation-bar-group',
26842 cls : 'roo-navigation-top-bar'
26846 cls : 'roo-navigation-bullets-bar',
26850 cls : 'roo-navigation-bar'
26857 cls : 'roo-navigation-bottom-bar'
26867 initEvents: function()
26872 onRender : function(ct, position)
26874 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26876 if(this.bullets.length){
26877 Roo.each(this.bullets, function(b){
26886 addItem : function(cfg)
26888 var item = new Roo.bootstrap.NavProgressItem(cfg);
26890 item.parentId = this.id;
26891 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26894 var top = new Roo.bootstrap.Element({
26896 cls : 'roo-navigation-bar-text'
26899 var bottom = new Roo.bootstrap.Element({
26901 cls : 'roo-navigation-bar-text'
26904 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26905 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26907 var topText = new Roo.bootstrap.Element({
26909 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26912 var bottomText = new Roo.bootstrap.Element({
26914 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26917 topText.onRender(top.el, null);
26918 bottomText.onRender(bottom.el, null);
26921 item.bottomEl = bottom;
26924 this.barItems.push(item);
26929 getActive : function()
26931 var active = false;
26933 Roo.each(this.barItems, function(v){
26935 if (!v.isActive()) {
26947 setActiveItem : function(item)
26951 Roo.each(this.barItems, function(v){
26952 if (v.rid == item.rid) {
26956 if (v.isActive()) {
26957 v.setActive(false);
26962 item.setActive(true);
26964 this.fireEvent('changed', this, item, prev);
26967 getBarItem: function(rid)
26971 Roo.each(this.barItems, function(e) {
26972 if (e.rid != rid) {
26983 indexOfItem : function(item)
26987 Roo.each(this.barItems, function(v, i){
26989 if (v.rid != item.rid) {
27000 setActiveNext : function()
27002 var i = this.indexOfItem(this.getActive());
27004 if (i > this.barItems.length) {
27008 this.setActiveItem(this.barItems[i+1]);
27011 setActivePrev : function()
27013 var i = this.indexOfItem(this.getActive());
27019 this.setActiveItem(this.barItems[i-1]);
27022 format : function()
27024 if(!this.barItems.length){
27028 var width = 100 / this.barItems.length;
27030 Roo.each(this.barItems, function(i){
27031 i.el.setStyle('width', width + '%');
27032 i.topEl.el.setStyle('width', width + '%');
27033 i.bottomEl.el.setStyle('width', width + '%');
27042 * Nav Progress Item
27047 * @class Roo.bootstrap.NavProgressItem
27048 * @extends Roo.bootstrap.Component
27049 * Bootstrap NavProgressItem class
27050 * @cfg {String} rid the reference id
27051 * @cfg {Boolean} active (true|false) Is item active default false
27052 * @cfg {Boolean} disabled (true|false) Is item active default false
27053 * @cfg {String} html
27054 * @cfg {String} position (top|bottom) text position default bottom
27055 * @cfg {String} icon show icon instead of number
27058 * Create a new NavProgressItem
27059 * @param {Object} config The config object
27061 Roo.bootstrap.NavProgressItem = function(config){
27062 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27067 * The raw click event for the entire grid.
27068 * @param {Roo.bootstrap.NavProgressItem} this
27069 * @param {Roo.EventObject} e
27076 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27082 position : 'bottom',
27085 getAutoCreate : function()
27087 var iconCls = 'roo-navigation-bar-item-icon';
27089 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27093 cls: 'roo-navigation-bar-item',
27103 cfg.cls += ' active';
27106 cfg.cls += ' disabled';
27112 disable : function()
27114 this.setDisabled(true);
27117 enable : function()
27119 this.setDisabled(false);
27122 initEvents: function()
27124 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27126 this.iconEl.on('click', this.onClick, this);
27129 onClick : function(e)
27131 e.preventDefault();
27137 if(this.fireEvent('click', this, e) === false){
27141 this.parent().setActiveItem(this);
27144 isActive: function ()
27146 return this.active;
27149 setActive : function(state)
27151 if(this.active == state){
27155 this.active = state;
27158 this.el.addClass('active');
27162 this.el.removeClass('active');
27167 setDisabled : function(state)
27169 if(this.disabled == state){
27173 this.disabled = state;
27176 this.el.addClass('disabled');
27180 this.el.removeClass('disabled');
27183 tooltipEl : function()
27185 return this.el.select('.roo-navigation-bar-item-icon', true).first();;