4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
994 * @cfg {Boolean} clickable (true|false) default false
998 * Create a new Container
999 * @param {Object} config The config object
1002 Roo.bootstrap.Container = function(config){
1003 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1009 * After the panel has been expand
1011 * @param {Roo.bootstrap.Container} this
1016 * After the panel has been collapsed
1018 * @param {Roo.bootstrap.Container} this
1023 * When a element is chick
1024 * @param {Roo.bootstrap.Container} this
1025 * @param {Roo.EventObject} e
1031 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1049 getChildContainer : function() {
1055 if (this.panel.length) {
1056 return this.el.select('.panel-body',true).first();
1063 getAutoCreate : function(){
1066 tag : this.tag || 'div',
1070 if (this.jumbotron) {
1071 cfg.cls = 'jumbotron';
1076 // - this is applied by the parent..
1078 // cfg.cls = this.cls + '';
1081 if (this.sticky.length) {
1083 var bd = Roo.get(document.body);
1084 if (!bd.hasClass('bootstrap-sticky')) {
1085 bd.addClass('bootstrap-sticky');
1086 Roo.select('html',true).setStyle('height', '100%');
1089 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1093 if (this.well.length) {
1094 switch (this.well) {
1097 cfg.cls +=' well well-' +this.well;
1106 cfg.cls += ' hidden';
1110 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1111 cfg.cls +=' alert alert-' + this.alert;
1116 if (this.panel.length) {
1117 cfg.cls += ' panel panel-' + this.panel;
1119 if (this.header.length) {
1123 if(this.expandable){
1125 cfg.cls = cfg.cls + ' expandable';
1129 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1137 cls : 'panel-title',
1138 html : (this.expandable ? ' ' : '') + this.header
1142 cls: 'panel-header-right',
1148 cls : 'panel-heading',
1149 style : this.expandable ? 'cursor: pointer' : '',
1157 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1162 if (this.footer.length) {
1164 cls : 'panel-footer',
1173 body.html = this.html || cfg.html;
1174 // prefix with the icons..
1176 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1179 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1184 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1185 cfg.cls = 'container';
1191 initEvents: function()
1193 if(this.expandable){
1194 var headerEl = this.headerEl();
1197 headerEl.on('click', this.onToggleClick, this);
1202 this.el.on('click', this.onClick, this);
1207 onToggleClick : function()
1209 var headerEl = this.headerEl();
1225 if(this.fireEvent('expand', this)) {
1227 this.expanded = true;
1229 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1231 this.el.select('.panel-body',true).first().removeClass('hide');
1233 var toggleEl = this.toggleEl();
1239 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1244 collapse : function()
1246 if(this.fireEvent('collapse', this)) {
1248 this.expanded = false;
1250 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1251 this.el.select('.panel-body',true).first().addClass('hide');
1253 var toggleEl = this.toggleEl();
1259 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1263 toggleEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1269 return this.el.select('.panel-heading .fa',true).first();
1272 headerEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-heading',true).first()
1281 titleEl : function()
1283 if(!this.el || !this.panel.length || !this.header.length){
1287 return this.el.select('.panel-title',true).first();
1290 setTitle : function(v)
1292 var titleEl = this.titleEl();
1298 titleEl.dom.innerHTML = v;
1301 getTitle : function()
1304 var titleEl = this.titleEl();
1310 return titleEl.dom.innerHTML;
1313 setRightTitle : function(v)
1315 var t = this.el.select('.panel-header-right',true).first();
1321 t.dom.innerHTML = v;
1324 onClick : function(e)
1328 this.fireEvent('click', this, e);
1342 * @class Roo.bootstrap.Img
1343 * @extends Roo.bootstrap.Component
1344 * Bootstrap Img class
1345 * @cfg {Boolean} imgResponsive false | true
1346 * @cfg {String} border rounded | circle | thumbnail
1347 * @cfg {String} src image source
1348 * @cfg {String} alt image alternative text
1349 * @cfg {String} href a tag href
1350 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1351 * @cfg {String} xsUrl xs image source
1352 * @cfg {String} smUrl sm image source
1353 * @cfg {String} mdUrl md image source
1354 * @cfg {String} lgUrl lg image source
1357 * Create a new Input
1358 * @param {Object} config The config object
1361 Roo.bootstrap.Img = function(config){
1362 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1368 * The img click event for the img.
1369 * @param {Roo.EventObject} e
1375 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1377 imgResponsive: true,
1387 getAutoCreate : function()
1389 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1390 return this.createSingleImg();
1395 cls: 'roo-image-responsive-group',
1400 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1402 if(!_this[size + 'Url']){
1408 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1409 html: _this.html || cfg.html,
1410 src: _this[size + 'Url']
1413 img.cls += ' roo-image-responsive-' + size;
1415 var s = ['xs', 'sm', 'md', 'lg'];
1417 s.splice(s.indexOf(size), 1);
1419 Roo.each(s, function(ss){
1420 img.cls += ' hidden-' + ss;
1423 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1424 cfg.cls += ' img-' + _this.border;
1428 cfg.alt = _this.alt;
1441 a.target = _this.target;
1445 cfg.cn.push((_this.href) ? a : img);
1452 createSingleImg : function()
1456 cls: (this.imgResponsive) ? 'img-responsive' : '',
1458 src : 'about:blank' // just incase src get's set to undefined?!?
1461 cfg.html = this.html || cfg.html;
1463 cfg.src = this.src || cfg.src;
1465 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1466 cfg.cls += ' img-' + this.border;
1483 a.target = this.target;
1488 return (this.href) ? a : cfg;
1491 initEvents: function()
1494 this.el.on('click', this.onClick, this);
1499 onClick : function(e)
1501 Roo.log('img onclick');
1502 this.fireEvent('click', this, e);
1516 * @class Roo.bootstrap.Link
1517 * @extends Roo.bootstrap.Component
1518 * Bootstrap Link Class
1519 * @cfg {String} alt image alternative text
1520 * @cfg {String} href a tag href
1521 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1522 * @cfg {String} html the content of the link.
1523 * @cfg {String} anchor name for the anchor link
1525 * @cfg {Boolean} preventDefault (true | false) default false
1529 * Create a new Input
1530 * @param {Object} config The config object
1533 Roo.bootstrap.Link = function(config){
1534 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1540 * The img click event for the img.
1541 * @param {Roo.EventObject} e
1547 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1551 preventDefault: false,
1555 getAutoCreate : function()
1561 // anchor's do not require html/href...
1562 if (this.anchor === false) {
1563 cfg.html = this.html || '';
1564 cfg.href = this.href || '#';
1566 cfg.name = this.anchor;
1567 if (this.html !== false) {
1568 cfg.html = this.html;
1570 if (this.href !== false) {
1571 cfg.href = this.href;
1575 if(this.alt !== false){
1580 if(this.target !== false) {
1581 cfg.target = this.target;
1587 initEvents: function() {
1589 if(!this.href || this.preventDefault){
1590 this.el.on('click', this.onClick, this);
1594 onClick : function(e)
1596 if(this.preventDefault){
1599 //Roo.log('img onclick');
1600 this.fireEvent('click', this, e);
1613 * @class Roo.bootstrap.Header
1614 * @extends Roo.bootstrap.Component
1615 * Bootstrap Header class
1616 * @cfg {String} html content of header
1617 * @cfg {Number} level (1|2|3|4|5|6) default 1
1620 * Create a new Header
1621 * @param {Object} config The config object
1625 Roo.bootstrap.Header = function(config){
1626 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1629 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1637 getAutoCreate : function(){
1642 tag: 'h' + (1 *this.level),
1643 html: this.html || ''
1655 * Ext JS Library 1.1.1
1656 * Copyright(c) 2006-2007, Ext JS, LLC.
1658 * Originally Released Under LGPL - original licence link has changed is not relivant.
1661 * <script type="text/javascript">
1665 * @class Roo.bootstrap.MenuMgr
1666 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1669 Roo.bootstrap.MenuMgr = function(){
1670 var menus, active, groups = {}, attached = false, lastShow = new Date();
1672 // private - called when first menu is created
1675 active = new Roo.util.MixedCollection();
1676 Roo.get(document).addKeyListener(27, function(){
1677 if(active.length > 0){
1685 if(active && active.length > 0){
1686 var c = active.clone();
1696 if(active.length < 1){
1697 Roo.get(document).un("mouseup", onMouseDown);
1705 var last = active.last();
1706 lastShow = new Date();
1709 Roo.get(document).on("mouseup", onMouseDown);
1714 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1715 m.parentMenu.activeChild = m;
1716 }else if(last && last.isVisible()){
1717 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1722 function onBeforeHide(m){
1724 m.activeChild.hide();
1726 if(m.autoHideTimer){
1727 clearTimeout(m.autoHideTimer);
1728 delete m.autoHideTimer;
1733 function onBeforeShow(m){
1734 var pm = m.parentMenu;
1735 if(!pm && !m.allowOtherMenus){
1737 }else if(pm && pm.activeChild && active != m){
1738 pm.activeChild.hide();
1742 // private this should really trigger on mouseup..
1743 function onMouseDown(e){
1744 Roo.log("on Mouse Up");
1745 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1755 function onBeforeCheck(mi, state){
1757 var g = groups[mi.group];
1758 for(var i = 0, l = g.length; i < l; i++){
1760 g[i].setChecked(false);
1769 * Hides all menus that are currently visible
1771 hideAll : function(){
1776 register : function(menu){
1780 menus[menu.id] = menu;
1781 menu.on("beforehide", onBeforeHide);
1782 menu.on("hide", onHide);
1783 menu.on("beforeshow", onBeforeShow);
1784 menu.on("show", onShow);
1786 if(g && menu.events["checkchange"]){
1790 groups[g].push(menu);
1791 menu.on("checkchange", onCheck);
1796 * Returns a {@link Roo.menu.Menu} object
1797 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1798 * be used to generate and return a new Menu instance.
1800 get : function(menu){
1801 if(typeof menu == "string"){ // menu id
1803 }else if(menu.events){ // menu instance
1806 /*else if(typeof menu.length == 'number'){ // array of menu items?
1807 return new Roo.bootstrap.Menu({items:menu});
1808 }else{ // otherwise, must be a config
1809 return new Roo.bootstrap.Menu(menu);
1816 unregister : function(menu){
1817 delete menus[menu.id];
1818 menu.un("beforehide", onBeforeHide);
1819 menu.un("hide", onHide);
1820 menu.un("beforeshow", onBeforeShow);
1821 menu.un("show", onShow);
1823 if(g && menu.events["checkchange"]){
1824 groups[g].remove(menu);
1825 menu.un("checkchange", onCheck);
1830 registerCheckable : function(menuItem){
1831 var g = menuItem.group;
1836 groups[g].push(menuItem);
1837 menuItem.on("beforecheckchange", onBeforeCheck);
1842 unregisterCheckable : function(menuItem){
1843 var g = menuItem.group;
1845 groups[g].remove(menuItem);
1846 menuItem.un("beforecheckchange", onBeforeCheck);
1858 * @class Roo.bootstrap.Menu
1859 * @extends Roo.bootstrap.Component
1860 * Bootstrap Menu class - container for MenuItems
1861 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1865 * @param {Object} config The config object
1869 Roo.bootstrap.Menu = function(config){
1870 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1871 if (this.registerMenu) {
1872 Roo.bootstrap.MenuMgr.register(this);
1877 * Fires before this menu is displayed
1878 * @param {Roo.menu.Menu} this
1883 * Fires before this menu is hidden
1884 * @param {Roo.menu.Menu} this
1889 * Fires after this menu is displayed
1890 * @param {Roo.menu.Menu} this
1895 * Fires after this menu is hidden
1896 * @param {Roo.menu.Menu} this
1901 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1902 * @param {Roo.menu.Menu} this
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904 * @param {Roo.EventObject} e
1909 * Fires when the mouse is hovering over this menu
1910 * @param {Roo.menu.Menu} this
1911 * @param {Roo.EventObject} e
1912 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1917 * Fires when the mouse exits this menu
1918 * @param {Roo.menu.Menu} this
1919 * @param {Roo.EventObject} e
1920 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1925 * Fires when a menu item contained in this menu is clicked
1926 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1927 * @param {Roo.EventObject} e
1931 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1934 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1938 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1941 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1943 registerMenu : true,
1945 menuItems :false, // stores the menu items..
1951 getChildContainer : function() {
1955 getAutoCreate : function(){
1957 //if (['right'].indexOf(this.align)!==-1) {
1958 // cfg.cn[1].cls += ' pull-right'
1964 cls : 'dropdown-menu' ,
1965 style : 'z-index:1000'
1969 if (this.type === 'submenu') {
1970 cfg.cls = 'submenu active';
1972 if (this.type === 'treeview') {
1973 cfg.cls = 'treeview-menu';
1978 initEvents : function() {
1980 // Roo.log("ADD event");
1981 // Roo.log(this.triggerEl.dom);
1982 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1984 this.triggerEl.addClass('dropdown-toggle');
1990 this.el.on('touchstart' , this.onTouch, this);
1992 this.el.on('click' , this.onClick, this);
1994 this.el.on("mouseover", this.onMouseOver, this);
1995 this.el.on("mouseout", this.onMouseOut, this);
1999 findTargetItem : function(e){
2000 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2004 //Roo.log(t); Roo.log(t.id);
2006 //Roo.log(this.menuitems);
2007 return this.menuitems.get(t.id);
2009 //return this.items.get(t.menuItemId);
2015 onTouch : function(e) {
2020 onClick : function(e){
2021 Roo.log("menu.onClick");
2022 var t = this.findTargetItem(e);
2023 if(!t || t.isContainer){
2028 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2029 if(t == this.activeItem && t.shouldDeactivate(e)){
2030 this.activeItem.deactivate();
2031 delete this.activeItem;
2035 this.setActiveItem(t, true);
2043 Roo.log('pass click event');
2047 this.fireEvent("click", this, t, e);
2051 onMouseOver : function(e){
2052 var t = this.findTargetItem(e);
2055 // if(t.canActivate && !t.disabled){
2056 // this.setActiveItem(t, true);
2060 this.fireEvent("mouseover", this, e, t);
2062 isVisible : function(){
2063 return !this.hidden;
2065 onMouseOut : function(e){
2066 var t = this.findTargetItem(e);
2069 // if(t == this.activeItem && t.shouldDeactivate(e)){
2070 // this.activeItem.deactivate();
2071 // delete this.activeItem;
2074 this.fireEvent("mouseout", this, e, t);
2079 * Displays this menu relative to another element
2080 * @param {String/HTMLElement/Roo.Element} element The element to align to
2081 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2082 * the element (defaults to this.defaultAlign)
2083 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2085 show : function(el, pos, parentMenu){
2086 this.parentMenu = parentMenu;
2090 this.fireEvent("beforeshow", this);
2091 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2094 * Displays this menu at a specific xy position
2095 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2096 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2098 showAt : function(xy, parentMenu, /* private: */_e){
2099 this.parentMenu = parentMenu;
2104 this.fireEvent("beforeshow", this);
2105 //xy = this.el.adjustForConstraints(xy);
2109 this.hideMenuItems();
2110 this.hidden = false;
2111 this.triggerEl.addClass('open');
2113 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2114 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2119 this.fireEvent("show", this);
2125 this.doFocus.defer(50, this);
2129 doFocus : function(){
2131 this.focusEl.focus();
2136 * Hides this menu and optionally all parent menus
2137 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2139 hide : function(deep){
2141 this.hideMenuItems();
2142 if(this.el && this.isVisible()){
2143 this.fireEvent("beforehide", this);
2144 if(this.activeItem){
2145 this.activeItem.deactivate();
2146 this.activeItem = null;
2148 this.triggerEl.removeClass('open');;
2150 this.fireEvent("hide", this);
2152 if(deep === true && this.parentMenu){
2153 this.parentMenu.hide(true);
2157 onTriggerPress : function(e)
2160 Roo.log('trigger press');
2161 //Roo.log(e.getTarget());
2162 // Roo.log(this.triggerEl.dom);
2163 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2167 if (this.isVisible()) {
2172 this.show(this.triggerEl, false, false);
2181 hideMenuItems : function()
2183 //$(backdrop).remove()
2184 Roo.select('.open',true).each(function(aa) {
2186 aa.removeClass('open');
2187 //var parent = getParent($(this))
2188 //var relatedTarget = { relatedTarget: this }
2190 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2191 //if (e.isDefaultPrevented()) return
2192 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2195 addxtypeChild : function (tree, cntr) {
2196 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2198 this.menuitems.add(comp);
2219 * @class Roo.bootstrap.MenuItem
2220 * @extends Roo.bootstrap.Component
2221 * Bootstrap MenuItem class
2222 * @cfg {String} html the menu label
2223 * @cfg {String} href the link
2224 * @cfg {Boolean} preventDefault (true | false) default true
2225 * @cfg {Boolean} isContainer (true | false) default false
2229 * Create a new MenuItem
2230 * @param {Object} config The config object
2234 Roo.bootstrap.MenuItem = function(config){
2235 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2240 * The raw click event for the entire grid.
2241 * @param {Roo.bootstrap.MenuItem} this
2242 * @param {Roo.EventObject} e
2248 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2252 preventDefault: true,
2253 isContainer : false,
2255 getAutoCreate : function(){
2257 if(this.isContainer){
2260 cls: 'dropdown-menu-item'
2266 cls: 'dropdown-menu-item',
2275 if (this.parent().type == 'treeview') {
2276 cfg.cls = 'treeview-menu';
2279 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2280 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2284 initEvents: function() {
2286 //this.el.select('a').on('click', this.onClick, this);
2289 onClick : function(e)
2291 Roo.log('item on click ');
2292 //if(this.preventDefault){
2293 // e.preventDefault();
2295 //this.parent().hideMenuItems();
2297 this.fireEvent('click', this, e);
2316 * @class Roo.bootstrap.MenuSeparator
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap MenuSeparator class
2321 * Create a new MenuItem
2322 * @param {Object} config The config object
2326 Roo.bootstrap.MenuSeparator = function(config){
2327 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2330 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2332 getAutoCreate : function(){
2351 * @class Roo.bootstrap.Modal
2352 * @extends Roo.bootstrap.Component
2353 * Bootstrap Modal class
2354 * @cfg {String} title Title of dialog
2355 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2356 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2357 * @cfg {Boolean} specificTitle default false
2358 * @cfg {Array} buttons Array of buttons or standard button set..
2359 * @cfg {String} buttonPosition (left|right|center) default right
2360 * @cfg {Boolean} animate default true
2361 * @cfg {Boolean} allow_close default true
2364 * Create a new Modal Dialog
2365 * @param {Object} config The config object
2368 Roo.bootstrap.Modal = function(config){
2369 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2374 * The raw btnclick event for the button
2375 * @param {Roo.EventObject} e
2379 this.buttons = this.buttons || [];
2382 this.tmpl = Roo.factory(this.tmpl);
2387 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2389 title : 'test dialog',
2399 specificTitle: false,
2401 buttonPosition: 'right',
2415 onRender : function(ct, position)
2417 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2420 var cfg = Roo.apply({}, this.getAutoCreate());
2423 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2425 //if (!cfg.name.length) {
2429 cfg.cls += ' ' + this.cls;
2432 cfg.style = this.style;
2434 this.el = Roo.get(document.body).createChild(cfg, position);
2436 //var type = this.el.dom.type;
2441 if(this.tabIndex !== undefined){
2442 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2446 this.bodyEl = this.el.select('.modal-body',true).first();
2447 this.closeEl = this.el.select('.modal-header .close', true).first();
2448 this.footerEl = this.el.select('.modal-footer',true).first();
2449 this.titleEl = this.el.select('.modal-title',true).first();
2453 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2454 this.maskEl.enableDisplayMode("block");
2456 //this.el.addClass("x-dlg-modal");
2458 if (this.buttons.length) {
2459 Roo.each(this.buttons, function(bb) {
2460 var b = Roo.apply({}, bb);
2461 b.xns = b.xns || Roo.bootstrap;
2462 b.xtype = b.xtype || 'Button';
2463 if (typeof(b.listeners) == 'undefined') {
2464 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2467 var btn = Roo.factory(b);
2469 btn.onRender(this.el.select('.modal-footer div').first());
2473 // render the children.
2476 if(typeof(this.items) != 'undefined'){
2477 var items = this.items;
2480 for(var i =0;i < items.length;i++) {
2481 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2485 this.items = nitems;
2487 // where are these used - they used to be body/close/footer
2491 //this.el.addClass([this.fieldClass, this.cls]);
2495 getAutoCreate : function(){
2500 html : this.html || ''
2505 cls : 'modal-title',
2509 if(this.specificTitle){
2515 if (this.allow_close) {
2526 style : 'display: none',
2529 cls: "modal-dialog",
2532 cls : "modal-content",
2535 cls : 'modal-header',
2540 cls : 'modal-footer',
2544 cls: 'btn-' + this.buttonPosition
2561 modal.cls += ' fade';
2567 getChildContainer : function() {
2572 getButtonContainer : function() {
2573 return this.el.select('.modal-footer div',true).first();
2576 initEvents : function()
2578 if (this.allow_close) {
2579 this.closeEl.on('click', this.hide, this);
2584 window.addEventListener("resize", function() { _this.resize(); } );
2590 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2595 if (!this.rendered) {
2599 this.el.setStyle('display', 'block');
2603 (function(){ _this.el.addClass('in'); }).defer(50);
2605 this.el.addClass('in');
2608 // not sure how we can show data in here..
2610 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2613 Roo.get(document.body).addClass("x-body-masked");
2614 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2616 this.el.setStyle('zIndex', '10001');
2618 this.fireEvent('show', this);
2625 Roo.get(document.body).removeClass("x-body-masked");
2626 this.el.removeClass('in');
2630 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2632 this.el.setStyle('display', 'none');
2635 this.fireEvent('hide', this);
2638 addButton : function(str, cb)
2642 var b = Roo.apply({}, { html : str } );
2643 b.xns = b.xns || Roo.bootstrap;
2644 b.xtype = b.xtype || 'Button';
2645 if (typeof(b.listeners) == 'undefined') {
2646 b.listeners = { click : cb.createDelegate(this) };
2649 var btn = Roo.factory(b);
2651 btn.onRender(this.el.select('.modal-footer div').first());
2657 setDefaultButton : function(btn)
2659 //this.el.select('.modal-footer').()
2661 resizeTo: function(w,h)
2665 setContentSize : function(w, h)
2669 onButtonClick: function(btn,e)
2672 this.fireEvent('btnclick', btn.name, e);
2675 * Set the title of the Dialog
2676 * @param {String} str new Title
2678 setTitle: function(str) {
2679 this.titleEl.dom.innerHTML = str;
2682 * Set the body of the Dialog
2683 * @param {String} str new Title
2685 setBody: function(str) {
2686 this.bodyEl.dom.innerHTML = str;
2689 * Set the body of the Dialog using the template
2690 * @param {Obj} data - apply this data to the template and replace the body contents.
2692 applyBody: function(obj)
2695 Roo.log("Error - using apply Body without a template");
2698 this.tmpl.overwrite(this.bodyEl, obj);
2704 Roo.apply(Roo.bootstrap.Modal, {
2706 * Button config that displays a single OK button
2715 * Button config that displays Yes and No buttons
2731 * Button config that displays OK and Cancel buttons
2746 * Button config that displays Yes, No and Cancel buttons
2769 * messagebox - can be used as a replace
2773 * @class Roo.MessageBox
2774 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2778 Roo.Msg.alert('Status', 'Changes saved successfully.');
2780 // Prompt for user data:
2781 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2783 // process text value...
2787 // Show a dialog using config options:
2789 title:'Save Changes?',
2790 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2791 buttons: Roo.Msg.YESNOCANCEL,
2798 Roo.bootstrap.MessageBox = function(){
2799 var dlg, opt, mask, waitTimer;
2800 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2801 var buttons, activeTextEl, bwidth;
2805 var handleButton = function(button){
2807 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2811 var handleHide = function(){
2813 dlg.el.removeClass(opt.cls);
2816 // Roo.TaskMgr.stop(waitTimer);
2817 // waitTimer = null;
2822 var updateButtons = function(b){
2825 buttons["ok"].hide();
2826 buttons["cancel"].hide();
2827 buttons["yes"].hide();
2828 buttons["no"].hide();
2829 //dlg.footer.dom.style.display = 'none';
2832 dlg.footerEl.dom.style.display = '';
2833 for(var k in buttons){
2834 if(typeof buttons[k] != "function"){
2837 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2838 width += buttons[k].el.getWidth()+15;
2848 var handleEsc = function(d, k, e){
2849 if(opt && opt.closable !== false){
2859 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2860 * @return {Roo.BasicDialog} The BasicDialog element
2862 getDialog : function(){
2864 dlg = new Roo.bootstrap.Modal( {
2867 //constraintoviewport:false,
2869 //collapsible : false,
2874 //buttonAlign:"center",
2875 closeClick : function(){
2876 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2879 handleButton("cancel");
2884 dlg.on("hide", handleHide);
2886 //dlg.addKeyListener(27, handleEsc);
2888 this.buttons = buttons;
2889 var bt = this.buttonText;
2890 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2891 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2892 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2893 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2895 bodyEl = dlg.bodyEl.createChild({
2897 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2898 '<textarea class="roo-mb-textarea"></textarea>' +
2899 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2901 msgEl = bodyEl.dom.firstChild;
2902 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2903 textboxEl.enableDisplayMode();
2904 textboxEl.addKeyListener([10,13], function(){
2905 if(dlg.isVisible() && opt && opt.buttons){
2908 }else if(opt.buttons.yes){
2909 handleButton("yes");
2913 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2914 textareaEl.enableDisplayMode();
2915 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2916 progressEl.enableDisplayMode();
2917 var pf = progressEl.dom.firstChild;
2919 pp = Roo.get(pf.firstChild);
2920 pp.setHeight(pf.offsetHeight);
2928 * Updates the message box body text
2929 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2930 * the XHTML-compliant non-breaking space character '&#160;')
2931 * @return {Roo.MessageBox} This message box
2933 updateText : function(text){
2934 if(!dlg.isVisible() && !opt.width){
2935 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2937 msgEl.innerHTML = text || ' ';
2939 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2940 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2942 Math.min(opt.width || cw , this.maxWidth),
2943 Math.max(opt.minWidth || this.minWidth, bwidth)
2946 activeTextEl.setWidth(w);
2948 if(dlg.isVisible()){
2949 dlg.fixedcenter = false;
2951 // to big, make it scroll. = But as usual stupid IE does not support
2954 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2955 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2956 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2958 bodyEl.dom.style.height = '';
2959 bodyEl.dom.style.overflowY = '';
2962 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2964 bodyEl.dom.style.overflowX = '';
2967 dlg.setContentSize(w, bodyEl.getHeight());
2968 if(dlg.isVisible()){
2969 dlg.fixedcenter = true;
2975 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2976 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2977 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2978 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2979 * @return {Roo.MessageBox} This message box
2981 updateProgress : function(value, text){
2983 this.updateText(text);
2985 if (pp) { // weird bug on my firefox - for some reason this is not defined
2986 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2992 * Returns true if the message box is currently displayed
2993 * @return {Boolean} True if the message box is visible, else false
2995 isVisible : function(){
2996 return dlg && dlg.isVisible();
3000 * Hides the message box if it is displayed
3003 if(this.isVisible()){
3009 * Displays a new message box, or reinitializes an existing message box, based on the config options
3010 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3011 * The following config object properties are supported:
3013 Property Type Description
3014 ---------- --------------- ------------------------------------------------------------------------------------
3015 animEl String/Element An id or Element from which the message box should animate as it opens and
3016 closes (defaults to undefined)
3017 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3018 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3019 closable Boolean False to hide the top-right close button (defaults to true). Note that
3020 progress and wait dialogs will ignore this property and always hide the
3021 close button as they can only be closed programmatically.
3022 cls String A custom CSS class to apply to the message box element
3023 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3024 displayed (defaults to 75)
3025 fn Function A callback function to execute after closing the dialog. The arguments to the
3026 function will be btn (the name of the button that was clicked, if applicable,
3027 e.g. "ok"), and text (the value of the active text field, if applicable).
3028 Progress and wait dialogs will ignore this option since they do not respond to
3029 user actions and can only be closed programmatically, so any required function
3030 should be called by the same code after it closes the dialog.
3031 icon String A CSS class that provides a background image to be used as an icon for
3032 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3033 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3034 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3035 modal Boolean False to allow user interaction with the page while the message box is
3036 displayed (defaults to true)
3037 msg String A string that will replace the existing message box body text (defaults
3038 to the XHTML-compliant non-breaking space character ' ')
3039 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3040 progress Boolean True to display a progress bar (defaults to false)
3041 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3042 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3043 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3044 title String The title text
3045 value String The string value to set into the active textbox element if displayed
3046 wait Boolean True to display a progress bar (defaults to false)
3047 width Number The width of the dialog in pixels
3054 msg: 'Please enter your address:',
3056 buttons: Roo.MessageBox.OKCANCEL,
3059 animEl: 'addAddressBtn'
3062 * @param {Object} config Configuration options
3063 * @return {Roo.MessageBox} This message box
3065 show : function(options)
3068 // this causes nightmares if you show one dialog after another
3069 // especially on callbacks..
3071 if(this.isVisible()){
3074 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3075 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3076 Roo.log("New Dialog Message:" + options.msg )
3077 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3078 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3081 var d = this.getDialog();
3083 d.setTitle(opt.title || " ");
3084 d.closeEl.setDisplayed(opt.closable !== false);
3085 activeTextEl = textboxEl;
3086 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3091 textareaEl.setHeight(typeof opt.multiline == "number" ?
3092 opt.multiline : this.defaultTextHeight);
3093 activeTextEl = textareaEl;
3102 progressEl.setDisplayed(opt.progress === true);
3103 this.updateProgress(0);
3104 activeTextEl.dom.value = opt.value || "";
3106 dlg.setDefaultButton(activeTextEl);
3108 var bs = opt.buttons;
3112 }else if(bs && bs.yes){
3113 db = buttons["yes"];
3115 dlg.setDefaultButton(db);
3117 bwidth = updateButtons(opt.buttons);
3118 this.updateText(opt.msg);
3120 d.el.addClass(opt.cls);
3122 d.proxyDrag = opt.proxyDrag === true;
3123 d.modal = opt.modal !== false;
3124 d.mask = opt.modal !== false ? mask : false;
3126 // force it to the end of the z-index stack so it gets a cursor in FF
3127 document.body.appendChild(dlg.el.dom);
3128 d.animateTarget = null;
3129 d.show(options.animEl);
3135 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3136 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3137 * and closing the message box when the process is complete.
3138 * @param {String} title The title bar text
3139 * @param {String} msg The message box body text
3140 * @return {Roo.MessageBox} This message box
3142 progress : function(title, msg){
3149 minWidth: this.minProgressWidth,
3156 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3157 * If a callback function is passed it will be called after the user clicks the button, and the
3158 * id of the button that was clicked will be passed as the only parameter to the callback
3159 * (could also be the top-right close button).
3160 * @param {String} title The title bar text
3161 * @param {String} msg The message box body text
3162 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3163 * @param {Object} scope (optional) The scope of the callback function
3164 * @return {Roo.MessageBox} This message box
3166 alert : function(title, msg, fn, scope){
3179 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3180 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3181 * You are responsible for closing the message box when the process is complete.
3182 * @param {String} msg The message box body text
3183 * @param {String} title (optional) The title bar text
3184 * @return {Roo.MessageBox} This message box
3186 wait : function(msg, title){
3197 waitTimer = Roo.TaskMgr.start({
3199 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3207 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3208 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3209 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3210 * @param {String} title The title bar text
3211 * @param {String} msg The message box body text
3212 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3213 * @param {Object} scope (optional) The scope of the callback function
3214 * @return {Roo.MessageBox} This message box
3216 confirm : function(title, msg, fn, scope){
3220 buttons: this.YESNO,
3229 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3230 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3231 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3232 * (could also be the top-right close button) and the text that was entered will be passed as the two
3233 * parameters to the callback.
3234 * @param {String} title The title bar text
3235 * @param {String} msg The message box body text
3236 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3237 * @param {Object} scope (optional) The scope of the callback function
3238 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3239 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3240 * @return {Roo.MessageBox} This message box
3242 prompt : function(title, msg, fn, scope, multiline){
3246 buttons: this.OKCANCEL,
3251 multiline: multiline,
3258 * Button config that displays a single OK button
3263 * Button config that displays Yes and No buttons
3266 YESNO : {yes:true, no:true},
3268 * Button config that displays OK and Cancel buttons
3271 OKCANCEL : {ok:true, cancel:true},
3273 * Button config that displays Yes, No and Cancel buttons
3276 YESNOCANCEL : {yes:true, no:true, cancel:true},
3279 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3282 defaultTextHeight : 75,
3284 * The maximum width in pixels of the message box (defaults to 600)
3289 * The minimum width in pixels of the message box (defaults to 100)
3294 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3295 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3298 minProgressWidth : 250,
3300 * An object containing the default button text strings that can be overriden for localized language support.
3301 * Supported properties are: ok, cancel, yes and no.
3302 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3315 * Shorthand for {@link Roo.MessageBox}
3317 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3318 Roo.Msg = Roo.Msg || Roo.MessageBox;
3327 * @class Roo.bootstrap.Navbar
3328 * @extends Roo.bootstrap.Component
3329 * Bootstrap Navbar class
3332 * Create a new Navbar
3333 * @param {Object} config The config object
3337 Roo.bootstrap.Navbar = function(config){
3338 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3342 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3351 getAutoCreate : function(){
3354 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3358 initEvents :function ()
3360 //Roo.log(this.el.select('.navbar-toggle',true));
3361 this.el.select('.navbar-toggle',true).on('click', function() {
3362 // Roo.log('click');
3363 this.el.select('.navbar-collapse',true).toggleClass('in');
3371 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3373 var size = this.el.getSize();
3374 this.maskEl.setSize(size.width, size.height);
3375 this.maskEl.enableDisplayMode("block");
3384 getChildContainer : function()
3386 if (this.el.select('.collapse').getCount()) {
3387 return this.el.select('.collapse',true).first();
3420 * @class Roo.bootstrap.NavSimplebar
3421 * @extends Roo.bootstrap.Navbar
3422 * Bootstrap Sidebar class
3424 * @cfg {Boolean} inverse is inverted color
3426 * @cfg {String} type (nav | pills | tabs)
3427 * @cfg {Boolean} arrangement stacked | justified
3428 * @cfg {String} align (left | right) alignment
3430 * @cfg {Boolean} main (true|false) main nav bar? default false
3431 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3433 * @cfg {String} tag (header|footer|nav|div) default is nav
3439 * Create a new Sidebar
3440 * @param {Object} config The config object
3444 Roo.bootstrap.NavSimplebar = function(config){
3445 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3448 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3464 getAutoCreate : function(){
3468 tag : this.tag || 'div',
3481 this.type = this.type || 'nav';
3482 if (['tabs','pills'].indexOf(this.type)!==-1) {
3483 cfg.cn[0].cls += ' nav-' + this.type
3487 if (this.type!=='nav') {
3488 Roo.log('nav type must be nav/tabs/pills')
3490 cfg.cn[0].cls += ' navbar-nav'
3496 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3497 cfg.cn[0].cls += ' nav-' + this.arrangement;
3501 if (this.align === 'right') {
3502 cfg.cn[0].cls += ' navbar-right';
3506 cfg.cls += ' navbar-inverse';
3533 * @class Roo.bootstrap.NavHeaderbar
3534 * @extends Roo.bootstrap.NavSimplebar
3535 * Bootstrap Sidebar class
3537 * @cfg {String} brand what is brand
3538 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3539 * @cfg {String} brand_href href of the brand
3540 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3541 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3542 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3543 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3546 * Create a new Sidebar
3547 * @param {Object} config The config object
3551 Roo.bootstrap.NavHeaderbar = function(config){
3552 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3556 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3563 desktopCenter : false,
3566 getAutoCreate : function(){
3569 tag: this.nav || 'nav',
3576 if (this.desktopCenter) {
3577 cn.push({cls : 'container', cn : []});
3584 cls: 'navbar-header',
3589 cls: 'navbar-toggle',
3590 'data-toggle': 'collapse',
3595 html: 'Toggle navigation'
3617 cls: 'collapse navbar-collapse',
3621 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3623 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3624 cfg.cls += ' navbar-' + this.position;
3626 // tag can override this..
3628 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3631 if (this.brand !== '') {
3634 href: this.brand_href ? this.brand_href : '#',
3635 cls: 'navbar-brand',
3643 cfg.cls += ' main-nav';
3651 getHeaderChildContainer : function()
3653 if (this.el.select('.navbar-header').getCount()) {
3654 return this.el.select('.navbar-header',true).first();
3657 return this.getChildContainer();
3661 initEvents : function()
3663 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3665 if (this.autohide) {
3670 Roo.get(document).on('scroll',function(e) {
3671 var ns = Roo.get(document).getScroll().top;
3672 var os = prevScroll;
3676 ft.removeClass('slideDown');
3677 ft.addClass('slideUp');
3680 ft.removeClass('slideUp');
3681 ft.addClass('slideDown');
3702 * @class Roo.bootstrap.NavSidebar
3703 * @extends Roo.bootstrap.Navbar
3704 * Bootstrap Sidebar class
3707 * Create a new Sidebar
3708 * @param {Object} config The config object
3712 Roo.bootstrap.NavSidebar = function(config){
3713 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3716 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3718 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3720 getAutoCreate : function(){
3725 cls: 'sidebar sidebar-nav'
3747 * @class Roo.bootstrap.NavGroup
3748 * @extends Roo.bootstrap.Component
3749 * Bootstrap NavGroup class
3750 * @cfg {String} align (left|right)
3751 * @cfg {Boolean} inverse
3752 * @cfg {String} type (nav|pills|tab) default nav
3753 * @cfg {String} navId - reference Id for navbar.
3757 * Create a new nav group
3758 * @param {Object} config The config object
3761 Roo.bootstrap.NavGroup = function(config){
3762 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3765 Roo.bootstrap.NavGroup.register(this);
3769 * Fires when the active item changes
3770 * @param {Roo.bootstrap.NavGroup} this
3771 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3772 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3779 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3790 getAutoCreate : function()
3792 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3799 if (['tabs','pills'].indexOf(this.type)!==-1) {
3800 cfg.cls += ' nav-' + this.type
3802 if (this.type!=='nav') {
3803 Roo.log('nav type must be nav/tabs/pills')
3805 cfg.cls += ' navbar-nav'
3808 if (this.parent().sidebar) {
3811 cls: 'dashboard-menu sidebar-menu'
3817 if (this.form === true) {
3823 if (this.align === 'right') {
3824 cfg.cls += ' navbar-right';
3826 cfg.cls += ' navbar-left';
3830 if (this.align === 'right') {
3831 cfg.cls += ' navbar-right';
3835 cfg.cls += ' navbar-inverse';
3843 * sets the active Navigation item
3844 * @param {Roo.bootstrap.NavItem} the new current navitem
3846 setActiveItem : function(item)
3849 Roo.each(this.navItems, function(v){
3854 v.setActive(false, true);
3861 item.setActive(true, true);
3862 this.fireEvent('changed', this, item, prev);
3867 * gets the active Navigation item
3868 * @return {Roo.bootstrap.NavItem} the current navitem
3870 getActive : function()
3874 Roo.each(this.navItems, function(v){
3885 indexOfNav : function()
3889 Roo.each(this.navItems, function(v,i){
3900 * adds a Navigation item
3901 * @param {Roo.bootstrap.NavItem} the navitem to add
3903 addItem : function(cfg)
3905 var cn = new Roo.bootstrap.NavItem(cfg);
3907 cn.parentId = this.id;
3908 cn.onRender(this.el, null);
3912 * register a Navigation item
3913 * @param {Roo.bootstrap.NavItem} the navitem to add
3915 register : function(item)
3917 this.navItems.push( item);
3918 item.navId = this.navId;
3923 * clear all the Navigation item
3926 clearAll : function()
3929 this.el.dom.innerHTML = '';
3932 getNavItem: function(tabId)
3935 Roo.each(this.navItems, function(e) {
3936 if (e.tabId == tabId) {
3946 setActiveNext : function()
3948 var i = this.indexOfNav(this.getActive());
3949 if (i > this.navItems.length) {
3952 this.setActiveItem(this.navItems[i+1]);
3954 setActivePrev : function()
3956 var i = this.indexOfNav(this.getActive());
3960 this.setActiveItem(this.navItems[i-1]);
3962 clearWasActive : function(except) {
3963 Roo.each(this.navItems, function(e) {
3964 if (e.tabId != except.tabId && e.was_active) {
3965 e.was_active = false;
3972 getWasActive : function ()
3975 Roo.each(this.navItems, function(e) {
3990 Roo.apply(Roo.bootstrap.NavGroup, {
3994 * register a Navigation Group
3995 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3997 register : function(navgrp)
3999 this.groups[navgrp.navId] = navgrp;
4003 * fetch a Navigation Group based on the navigation ID
4004 * @param {string} the navgroup to add
4005 * @returns {Roo.bootstrap.NavGroup} the navgroup
4007 get: function(navId) {
4008 if (typeof(this.groups[navId]) == 'undefined') {
4010 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4012 return this.groups[navId] ;
4027 * @class Roo.bootstrap.NavItem
4028 * @extends Roo.bootstrap.Component
4029 * Bootstrap Navbar.NavItem class
4030 * @cfg {String} href link to
4031 * @cfg {String} html content of button
4032 * @cfg {String} badge text inside badge
4033 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4034 * @cfg {String} glyphicon name of glyphicon
4035 * @cfg {String} icon name of font awesome icon
4036 * @cfg {Boolean} active Is item active
4037 * @cfg {Boolean} disabled Is item disabled
4039 * @cfg {Boolean} preventDefault (true | false) default false
4040 * @cfg {String} tabId the tab that this item activates.
4041 * @cfg {String} tagtype (a|span) render as a href or span?
4042 * @cfg {Boolean} animateRef (true|false) link to element default false
4045 * Create a new Navbar Item
4046 * @param {Object} config The config object
4048 Roo.bootstrap.NavItem = function(config){
4049 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4054 * The raw click event for the entire grid.
4055 * @param {Roo.EventObject} e
4060 * Fires when the active item active state changes
4061 * @param {Roo.bootstrap.NavItem} this
4062 * @param {boolean} state the new state
4068 * Fires when scroll to element
4069 * @param {Roo.bootstrap.NavItem} this
4070 * @param {Object} options
4071 * @param {Roo.EventObject} e
4079 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4087 preventDefault : false,
4094 getAutoCreate : function(){
4103 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4105 if (this.disabled) {
4106 cfg.cls += ' disabled';
4109 if (this.href || this.html || this.glyphicon || this.icon) {
4113 href : this.href || "#",
4114 html: this.html || ''
4119 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4122 if(this.glyphicon) {
4123 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4128 cfg.cn[0].html += " <span class='caret'></span>";
4132 if (this.badge !== '') {
4134 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4142 initEvents: function()
4144 if (typeof (this.menu) != 'undefined') {
4145 this.menu.parentType = this.xtype;
4146 this.menu.triggerEl = this.el;
4147 this.menu = this.addxtype(Roo.apply({}, this.menu));
4150 this.el.select('a',true).on('click', this.onClick, this);
4152 if(this.tagtype == 'span'){
4153 this.el.select('span',true).on('click', this.onClick, this);
4156 // at this point parent should be available..
4157 this.parent().register(this);
4160 onClick : function(e)
4163 this.preventDefault ||
4170 if (this.disabled) {
4174 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4175 if (tg && tg.transition) {
4176 Roo.log("waiting for the transitionend");
4182 //Roo.log("fire event clicked");
4183 if(this.fireEvent('click', this, e) === false){
4187 if(this.tagtype == 'span'){
4191 //Roo.log(this.href);
4192 var ael = this.el.select('a',true).first();
4195 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4196 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4197 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4198 return; // ignore... - it's a 'hash' to another page.
4202 this.scrollToElement(e);
4206 var p = this.parent();
4208 if (['tabs','pills'].indexOf(p.type)!==-1) {
4209 if (typeof(p.setActiveItem) !== 'undefined') {
4210 p.setActiveItem(this);
4214 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4215 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4216 // remove the collapsed menu expand...
4217 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4221 isActive: function () {
4224 setActive : function(state, fire, is_was_active)
4226 if (this.active && !state && this.navId) {
4227 this.was_active = true;
4228 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4230 nv.clearWasActive(this);
4234 this.active = state;
4237 this.el.removeClass('active');
4238 } else if (!this.el.hasClass('active')) {
4239 this.el.addClass('active');
4242 this.fireEvent('changed', this, state);
4245 // show a panel if it's registered and related..
4247 if (!this.navId || !this.tabId || !state || is_was_active) {
4251 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4255 var pan = tg.getPanelByName(this.tabId);
4259 // if we can not flip to new panel - go back to old nav highlight..
4260 if (false == tg.showPanel(pan)) {
4261 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4263 var onav = nv.getWasActive();
4265 onav.setActive(true, false, true);
4274 // this should not be here...
4275 setDisabled : function(state)
4277 this.disabled = state;
4279 this.el.removeClass('disabled');
4280 } else if (!this.el.hasClass('disabled')) {
4281 this.el.addClass('disabled');
4287 * Fetch the element to display the tooltip on.
4288 * @return {Roo.Element} defaults to this.el
4290 tooltipEl : function()
4292 return this.el.select('' + this.tagtype + '', true).first();
4295 scrollToElement : function(e)
4297 var c = document.body;
4300 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4302 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4303 c = document.documentElement;
4306 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4312 var o = target.calcOffsetsTo(c);
4319 this.fireEvent('scrollto', this, options, e);
4321 Roo.get(c).scrollTo('top', options.value, true);
4334 * <span> icon </span>
4335 * <span> text </span>
4336 * <span>badge </span>
4340 * @class Roo.bootstrap.NavSidebarItem
4341 * @extends Roo.bootstrap.NavItem
4342 * Bootstrap Navbar.NavSidebarItem class
4343 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4345 * Create a new Navbar Button
4346 * @param {Object} config The config object
4348 Roo.bootstrap.NavSidebarItem = function(config){
4349 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4354 * The raw click event for the entire grid.
4355 * @param {Roo.EventObject} e
4360 * Fires when the active item active state changes
4361 * @param {Roo.bootstrap.NavSidebarItem} this
4362 * @param {boolean} state the new state
4370 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4372 badgeWeight : 'default',
4374 getAutoCreate : function(){
4379 href : this.href || '#',
4391 html : this.html || ''
4396 cfg.cls += ' active';
4399 if (this.disabled) {
4400 cfg.cls += ' disabled';
4404 if (this.glyphicon || this.icon) {
4405 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4406 a.cn.push({ tag : 'i', cls : c }) ;
4411 if (this.badge !== '') {
4413 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4417 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4418 a.cls += 'dropdown-toggle treeview' ;
4429 initEvents : function()
4431 this.el.on('click', this.onClick, this);
4434 if(this.badge !== ''){
4436 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4441 onClick : function(e)
4448 if(this.preventDefault){
4452 this.fireEvent('click', this);
4455 disable : function()
4457 this.setDisabled(true);
4462 this.setDisabled(false);
4465 setDisabled : function(state)
4467 if(this.disabled == state){
4471 this.disabled = state;
4474 this.el.addClass('disabled');
4478 this.el.removeClass('disabled');
4483 setActive : function(state)
4485 if(this.active == state){
4489 this.active = state;
4492 this.el.addClass('active');
4496 this.el.removeClass('active');
4501 isActive: function ()
4506 setBadge : function(str)
4512 this.badgeEl.dom.innerHTML = str;
4529 * @class Roo.bootstrap.Row
4530 * @extends Roo.bootstrap.Component
4531 * Bootstrap Row class (contains columns...)
4535 * @param {Object} config The config object
4538 Roo.bootstrap.Row = function(config){
4539 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4542 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4544 getAutoCreate : function(){
4563 * @class Roo.bootstrap.Element
4564 * @extends Roo.bootstrap.Component
4565 * Bootstrap Element class
4566 * @cfg {String} html contents of the element
4567 * @cfg {String} tag tag of the element
4568 * @cfg {String} cls class of the element
4569 * @cfg {Boolean} preventDefault (true|false) default false
4570 * @cfg {Boolean} clickable (true|false) default false
4573 * Create a new Element
4574 * @param {Object} config The config object
4577 Roo.bootstrap.Element = function(config){
4578 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4584 * When a element is chick
4585 * @param {Roo.bootstrap.Element} this
4586 * @param {Roo.EventObject} e
4592 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4597 preventDefault: false,
4600 getAutoCreate : function(){
4611 initEvents: function()
4613 Roo.bootstrap.Element.superclass.initEvents.call(this);
4616 this.el.on('click', this.onClick, this);
4621 onClick : function(e)
4623 if(this.preventDefault){
4627 this.fireEvent('click', this, e);
4630 getValue : function()
4632 return this.el.dom.innerHTML;
4635 setValue : function(value)
4637 this.el.dom.innerHTML = value;
4652 * @class Roo.bootstrap.Pagination
4653 * @extends Roo.bootstrap.Component
4654 * Bootstrap Pagination class
4655 * @cfg {String} size xs | sm | md | lg
4656 * @cfg {Boolean} inverse false | true
4659 * Create a new Pagination
4660 * @param {Object} config The config object
4663 Roo.bootstrap.Pagination = function(config){
4664 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4667 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4673 getAutoCreate : function(){
4679 cfg.cls += ' inverse';
4685 cfg.cls += " " + this.cls;
4703 * @class Roo.bootstrap.PaginationItem
4704 * @extends Roo.bootstrap.Component
4705 * Bootstrap PaginationItem class
4706 * @cfg {String} html text
4707 * @cfg {String} href the link
4708 * @cfg {Boolean} preventDefault (true | false) default true
4709 * @cfg {Boolean} active (true | false) default false
4710 * @cfg {Boolean} disabled default false
4714 * Create a new PaginationItem
4715 * @param {Object} config The config object
4719 Roo.bootstrap.PaginationItem = function(config){
4720 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4725 * The raw click event for the entire grid.
4726 * @param {Roo.EventObject} e
4732 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4736 preventDefault: true,
4741 getAutoCreate : function(){
4747 href : this.href ? this.href : '#',
4748 html : this.html ? this.html : ''
4758 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4762 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4768 initEvents: function() {
4770 this.el.on('click', this.onClick, this);
4773 onClick : function(e)
4775 Roo.log('PaginationItem on click ');
4776 if(this.preventDefault){
4784 this.fireEvent('click', this, e);
4800 * @class Roo.bootstrap.Slider
4801 * @extends Roo.bootstrap.Component
4802 * Bootstrap Slider class
4805 * Create a new Slider
4806 * @param {Object} config The config object
4809 Roo.bootstrap.Slider = function(config){
4810 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4813 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4815 getAutoCreate : function(){
4819 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4823 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4835 * Ext JS Library 1.1.1
4836 * Copyright(c) 2006-2007, Ext JS, LLC.
4838 * Originally Released Under LGPL - original licence link has changed is not relivant.
4841 * <script type="text/javascript">
4846 * @class Roo.grid.ColumnModel
4847 * @extends Roo.util.Observable
4848 * This is the default implementation of a ColumnModel used by the Grid. It defines
4849 * the columns in the grid.
4852 var colModel = new Roo.grid.ColumnModel([
4853 {header: "Ticker", width: 60, sortable: true, locked: true},
4854 {header: "Company Name", width: 150, sortable: true},
4855 {header: "Market Cap.", width: 100, sortable: true},
4856 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4857 {header: "Employees", width: 100, sortable: true, resizable: false}
4862 * The config options listed for this class are options which may appear in each
4863 * individual column definition.
4864 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4866 * @param {Object} config An Array of column config objects. See this class's
4867 * config objects for details.
4869 Roo.grid.ColumnModel = function(config){
4871 * The config passed into the constructor
4873 this.config = config;
4876 // if no id, create one
4877 // if the column does not have a dataIndex mapping,
4878 // map it to the order it is in the config
4879 for(var i = 0, len = config.length; i < len; i++){
4881 if(typeof c.dataIndex == "undefined"){
4884 if(typeof c.renderer == "string"){
4885 c.renderer = Roo.util.Format[c.renderer];
4887 if(typeof c.id == "undefined"){
4890 if(c.editor && c.editor.xtype){
4891 c.editor = Roo.factory(c.editor, Roo.grid);
4893 if(c.editor && c.editor.isFormField){
4894 c.editor = new Roo.grid.GridEditor(c.editor);
4896 this.lookup[c.id] = c;
4900 * The width of columns which have no width specified (defaults to 100)
4903 this.defaultWidth = 100;
4906 * Default sortable of columns which have no sortable specified (defaults to false)
4909 this.defaultSortable = false;
4913 * @event widthchange
4914 * Fires when the width of a column changes.
4915 * @param {ColumnModel} this
4916 * @param {Number} columnIndex The column index
4917 * @param {Number} newWidth The new width
4919 "widthchange": true,
4921 * @event headerchange
4922 * Fires when the text of a header changes.
4923 * @param {ColumnModel} this
4924 * @param {Number} columnIndex The column index
4925 * @param {Number} newText The new header text
4927 "headerchange": true,
4929 * @event hiddenchange
4930 * Fires when a column is hidden or "unhidden".
4931 * @param {ColumnModel} this
4932 * @param {Number} columnIndex The column index
4933 * @param {Boolean} hidden true if hidden, false otherwise
4935 "hiddenchange": true,
4937 * @event columnmoved
4938 * Fires when a column is moved.
4939 * @param {ColumnModel} this
4940 * @param {Number} oldIndex
4941 * @param {Number} newIndex
4943 "columnmoved" : true,
4945 * @event columlockchange
4946 * Fires when a column's locked state is changed
4947 * @param {ColumnModel} this
4948 * @param {Number} colIndex
4949 * @param {Boolean} locked true if locked
4951 "columnlockchange" : true
4953 Roo.grid.ColumnModel.superclass.constructor.call(this);
4955 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4957 * @cfg {String} header The header text to display in the Grid view.
4960 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4961 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4962 * specified, the column's index is used as an index into the Record's data Array.
4965 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4966 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4969 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4970 * Defaults to the value of the {@link #defaultSortable} property.
4971 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4974 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4977 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4980 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4983 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4986 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4987 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4988 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4989 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4992 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4995 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4998 * @cfg {String} cursor (Optional)
5001 * @cfg {String} tooltip (Optional)
5004 * @cfg {Number} xs (Optional)
5007 * @cfg {Number} sm (Optional)
5010 * @cfg {Number} md (Optional)
5013 * @cfg {Number} lg (Optional)
5016 * Returns the id of the column at the specified index.
5017 * @param {Number} index The column index
5018 * @return {String} the id
5020 getColumnId : function(index){
5021 return this.config[index].id;
5025 * Returns the column for a specified id.
5026 * @param {String} id The column id
5027 * @return {Object} the column
5029 getColumnById : function(id){
5030 return this.lookup[id];
5035 * Returns the column for a specified dataIndex.
5036 * @param {String} dataIndex The column dataIndex
5037 * @return {Object|Boolean} the column or false if not found
5039 getColumnByDataIndex: function(dataIndex){
5040 var index = this.findColumnIndex(dataIndex);
5041 return index > -1 ? this.config[index] : false;
5045 * Returns the index for a specified column id.
5046 * @param {String} id The column id
5047 * @return {Number} the index, or -1 if not found
5049 getIndexById : function(id){
5050 for(var i = 0, len = this.config.length; i < len; i++){
5051 if(this.config[i].id == id){
5059 * Returns the index for a specified column dataIndex.
5060 * @param {String} dataIndex The column dataIndex
5061 * @return {Number} the index, or -1 if not found
5064 findColumnIndex : function(dataIndex){
5065 for(var i = 0, len = this.config.length; i < len; i++){
5066 if(this.config[i].dataIndex == dataIndex){
5074 moveColumn : function(oldIndex, newIndex){
5075 var c = this.config[oldIndex];
5076 this.config.splice(oldIndex, 1);
5077 this.config.splice(newIndex, 0, c);
5078 this.dataMap = null;
5079 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5082 isLocked : function(colIndex){
5083 return this.config[colIndex].locked === true;
5086 setLocked : function(colIndex, value, suppressEvent){
5087 if(this.isLocked(colIndex) == value){
5090 this.config[colIndex].locked = value;
5092 this.fireEvent("columnlockchange", this, colIndex, value);
5096 getTotalLockedWidth : function(){
5098 for(var i = 0; i < this.config.length; i++){
5099 if(this.isLocked(i) && !this.isHidden(i)){
5100 this.totalWidth += this.getColumnWidth(i);
5106 getLockedCount : function(){
5107 for(var i = 0, len = this.config.length; i < len; i++){
5108 if(!this.isLocked(i)){
5115 * Returns the number of columns.
5118 getColumnCount : function(visibleOnly){
5119 if(visibleOnly === true){
5121 for(var i = 0, len = this.config.length; i < len; i++){
5122 if(!this.isHidden(i)){
5128 return this.config.length;
5132 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5133 * @param {Function} fn
5134 * @param {Object} scope (optional)
5135 * @return {Array} result
5137 getColumnsBy : function(fn, scope){
5139 for(var i = 0, len = this.config.length; i < len; i++){
5140 var c = this.config[i];
5141 if(fn.call(scope||this, c, i) === true){
5149 * Returns true if the specified column is sortable.
5150 * @param {Number} col The column index
5153 isSortable : function(col){
5154 if(typeof this.config[col].sortable == "undefined"){
5155 return this.defaultSortable;
5157 return this.config[col].sortable;
5161 * Returns the rendering (formatting) function defined for the column.
5162 * @param {Number} col The column index.
5163 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5165 getRenderer : function(col){
5166 if(!this.config[col].renderer){
5167 return Roo.grid.ColumnModel.defaultRenderer;
5169 return this.config[col].renderer;
5173 * Sets the rendering (formatting) function for a column.
5174 * @param {Number} col The column index
5175 * @param {Function} fn The function to use to process the cell's raw data
5176 * to return HTML markup for the grid view. The render function is called with
5177 * the following parameters:<ul>
5178 * <li>Data value.</li>
5179 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5180 * <li>css A CSS style string to apply to the table cell.</li>
5181 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5182 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5183 * <li>Row index</li>
5184 * <li>Column index</li>
5185 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5187 setRenderer : function(col, fn){
5188 this.config[col].renderer = fn;
5192 * Returns the width for the specified column.
5193 * @param {Number} col The column index
5196 getColumnWidth : function(col){
5197 return this.config[col].width * 1 || this.defaultWidth;
5201 * Sets the width for a column.
5202 * @param {Number} col The column index
5203 * @param {Number} width The new width
5205 setColumnWidth : function(col, width, suppressEvent){
5206 this.config[col].width = width;
5207 this.totalWidth = null;
5209 this.fireEvent("widthchange", this, col, width);
5214 * Returns the total width of all columns.
5215 * @param {Boolean} includeHidden True to include hidden column widths
5218 getTotalWidth : function(includeHidden){
5219 if(!this.totalWidth){
5220 this.totalWidth = 0;
5221 for(var i = 0, len = this.config.length; i < len; i++){
5222 if(includeHidden || !this.isHidden(i)){
5223 this.totalWidth += this.getColumnWidth(i);
5227 return this.totalWidth;
5231 * Returns the header for the specified column.
5232 * @param {Number} col The column index
5235 getColumnHeader : function(col){
5236 return this.config[col].header;
5240 * Sets the header for a column.
5241 * @param {Number} col The column index
5242 * @param {String} header The new header
5244 setColumnHeader : function(col, header){
5245 this.config[col].header = header;
5246 this.fireEvent("headerchange", this, col, header);
5250 * Returns the tooltip for the specified column.
5251 * @param {Number} col The column index
5254 getColumnTooltip : function(col){
5255 return this.config[col].tooltip;
5258 * Sets the tooltip for a column.
5259 * @param {Number} col The column index
5260 * @param {String} tooltip The new tooltip
5262 setColumnTooltip : function(col, tooltip){
5263 this.config[col].tooltip = tooltip;
5267 * Returns the dataIndex for the specified column.
5268 * @param {Number} col The column index
5271 getDataIndex : function(col){
5272 return this.config[col].dataIndex;
5276 * Sets the dataIndex for a column.
5277 * @param {Number} col The column index
5278 * @param {Number} dataIndex The new dataIndex
5280 setDataIndex : function(col, dataIndex){
5281 this.config[col].dataIndex = dataIndex;
5287 * Returns true if the cell is editable.
5288 * @param {Number} colIndex The column index
5289 * @param {Number} rowIndex The row index
5292 isCellEditable : function(colIndex, rowIndex){
5293 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5297 * Returns the editor defined for the cell/column.
5298 * return false or null to disable editing.
5299 * @param {Number} colIndex The column index
5300 * @param {Number} rowIndex The row index
5303 getCellEditor : function(colIndex, rowIndex){
5304 return this.config[colIndex].editor;
5308 * Sets if a column is editable.
5309 * @param {Number} col The column index
5310 * @param {Boolean} editable True if the column is editable
5312 setEditable : function(col, editable){
5313 this.config[col].editable = editable;
5318 * Returns true if the column is hidden.
5319 * @param {Number} colIndex The column index
5322 isHidden : function(colIndex){
5323 return this.config[colIndex].hidden;
5328 * Returns true if the column width cannot be changed
5330 isFixed : function(colIndex){
5331 return this.config[colIndex].fixed;
5335 * Returns true if the column can be resized
5338 isResizable : function(colIndex){
5339 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5342 * Sets if a column is hidden.
5343 * @param {Number} colIndex The column index
5344 * @param {Boolean} hidden True if the column is hidden
5346 setHidden : function(colIndex, hidden){
5347 this.config[colIndex].hidden = hidden;
5348 this.totalWidth = null;
5349 this.fireEvent("hiddenchange", this, colIndex, hidden);
5353 * Sets the editor for a column.
5354 * @param {Number} col The column index
5355 * @param {Object} editor The editor object
5357 setEditor : function(col, editor){
5358 this.config[col].editor = editor;
5362 Roo.grid.ColumnModel.defaultRenderer = function(value){
5363 if(typeof value == "string" && value.length < 1){
5369 // Alias for backwards compatibility
5370 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5373 * Ext JS Library 1.1.1
5374 * Copyright(c) 2006-2007, Ext JS, LLC.
5376 * Originally Released Under LGPL - original licence link has changed is not relivant.
5379 * <script type="text/javascript">
5383 * @class Roo.LoadMask
5384 * A simple utility class for generically masking elements while loading data. If the element being masked has
5385 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5386 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5387 * element's UpdateManager load indicator and will be destroyed after the initial load.
5389 * Create a new LoadMask
5390 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5391 * @param {Object} config The config object
5393 Roo.LoadMask = function(el, config){
5394 this.el = Roo.get(el);
5395 Roo.apply(this, config);
5397 this.store.on('beforeload', this.onBeforeLoad, this);
5398 this.store.on('load', this.onLoad, this);
5399 this.store.on('loadexception', this.onLoadException, this);
5400 this.removeMask = false;
5402 var um = this.el.getUpdateManager();
5403 um.showLoadIndicator = false; // disable the default indicator
5404 um.on('beforeupdate', this.onBeforeLoad, this);
5405 um.on('update', this.onLoad, this);
5406 um.on('failure', this.onLoad, this);
5407 this.removeMask = true;
5411 Roo.LoadMask.prototype = {
5413 * @cfg {Boolean} removeMask
5414 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5415 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5419 * The text to display in a centered loading message box (defaults to 'Loading...')
5423 * @cfg {String} msgCls
5424 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5426 msgCls : 'x-mask-loading',
5429 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5435 * Disables the mask to prevent it from being displayed
5437 disable : function(){
5438 this.disabled = true;
5442 * Enables the mask so that it can be displayed
5444 enable : function(){
5445 this.disabled = false;
5448 onLoadException : function()
5452 if (typeof(arguments[3]) != 'undefined') {
5453 Roo.MessageBox.alert("Error loading",arguments[3]);
5457 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5458 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5467 this.el.unmask(this.removeMask);
5472 this.el.unmask(this.removeMask);
5476 onBeforeLoad : function(){
5478 this.el.mask(this.msg, this.msgCls);
5483 destroy : function(){
5485 this.store.un('beforeload', this.onBeforeLoad, this);
5486 this.store.un('load', this.onLoad, this);
5487 this.store.un('loadexception', this.onLoadException, this);
5489 var um = this.el.getUpdateManager();
5490 um.un('beforeupdate', this.onBeforeLoad, this);
5491 um.un('update', this.onLoad, this);
5492 um.un('failure', this.onLoad, this);
5503 * @class Roo.bootstrap.Table
5504 * @extends Roo.bootstrap.Component
5505 * Bootstrap Table class
5506 * @cfg {String} cls table class
5507 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5508 * @cfg {String} bgcolor Specifies the background color for a table
5509 * @cfg {Number} border Specifies whether the table cells should have borders or not
5510 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5511 * @cfg {Number} cellspacing Specifies the space between cells
5512 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5513 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5514 * @cfg {String} sortable Specifies that the table should be sortable
5515 * @cfg {String} summary Specifies a summary of the content of a table
5516 * @cfg {Number} width Specifies the width of a table
5517 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5519 * @cfg {boolean} striped Should the rows be alternative striped
5520 * @cfg {boolean} bordered Add borders to the table
5521 * @cfg {boolean} hover Add hover highlighting
5522 * @cfg {boolean} condensed Format condensed
5523 * @cfg {boolean} responsive Format condensed
5524 * @cfg {Boolean} loadMask (true|false) default false
5525 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5526 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5527 * @cfg {Boolean} rowSelection (true|false) default false
5528 * @cfg {Boolean} cellSelection (true|false) default false
5529 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5533 * Create a new Table
5534 * @param {Object} config The config object
5537 Roo.bootstrap.Table = function(config){
5538 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5541 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5542 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5543 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5544 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5548 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5549 this.sm = this.selModel;
5550 this.sm.xmodule = this.xmodule || false;
5552 if (this.cm && typeof(this.cm.config) == 'undefined') {
5553 this.colModel = new Roo.grid.ColumnModel(this.cm);
5554 this.cm = this.colModel;
5555 this.cm.xmodule = this.xmodule || false;
5558 this.store= Roo.factory(this.store, Roo.data);
5559 this.ds = this.store;
5560 this.ds.xmodule = this.xmodule || false;
5563 if (this.footer && this.store) {
5564 this.footer.dataSource = this.ds;
5565 this.footer = Roo.factory(this.footer);
5572 * Fires when a cell is clicked
5573 * @param {Roo.bootstrap.Table} this
5574 * @param {Roo.Element} el
5575 * @param {Number} rowIndex
5576 * @param {Number} columnIndex
5577 * @param {Roo.EventObject} e
5581 * @event celldblclick
5582 * Fires when a cell is double clicked
5583 * @param {Roo.bootstrap.Table} this
5584 * @param {Roo.Element} el
5585 * @param {Number} rowIndex
5586 * @param {Number} columnIndex
5587 * @param {Roo.EventObject} e
5589 "celldblclick" : true,
5592 * Fires when a row is clicked
5593 * @param {Roo.bootstrap.Table} this
5594 * @param {Roo.Element} el
5595 * @param {Number} rowIndex
5596 * @param {Roo.EventObject} e
5600 * @event rowdblclick
5601 * Fires when a row is double clicked
5602 * @param {Roo.bootstrap.Table} this
5603 * @param {Roo.Element} el
5604 * @param {Number} rowIndex
5605 * @param {Roo.EventObject} e
5607 "rowdblclick" : true,
5610 * Fires when a mouseover occur
5611 * @param {Roo.bootstrap.Table} this
5612 * @param {Roo.Element} el
5613 * @param {Number} rowIndex
5614 * @param {Number} columnIndex
5615 * @param {Roo.EventObject} e
5620 * Fires when a mouseout occur
5621 * @param {Roo.bootstrap.Table} this
5622 * @param {Roo.Element} el
5623 * @param {Number} rowIndex
5624 * @param {Number} columnIndex
5625 * @param {Roo.EventObject} e
5630 * Fires when a row is rendered, so you can change add a style to it.
5631 * @param {Roo.bootstrap.Table} this
5632 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5636 * @event rowsrendered
5637 * Fires when all the rows have been rendered
5638 * @param {Roo.bootstrap.Table} this
5640 'rowsrendered' : true
5645 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5670 rowSelection : false,
5671 cellSelection : false,
5674 // Roo.Element - the tbody
5677 getAutoCreate : function(){
5678 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5687 cfg.cls += ' table-striped';
5691 cfg.cls += ' table-hover';
5693 if (this.bordered) {
5694 cfg.cls += ' table-bordered';
5696 if (this.condensed) {
5697 cfg.cls += ' table-condensed';
5699 if (this.responsive) {
5700 cfg.cls += ' table-responsive';
5704 cfg.cls+= ' ' +this.cls;
5707 // this lot should be simplifed...
5710 cfg.align=this.align;
5713 cfg.bgcolor=this.bgcolor;
5716 cfg.border=this.border;
5718 if (this.cellpadding) {
5719 cfg.cellpadding=this.cellpadding;
5721 if (this.cellspacing) {
5722 cfg.cellspacing=this.cellspacing;
5725 cfg.frame=this.frame;
5728 cfg.rules=this.rules;
5730 if (this.sortable) {
5731 cfg.sortable=this.sortable;
5734 cfg.summary=this.summary;
5737 cfg.width=this.width;
5740 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5743 if(this.store || this.cm){
5744 if(this.headerShow){
5745 cfg.cn.push(this.renderHeader());
5748 cfg.cn.push(this.renderBody());
5750 if(this.footerShow){
5751 cfg.cn.push(this.renderFooter());
5754 cfg.cls+= ' TableGrid';
5757 return { cn : [ cfg ] };
5760 initEvents : function()
5762 if(!this.store || !this.cm){
5766 //Roo.log('initEvents with ds!!!!');
5768 this.mainBody = this.el.select('tbody', true).first();
5773 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5774 e.on('click', _this.sort, _this);
5777 this.el.on("click", this.onClick, this);
5778 this.el.on("dblclick", this.onDblClick, this);
5780 // why is this done????? = it breaks dialogs??
5781 //this.parent().el.setStyle('position', 'relative');
5785 this.footer.parentId = this.id;
5786 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5789 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5791 this.store.on('load', this.onLoad, this);
5792 this.store.on('beforeload', this.onBeforeLoad, this);
5793 this.store.on('update', this.onUpdate, this);
5794 this.store.on('add', this.onAdd, this);
5798 onMouseover : function(e, el)
5800 var cell = Roo.get(el);
5806 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5807 cell = cell.findParent('td', false, true);
5810 var row = cell.findParent('tr', false, true);
5811 var cellIndex = cell.dom.cellIndex;
5812 var rowIndex = row.dom.rowIndex - 1; // start from 0
5814 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5818 onMouseout : function(e, el)
5820 var cell = Roo.get(el);
5826 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5827 cell = cell.findParent('td', false, true);
5830 var row = cell.findParent('tr', false, true);
5831 var cellIndex = cell.dom.cellIndex;
5832 var rowIndex = row.dom.rowIndex - 1; // start from 0
5834 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5838 onClick : function(e, el)
5840 var cell = Roo.get(el);
5842 if(!cell || (!this.cellSelection && !this.rowSelection)){
5846 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5847 cell = cell.findParent('td', false, true);
5850 if(!cell || typeof(cell) == 'undefined'){
5854 var row = cell.findParent('tr', false, true);
5856 if(!row || typeof(row) == 'undefined'){
5860 var cellIndex = cell.dom.cellIndex;
5861 var rowIndex = this.getRowIndex(row);
5863 // why??? - should these not be based on SelectionModel?
5864 if(this.cellSelection){
5865 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5868 if(this.rowSelection){
5869 this.fireEvent('rowclick', this, row, rowIndex, e);
5875 onDblClick : function(e,el)
5877 var cell = Roo.get(el);
5879 if(!cell || (!this.CellSelection && !this.RowSelection)){
5883 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5884 cell = cell.findParent('td', false, true);
5887 if(!cell || typeof(cell) == 'undefined'){
5891 var row = cell.findParent('tr', false, true);
5893 if(!row || typeof(row) == 'undefined'){
5897 var cellIndex = cell.dom.cellIndex;
5898 var rowIndex = this.getRowIndex(row);
5900 if(this.CellSelection){
5901 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5904 if(this.RowSelection){
5905 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5909 sort : function(e,el)
5911 var col = Roo.get(el);
5913 if(!col.hasClass('sortable')){
5917 var sort = col.attr('sort');
5920 if(col.hasClass('glyphicon-arrow-up')){
5924 this.store.sortInfo = {field : sort, direction : dir};
5927 Roo.log("calling footer first");
5928 this.footer.onClick('first');
5931 this.store.load({ params : { start : 0 } });
5935 renderHeader : function()
5944 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5946 var config = cm.config[i];
5951 html: cm.getColumnHeader(i)
5956 if(typeof(config.lgHeader) != 'undefined'){
5957 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5960 if(typeof(config.mdHeader) != 'undefined'){
5961 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5964 if(typeof(config.smHeader) != 'undefined'){
5965 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5968 if(typeof(config.xsHeader) != 'undefined'){
5969 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5976 if(typeof(config.tooltip) != 'undefined'){
5977 c.tooltip = config.tooltip;
5980 if(typeof(config.colspan) != 'undefined'){
5981 c.colspan = config.colspan;
5984 if(typeof(config.hidden) != 'undefined' && config.hidden){
5985 c.style += ' display:none;';
5988 if(typeof(config.dataIndex) != 'undefined'){
5989 c.sort = config.dataIndex;
5992 if(typeof(config.sortable) != 'undefined' && config.sortable){
5996 if(typeof(config.align) != 'undefined' && config.align.length){
5997 c.style += ' text-align:' + config.align + ';';
6000 if(typeof(config.width) != 'undefined'){
6001 c.style += ' width:' + config.width + 'px;';
6004 if(typeof(config.cls) != 'undefined'){
6005 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6008 ['xs','sm','md','lg'].map(function(size){
6010 if(typeof(config[size]) == 'undefined'){
6014 if (!config[size]) { // 0 = hidden
6015 cfg.cls += ' hidden-' + size;
6019 cfg.cls += ' col-' + size + '-' + config[size];
6029 renderBody : function()
6039 colspan : this.cm.getColumnCount()
6049 renderFooter : function()
6059 colspan : this.cm.getColumnCount()
6073 Roo.log('ds onload');
6078 var ds = this.store;
6080 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6081 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6083 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6084 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6087 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6088 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6092 var tbody = this.mainBody;
6094 if(ds.getCount() > 0){
6095 ds.data.each(function(d,rowIndex){
6096 var row = this.renderRow(cm, ds, rowIndex);
6098 tbody.createChild(row);
6102 if(row.cellObjects.length){
6103 Roo.each(row.cellObjects, function(r){
6104 _this.renderCellObject(r);
6111 Roo.each(this.el.select('tbody td', true).elements, function(e){
6112 e.on('mouseover', _this.onMouseover, _this);
6115 Roo.each(this.el.select('tbody td', true).elements, function(e){
6116 e.on('mouseout', _this.onMouseout, _this);
6118 this.fireEvent('rowsrendered', this);
6119 //if(this.loadMask){
6120 // this.maskEl.hide();
6125 onUpdate : function(ds,record)
6127 this.refreshRow(record);
6130 onRemove : function(ds, record, index, isUpdate){
6131 if(isUpdate !== true){
6132 this.fireEvent("beforerowremoved", this, index, record);
6134 var bt = this.mainBody.dom;
6136 var rows = this.el.select('tbody > tr', true).elements;
6138 if(typeof(rows[index]) != 'undefined'){
6139 bt.removeChild(rows[index].dom);
6142 // if(bt.rows[index]){
6143 // bt.removeChild(bt.rows[index]);
6146 if(isUpdate !== true){
6147 //this.stripeRows(index);
6148 //this.syncRowHeights(index, index);
6150 this.fireEvent("rowremoved", this, index, record);
6154 onAdd : function(ds, records, rowIndex)
6156 //Roo.log('on Add called');
6157 // - note this does not handle multiple adding very well..
6158 var bt = this.mainBody.dom;
6159 for (var i =0 ; i < records.length;i++) {
6160 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6161 //Roo.log(records[i]);
6162 //Roo.log(this.store.getAt(rowIndex+i));
6163 this.insertRow(this.store, rowIndex + i, false);
6170 refreshRow : function(record){
6171 var ds = this.store, index;
6172 if(typeof record == 'number'){
6174 record = ds.getAt(index);
6176 index = ds.indexOf(record);
6178 this.insertRow(ds, index, true);
6179 this.onRemove(ds, record, index+1, true);
6180 //this.syncRowHeights(index, index);
6182 this.fireEvent("rowupdated", this, index, record);
6185 insertRow : function(dm, rowIndex, isUpdate){
6188 this.fireEvent("beforerowsinserted", this, rowIndex);
6190 //var s = this.getScrollState();
6191 var row = this.renderRow(this.cm, this.store, rowIndex);
6192 // insert before rowIndex..
6193 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6197 if(row.cellObjects.length){
6198 Roo.each(row.cellObjects, function(r){
6199 _this.renderCellObject(r);
6204 this.fireEvent("rowsinserted", this, rowIndex);
6205 //this.syncRowHeights(firstRow, lastRow);
6206 //this.stripeRows(firstRow);
6213 getRowDom : function(rowIndex)
6215 var rows = this.el.select('tbody > tr', true).elements;
6217 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6220 // returns the object tree for a tr..
6223 renderRow : function(cm, ds, rowIndex)
6226 var d = ds.getAt(rowIndex);
6233 var cellObjects = [];
6235 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6236 var config = cm.config[i];
6238 var renderer = cm.getRenderer(i);
6242 if(typeof(renderer) !== 'undefined'){
6243 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6245 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6246 // and are rendered into the cells after the row is rendered - using the id for the element.
6248 if(typeof(value) === 'object'){
6258 rowIndex : rowIndex,
6263 this.fireEvent('rowclass', this, rowcfg);
6267 cls : rowcfg.rowClass,
6269 html: (typeof(value) === 'object') ? '' : value
6276 if(typeof(config.colspan) != 'undefined'){
6277 td.colspan = config.colspan;
6280 if(typeof(config.hidden) != 'undefined' && config.hidden){
6281 td.style += ' display:none;';
6284 if(typeof(config.align) != 'undefined' && config.align.length){
6285 td.style += ' text-align:' + config.align + ';';
6288 if(typeof(config.width) != 'undefined'){
6289 td.style += ' width:' + config.width + 'px;';
6292 if(typeof(config.cursor) != 'undefined'){
6293 td.style += ' cursor:' + config.cursor + ';';
6296 if(typeof(config.cls) != 'undefined'){
6297 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6300 ['xs','sm','md','lg'].map(function(size){
6302 if(typeof(config[size]) == 'undefined'){
6306 if (!config[size]) { // 0 = hidden
6307 td.cls += ' hidden-' + size;
6311 td.cls += ' col-' + size + '-' + config[size];
6319 row.cellObjects = cellObjects;
6327 onBeforeLoad : function()
6329 //Roo.log('ds onBeforeLoad');
6333 //if(this.loadMask){
6334 // this.maskEl.show();
6342 this.el.select('tbody', true).first().dom.innerHTML = '';
6345 * Show or hide a row.
6346 * @param {Number} rowIndex to show or hide
6347 * @param {Boolean} state hide
6349 setRowVisibility : function(rowIndex, state)
6351 var bt = this.mainBody.dom;
6353 var rows = this.el.select('tbody > tr', true).elements;
6355 if(typeof(rows[rowIndex]) == 'undefined'){
6358 rows[rowIndex].dom.style.display = state ? '' : 'none';
6362 getSelectionModel : function(){
6364 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6366 return this.selModel;
6369 * Render the Roo.bootstrap object from renderder
6371 renderCellObject : function(r)
6375 var t = r.cfg.render(r.container);
6378 Roo.each(r.cfg.cn, function(c){
6380 container: t.getChildContainer(),
6383 _this.renderCellObject(child);
6388 getRowIndex : function(row)
6392 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6415 * @class Roo.bootstrap.TableCell
6416 * @extends Roo.bootstrap.Component
6417 * Bootstrap TableCell class
6418 * @cfg {String} html cell contain text
6419 * @cfg {String} cls cell class
6420 * @cfg {String} tag cell tag (td|th) default td
6421 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6422 * @cfg {String} align Aligns the content in a cell
6423 * @cfg {String} axis Categorizes cells
6424 * @cfg {String} bgcolor Specifies the background color of a cell
6425 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6426 * @cfg {Number} colspan Specifies the number of columns a cell should span
6427 * @cfg {String} headers Specifies one or more header cells a cell is related to
6428 * @cfg {Number} height Sets the height of a cell
6429 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6430 * @cfg {Number} rowspan Sets the number of rows a cell should span
6431 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6432 * @cfg {String} valign Vertical aligns the content in a cell
6433 * @cfg {Number} width Specifies the width of a cell
6436 * Create a new TableCell
6437 * @param {Object} config The config object
6440 Roo.bootstrap.TableCell = function(config){
6441 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6444 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6464 getAutoCreate : function(){
6465 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6485 cfg.align=this.align
6491 cfg.bgcolor=this.bgcolor
6494 cfg.charoff=this.charoff
6497 cfg.colspan=this.colspan
6500 cfg.headers=this.headers
6503 cfg.height=this.height
6506 cfg.nowrap=this.nowrap
6509 cfg.rowspan=this.rowspan
6512 cfg.scope=this.scope
6515 cfg.valign=this.valign
6518 cfg.width=this.width
6537 * @class Roo.bootstrap.TableRow
6538 * @extends Roo.bootstrap.Component
6539 * Bootstrap TableRow class
6540 * @cfg {String} cls row class
6541 * @cfg {String} align Aligns the content in a table row
6542 * @cfg {String} bgcolor Specifies a background color for a table row
6543 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6544 * @cfg {String} valign Vertical aligns the content in a table row
6547 * Create a new TableRow
6548 * @param {Object} config The config object
6551 Roo.bootstrap.TableRow = function(config){
6552 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6555 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6563 getAutoCreate : function(){
6564 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6574 cfg.align = this.align;
6577 cfg.bgcolor = this.bgcolor;
6580 cfg.charoff = this.charoff;
6583 cfg.valign = this.valign;
6601 * @class Roo.bootstrap.TableBody
6602 * @extends Roo.bootstrap.Component
6603 * Bootstrap TableBody class
6604 * @cfg {String} cls element class
6605 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6606 * @cfg {String} align Aligns the content inside the element
6607 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6608 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6611 * Create a new TableBody
6612 * @param {Object} config The config object
6615 Roo.bootstrap.TableBody = function(config){
6616 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6619 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6627 getAutoCreate : function(){
6628 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6642 cfg.align = this.align;
6645 cfg.charoff = this.charoff;
6648 cfg.valign = this.valign;
6655 // initEvents : function()
6662 // this.store = Roo.factory(this.store, Roo.data);
6663 // this.store.on('load', this.onLoad, this);
6665 // this.store.load();
6669 // onLoad: function ()
6671 // this.fireEvent('load', this);
6681 * Ext JS Library 1.1.1
6682 * Copyright(c) 2006-2007, Ext JS, LLC.
6684 * Originally Released Under LGPL - original licence link has changed is not relivant.
6687 * <script type="text/javascript">
6690 // as we use this in bootstrap.
6691 Roo.namespace('Roo.form');
6693 * @class Roo.form.Action
6694 * Internal Class used to handle form actions
6696 * @param {Roo.form.BasicForm} el The form element or its id
6697 * @param {Object} config Configuration options
6702 // define the action interface
6703 Roo.form.Action = function(form, options){
6705 this.options = options || {};
6708 * Client Validation Failed
6711 Roo.form.Action.CLIENT_INVALID = 'client';
6713 * Server Validation Failed
6716 Roo.form.Action.SERVER_INVALID = 'server';
6718 * Connect to Server Failed
6721 Roo.form.Action.CONNECT_FAILURE = 'connect';
6723 * Reading Data from Server Failed
6726 Roo.form.Action.LOAD_FAILURE = 'load';
6728 Roo.form.Action.prototype = {
6730 failureType : undefined,
6731 response : undefined,
6735 run : function(options){
6740 success : function(response){
6745 handleResponse : function(response){
6749 // default connection failure
6750 failure : function(response){
6752 this.response = response;
6753 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6754 this.form.afterAction(this, false);
6757 processResponse : function(response){
6758 this.response = response;
6759 if(!response.responseText){
6762 this.result = this.handleResponse(response);
6766 // utility functions used internally
6767 getUrl : function(appendParams){
6768 var url = this.options.url || this.form.url || this.form.el.dom.action;
6770 var p = this.getParams();
6772 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6778 getMethod : function(){
6779 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6782 getParams : function(){
6783 var bp = this.form.baseParams;
6784 var p = this.options.params;
6786 if(typeof p == "object"){
6787 p = Roo.urlEncode(Roo.applyIf(p, bp));
6788 }else if(typeof p == 'string' && bp){
6789 p += '&' + Roo.urlEncode(bp);
6792 p = Roo.urlEncode(bp);
6797 createCallback : function(){
6799 success: this.success,
6800 failure: this.failure,
6802 timeout: (this.form.timeout*1000),
6803 upload: this.form.fileUpload ? this.success : undefined
6808 Roo.form.Action.Submit = function(form, options){
6809 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6812 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6815 haveProgress : false,
6816 uploadComplete : false,
6818 // uploadProgress indicator.
6819 uploadProgress : function()
6821 if (!this.form.progressUrl) {
6825 if (!this.haveProgress) {
6826 Roo.MessageBox.progress("Uploading", "Uploading");
6828 if (this.uploadComplete) {
6829 Roo.MessageBox.hide();
6833 this.haveProgress = true;
6835 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6837 var c = new Roo.data.Connection();
6839 url : this.form.progressUrl,
6844 success : function(req){
6845 //console.log(data);
6849 rdata = Roo.decode(req.responseText)
6851 Roo.log("Invalid data from server..");
6855 if (!rdata || !rdata.success) {
6857 Roo.MessageBox.alert(Roo.encode(rdata));
6860 var data = rdata.data;
6862 if (this.uploadComplete) {
6863 Roo.MessageBox.hide();
6868 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6869 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6872 this.uploadProgress.defer(2000,this);
6875 failure: function(data) {
6876 Roo.log('progress url failed ');
6887 // run get Values on the form, so it syncs any secondary forms.
6888 this.form.getValues();
6890 var o = this.options;
6891 var method = this.getMethod();
6892 var isPost = method == 'POST';
6893 if(o.clientValidation === false || this.form.isValid()){
6895 if (this.form.progressUrl) {
6896 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6897 (new Date() * 1) + '' + Math.random());
6902 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6903 form:this.form.el.dom,
6904 url:this.getUrl(!isPost),
6906 params:isPost ? this.getParams() : null,
6907 isUpload: this.form.fileUpload
6910 this.uploadProgress();
6912 }else if (o.clientValidation !== false){ // client validation failed
6913 this.failureType = Roo.form.Action.CLIENT_INVALID;
6914 this.form.afterAction(this, false);
6918 success : function(response)
6920 this.uploadComplete= true;
6921 if (this.haveProgress) {
6922 Roo.MessageBox.hide();
6926 var result = this.processResponse(response);
6927 if(result === true || result.success){
6928 this.form.afterAction(this, true);
6932 this.form.markInvalid(result.errors);
6933 this.failureType = Roo.form.Action.SERVER_INVALID;
6935 this.form.afterAction(this, false);
6937 failure : function(response)
6939 this.uploadComplete= true;
6940 if (this.haveProgress) {
6941 Roo.MessageBox.hide();
6944 this.response = response;
6945 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6946 this.form.afterAction(this, false);
6949 handleResponse : function(response){
6950 if(this.form.errorReader){
6951 var rs = this.form.errorReader.read(response);
6954 for(var i = 0, len = rs.records.length; i < len; i++) {
6955 var r = rs.records[i];
6959 if(errors.length < 1){
6963 success : rs.success,
6969 ret = Roo.decode(response.responseText);
6973 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6983 Roo.form.Action.Load = function(form, options){
6984 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6985 this.reader = this.form.reader;
6988 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6993 Roo.Ajax.request(Roo.apply(
6994 this.createCallback(), {
6995 method:this.getMethod(),
6996 url:this.getUrl(false),
6997 params:this.getParams()
7001 success : function(response){
7003 var result = this.processResponse(response);
7004 if(result === true || !result.success || !result.data){
7005 this.failureType = Roo.form.Action.LOAD_FAILURE;
7006 this.form.afterAction(this, false);
7009 this.form.clearInvalid();
7010 this.form.setValues(result.data);
7011 this.form.afterAction(this, true);
7014 handleResponse : function(response){
7015 if(this.form.reader){
7016 var rs = this.form.reader.read(response);
7017 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7019 success : rs.success,
7023 return Roo.decode(response.responseText);
7027 Roo.form.Action.ACTION_TYPES = {
7028 'load' : Roo.form.Action.Load,
7029 'submit' : Roo.form.Action.Submit
7038 * @class Roo.bootstrap.Form
7039 * @extends Roo.bootstrap.Component
7040 * Bootstrap Form class
7041 * @cfg {String} method GET | POST (default POST)
7042 * @cfg {String} labelAlign top | left (default top)
7043 * @cfg {String} align left | right - for navbars
7044 * @cfg {Boolean} loadMask load mask when submit (default true)
7049 * @param {Object} config The config object
7053 Roo.bootstrap.Form = function(config){
7054 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7057 * @event clientvalidation
7058 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7059 * @param {Form} this
7060 * @param {Boolean} valid true if the form has passed client-side validation
7062 clientvalidation: true,
7064 * @event beforeaction
7065 * Fires before any action is performed. Return false to cancel the action.
7066 * @param {Form} this
7067 * @param {Action} action The action to be performed
7071 * @event actionfailed
7072 * Fires when an action fails.
7073 * @param {Form} this
7074 * @param {Action} action The action that failed
7076 actionfailed : true,
7078 * @event actioncomplete
7079 * Fires when an action is completed.
7080 * @param {Form} this
7081 * @param {Action} action The action that completed
7083 actioncomplete : true
7088 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7091 * @cfg {String} method
7092 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7097 * The URL to use for form actions if one isn't supplied in the action options.
7100 * @cfg {Boolean} fileUpload
7101 * Set to true if this form is a file upload.
7105 * @cfg {Object} baseParams
7106 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7110 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7114 * @cfg {Sting} align (left|right) for navbar forms
7119 activeAction : null,
7122 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7123 * element by passing it or its id or mask the form itself by passing in true.
7126 waitMsgTarget : false,
7130 getAutoCreate : function(){
7134 method : this.method || 'POST',
7135 id : this.id || Roo.id(),
7138 if (this.parent().xtype.match(/^Nav/)) {
7139 cfg.cls = 'navbar-form navbar-' + this.align;
7143 if (this.labelAlign == 'left' ) {
7144 cfg.cls += ' form-horizontal';
7150 initEvents : function()
7152 this.el.on('submit', this.onSubmit, this);
7153 // this was added as random key presses on the form where triggering form submit.
7154 this.el.on('keypress', function(e) {
7155 if (e.getCharCode() != 13) {
7158 // we might need to allow it for textareas.. and some other items.
7159 // check e.getTarget().
7161 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7165 Roo.log("keypress blocked");
7173 onSubmit : function(e){
7178 * Returns true if client-side validation on the form is successful.
7181 isValid : function(){
7182 var items = this.getItems();
7184 items.each(function(f){
7193 * Returns true if any fields in this form have changed since their original load.
7196 isDirty : function(){
7198 var items = this.getItems();
7199 items.each(function(f){
7209 * Performs a predefined action (submit or load) or custom actions you define on this form.
7210 * @param {String} actionName The name of the action type
7211 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7212 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7213 * accept other config options):
7215 Property Type Description
7216 ---------------- --------------- ----------------------------------------------------------------------------------
7217 url String The url for the action (defaults to the form's url)
7218 method String The form method to use (defaults to the form's method, or POST if not defined)
7219 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7220 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7221 validate the form on the client (defaults to false)
7223 * @return {BasicForm} this
7225 doAction : function(action, options){
7226 if(typeof action == 'string'){
7227 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7229 if(this.fireEvent('beforeaction', this, action) !== false){
7230 this.beforeAction(action);
7231 action.run.defer(100, action);
7237 beforeAction : function(action){
7238 var o = action.options;
7241 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7243 // not really supported yet.. ??
7245 //if(this.waitMsgTarget === true){
7246 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7247 //}else if(this.waitMsgTarget){
7248 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7249 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7251 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7257 afterAction : function(action, success){
7258 this.activeAction = null;
7259 var o = action.options;
7261 //if(this.waitMsgTarget === true){
7263 //}else if(this.waitMsgTarget){
7264 // this.waitMsgTarget.unmask();
7266 // Roo.MessageBox.updateProgress(1);
7267 // Roo.MessageBox.hide();
7274 Roo.callback(o.success, o.scope, [this, action]);
7275 this.fireEvent('actioncomplete', this, action);
7279 // failure condition..
7280 // we have a scenario where updates need confirming.
7281 // eg. if a locking scenario exists..
7282 // we look for { errors : { needs_confirm : true }} in the response.
7284 (typeof(action.result) != 'undefined') &&
7285 (typeof(action.result.errors) != 'undefined') &&
7286 (typeof(action.result.errors.needs_confirm) != 'undefined')
7289 Roo.log("not supported yet");
7292 Roo.MessageBox.confirm(
7293 "Change requires confirmation",
7294 action.result.errorMsg,
7299 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7309 Roo.callback(o.failure, o.scope, [this, action]);
7310 // show an error message if no failed handler is set..
7311 if (!this.hasListener('actionfailed')) {
7312 Roo.log("need to add dialog support");
7314 Roo.MessageBox.alert("Error",
7315 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7316 action.result.errorMsg :
7317 "Saving Failed, please check your entries or try again"
7322 this.fireEvent('actionfailed', this, action);
7327 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7328 * @param {String} id The value to search for
7331 findField : function(id){
7332 var items = this.getItems();
7333 var field = items.get(id);
7335 items.each(function(f){
7336 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7343 return field || null;
7346 * Mark fields in this form invalid in bulk.
7347 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7348 * @return {BasicForm} this
7350 markInvalid : function(errors){
7351 if(errors instanceof Array){
7352 for(var i = 0, len = errors.length; i < len; i++){
7353 var fieldError = errors[i];
7354 var f = this.findField(fieldError.id);
7356 f.markInvalid(fieldError.msg);
7362 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7363 field.markInvalid(errors[id]);
7367 //Roo.each(this.childForms || [], function (f) {
7368 // f.markInvalid(errors);
7375 * Set values for fields in this form in bulk.
7376 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7377 * @return {BasicForm} this
7379 setValues : function(values){
7380 if(values instanceof Array){ // array of objects
7381 for(var i = 0, len = values.length; i < len; i++){
7383 var f = this.findField(v.id);
7385 f.setValue(v.value);
7386 if(this.trackResetOnLoad){
7387 f.originalValue = f.getValue();
7391 }else{ // object hash
7394 if(typeof values[id] != 'function' && (field = this.findField(id))){
7396 if (field.setFromData &&
7398 field.displayField &&
7399 // combos' with local stores can
7400 // be queried via setValue()
7401 // to set their value..
7402 (field.store && !field.store.isLocal)
7406 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7407 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7408 field.setFromData(sd);
7411 field.setValue(values[id]);
7415 if(this.trackResetOnLoad){
7416 field.originalValue = field.getValue();
7422 //Roo.each(this.childForms || [], function (f) {
7423 // f.setValues(values);
7430 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7431 * they are returned as an array.
7432 * @param {Boolean} asString
7435 getValues : function(asString){
7436 //if (this.childForms) {
7437 // copy values from the child forms
7438 // Roo.each(this.childForms, function (f) {
7439 // this.setValues(f.getValues());
7445 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7446 if(asString === true){
7449 return Roo.urlDecode(fs);
7453 * Returns the fields in this form as an object with key/value pairs.
7454 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7457 getFieldValues : function(with_hidden)
7459 var items = this.getItems();
7461 items.each(function(f){
7465 var v = f.getValue();
7466 if (f.inputType =='radio') {
7467 if (typeof(ret[f.getName()]) == 'undefined') {
7468 ret[f.getName()] = ''; // empty..
7471 if (!f.el.dom.checked) {
7479 // not sure if this supported any more..
7480 if ((typeof(v) == 'object') && f.getRawValue) {
7481 v = f.getRawValue() ; // dates..
7483 // combo boxes where name != hiddenName...
7484 if (f.name != f.getName()) {
7485 ret[f.name] = f.getRawValue();
7487 ret[f.getName()] = v;
7494 * Clears all invalid messages in this form.
7495 * @return {BasicForm} this
7497 clearInvalid : function(){
7498 var items = this.getItems();
7500 items.each(function(f){
7511 * @return {BasicForm} this
7514 var items = this.getItems();
7515 items.each(function(f){
7519 Roo.each(this.childForms || [], function (f) {
7526 getItems : function()
7528 var r=new Roo.util.MixedCollection(false, function(o){
7529 return o.id || (o.id = Roo.id());
7531 var iter = function(el) {
7538 Roo.each(el.items,function(e) {
7558 * Ext JS Library 1.1.1
7559 * Copyright(c) 2006-2007, Ext JS, LLC.
7561 * Originally Released Under LGPL - original licence link has changed is not relivant.
7564 * <script type="text/javascript">
7567 * @class Roo.form.VTypes
7568 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7571 Roo.form.VTypes = function(){
7572 // closure these in so they are only created once.
7573 var alpha = /^[a-zA-Z_]+$/;
7574 var alphanum = /^[a-zA-Z0-9_]+$/;
7575 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7576 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7578 // All these messages and functions are configurable
7581 * The function used to validate email addresses
7582 * @param {String} value The email address
7584 'email' : function(v){
7585 return email.test(v);
7588 * The error text to display when the email validation function returns false
7591 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7593 * The keystroke filter mask to be applied on email input
7596 'emailMask' : /[a-z0-9_\.\-@]/i,
7599 * The function used to validate URLs
7600 * @param {String} value The URL
7602 'url' : function(v){
7606 * The error text to display when the url validation function returns false
7609 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7612 * The function used to validate alpha values
7613 * @param {String} value The value
7615 'alpha' : function(v){
7616 return alpha.test(v);
7619 * The error text to display when the alpha validation function returns false
7622 'alphaText' : 'This field should only contain letters and _',
7624 * The keystroke filter mask to be applied on alpha input
7627 'alphaMask' : /[a-z_]/i,
7630 * The function used to validate alphanumeric values
7631 * @param {String} value The value
7633 'alphanum' : function(v){
7634 return alphanum.test(v);
7637 * The error text to display when the alphanumeric validation function returns false
7640 'alphanumText' : 'This field should only contain letters, numbers and _',
7642 * The keystroke filter mask to be applied on alphanumeric input
7645 'alphanumMask' : /[a-z0-9_]/i
7655 * @class Roo.bootstrap.Input
7656 * @extends Roo.bootstrap.Component
7657 * Bootstrap Input class
7658 * @cfg {Boolean} disabled is it disabled
7659 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7660 * @cfg {String} name name of the input
7661 * @cfg {string} fieldLabel - the label associated
7662 * @cfg {string} placeholder - placeholder to put in text.
7663 * @cfg {string} before - input group add on before
7664 * @cfg {string} after - input group add on after
7665 * @cfg {string} size - (lg|sm) or leave empty..
7666 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7667 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7668 * @cfg {Number} md colspan out of 12 for computer-sized screens
7669 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7670 * @cfg {string} value default value of the input
7671 * @cfg {Number} labelWidth set the width of label (0-12)
7672 * @cfg {String} labelAlign (top|left)
7673 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7674 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7676 * @cfg {String} align (left|center|right) Default left
7677 * @cfg {Boolean} forceFeedback (true|false) Default false
7683 * Create a new Input
7684 * @param {Object} config The config object
7687 Roo.bootstrap.Input = function(config){
7688 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7693 * Fires when this field receives input focus.
7694 * @param {Roo.form.Field} this
7699 * Fires when this field loses input focus.
7700 * @param {Roo.form.Field} this
7705 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7706 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7707 * @param {Roo.form.Field} this
7708 * @param {Roo.EventObject} e The event object
7713 * Fires just before the field blurs if the field value has changed.
7714 * @param {Roo.form.Field} this
7715 * @param {Mixed} newValue The new value
7716 * @param {Mixed} oldValue The original value
7721 * Fires after the field has been marked as invalid.
7722 * @param {Roo.form.Field} this
7723 * @param {String} msg The validation message
7728 * Fires after the field has been validated with no errors.
7729 * @param {Roo.form.Field} this
7734 * Fires after the key up
7735 * @param {Roo.form.Field} this
7736 * @param {Roo.EventObject} e The event Object
7742 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7744 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7745 automatic validation (defaults to "keyup").
7747 validationEvent : "keyup",
7749 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7751 validateOnBlur : true,
7753 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7755 validationDelay : 250,
7757 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7759 focusClass : "x-form-focus", // not needed???
7763 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7765 invalidClass : "has-warning",
7768 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7770 validClass : "has-success",
7773 * @cfg {Boolean} hasFeedback (true|false) default true
7778 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7780 invalidFeedbackClass : "glyphicon-warning-sign",
7783 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7785 validFeedbackClass : "glyphicon-ok",
7788 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7790 selectOnFocus : false,
7793 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7797 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7802 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7804 disableKeyFilter : false,
7807 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7811 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7815 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7817 blankText : "This field is required",
7820 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7824 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7826 maxLength : Number.MAX_VALUE,
7828 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7830 minLengthText : "The minimum length for this field is {0}",
7832 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7834 maxLengthText : "The maximum length for this field is {0}",
7838 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7839 * If available, this function will be called only after the basic validators all return true, and will be passed the
7840 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7844 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7845 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7846 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7850 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7854 autocomplete: false,
7873 formatedValue : false,
7874 forceFeedback : false,
7876 parentLabelAlign : function()
7879 while (parent.parent()) {
7880 parent = parent.parent();
7881 if (typeof(parent.labelAlign) !='undefined') {
7882 return parent.labelAlign;
7889 getAutoCreate : function(){
7891 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7897 if(this.inputType != 'hidden'){
7898 cfg.cls = 'form-group' //input-group
7904 type : this.inputType,
7906 cls : 'form-control',
7907 placeholder : this.placeholder || '',
7908 autocomplete : this.autocomplete || 'new-password'
7913 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7916 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7917 input.maxLength = this.maxLength;
7920 if (this.disabled) {
7921 input.disabled=true;
7924 if (this.readOnly) {
7925 input.readonly=true;
7929 input.name = this.name;
7932 input.cls += ' input-' + this.size;
7935 ['xs','sm','md','lg'].map(function(size){
7936 if (settings[size]) {
7937 cfg.cls += ' col-' + size + '-' + settings[size];
7941 var inputblock = input;
7945 cls: 'glyphicon form-control-feedback'
7948 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7951 cls : 'has-feedback',
7959 if (this.before || this.after) {
7962 cls : 'input-group',
7966 if (this.before && typeof(this.before) == 'string') {
7968 inputblock.cn.push({
7970 cls : 'roo-input-before input-group-addon',
7974 if (this.before && typeof(this.before) == 'object') {
7975 this.before = Roo.factory(this.before);
7976 Roo.log(this.before);
7977 inputblock.cn.push({
7979 cls : 'roo-input-before input-group-' +
7980 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7984 inputblock.cn.push(input);
7986 if (this.after && typeof(this.after) == 'string') {
7987 inputblock.cn.push({
7989 cls : 'roo-input-after input-group-addon',
7993 if (this.after && typeof(this.after) == 'object') {
7994 this.after = Roo.factory(this.after);
7995 Roo.log(this.after);
7996 inputblock.cn.push({
7998 cls : 'roo-input-after input-group-' +
7999 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8003 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8004 inputblock.cls += ' has-feedback';
8005 inputblock.cn.push(feedback);
8009 if (align ==='left' && this.fieldLabel.length) {
8010 Roo.log("left and has label");
8016 cls : 'control-label col-sm-' + this.labelWidth,
8017 html : this.fieldLabel
8021 cls : "col-sm-" + (12 - this.labelWidth),
8028 } else if ( this.fieldLabel.length) {
8034 //cls : 'input-group-addon',
8035 html : this.fieldLabel
8045 Roo.log(" no label && no align");
8054 Roo.log('input-parentType: ' + this.parentType);
8056 if (this.parentType === 'Navbar' && this.parent().bar) {
8057 cfg.cls += ' navbar-form';
8065 * return the real input element.
8067 inputEl: function ()
8069 return this.el.select('input.form-control',true).first();
8072 tooltipEl : function()
8074 return this.inputEl();
8077 setDisabled : function(v)
8079 var i = this.inputEl().dom;
8081 i.removeAttribute('disabled');
8085 i.setAttribute('disabled','true');
8087 initEvents : function()
8090 this.inputEl().on("keydown" , this.fireKey, this);
8091 this.inputEl().on("focus", this.onFocus, this);
8092 this.inputEl().on("blur", this.onBlur, this);
8094 this.inputEl().relayEvent('keyup', this);
8096 // reference to original value for reset
8097 this.originalValue = this.getValue();
8098 //Roo.form.TextField.superclass.initEvents.call(this);
8099 if(this.validationEvent == 'keyup'){
8100 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8101 this.inputEl().on('keyup', this.filterValidation, this);
8103 else if(this.validationEvent !== false){
8104 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8107 if(this.selectOnFocus){
8108 this.on("focus", this.preFocus, this);
8111 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8112 this.inputEl().on("keypress", this.filterKeys, this);
8115 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8116 this.el.on("click", this.autoSize, this);
8119 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8120 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8123 if (typeof(this.before) == 'object') {
8124 this.before.render(this.el.select('.roo-input-before',true).first());
8126 if (typeof(this.after) == 'object') {
8127 this.after.render(this.el.select('.roo-input-after',true).first());
8132 filterValidation : function(e){
8133 if(!e.isNavKeyPress()){
8134 this.validationTask.delay(this.validationDelay);
8138 * Validates the field value
8139 * @return {Boolean} True if the value is valid, else false
8141 validate : function(){
8142 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8143 if(this.disabled || this.validateValue(this.getRawValue())){
8154 * Validates a value according to the field's validation rules and marks the field as invalid
8155 * if the validation fails
8156 * @param {Mixed} value The value to validate
8157 * @return {Boolean} True if the value is valid, else false
8159 validateValue : function(value){
8160 if(value.length < 1) { // if it's blank
8161 if(this.allowBlank){
8167 if(value.length < this.minLength){
8170 if(value.length > this.maxLength){
8174 var vt = Roo.form.VTypes;
8175 if(!vt[this.vtype](value, this)){
8179 if(typeof this.validator == "function"){
8180 var msg = this.validator(value);
8186 if(this.regex && !this.regex.test(value)){
8196 fireKey : function(e){
8197 //Roo.log('field ' + e.getKey());
8198 if(e.isNavKeyPress()){
8199 this.fireEvent("specialkey", this, e);
8202 focus : function (selectText){
8204 this.inputEl().focus();
8205 if(selectText === true){
8206 this.inputEl().dom.select();
8212 onFocus : function(){
8213 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8214 // this.el.addClass(this.focusClass);
8217 this.hasFocus = true;
8218 this.startValue = this.getValue();
8219 this.fireEvent("focus", this);
8223 beforeBlur : Roo.emptyFn,
8227 onBlur : function(){
8229 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8230 //this.el.removeClass(this.focusClass);
8232 this.hasFocus = false;
8233 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8236 var v = this.getValue();
8237 if(String(v) !== String(this.startValue)){
8238 this.fireEvent('change', this, v, this.startValue);
8240 this.fireEvent("blur", this);
8244 * Resets the current field value to the originally loaded value and clears any validation messages
8247 this.setValue(this.originalValue);
8251 * Returns the name of the field
8252 * @return {Mixed} name The name field
8254 getName: function(){
8258 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8259 * @return {Mixed} value The field value
8261 getValue : function(){
8263 var v = this.inputEl().getValue();
8268 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8269 * @return {Mixed} value The field value
8271 getRawValue : function(){
8272 var v = this.inputEl().getValue();
8278 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8279 * @param {Mixed} value The value to set
8281 setRawValue : function(v){
8282 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8285 selectText : function(start, end){
8286 var v = this.getRawValue();
8288 start = start === undefined ? 0 : start;
8289 end = end === undefined ? v.length : end;
8290 var d = this.inputEl().dom;
8291 if(d.setSelectionRange){
8292 d.setSelectionRange(start, end);
8293 }else if(d.createTextRange){
8294 var range = d.createTextRange();
8295 range.moveStart("character", start);
8296 range.moveEnd("character", v.length-end);
8303 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8304 * @param {Mixed} value The value to set
8306 setValue : function(v){
8309 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8315 processValue : function(value){
8316 if(this.stripCharsRe){
8317 var newValue = value.replace(this.stripCharsRe, '');
8318 if(newValue !== value){
8319 this.setRawValue(newValue);
8326 preFocus : function(){
8328 if(this.selectOnFocus){
8329 this.inputEl().dom.select();
8332 filterKeys : function(e){
8334 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8337 var c = e.getCharCode(), cc = String.fromCharCode(c);
8338 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8341 if(!this.maskRe.test(cc)){
8346 * Clear any invalid styles/messages for this field
8348 clearInvalid : function(){
8350 if(!this.el || this.preventMark){ // not rendered
8353 this.el.removeClass(this.invalidClass);
8355 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8357 var feedback = this.el.select('.form-control-feedback', true).first();
8360 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8365 this.fireEvent('valid', this);
8369 * Mark this field as valid
8371 markValid : function()
8373 if(!this.el || this.preventMark){ // not rendered
8377 this.el.removeClass([this.invalidClass, this.validClass]);
8379 var feedback = this.el.select('.form-control-feedback', true).first();
8382 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8385 if(this.disabled || this.allowBlank){
8389 var formGroup = this.el.findParent('.form-group', false, true);
8393 var label = formGroup.select('label', true).first();
8394 var icon = formGroup.select('i.fa-star', true).first();
8401 this.el.addClass(this.validClass);
8403 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8405 var feedback = this.el.select('.form-control-feedback', true).first();
8408 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8409 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8414 this.fireEvent('valid', this);
8418 * Mark this field as invalid
8419 * @param {String} msg The validation message
8421 markInvalid : function(msg)
8423 if(!this.el || this.preventMark){ // not rendered
8427 this.el.removeClass([this.invalidClass, this.validClass]);
8429 var feedback = this.el.select('.form-control-feedback', true).first();
8432 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8435 if(this.disabled || this.allowBlank){
8439 var formGroup = this.el.findParent('.form-group', false, true);
8442 var label = formGroup.select('label', true).first();
8443 var icon = formGroup.select('i.fa-star', true).first();
8445 if(!this.getValue().length && label && !icon){
8446 this.el.findParent('.form-group', false, true).createChild({
8448 cls : 'text-danger fa fa-lg fa-star',
8449 tooltip : 'This field is required',
8450 style : 'margin-right:5px;'
8456 this.el.addClass(this.invalidClass);
8458 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8460 var feedback = this.el.select('.form-control-feedback', true).first();
8463 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8465 if(this.getValue().length || this.forceFeedback){
8466 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8473 this.fireEvent('invalid', this, msg);
8476 SafariOnKeyDown : function(event)
8478 // this is a workaround for a password hang bug on chrome/ webkit.
8480 var isSelectAll = false;
8482 if(this.inputEl().dom.selectionEnd > 0){
8483 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8485 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8486 event.preventDefault();
8491 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8493 event.preventDefault();
8494 // this is very hacky as keydown always get's upper case.
8496 var cc = String.fromCharCode(event.getCharCode());
8497 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8501 adjustWidth : function(tag, w){
8502 tag = tag.toLowerCase();
8503 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8504 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8508 if(tag == 'textarea'){
8511 }else if(Roo.isOpera){
8515 if(tag == 'textarea'){
8534 * @class Roo.bootstrap.TextArea
8535 * @extends Roo.bootstrap.Input
8536 * Bootstrap TextArea class
8537 * @cfg {Number} cols Specifies the visible width of a text area
8538 * @cfg {Number} rows Specifies the visible number of lines in a text area
8539 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8540 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8541 * @cfg {string} html text
8544 * Create a new TextArea
8545 * @param {Object} config The config object
8548 Roo.bootstrap.TextArea = function(config){
8549 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8553 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8563 getAutoCreate : function(){
8565 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8576 value : this.value || '',
8577 html: this.html || '',
8578 cls : 'form-control',
8579 placeholder : this.placeholder || ''
8583 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8584 input.maxLength = this.maxLength;
8588 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8592 input.cols = this.cols;
8595 if (this.readOnly) {
8596 input.readonly = true;
8600 input.name = this.name;
8604 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8608 ['xs','sm','md','lg'].map(function(size){
8609 if (settings[size]) {
8610 cfg.cls += ' col-' + size + '-' + settings[size];
8614 var inputblock = input;
8616 if(this.hasFeedback && !this.allowBlank){
8620 cls: 'glyphicon form-control-feedback'
8624 cls : 'has-feedback',
8633 if (this.before || this.after) {
8636 cls : 'input-group',
8640 inputblock.cn.push({
8642 cls : 'input-group-addon',
8647 inputblock.cn.push(input);
8649 if(this.hasFeedback && !this.allowBlank){
8650 inputblock.cls += ' has-feedback';
8651 inputblock.cn.push(feedback);
8655 inputblock.cn.push({
8657 cls : 'input-group-addon',
8664 if (align ==='left' && this.fieldLabel.length) {
8665 Roo.log("left and has label");
8671 cls : 'control-label col-sm-' + this.labelWidth,
8672 html : this.fieldLabel
8676 cls : "col-sm-" + (12 - this.labelWidth),
8683 } else if ( this.fieldLabel.length) {
8689 //cls : 'input-group-addon',
8690 html : this.fieldLabel
8700 Roo.log(" no label && no align");
8710 if (this.disabled) {
8711 input.disabled=true;
8718 * return the real textarea element.
8720 inputEl: function ()
8722 return this.el.select('textarea.form-control',true).first();
8726 * Clear any invalid styles/messages for this field
8728 clearInvalid : function()
8731 if(!this.el || this.preventMark){ // not rendered
8735 var label = this.el.select('label', true).first();
8736 var icon = this.el.select('i.fa-star', true).first();
8742 this.el.removeClass(this.invalidClass);
8744 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8746 var feedback = this.el.select('.form-control-feedback', true).first();
8749 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8754 this.fireEvent('valid', this);
8758 * Mark this field as valid
8760 markValid : function()
8762 if(!this.el || this.preventMark){ // not rendered
8766 this.el.removeClass([this.invalidClass, this.validClass]);
8768 var feedback = this.el.select('.form-control-feedback', true).first();
8771 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8774 if(this.disabled || this.allowBlank){
8778 var label = this.el.select('label', true).first();
8779 var icon = this.el.select('i.fa-star', true).first();
8785 this.el.addClass(this.validClass);
8787 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8789 var feedback = this.el.select('.form-control-feedback', true).first();
8792 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8793 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8798 this.fireEvent('valid', this);
8802 * Mark this field as invalid
8803 * @param {String} msg The validation message
8805 markInvalid : function(msg)
8807 if(!this.el || this.preventMark){ // not rendered
8811 this.el.removeClass([this.invalidClass, this.validClass]);
8813 var feedback = this.el.select('.form-control-feedback', true).first();
8816 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8819 if(this.disabled || this.allowBlank){
8823 var label = this.el.select('label', true).first();
8824 var icon = this.el.select('i.fa-star', true).first();
8826 if(!this.getValue().length && label && !icon){
8827 this.el.createChild({
8829 cls : 'text-danger fa fa-lg fa-star',
8830 tooltip : 'This field is required',
8831 style : 'margin-right:5px;'
8835 this.el.addClass(this.invalidClass);
8837 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8839 var feedback = this.el.select('.form-control-feedback', true).first();
8842 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8844 if(this.getValue().length || this.forceFeedback){
8845 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8852 this.fireEvent('invalid', this, msg);
8860 * trigger field - base class for combo..
8865 * @class Roo.bootstrap.TriggerField
8866 * @extends Roo.bootstrap.Input
8867 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8868 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8869 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8870 * for which you can provide a custom implementation. For example:
8872 var trigger = new Roo.bootstrap.TriggerField();
8873 trigger.onTriggerClick = myTriggerFn;
8874 trigger.applyTo('my-field');
8877 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8878 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8879 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8880 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8881 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8884 * Create a new TriggerField.
8885 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8886 * to the base TextField)
8888 Roo.bootstrap.TriggerField = function(config){
8889 this.mimicing = false;
8890 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8893 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8895 * @cfg {String} triggerClass A CSS class to apply to the trigger
8898 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8903 * @cfg {Boolean} removable (true|false) special filter default false
8907 /** @cfg {Boolean} grow @hide */
8908 /** @cfg {Number} growMin @hide */
8909 /** @cfg {Number} growMax @hide */
8915 autoSize: Roo.emptyFn,
8922 actionMode : 'wrap',
8927 getAutoCreate : function(){
8929 var align = this.labelAlign || this.parentLabelAlign();
8934 cls: 'form-group' //input-group
8941 type : this.inputType,
8942 cls : 'form-control',
8943 autocomplete: 'new-password',
8944 placeholder : this.placeholder || ''
8948 input.name = this.name;
8951 input.cls += ' input-' + this.size;
8954 if (this.disabled) {
8955 input.disabled=true;
8958 var inputblock = input;
8960 if(this.hasFeedback && !this.allowBlank){
8964 cls: 'glyphicon form-control-feedback'
8967 if(this.removable && !this.editable && !this.tickable){
8969 cls : 'has-feedback',
8975 cls : 'roo-combo-removable-btn close'
8982 cls : 'has-feedback',
8991 if(this.removable && !this.editable && !this.tickable){
8993 cls : 'roo-removable',
8999 cls : 'roo-combo-removable-btn close'
9006 if (this.before || this.after) {
9009 cls : 'input-group',
9013 inputblock.cn.push({
9015 cls : 'input-group-addon',
9020 inputblock.cn.push(input);
9022 if(this.hasFeedback && !this.allowBlank){
9023 inputblock.cls += ' has-feedback';
9024 inputblock.cn.push(feedback);
9028 inputblock.cn.push({
9030 cls : 'input-group-addon',
9043 cls: 'form-hidden-field'
9051 Roo.log('multiple');
9059 cls: 'form-hidden-field'
9063 cls: 'select2-choices',
9067 cls: 'select2-search-field',
9080 cls: 'select2-container input-group',
9085 // cls: 'typeahead typeahead-long dropdown-menu',
9086 // style: 'display:none'
9091 if(!this.multiple && this.showToggleBtn){
9097 if (this.caret != false) {
9100 cls: 'fa fa-' + this.caret
9107 cls : 'input-group-addon btn dropdown-toggle',
9112 cls: 'combobox-clear',
9126 combobox.cls += ' select2-container-multi';
9129 if (align ==='left' && this.fieldLabel.length) {
9131 Roo.log("left and has label");
9137 cls : 'control-label col-sm-' + this.labelWidth,
9138 html : this.fieldLabel
9142 cls : "col-sm-" + (12 - this.labelWidth),
9149 } else if ( this.fieldLabel.length) {
9155 //cls : 'input-group-addon',
9156 html : this.fieldLabel
9166 Roo.log(" no label && no align");
9173 ['xs','sm','md','lg'].map(function(size){
9174 if (settings[size]) {
9175 cfg.cls += ' col-' + size + '-' + settings[size];
9186 onResize : function(w, h){
9187 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9188 // if(typeof w == 'number'){
9189 // var x = w - this.trigger.getWidth();
9190 // this.inputEl().setWidth(this.adjustWidth('input', x));
9191 // this.trigger.setStyle('left', x+'px');
9196 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9199 getResizeEl : function(){
9200 return this.inputEl();
9204 getPositionEl : function(){
9205 return this.inputEl();
9209 alignErrorIcon : function(){
9210 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9214 initEvents : function(){
9218 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9219 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9220 if(!this.multiple && this.showToggleBtn){
9221 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9222 if(this.hideTrigger){
9223 this.trigger.setDisplayed(false);
9225 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9229 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9232 if(this.removable && !this.editable && !this.tickable){
9233 var close = this.closeTriggerEl();
9236 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9237 close.on('click', this.removeBtnClick, this, close);
9241 //this.trigger.addClassOnOver('x-form-trigger-over');
9242 //this.trigger.addClassOnClick('x-form-trigger-click');
9245 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9249 closeTriggerEl : function()
9251 var close = this.el.select('.roo-combo-removable-btn', true).first();
9252 return close ? close : false;
9255 removeBtnClick : function(e, h, el)
9259 if(this.fireEvent("remove", this) !== false){
9264 createList : function()
9266 this.list = Roo.get(document.body).createChild({
9268 cls: 'typeahead typeahead-long dropdown-menu',
9269 style: 'display:none'
9272 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9277 initTrigger : function(){
9282 onDestroy : function(){
9284 this.trigger.removeAllListeners();
9285 // this.trigger.remove();
9288 // this.wrap.remove();
9290 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9294 onFocus : function(){
9295 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9298 this.wrap.addClass('x-trigger-wrap-focus');
9299 this.mimicing = true;
9300 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9301 if(this.monitorTab){
9302 this.el.on("keydown", this.checkTab, this);
9309 checkTab : function(e){
9310 if(e.getKey() == e.TAB){
9316 onBlur : function(){
9321 mimicBlur : function(e, t){
9323 if(!this.wrap.contains(t) && this.validateBlur()){
9330 triggerBlur : function(){
9331 this.mimicing = false;
9332 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9333 if(this.monitorTab){
9334 this.el.un("keydown", this.checkTab, this);
9336 //this.wrap.removeClass('x-trigger-wrap-focus');
9337 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9341 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9342 validateBlur : function(e, t){
9347 onDisable : function(){
9348 this.inputEl().dom.disabled = true;
9349 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9351 // this.wrap.addClass('x-item-disabled');
9356 onEnable : function(){
9357 this.inputEl().dom.disabled = false;
9358 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9360 // this.el.removeClass('x-item-disabled');
9365 onShow : function(){
9366 var ae = this.getActionEl();
9369 ae.dom.style.display = '';
9370 ae.dom.style.visibility = 'visible';
9376 onHide : function(){
9377 var ae = this.getActionEl();
9378 ae.dom.style.display = 'none';
9382 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9383 * by an implementing function.
9385 * @param {EventObject} e
9387 onTriggerClick : Roo.emptyFn
9391 * Ext JS Library 1.1.1
9392 * Copyright(c) 2006-2007, Ext JS, LLC.
9394 * Originally Released Under LGPL - original licence link has changed is not relivant.
9397 * <script type="text/javascript">
9402 * @class Roo.data.SortTypes
9404 * Defines the default sorting (casting?) comparison functions used when sorting data.
9406 Roo.data.SortTypes = {
9408 * Default sort that does nothing
9409 * @param {Mixed} s The value being converted
9410 * @return {Mixed} The comparison value
9417 * The regular expression used to strip tags
9421 stripTagsRE : /<\/?[^>]+>/gi,
9424 * Strips all HTML tags to sort on text only
9425 * @param {Mixed} s The value being converted
9426 * @return {String} The comparison value
9428 asText : function(s){
9429 return String(s).replace(this.stripTagsRE, "");
9433 * Strips all HTML tags to sort on text only - Case insensitive
9434 * @param {Mixed} s The value being converted
9435 * @return {String} The comparison value
9437 asUCText : function(s){
9438 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9442 * Case insensitive string
9443 * @param {Mixed} s The value being converted
9444 * @return {String} The comparison value
9446 asUCString : function(s) {
9447 return String(s).toUpperCase();
9452 * @param {Mixed} s The value being converted
9453 * @return {Number} The comparison value
9455 asDate : function(s) {
9459 if(s instanceof Date){
9462 return Date.parse(String(s));
9467 * @param {Mixed} s The value being converted
9468 * @return {Float} The comparison value
9470 asFloat : function(s) {
9471 var val = parseFloat(String(s).replace(/,/g, ""));
9480 * @param {Mixed} s The value being converted
9481 * @return {Number} The comparison value
9483 asInt : function(s) {
9484 var val = parseInt(String(s).replace(/,/g, ""));
9492 * Ext JS Library 1.1.1
9493 * Copyright(c) 2006-2007, Ext JS, LLC.
9495 * Originally Released Under LGPL - original licence link has changed is not relivant.
9498 * <script type="text/javascript">
9502 * @class Roo.data.Record
9503 * Instances of this class encapsulate both record <em>definition</em> information, and record
9504 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9505 * to access Records cached in an {@link Roo.data.Store} object.<br>
9507 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9508 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9511 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9513 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9514 * {@link #create}. The parameters are the same.
9515 * @param {Array} data An associative Array of data values keyed by the field name.
9516 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9517 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9518 * not specified an integer id is generated.
9520 Roo.data.Record = function(data, id){
9521 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9526 * Generate a constructor for a specific record layout.
9527 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9528 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9529 * Each field definition object may contain the following properties: <ul>
9530 * <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,
9531 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9532 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9533 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9534 * is being used, then this is a string containing the javascript expression to reference the data relative to
9535 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9536 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9537 * this may be omitted.</p></li>
9538 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9539 * <ul><li>auto (Default, implies no conversion)</li>
9544 * <li>date</li></ul></p></li>
9545 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9546 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9547 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9548 * by the Reader into an object that will be stored in the Record. It is passed the
9549 * following parameters:<ul>
9550 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9552 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9554 * <br>usage:<br><pre><code>
9555 var TopicRecord = Roo.data.Record.create(
9556 {name: 'title', mapping: 'topic_title'},
9557 {name: 'author', mapping: 'username'},
9558 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9559 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9560 {name: 'lastPoster', mapping: 'user2'},
9561 {name: 'excerpt', mapping: 'post_text'}
9564 var myNewRecord = new TopicRecord({
9565 title: 'Do my job please',
9568 lastPost: new Date(),
9569 lastPoster: 'Animal',
9570 excerpt: 'No way dude!'
9572 myStore.add(myNewRecord);
9577 Roo.data.Record.create = function(o){
9579 f.superclass.constructor.apply(this, arguments);
9581 Roo.extend(f, Roo.data.Record);
9582 var p = f.prototype;
9583 p.fields = new Roo.util.MixedCollection(false, function(field){
9586 for(var i = 0, len = o.length; i < len; i++){
9587 p.fields.add(new Roo.data.Field(o[i]));
9589 f.getField = function(name){
9590 return p.fields.get(name);
9595 Roo.data.Record.AUTO_ID = 1000;
9596 Roo.data.Record.EDIT = 'edit';
9597 Roo.data.Record.REJECT = 'reject';
9598 Roo.data.Record.COMMIT = 'commit';
9600 Roo.data.Record.prototype = {
9602 * Readonly flag - true if this record has been modified.
9611 join : function(store){
9616 * Set the named field to the specified value.
9617 * @param {String} name The name of the field to set.
9618 * @param {Object} value The value to set the field to.
9620 set : function(name, value){
9621 if(this.data[name] == value){
9628 if(typeof this.modified[name] == 'undefined'){
9629 this.modified[name] = this.data[name];
9631 this.data[name] = value;
9632 if(!this.editing && this.store){
9633 this.store.afterEdit(this);
9638 * Get the value of the named field.
9639 * @param {String} name The name of the field to get the value of.
9640 * @return {Object} The value of the field.
9642 get : function(name){
9643 return this.data[name];
9647 beginEdit : function(){
9648 this.editing = true;
9653 cancelEdit : function(){
9654 this.editing = false;
9655 delete this.modified;
9659 endEdit : function(){
9660 this.editing = false;
9661 if(this.dirty && this.store){
9662 this.store.afterEdit(this);
9667 * Usually called by the {@link Roo.data.Store} which owns the Record.
9668 * Rejects all changes made to the Record since either creation, or the last commit operation.
9669 * Modified fields are reverted to their original values.
9671 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9672 * of reject operations.
9674 reject : function(){
9675 var m = this.modified;
9677 if(typeof m[n] != "function"){
9678 this.data[n] = m[n];
9682 delete this.modified;
9683 this.editing = false;
9685 this.store.afterReject(this);
9690 * Usually called by the {@link Roo.data.Store} which owns the Record.
9691 * Commits all changes made to the Record since either creation, or the last commit operation.
9693 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9694 * of commit operations.
9696 commit : function(){
9698 delete this.modified;
9699 this.editing = false;
9701 this.store.afterCommit(this);
9706 hasError : function(){
9707 return this.error != null;
9711 clearError : function(){
9716 * Creates a copy of this record.
9717 * @param {String} id (optional) A new record id if you don't want to use this record's id
9720 copy : function(newId) {
9721 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9725 * Ext JS Library 1.1.1
9726 * Copyright(c) 2006-2007, Ext JS, LLC.
9728 * Originally Released Under LGPL - original licence link has changed is not relivant.
9731 * <script type="text/javascript">
9737 * @class Roo.data.Store
9738 * @extends Roo.util.Observable
9739 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9740 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9742 * 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
9743 * has no knowledge of the format of the data returned by the Proxy.<br>
9745 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9746 * instances from the data object. These records are cached and made available through accessor functions.
9748 * Creates a new Store.
9749 * @param {Object} config A config object containing the objects needed for the Store to access data,
9750 * and read the data into Records.
9752 Roo.data.Store = function(config){
9753 this.data = new Roo.util.MixedCollection(false);
9754 this.data.getKey = function(o){
9757 this.baseParams = {};
9764 "multisort" : "_multisort"
9767 if(config && config.data){
9768 this.inlineData = config.data;
9772 Roo.apply(this, config);
9774 if(this.reader){ // reader passed
9775 this.reader = Roo.factory(this.reader, Roo.data);
9776 this.reader.xmodule = this.xmodule || false;
9777 if(!this.recordType){
9778 this.recordType = this.reader.recordType;
9780 if(this.reader.onMetaChange){
9781 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9785 if(this.recordType){
9786 this.fields = this.recordType.prototype.fields;
9792 * @event datachanged
9793 * Fires when the data cache has changed, and a widget which is using this Store
9794 * as a Record cache should refresh its view.
9795 * @param {Store} this
9800 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9801 * @param {Store} this
9802 * @param {Object} meta The JSON metadata
9807 * Fires when Records have been added to the Store
9808 * @param {Store} this
9809 * @param {Roo.data.Record[]} records The array of Records added
9810 * @param {Number} index The index at which the record(s) were added
9815 * Fires when a Record has been removed from the Store
9816 * @param {Store} this
9817 * @param {Roo.data.Record} record The Record that was removed
9818 * @param {Number} index The index at which the record was removed
9823 * Fires when a Record has been updated
9824 * @param {Store} this
9825 * @param {Roo.data.Record} record The Record that was updated
9826 * @param {String} operation The update operation being performed. Value may be one of:
9828 Roo.data.Record.EDIT
9829 Roo.data.Record.REJECT
9830 Roo.data.Record.COMMIT
9836 * Fires when the data cache has been cleared.
9837 * @param {Store} this
9842 * Fires before a request is made for a new data object. If the beforeload handler returns false
9843 * the load action will be canceled.
9844 * @param {Store} this
9845 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9849 * @event beforeloadadd
9850 * Fires after a new set of Records has been loaded.
9851 * @param {Store} this
9852 * @param {Roo.data.Record[]} records The Records that were loaded
9853 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9855 beforeloadadd : true,
9858 * Fires after a new set of Records has been loaded, before they are added to the store.
9859 * @param {Store} this
9860 * @param {Roo.data.Record[]} records The Records that were loaded
9861 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9862 * @params {Object} return from reader
9866 * @event loadexception
9867 * Fires if an exception occurs in the Proxy during loading.
9868 * Called with the signature of the Proxy's "loadexception" event.
9869 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9872 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9873 * @param {Object} load options
9874 * @param {Object} jsonData from your request (normally this contains the Exception)
9876 loadexception : true
9880 this.proxy = Roo.factory(this.proxy, Roo.data);
9881 this.proxy.xmodule = this.xmodule || false;
9882 this.relayEvents(this.proxy, ["loadexception"]);
9884 this.sortToggle = {};
9885 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9887 Roo.data.Store.superclass.constructor.call(this);
9889 if(this.inlineData){
9890 this.loadData(this.inlineData);
9891 delete this.inlineData;
9895 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9897 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9898 * without a remote query - used by combo/forms at present.
9902 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9905 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9908 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9909 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9912 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9913 * on any HTTP request
9916 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9919 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9923 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9924 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9929 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9930 * loaded or when a record is removed. (defaults to false).
9932 pruneModifiedRecords : false,
9938 * Add Records to the Store and fires the add event.
9939 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9941 add : function(records){
9942 records = [].concat(records);
9943 for(var i = 0, len = records.length; i < len; i++){
9944 records[i].join(this);
9946 var index = this.data.length;
9947 this.data.addAll(records);
9948 this.fireEvent("add", this, records, index);
9952 * Remove a Record from the Store and fires the remove event.
9953 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9955 remove : function(record){
9956 var index = this.data.indexOf(record);
9957 this.data.removeAt(index);
9958 if(this.pruneModifiedRecords){
9959 this.modified.remove(record);
9961 this.fireEvent("remove", this, record, index);
9965 * Remove all Records from the Store and fires the clear event.
9967 removeAll : function(){
9969 if(this.pruneModifiedRecords){
9972 this.fireEvent("clear", this);
9976 * Inserts Records to the Store at the given index and fires the add event.
9977 * @param {Number} index The start index at which to insert the passed Records.
9978 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9980 insert : function(index, records){
9981 records = [].concat(records);
9982 for(var i = 0, len = records.length; i < len; i++){
9983 this.data.insert(index, records[i]);
9984 records[i].join(this);
9986 this.fireEvent("add", this, records, index);
9990 * Get the index within the cache of the passed Record.
9991 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9992 * @return {Number} The index of the passed Record. Returns -1 if not found.
9994 indexOf : function(record){
9995 return this.data.indexOf(record);
9999 * Get the index within the cache of the Record with the passed id.
10000 * @param {String} id The id of the Record to find.
10001 * @return {Number} The index of the Record. Returns -1 if not found.
10003 indexOfId : function(id){
10004 return this.data.indexOfKey(id);
10008 * Get the Record with the specified id.
10009 * @param {String} id The id of the Record to find.
10010 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10012 getById : function(id){
10013 return this.data.key(id);
10017 * Get the Record at the specified index.
10018 * @param {Number} index The index of the Record to find.
10019 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10021 getAt : function(index){
10022 return this.data.itemAt(index);
10026 * Returns a range of Records between specified indices.
10027 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10028 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10029 * @return {Roo.data.Record[]} An array of Records
10031 getRange : function(start, end){
10032 return this.data.getRange(start, end);
10036 storeOptions : function(o){
10037 o = Roo.apply({}, o);
10040 this.lastOptions = o;
10044 * Loads the Record cache from the configured Proxy using the configured Reader.
10046 * If using remote paging, then the first load call must specify the <em>start</em>
10047 * and <em>limit</em> properties in the options.params property to establish the initial
10048 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10050 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10051 * and this call will return before the new data has been loaded. Perform any post-processing
10052 * in a callback function, or in a "load" event handler.</strong>
10054 * @param {Object} options An object containing properties which control loading options:<ul>
10055 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10056 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10057 * passed the following arguments:<ul>
10058 * <li>r : Roo.data.Record[]</li>
10059 * <li>options: Options object from the load call</li>
10060 * <li>success: Boolean success indicator</li></ul></li>
10061 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10062 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10065 load : function(options){
10066 options = options || {};
10067 if(this.fireEvent("beforeload", this, options) !== false){
10068 this.storeOptions(options);
10069 var p = Roo.apply(options.params || {}, this.baseParams);
10070 // if meta was not loaded from remote source.. try requesting it.
10071 if (!this.reader.metaFromRemote) {
10072 p._requestMeta = 1;
10074 if(this.sortInfo && this.remoteSort){
10075 var pn = this.paramNames;
10076 p[pn["sort"]] = this.sortInfo.field;
10077 p[pn["dir"]] = this.sortInfo.direction;
10079 if (this.multiSort) {
10080 var pn = this.paramNames;
10081 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10084 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10089 * Reloads the Record cache from the configured Proxy using the configured Reader and
10090 * the options from the last load operation performed.
10091 * @param {Object} options (optional) An object containing properties which may override the options
10092 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10093 * the most recently used options are reused).
10095 reload : function(options){
10096 this.load(Roo.applyIf(options||{}, this.lastOptions));
10100 // Called as a callback by the Reader during a load operation.
10101 loadRecords : function(o, options, success){
10102 if(!o || success === false){
10103 if(success !== false){
10104 this.fireEvent("load", this, [], options, o);
10106 if(options.callback){
10107 options.callback.call(options.scope || this, [], options, false);
10111 // if data returned failure - throw an exception.
10112 if (o.success === false) {
10113 // show a message if no listener is registered.
10114 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10115 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10117 // loadmask wil be hooked into this..
10118 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10121 var r = o.records, t = o.totalRecords || r.length;
10123 this.fireEvent("beforeloadadd", this, r, options, o);
10125 if(!options || options.add !== true){
10126 if(this.pruneModifiedRecords){
10127 this.modified = [];
10129 for(var i = 0, len = r.length; i < len; i++){
10133 this.data = this.snapshot;
10134 delete this.snapshot;
10137 this.data.addAll(r);
10138 this.totalLength = t;
10140 this.fireEvent("datachanged", this);
10142 this.totalLength = Math.max(t, this.data.length+r.length);
10145 this.fireEvent("load", this, r, options, o);
10146 if(options.callback){
10147 options.callback.call(options.scope || this, r, options, true);
10153 * Loads data from a passed data block. A Reader which understands the format of the data
10154 * must have been configured in the constructor.
10155 * @param {Object} data The data block from which to read the Records. The format of the data expected
10156 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10157 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10159 loadData : function(o, append){
10160 var r = this.reader.readRecords(o);
10161 this.loadRecords(r, {add: append}, true);
10165 * Gets the number of cached records.
10167 * <em>If using paging, this may not be the total size of the dataset. If the data object
10168 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10169 * the data set size</em>
10171 getCount : function(){
10172 return this.data.length || 0;
10176 * Gets the total number of records in the dataset as returned by the server.
10178 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10179 * the dataset size</em>
10181 getTotalCount : function(){
10182 return this.totalLength || 0;
10186 * Returns the sort state of the Store as an object with two properties:
10188 field {String} The name of the field by which the Records are sorted
10189 direction {String} The sort order, "ASC" or "DESC"
10192 getSortState : function(){
10193 return this.sortInfo;
10197 applySort : function(){
10198 if(this.sortInfo && !this.remoteSort){
10199 var s = this.sortInfo, f = s.field;
10200 var st = this.fields.get(f).sortType;
10201 var fn = function(r1, r2){
10202 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10203 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10205 this.data.sort(s.direction, fn);
10206 if(this.snapshot && this.snapshot != this.data){
10207 this.snapshot.sort(s.direction, fn);
10213 * Sets the default sort column and order to be used by the next load operation.
10214 * @param {String} fieldName The name of the field to sort by.
10215 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10217 setDefaultSort : function(field, dir){
10218 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10222 * Sort the Records.
10223 * If remote sorting is used, the sort is performed on the server, and the cache is
10224 * reloaded. If local sorting is used, the cache is sorted internally.
10225 * @param {String} fieldName The name of the field to sort by.
10226 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10228 sort : function(fieldName, dir){
10229 var f = this.fields.get(fieldName);
10231 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10233 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10234 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10239 this.sortToggle[f.name] = dir;
10240 this.sortInfo = {field: f.name, direction: dir};
10241 if(!this.remoteSort){
10243 this.fireEvent("datachanged", this);
10245 this.load(this.lastOptions);
10250 * Calls the specified function for each of the Records in the cache.
10251 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10252 * Returning <em>false</em> aborts and exits the iteration.
10253 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10255 each : function(fn, scope){
10256 this.data.each(fn, scope);
10260 * Gets all records modified since the last commit. Modified records are persisted across load operations
10261 * (e.g., during paging).
10262 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10264 getModifiedRecords : function(){
10265 return this.modified;
10269 createFilterFn : function(property, value, anyMatch){
10270 if(!value.exec){ // not a regex
10271 value = String(value);
10272 if(value.length == 0){
10275 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10277 return function(r){
10278 return value.test(r.data[property]);
10283 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10284 * @param {String} property A field on your records
10285 * @param {Number} start The record index to start at (defaults to 0)
10286 * @param {Number} end The last record index to include (defaults to length - 1)
10287 * @return {Number} The sum
10289 sum : function(property, start, end){
10290 var rs = this.data.items, v = 0;
10291 start = start || 0;
10292 end = (end || end === 0) ? end : rs.length-1;
10294 for(var i = start; i <= end; i++){
10295 v += (rs[i].data[property] || 0);
10301 * Filter the records by a specified property.
10302 * @param {String} field A field on your records
10303 * @param {String/RegExp} value Either a string that the field
10304 * should start with or a RegExp to test against the field
10305 * @param {Boolean} anyMatch True to match any part not just the beginning
10307 filter : function(property, value, anyMatch){
10308 var fn = this.createFilterFn(property, value, anyMatch);
10309 return fn ? this.filterBy(fn) : this.clearFilter();
10313 * Filter by a function. The specified function will be called with each
10314 * record in this data source. If the function returns true the record is included,
10315 * otherwise it is filtered.
10316 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10317 * @param {Object} scope (optional) The scope of the function (defaults to this)
10319 filterBy : function(fn, scope){
10320 this.snapshot = this.snapshot || this.data;
10321 this.data = this.queryBy(fn, scope||this);
10322 this.fireEvent("datachanged", this);
10326 * Query the records by a specified property.
10327 * @param {String} field A field on your records
10328 * @param {String/RegExp} value Either a string that the field
10329 * should start with or a RegExp to test against the field
10330 * @param {Boolean} anyMatch True to match any part not just the beginning
10331 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10333 query : function(property, value, anyMatch){
10334 var fn = this.createFilterFn(property, value, anyMatch);
10335 return fn ? this.queryBy(fn) : this.data.clone();
10339 * Query by a function. The specified function will be called with each
10340 * record in this data source. If the function returns true the record is included
10342 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10343 * @param {Object} scope (optional) The scope of the function (defaults to this)
10344 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10346 queryBy : function(fn, scope){
10347 var data = this.snapshot || this.data;
10348 return data.filterBy(fn, scope||this);
10352 * Collects unique values for a particular dataIndex from this store.
10353 * @param {String} dataIndex The property to collect
10354 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10355 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10356 * @return {Array} An array of the unique values
10358 collect : function(dataIndex, allowNull, bypassFilter){
10359 var d = (bypassFilter === true && this.snapshot) ?
10360 this.snapshot.items : this.data.items;
10361 var v, sv, r = [], l = {};
10362 for(var i = 0, len = d.length; i < len; i++){
10363 v = d[i].data[dataIndex];
10365 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10374 * Revert to a view of the Record cache with no filtering applied.
10375 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10377 clearFilter : function(suppressEvent){
10378 if(this.snapshot && this.snapshot != this.data){
10379 this.data = this.snapshot;
10380 delete this.snapshot;
10381 if(suppressEvent !== true){
10382 this.fireEvent("datachanged", this);
10388 afterEdit : function(record){
10389 if(this.modified.indexOf(record) == -1){
10390 this.modified.push(record);
10392 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10396 afterReject : function(record){
10397 this.modified.remove(record);
10398 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10402 afterCommit : function(record){
10403 this.modified.remove(record);
10404 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10408 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10409 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10411 commitChanges : function(){
10412 var m = this.modified.slice(0);
10413 this.modified = [];
10414 for(var i = 0, len = m.length; i < len; i++){
10420 * Cancel outstanding changes on all changed records.
10422 rejectChanges : function(){
10423 var m = this.modified.slice(0);
10424 this.modified = [];
10425 for(var i = 0, len = m.length; i < len; i++){
10430 onMetaChange : function(meta, rtype, o){
10431 this.recordType = rtype;
10432 this.fields = rtype.prototype.fields;
10433 delete this.snapshot;
10434 this.sortInfo = meta.sortInfo || this.sortInfo;
10435 this.modified = [];
10436 this.fireEvent('metachange', this, this.reader.meta);
10439 moveIndex : function(data, type)
10441 var index = this.indexOf(data);
10443 var newIndex = index + type;
10447 this.insert(newIndex, data);
10452 * Ext JS Library 1.1.1
10453 * Copyright(c) 2006-2007, Ext JS, LLC.
10455 * Originally Released Under LGPL - original licence link has changed is not relivant.
10458 * <script type="text/javascript">
10462 * @class Roo.data.SimpleStore
10463 * @extends Roo.data.Store
10464 * Small helper class to make creating Stores from Array data easier.
10465 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10466 * @cfg {Array} fields An array of field definition objects, or field name strings.
10467 * @cfg {Array} data The multi-dimensional array of data
10469 * @param {Object} config
10471 Roo.data.SimpleStore = function(config){
10472 Roo.data.SimpleStore.superclass.constructor.call(this, {
10474 reader: new Roo.data.ArrayReader({
10477 Roo.data.Record.create(config.fields)
10479 proxy : new Roo.data.MemoryProxy(config.data)
10483 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10485 * Ext JS Library 1.1.1
10486 * Copyright(c) 2006-2007, Ext JS, LLC.
10488 * Originally Released Under LGPL - original licence link has changed is not relivant.
10491 * <script type="text/javascript">
10496 * @extends Roo.data.Store
10497 * @class Roo.data.JsonStore
10498 * Small helper class to make creating Stores for JSON data easier. <br/>
10500 var store = new Roo.data.JsonStore({
10501 url: 'get-images.php',
10503 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10506 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10507 * JsonReader and HttpProxy (unless inline data is provided).</b>
10508 * @cfg {Array} fields An array of field definition objects, or field name strings.
10510 * @param {Object} config
10512 Roo.data.JsonStore = function(c){
10513 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10514 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10515 reader: new Roo.data.JsonReader(c, c.fields)
10518 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10520 * Ext JS Library 1.1.1
10521 * Copyright(c) 2006-2007, Ext JS, LLC.
10523 * Originally Released Under LGPL - original licence link has changed is not relivant.
10526 * <script type="text/javascript">
10530 Roo.data.Field = function(config){
10531 if(typeof config == "string"){
10532 config = {name: config};
10534 Roo.apply(this, config);
10537 this.type = "auto";
10540 var st = Roo.data.SortTypes;
10541 // named sortTypes are supported, here we look them up
10542 if(typeof this.sortType == "string"){
10543 this.sortType = st[this.sortType];
10546 // set default sortType for strings and dates
10547 if(!this.sortType){
10550 this.sortType = st.asUCString;
10553 this.sortType = st.asDate;
10556 this.sortType = st.none;
10561 var stripRe = /[\$,%]/g;
10563 // prebuilt conversion function for this field, instead of
10564 // switching every time we're reading a value
10566 var cv, dateFormat = this.dateFormat;
10571 cv = function(v){ return v; };
10574 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10578 return v !== undefined && v !== null && v !== '' ?
10579 parseInt(String(v).replace(stripRe, ""), 10) : '';
10584 return v !== undefined && v !== null && v !== '' ?
10585 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10590 cv = function(v){ return v === true || v === "true" || v == 1; };
10597 if(v instanceof Date){
10601 if(dateFormat == "timestamp"){
10602 return new Date(v*1000);
10604 return Date.parseDate(v, dateFormat);
10606 var parsed = Date.parse(v);
10607 return parsed ? new Date(parsed) : null;
10616 Roo.data.Field.prototype = {
10624 * Ext JS Library 1.1.1
10625 * Copyright(c) 2006-2007, Ext JS, LLC.
10627 * Originally Released Under LGPL - original licence link has changed is not relivant.
10630 * <script type="text/javascript">
10633 // Base class for reading structured data from a data source. This class is intended to be
10634 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10637 * @class Roo.data.DataReader
10638 * Base class for reading structured data from a data source. This class is intended to be
10639 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10642 Roo.data.DataReader = function(meta, recordType){
10646 this.recordType = recordType instanceof Array ?
10647 Roo.data.Record.create(recordType) : recordType;
10650 Roo.data.DataReader.prototype = {
10652 * Create an empty record
10653 * @param {Object} data (optional) - overlay some values
10654 * @return {Roo.data.Record} record created.
10656 newRow : function(d) {
10658 this.recordType.prototype.fields.each(function(c) {
10660 case 'int' : da[c.name] = 0; break;
10661 case 'date' : da[c.name] = new Date(); break;
10662 case 'float' : da[c.name] = 0.0; break;
10663 case 'boolean' : da[c.name] = false; break;
10664 default : da[c.name] = ""; break;
10668 return new this.recordType(Roo.apply(da, d));
10673 * Ext JS Library 1.1.1
10674 * Copyright(c) 2006-2007, Ext JS, LLC.
10676 * Originally Released Under LGPL - original licence link has changed is not relivant.
10679 * <script type="text/javascript">
10683 * @class Roo.data.DataProxy
10684 * @extends Roo.data.Observable
10685 * This class is an abstract base class for implementations which provide retrieval of
10686 * unformatted data objects.<br>
10688 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10689 * (of the appropriate type which knows how to parse the data object) to provide a block of
10690 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10692 * Custom implementations must implement the load method as described in
10693 * {@link Roo.data.HttpProxy#load}.
10695 Roo.data.DataProxy = function(){
10698 * @event beforeload
10699 * Fires before a network request is made to retrieve a data object.
10700 * @param {Object} This DataProxy object.
10701 * @param {Object} params The params parameter to the load function.
10706 * Fires before the load method's callback is called.
10707 * @param {Object} This DataProxy object.
10708 * @param {Object} o The data object.
10709 * @param {Object} arg The callback argument object passed to the load function.
10713 * @event loadexception
10714 * Fires if an Exception occurs during data retrieval.
10715 * @param {Object} This DataProxy object.
10716 * @param {Object} o The data object.
10717 * @param {Object} arg The callback argument object passed to the load function.
10718 * @param {Object} e The Exception.
10720 loadexception : true
10722 Roo.data.DataProxy.superclass.constructor.call(this);
10725 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10728 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10732 * Ext JS Library 1.1.1
10733 * Copyright(c) 2006-2007, Ext JS, LLC.
10735 * Originally Released Under LGPL - original licence link has changed is not relivant.
10738 * <script type="text/javascript">
10741 * @class Roo.data.MemoryProxy
10742 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10743 * to the Reader when its load method is called.
10745 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10747 Roo.data.MemoryProxy = function(data){
10751 Roo.data.MemoryProxy.superclass.constructor.call(this);
10755 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10757 * Load data from the requested source (in this case an in-memory
10758 * data object passed to the constructor), read the data object into
10759 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10760 * process that block using the passed callback.
10761 * @param {Object} params This parameter is not used by the MemoryProxy class.
10762 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10763 * object into a block of Roo.data.Records.
10764 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10765 * The function must be passed <ul>
10766 * <li>The Record block object</li>
10767 * <li>The "arg" argument from the load function</li>
10768 * <li>A boolean success indicator</li>
10770 * @param {Object} scope The scope in which to call the callback
10771 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10773 load : function(params, reader, callback, scope, arg){
10774 params = params || {};
10777 result = reader.readRecords(this.data);
10779 this.fireEvent("loadexception", this, arg, null, e);
10780 callback.call(scope, null, arg, false);
10783 callback.call(scope, result, arg, true);
10787 update : function(params, records){
10792 * Ext JS Library 1.1.1
10793 * Copyright(c) 2006-2007, Ext JS, LLC.
10795 * Originally Released Under LGPL - original licence link has changed is not relivant.
10798 * <script type="text/javascript">
10801 * @class Roo.data.HttpProxy
10802 * @extends Roo.data.DataProxy
10803 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10804 * configured to reference a certain URL.<br><br>
10806 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10807 * from which the running page was served.<br><br>
10809 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10811 * Be aware that to enable the browser to parse an XML document, the server must set
10812 * the Content-Type header in the HTTP response to "text/xml".
10814 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10815 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10816 * will be used to make the request.
10818 Roo.data.HttpProxy = function(conn){
10819 Roo.data.HttpProxy.superclass.constructor.call(this);
10820 // is conn a conn config or a real conn?
10822 this.useAjax = !conn || !conn.events;
10826 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10827 // thse are take from connection...
10830 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10833 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10834 * extra parameters to each request made by this object. (defaults to undefined)
10837 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10838 * to each request made by this object. (defaults to undefined)
10841 * @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)
10844 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10847 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10853 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10857 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10858 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10859 * a finer-grained basis than the DataProxy events.
10861 getConnection : function(){
10862 return this.useAjax ? Roo.Ajax : this.conn;
10866 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10867 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10868 * process that block using the passed callback.
10869 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10870 * for the request to the remote server.
10871 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10872 * object into a block of Roo.data.Records.
10873 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10874 * The function must be passed <ul>
10875 * <li>The Record block object</li>
10876 * <li>The "arg" argument from the load function</li>
10877 * <li>A boolean success indicator</li>
10879 * @param {Object} scope The scope in which to call the callback
10880 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10882 load : function(params, reader, callback, scope, arg){
10883 if(this.fireEvent("beforeload", this, params) !== false){
10885 params : params || {},
10887 callback : callback,
10892 callback : this.loadResponse,
10896 Roo.applyIf(o, this.conn);
10897 if(this.activeRequest){
10898 Roo.Ajax.abort(this.activeRequest);
10900 this.activeRequest = Roo.Ajax.request(o);
10902 this.conn.request(o);
10905 callback.call(scope||this, null, arg, false);
10910 loadResponse : function(o, success, response){
10911 delete this.activeRequest;
10913 this.fireEvent("loadexception", this, o, response);
10914 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10919 result = o.reader.read(response);
10921 this.fireEvent("loadexception", this, o, response, e);
10922 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10926 this.fireEvent("load", this, o, o.request.arg);
10927 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10931 update : function(dataSet){
10936 updateResponse : function(dataSet){
10941 * Ext JS Library 1.1.1
10942 * Copyright(c) 2006-2007, Ext JS, LLC.
10944 * Originally Released Under LGPL - original licence link has changed is not relivant.
10947 * <script type="text/javascript">
10951 * @class Roo.data.ScriptTagProxy
10952 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10953 * other than the originating domain of the running page.<br><br>
10955 * <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
10956 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10958 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10959 * source code that is used as the source inside a <script> tag.<br><br>
10961 * In order for the browser to process the returned data, the server must wrap the data object
10962 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10963 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10964 * depending on whether the callback name was passed:
10967 boolean scriptTag = false;
10968 String cb = request.getParameter("callback");
10971 response.setContentType("text/javascript");
10973 response.setContentType("application/x-json");
10975 Writer out = response.getWriter();
10977 out.write(cb + "(");
10979 out.print(dataBlock.toJsonString());
10986 * @param {Object} config A configuration object.
10988 Roo.data.ScriptTagProxy = function(config){
10989 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10990 Roo.apply(this, config);
10991 this.head = document.getElementsByTagName("head")[0];
10994 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10996 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10998 * @cfg {String} url The URL from which to request the data object.
11001 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11005 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11006 * the server the name of the callback function set up by the load call to process the returned data object.
11007 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11008 * javascript output which calls this named function passing the data object as its only parameter.
11010 callbackParam : "callback",
11012 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11013 * name to the request.
11018 * Load data from the configured URL, read the data object into
11019 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11020 * process that block using the passed callback.
11021 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11022 * for the request to the remote server.
11023 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11024 * object into a block of Roo.data.Records.
11025 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11026 * The function must be passed <ul>
11027 * <li>The Record block object</li>
11028 * <li>The "arg" argument from the load function</li>
11029 * <li>A boolean success indicator</li>
11031 * @param {Object} scope The scope in which to call the callback
11032 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11034 load : function(params, reader, callback, scope, arg){
11035 if(this.fireEvent("beforeload", this, params) !== false){
11037 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11039 var url = this.url;
11040 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11042 url += "&_dc=" + (new Date().getTime());
11044 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11047 cb : "stcCallback"+transId,
11048 scriptId : "stcScript"+transId,
11052 callback : callback,
11058 window[trans.cb] = function(o){
11059 conn.handleResponse(o, trans);
11062 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11064 if(this.autoAbort !== false){
11068 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11070 var script = document.createElement("script");
11071 script.setAttribute("src", url);
11072 script.setAttribute("type", "text/javascript");
11073 script.setAttribute("id", trans.scriptId);
11074 this.head.appendChild(script);
11076 this.trans = trans;
11078 callback.call(scope||this, null, arg, false);
11083 isLoading : function(){
11084 return this.trans ? true : false;
11088 * Abort the current server request.
11090 abort : function(){
11091 if(this.isLoading()){
11092 this.destroyTrans(this.trans);
11097 destroyTrans : function(trans, isLoaded){
11098 this.head.removeChild(document.getElementById(trans.scriptId));
11099 clearTimeout(trans.timeoutId);
11101 window[trans.cb] = undefined;
11103 delete window[trans.cb];
11106 // if hasn't been loaded, wait for load to remove it to prevent script error
11107 window[trans.cb] = function(){
11108 window[trans.cb] = undefined;
11110 delete window[trans.cb];
11117 handleResponse : function(o, trans){
11118 this.trans = false;
11119 this.destroyTrans(trans, true);
11122 result = trans.reader.readRecords(o);
11124 this.fireEvent("loadexception", this, o, trans.arg, e);
11125 trans.callback.call(trans.scope||window, null, trans.arg, false);
11128 this.fireEvent("load", this, o, trans.arg);
11129 trans.callback.call(trans.scope||window, result, trans.arg, true);
11133 handleFailure : function(trans){
11134 this.trans = false;
11135 this.destroyTrans(trans, false);
11136 this.fireEvent("loadexception", this, null, trans.arg);
11137 trans.callback.call(trans.scope||window, null, trans.arg, false);
11141 * Ext JS Library 1.1.1
11142 * Copyright(c) 2006-2007, Ext JS, LLC.
11144 * Originally Released Under LGPL - original licence link has changed is not relivant.
11147 * <script type="text/javascript">
11151 * @class Roo.data.JsonReader
11152 * @extends Roo.data.DataReader
11153 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11154 * based on mappings in a provided Roo.data.Record constructor.
11156 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11157 * in the reply previously.
11162 var RecordDef = Roo.data.Record.create([
11163 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11164 {name: 'occupation'} // This field will use "occupation" as the mapping.
11166 var myReader = new Roo.data.JsonReader({
11167 totalProperty: "results", // The property which contains the total dataset size (optional)
11168 root: "rows", // The property which contains an Array of row objects
11169 id: "id" // The property within each row object that provides an ID for the record (optional)
11173 * This would consume a JSON file like this:
11175 { 'results': 2, 'rows': [
11176 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11177 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11180 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11181 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11182 * paged from the remote server.
11183 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11184 * @cfg {String} root name of the property which contains the Array of row objects.
11185 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11186 * @cfg {Array} fields Array of field definition objects
11188 * Create a new JsonReader
11189 * @param {Object} meta Metadata configuration options
11190 * @param {Object} recordType Either an Array of field definition objects,
11191 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11193 Roo.data.JsonReader = function(meta, recordType){
11196 // set some defaults:
11197 Roo.applyIf(meta, {
11198 totalProperty: 'total',
11199 successProperty : 'success',
11204 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11206 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11209 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11210 * Used by Store query builder to append _requestMeta to params.
11213 metaFromRemote : false,
11215 * This method is only used by a DataProxy which has retrieved data from a remote server.
11216 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11217 * @return {Object} data A data block which is used by an Roo.data.Store object as
11218 * a cache of Roo.data.Records.
11220 read : function(response){
11221 var json = response.responseText;
11223 var o = /* eval:var:o */ eval("("+json+")");
11225 throw {message: "JsonReader.read: Json object not found"};
11231 this.metaFromRemote = true;
11232 this.meta = o.metaData;
11233 this.recordType = Roo.data.Record.create(o.metaData.fields);
11234 this.onMetaChange(this.meta, this.recordType, o);
11236 return this.readRecords(o);
11239 // private function a store will implement
11240 onMetaChange : function(meta, recordType, o){
11247 simpleAccess: function(obj, subsc) {
11254 getJsonAccessor: function(){
11256 return function(expr) {
11258 return(re.test(expr))
11259 ? new Function("obj", "return obj." + expr)
11264 return Roo.emptyFn;
11269 * Create a data block containing Roo.data.Records from an XML document.
11270 * @param {Object} o An object which contains an Array of row objects in the property specified
11271 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11272 * which contains the total size of the dataset.
11273 * @return {Object} data A data block which is used by an Roo.data.Store object as
11274 * a cache of Roo.data.Records.
11276 readRecords : function(o){
11278 * After any data loads, the raw JSON data is available for further custom processing.
11282 var s = this.meta, Record = this.recordType,
11283 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11285 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11287 if(s.totalProperty) {
11288 this.getTotal = this.getJsonAccessor(s.totalProperty);
11290 if(s.successProperty) {
11291 this.getSuccess = this.getJsonAccessor(s.successProperty);
11293 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11295 var g = this.getJsonAccessor(s.id);
11296 this.getId = function(rec) {
11298 return (r === undefined || r === "") ? null : r;
11301 this.getId = function(){return null;};
11304 for(var jj = 0; jj < fl; jj++){
11306 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11307 this.ef[jj] = this.getJsonAccessor(map);
11311 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11312 if(s.totalProperty){
11313 var vt = parseInt(this.getTotal(o), 10);
11318 if(s.successProperty){
11319 var vs = this.getSuccess(o);
11320 if(vs === false || vs === 'false'){
11325 for(var i = 0; i < c; i++){
11328 var id = this.getId(n);
11329 for(var j = 0; j < fl; j++){
11331 var v = this.ef[j](n);
11333 Roo.log('missing convert for ' + f.name);
11337 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11339 var record = new Record(values, id);
11341 records[i] = record;
11347 totalRecords : totalRecords
11352 * Ext JS Library 1.1.1
11353 * Copyright(c) 2006-2007, Ext JS, LLC.
11355 * Originally Released Under LGPL - original licence link has changed is not relivant.
11358 * <script type="text/javascript">
11362 * @class Roo.data.ArrayReader
11363 * @extends Roo.data.DataReader
11364 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11365 * Each element of that Array represents a row of data fields. The
11366 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11367 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11371 var RecordDef = Roo.data.Record.create([
11372 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11373 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11375 var myReader = new Roo.data.ArrayReader({
11376 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11380 * This would consume an Array like this:
11382 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11384 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11386 * Create a new JsonReader
11387 * @param {Object} meta Metadata configuration options.
11388 * @param {Object} recordType Either an Array of field definition objects
11389 * as specified to {@link Roo.data.Record#create},
11390 * or an {@link Roo.data.Record} object
11391 * created using {@link Roo.data.Record#create}.
11393 Roo.data.ArrayReader = function(meta, recordType){
11394 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11397 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11399 * Create a data block containing Roo.data.Records from an XML document.
11400 * @param {Object} o An Array of row objects which represents the dataset.
11401 * @return {Object} data A data block which is used by an Roo.data.Store object as
11402 * a cache of Roo.data.Records.
11404 readRecords : function(o){
11405 var sid = this.meta ? this.meta.id : null;
11406 var recordType = this.recordType, fields = recordType.prototype.fields;
11409 for(var i = 0; i < root.length; i++){
11412 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11413 for(var j = 0, jlen = fields.length; j < jlen; j++){
11414 var f = fields.items[j];
11415 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11416 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11418 values[f.name] = v;
11420 var record = new recordType(values, id);
11422 records[records.length] = record;
11426 totalRecords : records.length
11435 * @class Roo.bootstrap.ComboBox
11436 * @extends Roo.bootstrap.TriggerField
11437 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11438 * @cfg {Boolean} append (true|false) default false
11439 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11440 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11441 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11442 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11443 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11444 * @cfg {Boolean} animate default true
11445 * @cfg {Boolean} emptyResultText only for touch device
11446 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11448 * Create a new ComboBox.
11449 * @param {Object} config Configuration options
11451 Roo.bootstrap.ComboBox = function(config){
11452 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11456 * Fires when the dropdown list is expanded
11457 * @param {Roo.bootstrap.ComboBox} combo This combo box
11462 * Fires when the dropdown list is collapsed
11463 * @param {Roo.bootstrap.ComboBox} combo This combo box
11467 * @event beforeselect
11468 * Fires before a list item is selected. Return false to cancel the selection.
11469 * @param {Roo.bootstrap.ComboBox} combo This combo box
11470 * @param {Roo.data.Record} record The data record returned from the underlying store
11471 * @param {Number} index The index of the selected item in the dropdown list
11473 'beforeselect' : true,
11476 * Fires when a list item is selected
11477 * @param {Roo.bootstrap.ComboBox} combo This combo box
11478 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11479 * @param {Number} index The index of the selected item in the dropdown list
11483 * @event beforequery
11484 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11485 * The event object passed has these properties:
11486 * @param {Roo.bootstrap.ComboBox} combo This combo box
11487 * @param {String} query The query
11488 * @param {Boolean} forceAll true to force "all" query
11489 * @param {Boolean} cancel true to cancel the query
11490 * @param {Object} e The query event object
11492 'beforequery': true,
11495 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11496 * @param {Roo.bootstrap.ComboBox} combo This combo box
11501 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11502 * @param {Roo.bootstrap.ComboBox} combo This combo box
11503 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11508 * Fires when the remove value from the combobox array
11509 * @param {Roo.bootstrap.ComboBox} combo This combo box
11513 * @event specialfilter
11514 * Fires when specialfilter
11515 * @param {Roo.bootstrap.ComboBox} combo This combo box
11517 'specialfilter' : true,
11520 * Fires when tick the element
11521 * @param {Roo.bootstrap.ComboBox} combo This combo box
11525 * @event touchviewdisplay
11526 * Fires when touch view require special display (default is using displayField)
11527 * @param {Roo.bootstrap.ComboBox} combo This combo box
11528 * @param {Object} cfg set html .
11530 'touchviewdisplay' : true
11535 this.tickItems = [];
11537 this.selectedIndex = -1;
11538 if(this.mode == 'local'){
11539 if(config.queryDelay === undefined){
11540 this.queryDelay = 10;
11542 if(config.minChars === undefined){
11548 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11551 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11552 * rendering into an Roo.Editor, defaults to false)
11555 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11556 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11559 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11562 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11563 * the dropdown list (defaults to undefined, with no header element)
11567 * @cfg {String/Roo.Template} tpl The template to use to render the output
11571 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11573 listWidth: undefined,
11575 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11576 * mode = 'remote' or 'text' if mode = 'local')
11578 displayField: undefined,
11581 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11582 * mode = 'remote' or 'value' if mode = 'local').
11583 * Note: use of a valueField requires the user make a selection
11584 * in order for a value to be mapped.
11586 valueField: undefined,
11590 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11591 * field's data value (defaults to the underlying DOM element's name)
11593 hiddenName: undefined,
11595 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11599 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11601 selectedClass: 'active',
11604 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11608 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11609 * anchor positions (defaults to 'tl-bl')
11611 listAlign: 'tl-bl?',
11613 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11617 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11618 * query specified by the allQuery config option (defaults to 'query')
11620 triggerAction: 'query',
11622 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11623 * (defaults to 4, does not apply if editable = false)
11627 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11628 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11632 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11633 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11637 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11638 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11642 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11643 * when editable = true (defaults to false)
11645 selectOnFocus:false,
11647 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11649 queryParam: 'query',
11651 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11652 * when mode = 'remote' (defaults to 'Loading...')
11654 loadingText: 'Loading...',
11656 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11660 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11664 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11665 * traditional select (defaults to true)
11669 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11673 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11677 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11678 * listWidth has a higher value)
11682 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11683 * allow the user to set arbitrary text into the field (defaults to false)
11685 forceSelection:false,
11687 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11688 * if typeAhead = true (defaults to 250)
11690 typeAheadDelay : 250,
11692 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11693 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11695 valueNotFoundText : undefined,
11697 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11699 blockFocus : false,
11702 * @cfg {Boolean} disableClear Disable showing of clear button.
11704 disableClear : false,
11706 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11708 alwaysQuery : false,
11711 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11716 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11718 invalidClass : "has-warning",
11721 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11723 validClass : "has-success",
11726 * @cfg {Boolean} specialFilter (true|false) special filter default false
11728 specialFilter : false,
11731 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11733 mobileTouchView : true,
11745 btnPosition : 'right',
11746 triggerList : true,
11747 showToggleBtn : true,
11749 emptyResultText: 'Empty',
11750 triggerText : 'Select',
11752 // element that contains real text value.. (when hidden is used..)
11754 getAutoCreate : function()
11762 if(Roo.isTouch && this.mobileTouchView){
11763 cfg = this.getAutoCreateTouchView();
11770 if(!this.tickable){
11771 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11776 * ComboBox with tickable selections
11779 var align = this.labelAlign || this.parentLabelAlign();
11782 cls : 'form-group roo-combobox-tickable' //input-group
11787 cls : 'tickable-buttons',
11792 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11793 html : this.triggerText
11799 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11806 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11813 buttons.cn.unshift({
11815 cls: 'select2-search-field-input'
11821 Roo.each(buttons.cn, function(c){
11823 c.cls += ' btn-' + _this.size;
11826 if (_this.disabled) {
11837 cls: 'form-hidden-field'
11841 cls: 'select2-choices',
11845 cls: 'select2-search-field',
11857 cls: 'select2-container input-group select2-container-multi',
11862 // cls: 'typeahead typeahead-long dropdown-menu',
11863 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11868 if(this.hasFeedback && !this.allowBlank){
11872 cls: 'glyphicon form-control-feedback'
11875 combobox.cn.push(feedback);
11878 if (align ==='left' && this.fieldLabel.length) {
11880 Roo.log("left and has label");
11886 cls : 'control-label col-sm-' + this.labelWidth,
11887 html : this.fieldLabel
11891 cls : "col-sm-" + (12 - this.labelWidth),
11898 } else if ( this.fieldLabel.length) {
11904 //cls : 'input-group-addon',
11905 html : this.fieldLabel
11915 Roo.log(" no label && no align");
11922 ['xs','sm','md','lg'].map(function(size){
11923 if (settings[size]) {
11924 cfg.cls += ' col-' + size + '-' + settings[size];
11932 _initEventsCalled : false,
11935 initEvents: function()
11938 if (this._initEventsCalled) { // as we call render... prevent looping...
11941 this._initEventsCalled = true;
11944 throw "can not find store for combo";
11947 this.store = Roo.factory(this.store, Roo.data);
11949 // if we are building from html. then this element is so complex, that we can not really
11950 // use the rendered HTML.
11951 // so we have to trash and replace the previous code.
11952 if (Roo.XComponent.build_from_html) {
11954 // remove this element....
11955 var e = this.el.dom, k=0;
11956 while (e ) { e = e.previousSibling; ++k;}
11961 this.rendered = false;
11963 this.render(this.parent().getChildContainer(true), k);
11974 if(Roo.isTouch && this.mobileTouchView){
11975 this.initTouchView();
11980 this.initTickableEvents();
11984 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11986 if(this.hiddenName){
11988 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11990 this.hiddenField.dom.value =
11991 this.hiddenValue !== undefined ? this.hiddenValue :
11992 this.value !== undefined ? this.value : '';
11994 // prevent input submission
11995 this.el.dom.removeAttribute('name');
11996 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12001 // this.el.dom.setAttribute('autocomplete', 'off');
12004 var cls = 'x-combo-list';
12006 //this.list = new Roo.Layer({
12007 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12013 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12014 _this.list.setWidth(lw);
12017 this.list.on('mouseover', this.onViewOver, this);
12018 this.list.on('mousemove', this.onViewMove, this);
12020 this.list.on('scroll', this.onViewScroll, this);
12023 this.list.swallowEvent('mousewheel');
12024 this.assetHeight = 0;
12027 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12028 this.assetHeight += this.header.getHeight();
12031 this.innerList = this.list.createChild({cls:cls+'-inner'});
12032 this.innerList.on('mouseover', this.onViewOver, this);
12033 this.innerList.on('mousemove', this.onViewMove, this);
12034 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12036 if(this.allowBlank && !this.pageSize && !this.disableClear){
12037 this.footer = this.list.createChild({cls:cls+'-ft'});
12038 this.pageTb = new Roo.Toolbar(this.footer);
12042 this.footer = this.list.createChild({cls:cls+'-ft'});
12043 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12044 {pageSize: this.pageSize});
12048 if (this.pageTb && this.allowBlank && !this.disableClear) {
12050 this.pageTb.add(new Roo.Toolbar.Fill(), {
12051 cls: 'x-btn-icon x-btn-clear',
12053 handler: function()
12056 _this.clearValue();
12057 _this.onSelect(false, -1);
12062 this.assetHeight += this.footer.getHeight();
12067 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12070 this.view = new Roo.View(this.list, this.tpl, {
12071 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12073 //this.view.wrapEl.setDisplayed(false);
12074 this.view.on('click', this.onViewClick, this);
12078 this.store.on('beforeload', this.onBeforeLoad, this);
12079 this.store.on('load', this.onLoad, this);
12080 this.store.on('loadexception', this.onLoadException, this);
12082 if(this.resizable){
12083 this.resizer = new Roo.Resizable(this.list, {
12084 pinned:true, handles:'se'
12086 this.resizer.on('resize', function(r, w, h){
12087 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12088 this.listWidth = w;
12089 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12090 this.restrictHeight();
12092 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12095 if(!this.editable){
12096 this.editable = true;
12097 this.setEditable(false);
12102 if (typeof(this.events.add.listeners) != 'undefined') {
12104 this.addicon = this.wrap.createChild(
12105 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12107 this.addicon.on('click', function(e) {
12108 this.fireEvent('add', this);
12111 if (typeof(this.events.edit.listeners) != 'undefined') {
12113 this.editicon = this.wrap.createChild(
12114 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12115 if (this.addicon) {
12116 this.editicon.setStyle('margin-left', '40px');
12118 this.editicon.on('click', function(e) {
12120 // we fire even if inothing is selected..
12121 this.fireEvent('edit', this, this.lastData );
12127 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12128 "up" : function(e){
12129 this.inKeyMode = true;
12133 "down" : function(e){
12134 if(!this.isExpanded()){
12135 this.onTriggerClick();
12137 this.inKeyMode = true;
12142 "enter" : function(e){
12143 // this.onViewClick();
12147 if(this.fireEvent("specialkey", this, e)){
12148 this.onViewClick(false);
12154 "esc" : function(e){
12158 "tab" : function(e){
12161 if(this.fireEvent("specialkey", this, e)){
12162 this.onViewClick(false);
12170 doRelay : function(foo, bar, hname){
12171 if(hname == 'down' || this.scope.isExpanded()){
12172 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12181 this.queryDelay = Math.max(this.queryDelay || 10,
12182 this.mode == 'local' ? 10 : 250);
12185 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12187 if(this.typeAhead){
12188 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12190 if(this.editable !== false){
12191 this.inputEl().on("keyup", this.onKeyUp, this);
12193 if(this.forceSelection){
12194 this.inputEl().on('blur', this.doForce, this);
12198 this.choices = this.el.select('ul.select2-choices', true).first();
12199 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12203 initTickableEvents: function()
12207 if(this.hiddenName){
12209 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12211 this.hiddenField.dom.value =
12212 this.hiddenValue !== undefined ? this.hiddenValue :
12213 this.value !== undefined ? this.value : '';
12215 // prevent input submission
12216 this.el.dom.removeAttribute('name');
12217 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12222 // this.list = this.el.select('ul.dropdown-menu',true).first();
12224 this.choices = this.el.select('ul.select2-choices', true).first();
12225 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12226 if(this.triggerList){
12227 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12230 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12231 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12233 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12234 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12236 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12237 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12239 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12240 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12241 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12244 this.cancelBtn.hide();
12249 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12250 _this.list.setWidth(lw);
12253 this.list.on('mouseover', this.onViewOver, this);
12254 this.list.on('mousemove', this.onViewMove, this);
12256 this.list.on('scroll', this.onViewScroll, this);
12259 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>';
12262 this.view = new Roo.View(this.list, this.tpl, {
12263 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12266 //this.view.wrapEl.setDisplayed(false);
12267 this.view.on('click', this.onViewClick, this);
12271 this.store.on('beforeload', this.onBeforeLoad, this);
12272 this.store.on('load', this.onLoad, this);
12273 this.store.on('loadexception', this.onLoadException, this);
12276 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12277 "up" : function(e){
12278 this.inKeyMode = true;
12282 "down" : function(e){
12283 this.inKeyMode = true;
12287 "enter" : function(e){
12288 if(this.fireEvent("specialkey", this, e)){
12289 this.onViewClick(false);
12295 "esc" : function(e){
12296 this.onTickableFooterButtonClick(e, false, false);
12299 "tab" : function(e){
12300 this.fireEvent("specialkey", this, e);
12302 this.onTickableFooterButtonClick(e, false, false);
12309 doRelay : function(e, fn, key){
12310 if(this.scope.isExpanded()){
12311 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12320 this.queryDelay = Math.max(this.queryDelay || 10,
12321 this.mode == 'local' ? 10 : 250);
12324 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12326 if(this.typeAhead){
12327 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12330 if(this.editable !== false){
12331 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12336 onDestroy : function(){
12338 this.view.setStore(null);
12339 this.view.el.removeAllListeners();
12340 this.view.el.remove();
12341 this.view.purgeListeners();
12344 this.list.dom.innerHTML = '';
12348 this.store.un('beforeload', this.onBeforeLoad, this);
12349 this.store.un('load', this.onLoad, this);
12350 this.store.un('loadexception', this.onLoadException, this);
12352 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12356 fireKey : function(e){
12357 if(e.isNavKeyPress() && !this.list.isVisible()){
12358 this.fireEvent("specialkey", this, e);
12363 onResize: function(w, h){
12364 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12366 // if(typeof w != 'number'){
12367 // // we do not handle it!?!?
12370 // var tw = this.trigger.getWidth();
12371 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12372 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12374 // this.inputEl().setWidth( this.adjustWidth('input', x));
12376 // //this.trigger.setStyle('left', x+'px');
12378 // if(this.list && this.listWidth === undefined){
12379 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12380 // this.list.setWidth(lw);
12381 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12389 * Allow or prevent the user from directly editing the field text. If false is passed,
12390 * the user will only be able to select from the items defined in the dropdown list. This method
12391 * is the runtime equivalent of setting the 'editable' config option at config time.
12392 * @param {Boolean} value True to allow the user to directly edit the field text
12394 setEditable : function(value){
12395 if(value == this.editable){
12398 this.editable = value;
12400 this.inputEl().dom.setAttribute('readOnly', true);
12401 this.inputEl().on('mousedown', this.onTriggerClick, this);
12402 this.inputEl().addClass('x-combo-noedit');
12404 this.inputEl().dom.setAttribute('readOnly', false);
12405 this.inputEl().un('mousedown', this.onTriggerClick, this);
12406 this.inputEl().removeClass('x-combo-noedit');
12412 onBeforeLoad : function(combo,opts){
12413 if(!this.hasFocus){
12417 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12419 this.restrictHeight();
12420 this.selectedIndex = -1;
12424 onLoad : function(){
12426 this.hasQuery = false;
12428 if(!this.hasFocus){
12432 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12433 this.loading.hide();
12436 if(this.store.getCount() > 0){
12438 this.restrictHeight();
12439 if(this.lastQuery == this.allQuery){
12440 if(this.editable && !this.tickable){
12441 this.inputEl().dom.select();
12445 !this.selectByValue(this.value, true) &&
12448 !this.store.lastOptions ||
12449 typeof(this.store.lastOptions.add) == 'undefined' ||
12450 this.store.lastOptions.add != true
12453 this.select(0, true);
12456 if(this.autoFocus){
12459 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12460 this.taTask.delay(this.typeAheadDelay);
12464 this.onEmptyResults();
12470 onLoadException : function()
12472 this.hasQuery = false;
12474 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12475 this.loading.hide();
12478 if(this.tickable && this.editable){
12483 // only causes errors at present
12484 //Roo.log(this.store.reader.jsonData);
12485 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12487 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12493 onTypeAhead : function(){
12494 if(this.store.getCount() > 0){
12495 var r = this.store.getAt(0);
12496 var newValue = r.data[this.displayField];
12497 var len = newValue.length;
12498 var selStart = this.getRawValue().length;
12500 if(selStart != len){
12501 this.setRawValue(newValue);
12502 this.selectText(selStart, newValue.length);
12508 onSelect : function(record, index){
12510 if(this.fireEvent('beforeselect', this, record, index) !== false){
12512 this.setFromData(index > -1 ? record.data : false);
12515 this.fireEvent('select', this, record, index);
12520 * Returns the currently selected field value or empty string if no value is set.
12521 * @return {String} value The selected value
12523 getValue : function(){
12526 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12529 if(this.valueField){
12530 return typeof this.value != 'undefined' ? this.value : '';
12532 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12537 * Clears any text/value currently set in the field
12539 clearValue : function(){
12540 if(this.hiddenField){
12541 this.hiddenField.dom.value = '';
12544 this.setRawValue('');
12545 this.lastSelectionText = '';
12546 this.lastData = false;
12548 var close = this.closeTriggerEl();
12557 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12558 * will be displayed in the field. If the value does not match the data value of an existing item,
12559 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12560 * Otherwise the field will be blank (although the value will still be set).
12561 * @param {String} value The value to match
12563 setValue : function(v){
12570 if(this.valueField){
12571 var r = this.findRecord(this.valueField, v);
12573 text = r.data[this.displayField];
12574 }else if(this.valueNotFoundText !== undefined){
12575 text = this.valueNotFoundText;
12578 this.lastSelectionText = text;
12579 if(this.hiddenField){
12580 this.hiddenField.dom.value = v;
12582 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12585 var close = this.closeTriggerEl();
12588 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12592 * @property {Object} the last set data for the element
12597 * Sets the value of the field based on a object which is related to the record format for the store.
12598 * @param {Object} value the value to set as. or false on reset?
12600 setFromData : function(o){
12607 var dv = ''; // display value
12608 var vv = ''; // value value..
12610 if (this.displayField) {
12611 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12613 // this is an error condition!!!
12614 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12617 if(this.valueField){
12618 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12621 var close = this.closeTriggerEl();
12624 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12627 if(this.hiddenField){
12628 this.hiddenField.dom.value = vv;
12630 this.lastSelectionText = dv;
12631 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12635 // no hidden field.. - we store the value in 'value', but still display
12636 // display field!!!!
12637 this.lastSelectionText = dv;
12638 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12645 reset : function(){
12646 // overridden so that last data is reset..
12653 this.setValue(this.originalValue);
12654 this.clearInvalid();
12655 this.lastData = false;
12657 this.view.clearSelections();
12661 findRecord : function(prop, value){
12663 if(this.store.getCount() > 0){
12664 this.store.each(function(r){
12665 if(r.data[prop] == value){
12675 getName: function()
12677 // returns hidden if it's set..
12678 if (!this.rendered) {return ''};
12679 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12683 onViewMove : function(e, t){
12684 this.inKeyMode = false;
12688 onViewOver : function(e, t){
12689 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12692 var item = this.view.findItemFromChild(t);
12695 var index = this.view.indexOf(item);
12696 this.select(index, false);
12701 onViewClick : function(view, doFocus, el, e)
12703 var index = this.view.getSelectedIndexes()[0];
12705 var r = this.store.getAt(index);
12709 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12716 Roo.each(this.tickItems, function(v,k){
12718 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12720 _this.tickItems.splice(k, 1);
12722 if(typeof(e) == 'undefined' && view == false){
12723 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12735 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12736 this.tickItems.push(r.data);
12739 if(typeof(e) == 'undefined' && view == false){
12740 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12747 this.onSelect(r, index);
12749 if(doFocus !== false && !this.blockFocus){
12750 this.inputEl().focus();
12755 restrictHeight : function(){
12756 //this.innerList.dom.style.height = '';
12757 //var inner = this.innerList.dom;
12758 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12759 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12760 //this.list.beginUpdate();
12761 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12762 this.list.alignTo(this.inputEl(), this.listAlign);
12763 this.list.alignTo(this.inputEl(), this.listAlign);
12764 //this.list.endUpdate();
12768 onEmptyResults : function(){
12770 if(this.tickable && this.editable){
12771 this.restrictHeight();
12779 * Returns true if the dropdown list is expanded, else false.
12781 isExpanded : function(){
12782 return this.list.isVisible();
12786 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12787 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12788 * @param {String} value The data value of the item to select
12789 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12790 * selected item if it is not currently in view (defaults to true)
12791 * @return {Boolean} True if the value matched an item in the list, else false
12793 selectByValue : function(v, scrollIntoView){
12794 if(v !== undefined && v !== null){
12795 var r = this.findRecord(this.valueField || this.displayField, v);
12797 this.select(this.store.indexOf(r), scrollIntoView);
12805 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12806 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12807 * @param {Number} index The zero-based index of the list item to select
12808 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12809 * selected item if it is not currently in view (defaults to true)
12811 select : function(index, scrollIntoView){
12812 this.selectedIndex = index;
12813 this.view.select(index);
12814 if(scrollIntoView !== false){
12815 var el = this.view.getNode(index);
12817 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12820 this.list.scrollChildIntoView(el, false);
12826 selectNext : function(){
12827 var ct = this.store.getCount();
12829 if(this.selectedIndex == -1){
12831 }else if(this.selectedIndex < ct-1){
12832 this.select(this.selectedIndex+1);
12838 selectPrev : function(){
12839 var ct = this.store.getCount();
12841 if(this.selectedIndex == -1){
12843 }else if(this.selectedIndex != 0){
12844 this.select(this.selectedIndex-1);
12850 onKeyUp : function(e){
12851 if(this.editable !== false && !e.isSpecialKey()){
12852 this.lastKey = e.getKey();
12853 this.dqTask.delay(this.queryDelay);
12858 validateBlur : function(){
12859 return !this.list || !this.list.isVisible();
12863 initQuery : function(){
12865 var v = this.getRawValue();
12867 if(this.tickable && this.editable){
12868 v = this.tickableInputEl().getValue();
12875 doForce : function(){
12876 if(this.inputEl().dom.value.length > 0){
12877 this.inputEl().dom.value =
12878 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12884 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12885 * query allowing the query action to be canceled if needed.
12886 * @param {String} query The SQL query to execute
12887 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12888 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12889 * saved in the current store (defaults to false)
12891 doQuery : function(q, forceAll){
12893 if(q === undefined || q === null){
12898 forceAll: forceAll,
12902 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12907 forceAll = qe.forceAll;
12908 if(forceAll === true || (q.length >= this.minChars)){
12910 this.hasQuery = true;
12912 if(this.lastQuery != q || this.alwaysQuery){
12913 this.lastQuery = q;
12914 if(this.mode == 'local'){
12915 this.selectedIndex = -1;
12917 this.store.clearFilter();
12920 if(this.specialFilter){
12921 this.fireEvent('specialfilter', this);
12926 this.store.filter(this.displayField, q);
12929 this.store.fireEvent("datachanged", this.store);
12936 this.store.baseParams[this.queryParam] = q;
12938 var options = {params : this.getParams(q)};
12941 options.add = true;
12942 options.params.start = this.page * this.pageSize;
12945 this.store.load(options);
12948 * this code will make the page width larger, at the beginning, the list not align correctly,
12949 * we should expand the list on onLoad
12950 * so command out it
12955 this.selectedIndex = -1;
12960 this.loadNext = false;
12964 getParams : function(q){
12966 //p[this.queryParam] = q;
12970 p.limit = this.pageSize;
12976 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12978 collapse : function(){
12979 if(!this.isExpanded()){
12986 this.hasFocus = false;
12988 this.cancelBtn.hide();
12989 this.trigger.show();
12992 this.tickableInputEl().dom.value = '';
12993 this.tickableInputEl().blur();
12998 Roo.get(document).un('mousedown', this.collapseIf, this);
12999 Roo.get(document).un('mousewheel', this.collapseIf, this);
13000 if (!this.editable) {
13001 Roo.get(document).un('keydown', this.listKeyPress, this);
13003 this.fireEvent('collapse', this);
13007 collapseIf : function(e){
13008 var in_combo = e.within(this.el);
13009 var in_list = e.within(this.list);
13010 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13012 if (in_combo || in_list || is_list) {
13013 //e.stopPropagation();
13018 this.onTickableFooterButtonClick(e, false, false);
13026 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13028 expand : function(){
13030 if(this.isExpanded() || !this.hasFocus){
13034 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13035 this.list.setWidth(lw);
13042 this.restrictHeight();
13046 this.tickItems = Roo.apply([], this.item);
13049 this.cancelBtn.show();
13050 this.trigger.hide();
13053 this.tickableInputEl().focus();
13058 Roo.get(document).on('mousedown', this.collapseIf, this);
13059 Roo.get(document).on('mousewheel', this.collapseIf, this);
13060 if (!this.editable) {
13061 Roo.get(document).on('keydown', this.listKeyPress, this);
13064 this.fireEvent('expand', this);
13068 // Implements the default empty TriggerField.onTriggerClick function
13069 onTriggerClick : function(e)
13071 Roo.log('trigger click');
13073 if(this.disabled || !this.triggerList){
13078 this.loadNext = false;
13080 if(this.isExpanded()){
13082 if (!this.blockFocus) {
13083 this.inputEl().focus();
13087 this.hasFocus = true;
13088 if(this.triggerAction == 'all') {
13089 this.doQuery(this.allQuery, true);
13091 this.doQuery(this.getRawValue());
13093 if (!this.blockFocus) {
13094 this.inputEl().focus();
13099 onTickableTriggerClick : function(e)
13106 this.loadNext = false;
13107 this.hasFocus = true;
13109 if(this.triggerAction == 'all') {
13110 this.doQuery(this.allQuery, true);
13112 this.doQuery(this.getRawValue());
13116 onSearchFieldClick : function(e)
13118 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13119 this.onTickableFooterButtonClick(e, false, false);
13123 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13128 this.loadNext = false;
13129 this.hasFocus = true;
13131 if(this.triggerAction == 'all') {
13132 this.doQuery(this.allQuery, true);
13134 this.doQuery(this.getRawValue());
13138 listKeyPress : function(e)
13140 //Roo.log('listkeypress');
13141 // scroll to first matching element based on key pres..
13142 if (e.isSpecialKey()) {
13145 var k = String.fromCharCode(e.getKey()).toUpperCase();
13148 var csel = this.view.getSelectedNodes();
13149 var cselitem = false;
13151 var ix = this.view.indexOf(csel[0]);
13152 cselitem = this.store.getAt(ix);
13153 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13159 this.store.each(function(v) {
13161 // start at existing selection.
13162 if (cselitem.id == v.id) {
13168 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13169 match = this.store.indexOf(v);
13175 if (match === false) {
13176 return true; // no more action?
13179 this.view.select(match);
13180 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13181 sn.scrollIntoView(sn.dom.parentNode, false);
13184 onViewScroll : function(e, t){
13186 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){
13190 this.hasQuery = true;
13192 this.loading = this.list.select('.loading', true).first();
13194 if(this.loading === null){
13195 this.list.createChild({
13197 cls: 'loading select2-more-results select2-active',
13198 html: 'Loading more results...'
13201 this.loading = this.list.select('.loading', true).first();
13203 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13205 this.loading.hide();
13208 this.loading.show();
13213 this.loadNext = true;
13215 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13220 addItem : function(o)
13222 var dv = ''; // display value
13224 if (this.displayField) {
13225 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13227 // this is an error condition!!!
13228 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13235 var choice = this.choices.createChild({
13237 cls: 'select2-search-choice',
13246 cls: 'select2-search-choice-close',
13251 }, this.searchField);
13253 var close = choice.select('a.select2-search-choice-close', true).first();
13255 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13263 this.inputEl().dom.value = '';
13268 onRemoveItem : function(e, _self, o)
13270 e.preventDefault();
13272 this.lastItem = Roo.apply([], this.item);
13274 var index = this.item.indexOf(o.data) * 1;
13277 Roo.log('not this item?!');
13281 this.item.splice(index, 1);
13286 this.fireEvent('remove', this, e);
13292 syncValue : function()
13294 if(!this.item.length){
13301 Roo.each(this.item, function(i){
13302 if(_this.valueField){
13303 value.push(i[_this.valueField]);
13310 this.value = value.join(',');
13312 if(this.hiddenField){
13313 this.hiddenField.dom.value = this.value;
13316 this.store.fireEvent("datachanged", this.store);
13319 clearItem : function()
13321 if(!this.multiple){
13327 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13335 if(this.tickable && !Roo.isTouch){
13336 this.view.refresh();
13340 inputEl: function ()
13342 if(Roo.isTouch && this.mobileTouchView){
13343 return this.el.select('input.form-control',true).first();
13347 return this.searchField;
13350 return this.el.select('input.form-control',true).first();
13354 onTickableFooterButtonClick : function(e, btn, el)
13356 e.preventDefault();
13358 this.lastItem = Roo.apply([], this.item);
13360 if(btn && btn.name == 'cancel'){
13361 this.tickItems = Roo.apply([], this.item);
13370 Roo.each(this.tickItems, function(o){
13378 validate : function()
13380 var v = this.getRawValue();
13383 v = this.getValue();
13386 if(this.disabled || this.allowBlank || v.length){
13391 this.markInvalid();
13395 tickableInputEl : function()
13397 if(!this.tickable || !this.editable){
13398 return this.inputEl();
13401 return this.inputEl().select('.select2-search-field-input', true).first();
13405 getAutoCreateTouchView : function()
13410 cls: 'form-group' //input-group
13416 type : this.inputType,
13417 cls : 'form-control x-combo-noedit',
13418 autocomplete: 'new-password',
13419 placeholder : this.placeholder || '',
13424 input.name = this.name;
13428 input.cls += ' input-' + this.size;
13431 if (this.disabled) {
13432 input.disabled = true;
13443 inputblock.cls += ' input-group';
13445 inputblock.cn.unshift({
13447 cls : 'input-group-addon',
13452 if(this.removable && !this.multiple){
13453 inputblock.cls += ' roo-removable';
13455 inputblock.cn.push({
13458 cls : 'roo-combo-removable-btn close'
13462 if(this.hasFeedback && !this.allowBlank){
13464 inputblock.cls += ' has-feedback';
13466 inputblock.cn.push({
13468 cls: 'glyphicon form-control-feedback'
13475 inputblock.cls += (this.before) ? '' : ' input-group';
13477 inputblock.cn.push({
13479 cls : 'input-group-addon',
13490 cls: 'form-hidden-field'
13504 cls: 'form-hidden-field'
13508 cls: 'select2-choices',
13512 cls: 'select2-search-field',
13525 cls: 'select2-container input-group',
13532 combobox.cls += ' select2-container-multi';
13535 var align = this.labelAlign || this.parentLabelAlign();
13539 if(this.fieldLabel.length){
13541 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13542 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13547 cls : 'control-label ' + lw,
13548 html : this.fieldLabel
13560 var settings = this;
13562 ['xs','sm','md','lg'].map(function(size){
13563 if (settings[size]) {
13564 cfg.cls += ' col-' + size + '-' + settings[size];
13571 initTouchView : function()
13573 this.renderTouchView();
13575 this.touchViewEl.on('scroll', function(){
13576 this.el.dom.scrollTop = 0;
13579 this.inputEl().on("click", this.showTouchView, this);
13580 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13581 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13583 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13585 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13586 this.store.on('load', this.onTouchViewLoad, this);
13587 this.store.on('loadexception', this.onTouchViewLoadException, this);
13589 if(this.hiddenName){
13591 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13593 this.hiddenField.dom.value =
13594 this.hiddenValue !== undefined ? this.hiddenValue :
13595 this.value !== undefined ? this.value : '';
13597 this.el.dom.removeAttribute('name');
13598 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13602 this.choices = this.el.select('ul.select2-choices', true).first();
13603 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13606 if(this.removable && !this.multiple){
13607 var close = this.closeTriggerEl();
13609 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13610 close.on('click', this.removeBtnClick, this, close);
13619 renderTouchView : function()
13621 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13622 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13624 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13625 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13627 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13628 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13629 this.touchViewBodyEl.setStyle('overflow', 'auto');
13631 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13632 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13634 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13635 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639 showTouchView : function()
13641 this.touchViewHeaderEl.hide();
13643 if(this.fieldLabel.length){
13644 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13645 this.touchViewHeaderEl.show();
13648 this.touchViewEl.show();
13650 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13651 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13653 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13655 if(this.fieldLabel.length){
13656 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13659 this.touchViewBodyEl.setHeight(bodyHeight);
13663 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13665 this.touchViewEl.addClass('in');
13668 this.doTouchViewQuery();
13672 hideTouchView : function()
13674 this.touchViewEl.removeClass('in');
13678 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13680 this.touchViewEl.setStyle('display', 'none');
13685 setTouchViewValue : function()
13692 Roo.each(this.tickItems, function(o){
13697 this.hideTouchView();
13700 doTouchViewQuery : function()
13709 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13713 if(!this.alwaysQuery || this.mode == 'local'){
13714 this.onTouchViewLoad();
13721 onTouchViewBeforeLoad : function(combo,opts)
13727 onTouchViewLoad : function()
13729 if(this.store.getCount() < 1){
13730 this.onTouchViewEmptyResults();
13734 this.clearTouchView();
13736 var rawValue = this.getRawValue();
13738 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13740 this.tickItems = [];
13742 this.store.data.each(function(d, rowIndex){
13743 var row = this.touchViewListGroup.createChild(template);
13745 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13748 html : d.data[this.displayField]
13751 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13752 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13756 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13757 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13760 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13761 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13762 this.tickItems.push(d.data);
13765 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13769 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13771 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13773 if(this.fieldLabel.length){
13774 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13777 var listHeight = this.touchViewListGroup.getHeight();
13781 if(firstChecked && listHeight > bodyHeight){
13782 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13787 onTouchViewLoadException : function()
13789 this.hideTouchView();
13792 onTouchViewEmptyResults : function()
13794 this.clearTouchView();
13796 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13798 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13802 clearTouchView : function()
13804 this.touchViewListGroup.dom.innerHTML = '';
13807 onTouchViewClick : function(e, el, o)
13809 e.preventDefault();
13812 var rowIndex = o.rowIndex;
13814 var r = this.store.getAt(rowIndex);
13816 if(!this.multiple){
13817 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13818 c.dom.removeAttribute('checked');
13821 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13823 this.setFromData(r.data);
13825 var close = this.closeTriggerEl();
13831 this.hideTouchView();
13833 this.fireEvent('select', this, r, rowIndex);
13838 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13839 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13840 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13844 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13845 this.addItem(r.data);
13846 this.tickItems.push(r.data);
13852 * @cfg {Boolean} grow
13856 * @cfg {Number} growMin
13860 * @cfg {Number} growMax
13869 Roo.apply(Roo.bootstrap.ComboBox, {
13873 cls: 'modal-header',
13895 cls: 'list-group-item',
13899 cls: 'roo-combobox-list-group-item-value'
13903 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13917 listItemCheckbox : {
13919 cls: 'list-group-item',
13923 cls: 'roo-combobox-list-group-item-value'
13927 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13943 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13948 cls: 'modal-footer',
13956 cls: 'col-xs-6 text-left',
13959 cls: 'btn btn-danger roo-touch-view-cancel',
13965 cls: 'col-xs-6 text-right',
13968 cls: 'btn btn-success roo-touch-view-ok',
13979 Roo.apply(Roo.bootstrap.ComboBox, {
13981 touchViewTemplate : {
13983 cls: 'modal fade roo-combobox-touch-view',
13987 cls: 'modal-dialog',
13991 cls: 'modal-content',
13993 Roo.bootstrap.ComboBox.header,
13994 Roo.bootstrap.ComboBox.body,
13995 Roo.bootstrap.ComboBox.footer
14004 * Ext JS Library 1.1.1
14005 * Copyright(c) 2006-2007, Ext JS, LLC.
14007 * Originally Released Under LGPL - original licence link has changed is not relivant.
14010 * <script type="text/javascript">
14015 * @extends Roo.util.Observable
14016 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14017 * This class also supports single and multi selection modes. <br>
14018 * Create a data model bound view:
14020 var store = new Roo.data.Store(...);
14022 var view = new Roo.View({
14024 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14026 singleSelect: true,
14027 selectedClass: "ydataview-selected",
14031 // listen for node click?
14032 view.on("click", function(vw, index, node, e){
14033 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14037 dataModel.load("foobar.xml");
14039 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14041 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14042 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14044 * Note: old style constructor is still suported (container, template, config)
14047 * Create a new View
14048 * @param {Object} config The config object
14051 Roo.View = function(config, depreciated_tpl, depreciated_config){
14053 this.parent = false;
14055 if (typeof(depreciated_tpl) == 'undefined') {
14056 // new way.. - universal constructor.
14057 Roo.apply(this, config);
14058 this.el = Roo.get(this.el);
14061 this.el = Roo.get(config);
14062 this.tpl = depreciated_tpl;
14063 Roo.apply(this, depreciated_config);
14065 this.wrapEl = this.el.wrap().wrap();
14066 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14069 if(typeof(this.tpl) == "string"){
14070 this.tpl = new Roo.Template(this.tpl);
14072 // support xtype ctors..
14073 this.tpl = new Roo.factory(this.tpl, Roo);
14077 this.tpl.compile();
14082 * @event beforeclick
14083 * Fires before a click is processed. Returns false to cancel the default action.
14084 * @param {Roo.View} this
14085 * @param {Number} index The index of the target node
14086 * @param {HTMLElement} node The target node
14087 * @param {Roo.EventObject} e The raw event object
14089 "beforeclick" : true,
14092 * Fires when a template node is clicked.
14093 * @param {Roo.View} this
14094 * @param {Number} index The index of the target node
14095 * @param {HTMLElement} node The target node
14096 * @param {Roo.EventObject} e The raw event object
14101 * Fires when a template node is double clicked.
14102 * @param {Roo.View} this
14103 * @param {Number} index The index of the target node
14104 * @param {HTMLElement} node The target node
14105 * @param {Roo.EventObject} e The raw event object
14109 * @event contextmenu
14110 * Fires when a template node is right clicked.
14111 * @param {Roo.View} this
14112 * @param {Number} index The index of the target node
14113 * @param {HTMLElement} node The target node
14114 * @param {Roo.EventObject} e The raw event object
14116 "contextmenu" : true,
14118 * @event selectionchange
14119 * Fires when the selected nodes change.
14120 * @param {Roo.View} this
14121 * @param {Array} selections Array of the selected nodes
14123 "selectionchange" : true,
14126 * @event beforeselect
14127 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14128 * @param {Roo.View} this
14129 * @param {HTMLElement} node The node to be selected
14130 * @param {Array} selections Array of currently selected nodes
14132 "beforeselect" : true,
14134 * @event preparedata
14135 * Fires on every row to render, to allow you to change the data.
14136 * @param {Roo.View} this
14137 * @param {Object} data to be rendered (change this)
14139 "preparedata" : true
14147 "click": this.onClick,
14148 "dblclick": this.onDblClick,
14149 "contextmenu": this.onContextMenu,
14153 this.selections = [];
14155 this.cmp = new Roo.CompositeElementLite([]);
14157 this.store = Roo.factory(this.store, Roo.data);
14158 this.setStore(this.store, true);
14161 if ( this.footer && this.footer.xtype) {
14163 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14165 this.footer.dataSource = this.store;
14166 this.footer.container = fctr;
14167 this.footer = Roo.factory(this.footer, Roo);
14168 fctr.insertFirst(this.el);
14170 // this is a bit insane - as the paging toolbar seems to detach the el..
14171 // dom.parentNode.parentNode.parentNode
14172 // they get detached?
14176 Roo.View.superclass.constructor.call(this);
14181 Roo.extend(Roo.View, Roo.util.Observable, {
14184 * @cfg {Roo.data.Store} store Data store to load data from.
14189 * @cfg {String|Roo.Element} el The container element.
14194 * @cfg {String|Roo.Template} tpl The template used by this View
14198 * @cfg {String} dataName the named area of the template to use as the data area
14199 * Works with domtemplates roo-name="name"
14203 * @cfg {String} selectedClass The css class to add to selected nodes
14205 selectedClass : "x-view-selected",
14207 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14212 * @cfg {String} text to display on mask (default Loading)
14216 * @cfg {Boolean} multiSelect Allow multiple selection
14218 multiSelect : false,
14220 * @cfg {Boolean} singleSelect Allow single selection
14222 singleSelect: false,
14225 * @cfg {Boolean} toggleSelect - selecting
14227 toggleSelect : false,
14230 * @cfg {Boolean} tickable - selecting
14235 * Returns the element this view is bound to.
14236 * @return {Roo.Element}
14238 getEl : function(){
14239 return this.wrapEl;
14245 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14247 refresh : function(){
14248 //Roo.log('refresh');
14251 // if we are using something like 'domtemplate', then
14252 // the what gets used is:
14253 // t.applySubtemplate(NAME, data, wrapping data..)
14254 // the outer template then get' applied with
14255 // the store 'extra data'
14256 // and the body get's added to the
14257 // roo-name="data" node?
14258 // <span class='roo-tpl-{name}'></span> ?????
14262 this.clearSelections();
14263 this.el.update("");
14265 var records = this.store.getRange();
14266 if(records.length < 1) {
14268 // is this valid?? = should it render a template??
14270 this.el.update(this.emptyText);
14274 if (this.dataName) {
14275 this.el.update(t.apply(this.store.meta)); //????
14276 el = this.el.child('.roo-tpl-' + this.dataName);
14279 for(var i = 0, len = records.length; i < len; i++){
14280 var data = this.prepareData(records[i].data, i, records[i]);
14281 this.fireEvent("preparedata", this, data, i, records[i]);
14283 var d = Roo.apply({}, data);
14286 Roo.apply(d, {'roo-id' : Roo.id()});
14290 Roo.each(this.parent.item, function(item){
14291 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14294 Roo.apply(d, {'roo-data-checked' : 'checked'});
14298 html[html.length] = Roo.util.Format.trim(
14300 t.applySubtemplate(this.dataName, d, this.store.meta) :
14307 el.update(html.join(""));
14308 this.nodes = el.dom.childNodes;
14309 this.updateIndexes(0);
14314 * Function to override to reformat the data that is sent to
14315 * the template for each node.
14316 * DEPRICATED - use the preparedata event handler.
14317 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14318 * a JSON object for an UpdateManager bound view).
14320 prepareData : function(data, index, record)
14322 this.fireEvent("preparedata", this, data, index, record);
14326 onUpdate : function(ds, record){
14327 // Roo.log('on update');
14328 this.clearSelections();
14329 var index = this.store.indexOf(record);
14330 var n = this.nodes[index];
14331 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14332 n.parentNode.removeChild(n);
14333 this.updateIndexes(index, index);
14339 onAdd : function(ds, records, index)
14341 //Roo.log(['on Add', ds, records, index] );
14342 this.clearSelections();
14343 if(this.nodes.length == 0){
14347 var n = this.nodes[index];
14348 for(var i = 0, len = records.length; i < len; i++){
14349 var d = this.prepareData(records[i].data, i, records[i]);
14351 this.tpl.insertBefore(n, d);
14354 this.tpl.append(this.el, d);
14357 this.updateIndexes(index);
14360 onRemove : function(ds, record, index){
14361 // Roo.log('onRemove');
14362 this.clearSelections();
14363 var el = this.dataName ?
14364 this.el.child('.roo-tpl-' + this.dataName) :
14367 el.dom.removeChild(this.nodes[index]);
14368 this.updateIndexes(index);
14372 * Refresh an individual node.
14373 * @param {Number} index
14375 refreshNode : function(index){
14376 this.onUpdate(this.store, this.store.getAt(index));
14379 updateIndexes : function(startIndex, endIndex){
14380 var ns = this.nodes;
14381 startIndex = startIndex || 0;
14382 endIndex = endIndex || ns.length - 1;
14383 for(var i = startIndex; i <= endIndex; i++){
14384 ns[i].nodeIndex = i;
14389 * Changes the data store this view uses and refresh the view.
14390 * @param {Store} store
14392 setStore : function(store, initial){
14393 if(!initial && this.store){
14394 this.store.un("datachanged", this.refresh);
14395 this.store.un("add", this.onAdd);
14396 this.store.un("remove", this.onRemove);
14397 this.store.un("update", this.onUpdate);
14398 this.store.un("clear", this.refresh);
14399 this.store.un("beforeload", this.onBeforeLoad);
14400 this.store.un("load", this.onLoad);
14401 this.store.un("loadexception", this.onLoad);
14405 store.on("datachanged", this.refresh, this);
14406 store.on("add", this.onAdd, this);
14407 store.on("remove", this.onRemove, this);
14408 store.on("update", this.onUpdate, this);
14409 store.on("clear", this.refresh, this);
14410 store.on("beforeload", this.onBeforeLoad, this);
14411 store.on("load", this.onLoad, this);
14412 store.on("loadexception", this.onLoad, this);
14420 * onbeforeLoad - masks the loading area.
14423 onBeforeLoad : function(store,opts)
14425 //Roo.log('onBeforeLoad');
14427 this.el.update("");
14429 this.el.mask(this.mask ? this.mask : "Loading" );
14431 onLoad : function ()
14438 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14439 * @param {HTMLElement} node
14440 * @return {HTMLElement} The template node
14442 findItemFromChild : function(node){
14443 var el = this.dataName ?
14444 this.el.child('.roo-tpl-' + this.dataName,true) :
14447 if(!node || node.parentNode == el){
14450 var p = node.parentNode;
14451 while(p && p != el){
14452 if(p.parentNode == el){
14461 onClick : function(e){
14462 var item = this.findItemFromChild(e.getTarget());
14464 var index = this.indexOf(item);
14465 if(this.onItemClick(item, index, e) !== false){
14466 this.fireEvent("click", this, index, item, e);
14469 this.clearSelections();
14474 onContextMenu : function(e){
14475 var item = this.findItemFromChild(e.getTarget());
14477 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14482 onDblClick : function(e){
14483 var item = this.findItemFromChild(e.getTarget());
14485 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14489 onItemClick : function(item, index, e)
14491 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14494 if (this.toggleSelect) {
14495 var m = this.isSelected(item) ? 'unselect' : 'select';
14498 _t[m](item, true, false);
14501 if(this.multiSelect || this.singleSelect){
14502 if(this.multiSelect && e.shiftKey && this.lastSelection){
14503 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14505 this.select(item, this.multiSelect && e.ctrlKey);
14506 this.lastSelection = item;
14509 if(!this.tickable){
14510 e.preventDefault();
14518 * Get the number of selected nodes.
14521 getSelectionCount : function(){
14522 return this.selections.length;
14526 * Get the currently selected nodes.
14527 * @return {Array} An array of HTMLElements
14529 getSelectedNodes : function(){
14530 return this.selections;
14534 * Get the indexes of the selected nodes.
14537 getSelectedIndexes : function(){
14538 var indexes = [], s = this.selections;
14539 for(var i = 0, len = s.length; i < len; i++){
14540 indexes.push(s[i].nodeIndex);
14546 * Clear all selections
14547 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14549 clearSelections : function(suppressEvent){
14550 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14551 this.cmp.elements = this.selections;
14552 this.cmp.removeClass(this.selectedClass);
14553 this.selections = [];
14554 if(!suppressEvent){
14555 this.fireEvent("selectionchange", this, this.selections);
14561 * Returns true if the passed node is selected
14562 * @param {HTMLElement/Number} node The node or node index
14563 * @return {Boolean}
14565 isSelected : function(node){
14566 var s = this.selections;
14570 node = this.getNode(node);
14571 return s.indexOf(node) !== -1;
14576 * @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
14577 * @param {Boolean} keepExisting (optional) true to keep existing selections
14578 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14580 select : function(nodeInfo, keepExisting, suppressEvent){
14581 if(nodeInfo instanceof Array){
14583 this.clearSelections(true);
14585 for(var i = 0, len = nodeInfo.length; i < len; i++){
14586 this.select(nodeInfo[i], true, true);
14590 var node = this.getNode(nodeInfo);
14591 if(!node || this.isSelected(node)){
14592 return; // already selected.
14595 this.clearSelections(true);
14598 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14599 Roo.fly(node).addClass(this.selectedClass);
14600 this.selections.push(node);
14601 if(!suppressEvent){
14602 this.fireEvent("selectionchange", this, this.selections);
14610 * @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
14611 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14612 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14614 unselect : function(nodeInfo, keepExisting, suppressEvent)
14616 if(nodeInfo instanceof Array){
14617 Roo.each(this.selections, function(s) {
14618 this.unselect(s, nodeInfo);
14622 var node = this.getNode(nodeInfo);
14623 if(!node || !this.isSelected(node)){
14624 //Roo.log("not selected");
14625 return; // not selected.
14629 Roo.each(this.selections, function(s) {
14631 Roo.fly(node).removeClass(this.selectedClass);
14638 this.selections= ns;
14639 this.fireEvent("selectionchange", this, this.selections);
14643 * Gets a template node.
14644 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14645 * @return {HTMLElement} The node or null if it wasn't found
14647 getNode : function(nodeInfo){
14648 if(typeof nodeInfo == "string"){
14649 return document.getElementById(nodeInfo);
14650 }else if(typeof nodeInfo == "number"){
14651 return this.nodes[nodeInfo];
14657 * Gets a range template nodes.
14658 * @param {Number} startIndex
14659 * @param {Number} endIndex
14660 * @return {Array} An array of nodes
14662 getNodes : function(start, end){
14663 var ns = this.nodes;
14664 start = start || 0;
14665 end = typeof end == "undefined" ? ns.length - 1 : end;
14668 for(var i = start; i <= end; i++){
14672 for(var i = start; i >= end; i--){
14680 * Finds the index of the passed node
14681 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14682 * @return {Number} The index of the node or -1
14684 indexOf : function(node){
14685 node = this.getNode(node);
14686 if(typeof node.nodeIndex == "number"){
14687 return node.nodeIndex;
14689 var ns = this.nodes;
14690 for(var i = 0, len = ns.length; i < len; i++){
14701 * based on jquery fullcalendar
14705 Roo.bootstrap = Roo.bootstrap || {};
14707 * @class Roo.bootstrap.Calendar
14708 * @extends Roo.bootstrap.Component
14709 * Bootstrap Calendar class
14710 * @cfg {Boolean} loadMask (true|false) default false
14711 * @cfg {Object} header generate the user specific header of the calendar, default false
14714 * Create a new Container
14715 * @param {Object} config The config object
14720 Roo.bootstrap.Calendar = function(config){
14721 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14725 * Fires when a date is selected
14726 * @param {DatePicker} this
14727 * @param {Date} date The selected date
14731 * @event monthchange
14732 * Fires when the displayed month changes
14733 * @param {DatePicker} this
14734 * @param {Date} date The selected month
14736 'monthchange': true,
14738 * @event evententer
14739 * Fires when mouse over an event
14740 * @param {Calendar} this
14741 * @param {event} Event
14743 'evententer': true,
14745 * @event eventleave
14746 * Fires when the mouse leaves an
14747 * @param {Calendar} this
14750 'eventleave': true,
14752 * @event eventclick
14753 * Fires when the mouse click an
14754 * @param {Calendar} this
14763 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14766 * @cfg {Number} startDay
14767 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14775 getAutoCreate : function(){
14778 var fc_button = function(name, corner, style, content ) {
14779 return Roo.apply({},{
14781 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14783 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14786 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14797 style : 'width:100%',
14804 cls : 'fc-header-left',
14806 fc_button('prev', 'left', 'arrow', '‹' ),
14807 fc_button('next', 'right', 'arrow', '›' ),
14808 { tag: 'span', cls: 'fc-header-space' },
14809 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14817 cls : 'fc-header-center',
14821 cls: 'fc-header-title',
14824 html : 'month / year'
14832 cls : 'fc-header-right',
14834 /* fc_button('month', 'left', '', 'month' ),
14835 fc_button('week', '', '', 'week' ),
14836 fc_button('day', 'right', '', 'day' )
14848 header = this.header;
14851 var cal_heads = function() {
14853 // fixme - handle this.
14855 for (var i =0; i < Date.dayNames.length; i++) {
14856 var d = Date.dayNames[i];
14859 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14860 html : d.substring(0,3)
14864 ret[0].cls += ' fc-first';
14865 ret[6].cls += ' fc-last';
14868 var cal_cell = function(n) {
14871 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14876 cls: 'fc-day-number',
14880 cls: 'fc-day-content',
14884 style: 'position: relative;' // height: 17px;
14896 var cal_rows = function() {
14899 for (var r = 0; r < 6; r++) {
14906 for (var i =0; i < Date.dayNames.length; i++) {
14907 var d = Date.dayNames[i];
14908 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14911 row.cn[0].cls+=' fc-first';
14912 row.cn[0].cn[0].style = 'min-height:90px';
14913 row.cn[6].cls+=' fc-last';
14917 ret[0].cls += ' fc-first';
14918 ret[4].cls += ' fc-prev-last';
14919 ret[5].cls += ' fc-last';
14926 cls: 'fc-border-separate',
14927 style : 'width:100%',
14935 cls : 'fc-first fc-last',
14953 cls : 'fc-content',
14954 style : "position: relative;",
14957 cls : 'fc-view fc-view-month fc-grid',
14958 style : 'position: relative',
14959 unselectable : 'on',
14962 cls : 'fc-event-container',
14963 style : 'position:absolute;z-index:8;top:0;left:0;'
14981 initEvents : function()
14984 throw "can not find store for calendar";
14990 style: "text-align:center",
14994 style: "background-color:white;width:50%;margin:250 auto",
14998 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15009 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15011 var size = this.el.select('.fc-content', true).first().getSize();
15012 this.maskEl.setSize(size.width, size.height);
15013 this.maskEl.enableDisplayMode("block");
15014 if(!this.loadMask){
15015 this.maskEl.hide();
15018 this.store = Roo.factory(this.store, Roo.data);
15019 this.store.on('load', this.onLoad, this);
15020 this.store.on('beforeload', this.onBeforeLoad, this);
15024 this.cells = this.el.select('.fc-day',true);
15025 //Roo.log(this.cells);
15026 this.textNodes = this.el.query('.fc-day-number');
15027 this.cells.addClassOnOver('fc-state-hover');
15029 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15030 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15031 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15032 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15034 this.on('monthchange', this.onMonthChange, this);
15036 this.update(new Date().clearTime());
15039 resize : function() {
15040 var sz = this.el.getSize();
15042 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15043 this.el.select('.fc-day-content div',true).setHeight(34);
15048 showPrevMonth : function(e){
15049 this.update(this.activeDate.add("mo", -1));
15051 showToday : function(e){
15052 this.update(new Date().clearTime());
15055 showNextMonth : function(e){
15056 this.update(this.activeDate.add("mo", 1));
15060 showPrevYear : function(){
15061 this.update(this.activeDate.add("y", -1));
15065 showNextYear : function(){
15066 this.update(this.activeDate.add("y", 1));
15071 update : function(date)
15073 var vd = this.activeDate;
15074 this.activeDate = date;
15075 // if(vd && this.el){
15076 // var t = date.getTime();
15077 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15078 // Roo.log('using add remove');
15080 // this.fireEvent('monthchange', this, date);
15082 // this.cells.removeClass("fc-state-highlight");
15083 // this.cells.each(function(c){
15084 // if(c.dateValue == t){
15085 // c.addClass("fc-state-highlight");
15086 // setTimeout(function(){
15087 // try{c.dom.firstChild.focus();}catch(e){}
15097 var days = date.getDaysInMonth();
15099 var firstOfMonth = date.getFirstDateOfMonth();
15100 var startingPos = firstOfMonth.getDay()-this.startDay;
15102 if(startingPos < this.startDay){
15106 var pm = date.add(Date.MONTH, -1);
15107 var prevStart = pm.getDaysInMonth()-startingPos;
15109 this.cells = this.el.select('.fc-day',true);
15110 this.textNodes = this.el.query('.fc-day-number');
15111 this.cells.addClassOnOver('fc-state-hover');
15113 var cells = this.cells.elements;
15114 var textEls = this.textNodes;
15116 Roo.each(cells, function(cell){
15117 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15120 days += startingPos;
15122 // convert everything to numbers so it's fast
15123 var day = 86400000;
15124 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15127 //Roo.log(prevStart);
15129 var today = new Date().clearTime().getTime();
15130 var sel = date.clearTime().getTime();
15131 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15132 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15133 var ddMatch = this.disabledDatesRE;
15134 var ddText = this.disabledDatesText;
15135 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15136 var ddaysText = this.disabledDaysText;
15137 var format = this.format;
15139 var setCellClass = function(cal, cell){
15143 //Roo.log('set Cell Class');
15145 var t = d.getTime();
15149 cell.dateValue = t;
15151 cell.className += " fc-today";
15152 cell.className += " fc-state-highlight";
15153 cell.title = cal.todayText;
15156 // disable highlight in other month..
15157 //cell.className += " fc-state-highlight";
15162 cell.className = " fc-state-disabled";
15163 cell.title = cal.minText;
15167 cell.className = " fc-state-disabled";
15168 cell.title = cal.maxText;
15172 if(ddays.indexOf(d.getDay()) != -1){
15173 cell.title = ddaysText;
15174 cell.className = " fc-state-disabled";
15177 if(ddMatch && format){
15178 var fvalue = d.dateFormat(format);
15179 if(ddMatch.test(fvalue)){
15180 cell.title = ddText.replace("%0", fvalue);
15181 cell.className = " fc-state-disabled";
15185 if (!cell.initialClassName) {
15186 cell.initialClassName = cell.dom.className;
15189 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15194 for(; i < startingPos; i++) {
15195 textEls[i].innerHTML = (++prevStart);
15196 d.setDate(d.getDate()+1);
15198 cells[i].className = "fc-past fc-other-month";
15199 setCellClass(this, cells[i]);
15204 for(; i < days; i++){
15205 intDay = i - startingPos + 1;
15206 textEls[i].innerHTML = (intDay);
15207 d.setDate(d.getDate()+1);
15209 cells[i].className = ''; // "x-date-active";
15210 setCellClass(this, cells[i]);
15214 for(; i < 42; i++) {
15215 textEls[i].innerHTML = (++extraDays);
15216 d.setDate(d.getDate()+1);
15218 cells[i].className = "fc-future fc-other-month";
15219 setCellClass(this, cells[i]);
15222 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15224 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15226 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15227 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15229 if(totalRows != 6){
15230 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15231 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15234 this.fireEvent('monthchange', this, date);
15238 if(!this.internalRender){
15239 var main = this.el.dom.firstChild;
15240 var w = main.offsetWidth;
15241 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15242 Roo.fly(main).setWidth(w);
15243 this.internalRender = true;
15244 // opera does not respect the auto grow header center column
15245 // then, after it gets a width opera refuses to recalculate
15246 // without a second pass
15247 if(Roo.isOpera && !this.secondPass){
15248 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15249 this.secondPass = true;
15250 this.update.defer(10, this, [date]);
15257 findCell : function(dt) {
15258 dt = dt.clearTime().getTime();
15260 this.cells.each(function(c){
15261 //Roo.log("check " +c.dateValue + '?=' + dt);
15262 if(c.dateValue == dt){
15272 findCells : function(ev) {
15273 var s = ev.start.clone().clearTime().getTime();
15275 var e= ev.end.clone().clearTime().getTime();
15278 this.cells.each(function(c){
15279 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15281 if(c.dateValue > e){
15284 if(c.dateValue < s){
15293 // findBestRow: function(cells)
15297 // for (var i =0 ; i < cells.length;i++) {
15298 // ret = Math.max(cells[i].rows || 0,ret);
15305 addItem : function(ev)
15307 // look for vertical location slot in
15308 var cells = this.findCells(ev);
15310 // ev.row = this.findBestRow(cells);
15312 // work out the location.
15316 for(var i =0; i < cells.length; i++) {
15318 cells[i].row = cells[0].row;
15321 cells[i].row = cells[i].row + 1;
15331 if (crow.start.getY() == cells[i].getY()) {
15333 crow.end = cells[i];
15350 cells[0].events.push(ev);
15352 this.calevents.push(ev);
15355 clearEvents: function() {
15357 if(!this.calevents){
15361 Roo.each(this.cells.elements, function(c){
15367 Roo.each(this.calevents, function(e) {
15368 Roo.each(e.els, function(el) {
15369 el.un('mouseenter' ,this.onEventEnter, this);
15370 el.un('mouseleave' ,this.onEventLeave, this);
15375 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15381 renderEvents: function()
15385 this.cells.each(function(c) {
15394 if(c.row != c.events.length){
15395 r = 4 - (4 - (c.row - c.events.length));
15398 c.events = ev.slice(0, r);
15399 c.more = ev.slice(r);
15401 if(c.more.length && c.more.length == 1){
15402 c.events.push(c.more.pop());
15405 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15409 this.cells.each(function(c) {
15411 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15414 for (var e = 0; e < c.events.length; e++){
15415 var ev = c.events[e];
15416 var rows = ev.rows;
15418 for(var i = 0; i < rows.length; i++) {
15420 // how many rows should it span..
15423 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15424 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15426 unselectable : "on",
15429 cls: 'fc-event-inner',
15433 // cls: 'fc-event-time',
15434 // html : cells.length > 1 ? '' : ev.time
15438 cls: 'fc-event-title',
15439 html : String.format('{0}', ev.title)
15446 cls: 'ui-resizable-handle ui-resizable-e',
15447 html : '  '
15454 cfg.cls += ' fc-event-start';
15456 if ((i+1) == rows.length) {
15457 cfg.cls += ' fc-event-end';
15460 var ctr = _this.el.select('.fc-event-container',true).first();
15461 var cg = ctr.createChild(cfg);
15463 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15464 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15466 var r = (c.more.length) ? 1 : 0;
15467 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15468 cg.setWidth(ebox.right - sbox.x -2);
15470 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15471 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15472 cg.on('click', _this.onEventClick, _this, ev);
15483 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15484 style : 'position: absolute',
15485 unselectable : "on",
15488 cls: 'fc-event-inner',
15492 cls: 'fc-event-title',
15500 cls: 'ui-resizable-handle ui-resizable-e',
15501 html : '  '
15507 var ctr = _this.el.select('.fc-event-container',true).first();
15508 var cg = ctr.createChild(cfg);
15510 var sbox = c.select('.fc-day-content',true).first().getBox();
15511 var ebox = c.select('.fc-day-content',true).first().getBox();
15513 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15514 cg.setWidth(ebox.right - sbox.x -2);
15516 cg.on('click', _this.onMoreEventClick, _this, c.more);
15526 onEventEnter: function (e, el,event,d) {
15527 this.fireEvent('evententer', this, el, event);
15530 onEventLeave: function (e, el,event,d) {
15531 this.fireEvent('eventleave', this, el, event);
15534 onEventClick: function (e, el,event,d) {
15535 this.fireEvent('eventclick', this, el, event);
15538 onMonthChange: function () {
15542 onMoreEventClick: function(e, el, more)
15546 this.calpopover.placement = 'right';
15547 this.calpopover.setTitle('More');
15549 this.calpopover.setContent('');
15551 var ctr = this.calpopover.el.select('.popover-content', true).first();
15553 Roo.each(more, function(m){
15555 cls : 'fc-event-hori fc-event-draggable',
15558 var cg = ctr.createChild(cfg);
15560 cg.on('click', _this.onEventClick, _this, m);
15563 this.calpopover.show(el);
15568 onLoad: function ()
15570 this.calevents = [];
15573 if(this.store.getCount() > 0){
15574 this.store.data.each(function(d){
15577 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15578 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15579 time : d.data.start_time,
15580 title : d.data.title,
15581 description : d.data.description,
15582 venue : d.data.venue
15587 this.renderEvents();
15589 if(this.calevents.length && this.loadMask){
15590 this.maskEl.hide();
15594 onBeforeLoad: function()
15596 this.clearEvents();
15598 this.maskEl.show();
15612 * @class Roo.bootstrap.Popover
15613 * @extends Roo.bootstrap.Component
15614 * Bootstrap Popover class
15615 * @cfg {String} html contents of the popover (or false to use children..)
15616 * @cfg {String} title of popover (or false to hide)
15617 * @cfg {String} placement how it is placed
15618 * @cfg {String} trigger click || hover (or false to trigger manually)
15619 * @cfg {String} over what (parent or false to trigger manually.)
15620 * @cfg {Number} delay - delay before showing
15623 * Create a new Popover
15624 * @param {Object} config The config object
15627 Roo.bootstrap.Popover = function(config){
15628 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15631 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15633 title: 'Fill in a title',
15636 placement : 'right',
15637 trigger : 'hover', // hover
15643 can_build_overlaid : false,
15645 getChildContainer : function()
15647 return this.el.select('.popover-content',true).first();
15650 getAutoCreate : function(){
15651 Roo.log('make popover?');
15653 cls : 'popover roo-dynamic',
15654 style: 'display:block',
15660 cls : 'popover-inner',
15664 cls: 'popover-title',
15668 cls : 'popover-content',
15679 setTitle: function(str)
15682 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15684 setContent: function(str)
15687 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15689 // as it get's added to the bottom of the page.
15690 onRender : function(ct, position)
15692 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15694 var cfg = Roo.apply({}, this.getAutoCreate());
15698 cfg.cls += ' ' + this.cls;
15701 cfg.style = this.style;
15703 //Roo.log("adding to ");
15704 this.el = Roo.get(document.body).createChild(cfg, position);
15710 initEvents : function()
15712 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15713 this.el.enableDisplayMode('block');
15715 if (this.over === false) {
15718 if (this.triggers === false) {
15721 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15722 var triggers = this.trigger ? this.trigger.split(' ') : [];
15723 Roo.each(triggers, function(trigger) {
15725 if (trigger == 'click') {
15726 on_el.on('click', this.toggle, this);
15727 } else if (trigger != 'manual') {
15728 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15729 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15731 on_el.on(eventIn ,this.enter, this);
15732 on_el.on(eventOut, this.leave, this);
15743 toggle : function () {
15744 this.hoverState == 'in' ? this.leave() : this.enter();
15747 enter : function () {
15750 clearTimeout(this.timeout);
15752 this.hoverState = 'in';
15754 if (!this.delay || !this.delay.show) {
15759 this.timeout = setTimeout(function () {
15760 if (_t.hoverState == 'in') {
15763 }, this.delay.show)
15765 leave : function() {
15766 clearTimeout(this.timeout);
15768 this.hoverState = 'out';
15770 if (!this.delay || !this.delay.hide) {
15775 this.timeout = setTimeout(function () {
15776 if (_t.hoverState == 'out') {
15779 }, this.delay.hide)
15782 show : function (on_el)
15785 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15788 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15789 if (this.html !== false) {
15790 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15792 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15793 if (!this.title.length) {
15794 this.el.select('.popover-title',true).hide();
15797 var placement = typeof this.placement == 'function' ?
15798 this.placement.call(this, this.el, on_el) :
15801 var autoToken = /\s?auto?\s?/i;
15802 var autoPlace = autoToken.test(placement);
15804 placement = placement.replace(autoToken, '') || 'top';
15808 //this.el.setXY([0,0]);
15810 this.el.dom.style.display='block';
15811 this.el.addClass(placement);
15813 //this.el.appendTo(on_el);
15815 var p = this.getPosition();
15816 var box = this.el.getBox();
15821 var align = Roo.bootstrap.Popover.alignment[placement];
15822 this.el.alignTo(on_el, align[0],align[1]);
15823 //var arrow = this.el.select('.arrow',true).first();
15824 //arrow.set(align[2],
15826 this.el.addClass('in');
15829 if (this.el.hasClass('fade')) {
15836 this.el.setXY([0,0]);
15837 this.el.removeClass('in');
15839 this.hoverState = null;
15845 Roo.bootstrap.Popover.alignment = {
15846 'left' : ['r-l', [-10,0], 'right'],
15847 'right' : ['l-r', [10,0], 'left'],
15848 'bottom' : ['t-b', [0,10], 'top'],
15849 'top' : [ 'b-t', [0,-10], 'bottom']
15860 * @class Roo.bootstrap.Progress
15861 * @extends Roo.bootstrap.Component
15862 * Bootstrap Progress class
15863 * @cfg {Boolean} striped striped of the progress bar
15864 * @cfg {Boolean} active animated of the progress bar
15868 * Create a new Progress
15869 * @param {Object} config The config object
15872 Roo.bootstrap.Progress = function(config){
15873 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15876 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15881 getAutoCreate : function(){
15889 cfg.cls += ' progress-striped';
15893 cfg.cls += ' active';
15912 * @class Roo.bootstrap.ProgressBar
15913 * @extends Roo.bootstrap.Component
15914 * Bootstrap ProgressBar class
15915 * @cfg {Number} aria_valuenow aria-value now
15916 * @cfg {Number} aria_valuemin aria-value min
15917 * @cfg {Number} aria_valuemax aria-value max
15918 * @cfg {String} label label for the progress bar
15919 * @cfg {String} panel (success | info | warning | danger )
15920 * @cfg {String} role role of the progress bar
15921 * @cfg {String} sr_only text
15925 * Create a new ProgressBar
15926 * @param {Object} config The config object
15929 Roo.bootstrap.ProgressBar = function(config){
15930 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15933 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15937 aria_valuemax : 100,
15943 getAutoCreate : function()
15948 cls: 'progress-bar',
15949 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15961 cfg.role = this.role;
15964 if(this.aria_valuenow){
15965 cfg['aria-valuenow'] = this.aria_valuenow;
15968 if(this.aria_valuemin){
15969 cfg['aria-valuemin'] = this.aria_valuemin;
15972 if(this.aria_valuemax){
15973 cfg['aria-valuemax'] = this.aria_valuemax;
15976 if(this.label && !this.sr_only){
15977 cfg.html = this.label;
15981 cfg.cls += ' progress-bar-' + this.panel;
15987 update : function(aria_valuenow)
15989 this.aria_valuenow = aria_valuenow;
15991 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16006 * @class Roo.bootstrap.TabGroup
16007 * @extends Roo.bootstrap.Column
16008 * Bootstrap Column class
16009 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16010 * @cfg {Boolean} carousel true to make the group behave like a carousel
16011 * @cfg {Boolean} bullets show bullets for the panels
16012 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16013 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16014 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16017 * Create a new TabGroup
16018 * @param {Object} config The config object
16021 Roo.bootstrap.TabGroup = function(config){
16022 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16024 this.navId = Roo.id();
16027 Roo.bootstrap.TabGroup.register(this);
16031 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16034 transition : false,
16039 slideOnTouch : false,
16041 getAutoCreate : function()
16043 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16045 cfg.cls += ' tab-content';
16047 Roo.log('get auto create...............');
16049 if (this.carousel) {
16050 cfg.cls += ' carousel slide';
16053 cls : 'carousel-inner'
16056 if(this.bullets && !Roo.isTouch){
16059 cls : 'carousel-bullets',
16063 if(this.bullets_cls){
16064 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16067 for (var i = 0; i < this.bullets; i++){
16069 cls : 'bullet bullet-' + i
16077 cfg.cn[0].cn = bullets;
16084 initEvents: function()
16086 Roo.log('-------- init events on tab group ---------');
16092 if(Roo.isTouch && this.slideOnTouch){
16093 this.el.on("touchstart", this.onTouchStart, this);
16096 if(this.autoslide){
16099 this.slideFn = window.setInterval(function() {
16100 _this.showPanelNext();
16106 onTouchStart : function(e, el, o)
16108 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16112 this.showPanelNext();
16115 getChildContainer : function()
16117 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16121 * register a Navigation item
16122 * @param {Roo.bootstrap.NavItem} the navitem to add
16124 register : function(item)
16126 this.tabs.push( item);
16127 item.navId = this.navId; // not really needed..
16132 getActivePanel : function()
16135 Roo.each(this.tabs, function(t) {
16145 getPanelByName : function(n)
16148 Roo.each(this.tabs, function(t) {
16149 if (t.tabId == n) {
16157 indexOfPanel : function(p)
16160 Roo.each(this.tabs, function(t,i) {
16161 if (t.tabId == p.tabId) {
16170 * show a specific panel
16171 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16172 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16174 showPanel : function (pan)
16176 if(this.transition){
16177 Roo.log("waiting for the transitionend");
16181 if (typeof(pan) == 'number') {
16182 pan = this.tabs[pan];
16184 if (typeof(pan) == 'string') {
16185 pan = this.getPanelByName(pan);
16187 if (pan.tabId == this.getActivePanel().tabId) {
16190 var cur = this.getActivePanel();
16192 if (false === cur.fireEvent('beforedeactivate')) {
16196 if(this.bullets > 0 && !Roo.isTouch){
16197 this.setActiveBullet(this.indexOfPanel(pan));
16200 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16202 this.transition = true;
16203 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16204 var lr = dir == 'next' ? 'left' : 'right';
16205 pan.el.addClass(dir); // or prev
16206 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16207 cur.el.addClass(lr); // or right
16208 pan.el.addClass(lr);
16211 cur.el.on('transitionend', function() {
16212 Roo.log("trans end?");
16214 pan.el.removeClass([lr,dir]);
16215 pan.setActive(true);
16217 cur.el.removeClass([lr]);
16218 cur.setActive(false);
16220 _this.transition = false;
16222 }, this, { single: true } );
16227 cur.setActive(false);
16228 pan.setActive(true);
16233 showPanelNext : function()
16235 var i = this.indexOfPanel(this.getActivePanel());
16237 if (i >= this.tabs.length - 1 && !this.autoslide) {
16241 if (i >= this.tabs.length - 1 && this.autoslide) {
16245 this.showPanel(this.tabs[i+1]);
16248 showPanelPrev : function()
16250 var i = this.indexOfPanel(this.getActivePanel());
16252 if (i < 1 && !this.autoslide) {
16256 if (i < 1 && this.autoslide) {
16257 i = this.tabs.length;
16260 this.showPanel(this.tabs[i-1]);
16264 addBullet: function()
16266 if(!this.bullets || Roo.isTouch){
16269 var ctr = this.el.select('.carousel-bullets',true).first();
16270 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16271 var bullet = ctr.createChild({
16272 cls : 'bullet bullet-' + i
16273 },ctr.dom.lastChild);
16278 bullet.on('click', (function(e, el, o, ii, t){
16280 e.preventDefault();
16282 this.showPanel(ii);
16284 if(this.autoslide && this.slideFn){
16285 clearInterval(this.slideFn);
16286 this.slideFn = window.setInterval(function() {
16287 _this.showPanelNext();
16291 }).createDelegate(this, [i, bullet], true));
16296 setActiveBullet : function(i)
16302 Roo.each(this.el.select('.bullet', true).elements, function(el){
16303 el.removeClass('selected');
16306 var bullet = this.el.select('.bullet-' + i, true).first();
16312 bullet.addClass('selected');
16323 Roo.apply(Roo.bootstrap.TabGroup, {
16327 * register a Navigation Group
16328 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16330 register : function(navgrp)
16332 this.groups[navgrp.navId] = navgrp;
16336 * fetch a Navigation Group based on the navigation ID
16337 * if one does not exist , it will get created.
16338 * @param {string} the navgroup to add
16339 * @returns {Roo.bootstrap.NavGroup} the navgroup
16341 get: function(navId) {
16342 if (typeof(this.groups[navId]) == 'undefined') {
16343 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16345 return this.groups[navId] ;
16360 * @class Roo.bootstrap.TabPanel
16361 * @extends Roo.bootstrap.Component
16362 * Bootstrap TabPanel class
16363 * @cfg {Boolean} active panel active
16364 * @cfg {String} html panel content
16365 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16366 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16370 * Create a new TabPanel
16371 * @param {Object} config The config object
16374 Roo.bootstrap.TabPanel = function(config){
16375 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16379 * Fires when the active status changes
16380 * @param {Roo.bootstrap.TabPanel} this
16381 * @param {Boolean} state the new state
16386 * @event beforedeactivate
16387 * Fires before a tab is de-activated - can be used to do validation on a form.
16388 * @param {Roo.bootstrap.TabPanel} this
16389 * @return {Boolean} false if there is an error
16392 'beforedeactivate': true
16395 this.tabId = this.tabId || Roo.id();
16399 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16406 getAutoCreate : function(){
16409 // item is needed for carousel - not sure if it has any effect otherwise
16410 cls: 'tab-pane item',
16411 html: this.html || ''
16415 cfg.cls += ' active';
16419 cfg.tabId = this.tabId;
16426 initEvents: function()
16428 Roo.log('-------- init events on tab panel ---------');
16430 var p = this.parent();
16431 this.navId = this.navId || p.navId;
16433 if (typeof(this.navId) != 'undefined') {
16434 // not really needed.. but just in case.. parent should be a NavGroup.
16435 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16436 Roo.log(['register', tg, this]);
16439 var i = tg.tabs.length - 1;
16441 if(this.active && tg.bullets > 0 && i < tg.bullets){
16442 tg.setActiveBullet(i);
16449 onRender : function(ct, position)
16451 // Roo.log("Call onRender: " + this.xtype);
16453 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16461 setActive: function(state)
16463 Roo.log("panel - set active " + this.tabId + "=" + state);
16465 this.active = state;
16467 this.el.removeClass('active');
16469 } else if (!this.el.hasClass('active')) {
16470 this.el.addClass('active');
16473 this.fireEvent('changed', this, state);
16490 * @class Roo.bootstrap.DateField
16491 * @extends Roo.bootstrap.Input
16492 * Bootstrap DateField class
16493 * @cfg {Number} weekStart default 0
16494 * @cfg {String} viewMode default empty, (months|years)
16495 * @cfg {String} minViewMode default empty, (months|years)
16496 * @cfg {Number} startDate default -Infinity
16497 * @cfg {Number} endDate default Infinity
16498 * @cfg {Boolean} todayHighlight default false
16499 * @cfg {Boolean} todayBtn default false
16500 * @cfg {Boolean} calendarWeeks default false
16501 * @cfg {Object} daysOfWeekDisabled default empty
16502 * @cfg {Boolean} singleMode default false (true | false)
16504 * @cfg {Boolean} keyboardNavigation default true
16505 * @cfg {String} language default en
16508 * Create a new DateField
16509 * @param {Object} config The config object
16512 Roo.bootstrap.DateField = function(config){
16513 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16517 * Fires when this field show.
16518 * @param {Roo.bootstrap.DateField} this
16519 * @param {Mixed} date The date value
16524 * Fires when this field hide.
16525 * @param {Roo.bootstrap.DateField} this
16526 * @param {Mixed} date The date value
16531 * Fires when select a date.
16532 * @param {Roo.bootstrap.DateField} this
16533 * @param {Mixed} date The date value
16537 * @event beforeselect
16538 * Fires when before select a date.
16539 * @param {Roo.bootstrap.DateField} this
16540 * @param {Mixed} date The date value
16542 beforeselect : true
16546 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16549 * @cfg {String} format
16550 * The default date format string which can be overriden for localization support. The format must be
16551 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16555 * @cfg {String} altFormats
16556 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16557 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16559 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16567 todayHighlight : false,
16573 keyboardNavigation: true,
16575 calendarWeeks: false,
16577 startDate: -Infinity,
16581 daysOfWeekDisabled: [],
16585 singleMode : false,
16587 UTCDate: function()
16589 return new Date(Date.UTC.apply(Date, arguments));
16592 UTCToday: function()
16594 var today = new Date();
16595 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16598 getDate: function() {
16599 var d = this.getUTCDate();
16600 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16603 getUTCDate: function() {
16607 setDate: function(d) {
16608 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16611 setUTCDate: function(d) {
16613 this.setValue(this.formatDate(this.date));
16616 onRender: function(ct, position)
16619 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16621 this.language = this.language || 'en';
16622 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16623 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16625 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16626 this.format = this.format || 'm/d/y';
16627 this.isInline = false;
16628 this.isInput = true;
16629 this.component = this.el.select('.add-on', true).first() || false;
16630 this.component = (this.component && this.component.length === 0) ? false : this.component;
16631 this.hasInput = this.component && this.inputEL().length;
16633 if (typeof(this.minViewMode === 'string')) {
16634 switch (this.minViewMode) {
16636 this.minViewMode = 1;
16639 this.minViewMode = 2;
16642 this.minViewMode = 0;
16647 if (typeof(this.viewMode === 'string')) {
16648 switch (this.viewMode) {
16661 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16663 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16665 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16667 this.picker().on('mousedown', this.onMousedown, this);
16668 this.picker().on('click', this.onClick, this);
16670 this.picker().addClass('datepicker-dropdown');
16672 this.startViewMode = this.viewMode;
16674 if(this.singleMode){
16675 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16676 v.setVisibilityMode(Roo.Element.DISPLAY);
16680 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16681 v.setStyle('width', '189px');
16685 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16686 if(!this.calendarWeeks){
16691 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16692 v.attr('colspan', function(i, val){
16693 return parseInt(val) + 1;
16698 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16700 this.setStartDate(this.startDate);
16701 this.setEndDate(this.endDate);
16703 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16710 if(this.isInline) {
16715 picker : function()
16717 return this.pickerEl;
16718 // return this.el.select('.datepicker', true).first();
16721 fillDow: function()
16723 var dowCnt = this.weekStart;
16732 if(this.calendarWeeks){
16740 while (dowCnt < this.weekStart + 7) {
16744 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16748 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16751 fillMonths: function()
16754 var months = this.picker().select('>.datepicker-months td', true).first();
16756 months.dom.innerHTML = '';
16762 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16765 months.createChild(month);
16772 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;
16774 if (this.date < this.startDate) {
16775 this.viewDate = new Date(this.startDate);
16776 } else if (this.date > this.endDate) {
16777 this.viewDate = new Date(this.endDate);
16779 this.viewDate = new Date(this.date);
16787 var d = new Date(this.viewDate),
16788 year = d.getUTCFullYear(),
16789 month = d.getUTCMonth(),
16790 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16791 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16792 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16793 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16794 currentDate = this.date && this.date.valueOf(),
16795 today = this.UTCToday();
16797 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16799 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16801 // this.picker.select('>tfoot th.today').
16802 // .text(dates[this.language].today)
16803 // .toggle(this.todayBtn !== false);
16805 this.updateNavArrows();
16808 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16810 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16812 prevMonth.setUTCDate(day);
16814 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16816 var nextMonth = new Date(prevMonth);
16818 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16820 nextMonth = nextMonth.valueOf();
16822 var fillMonths = false;
16824 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16826 while(prevMonth.valueOf() < nextMonth) {
16829 if (prevMonth.getUTCDay() === this.weekStart) {
16831 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16839 if(this.calendarWeeks){
16840 // ISO 8601: First week contains first thursday.
16841 // ISO also states week starts on Monday, but we can be more abstract here.
16843 // Start of current week: based on weekstart/current date
16844 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16845 // Thursday of this week
16846 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16847 // First Thursday of year, year from thursday
16848 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16849 // Calendar week: ms between thursdays, div ms per day, div 7 days
16850 calWeek = (th - yth) / 864e5 / 7 + 1;
16852 fillMonths.cn.push({
16860 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16862 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16865 if (this.todayHighlight &&
16866 prevMonth.getUTCFullYear() == today.getFullYear() &&
16867 prevMonth.getUTCMonth() == today.getMonth() &&
16868 prevMonth.getUTCDate() == today.getDate()) {
16869 clsName += ' today';
16872 if (currentDate && prevMonth.valueOf() === currentDate) {
16873 clsName += ' active';
16876 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16877 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16878 clsName += ' disabled';
16881 fillMonths.cn.push({
16883 cls: 'day ' + clsName,
16884 html: prevMonth.getDate()
16887 prevMonth.setDate(prevMonth.getDate()+1);
16890 var currentYear = this.date && this.date.getUTCFullYear();
16891 var currentMonth = this.date && this.date.getUTCMonth();
16893 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16895 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16896 v.removeClass('active');
16898 if(currentYear === year && k === currentMonth){
16899 v.addClass('active');
16902 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16903 v.addClass('disabled');
16909 year = parseInt(year/10, 10) * 10;
16911 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16913 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16916 for (var i = -1; i < 11; i++) {
16917 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16919 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16927 showMode: function(dir)
16930 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16933 Roo.each(this.picker().select('>div',true).elements, function(v){
16934 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16937 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16942 if(this.isInline) {
16946 this.picker().removeClass(['bottom', 'top']);
16948 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16950 * place to the top of element!
16954 this.picker().addClass('top');
16955 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16960 this.picker().addClass('bottom');
16962 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16965 parseDate : function(value)
16967 if(!value || value instanceof Date){
16970 var v = Date.parseDate(value, this.format);
16971 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16972 v = Date.parseDate(value, 'Y-m-d');
16974 if(!v && this.altFormats){
16975 if(!this.altFormatsArray){
16976 this.altFormatsArray = this.altFormats.split("|");
16978 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16979 v = Date.parseDate(value, this.altFormatsArray[i]);
16985 formatDate : function(date, fmt)
16987 return (!date || !(date instanceof Date)) ?
16988 date : date.dateFormat(fmt || this.format);
16991 onFocus : function()
16993 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16997 onBlur : function()
16999 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17001 var d = this.inputEl().getValue();
17010 this.picker().show();
17014 this.fireEvent('show', this, this.date);
17019 if(this.isInline) {
17022 this.picker().hide();
17023 this.viewMode = this.startViewMode;
17026 this.fireEvent('hide', this, this.date);
17030 onMousedown: function(e)
17032 e.stopPropagation();
17033 e.preventDefault();
17038 Roo.bootstrap.DateField.superclass.keyup.call(this);
17042 setValue: function(v)
17044 if(this.fireEvent('beforeselect', this, v) !== false){
17045 var d = new Date(this.parseDate(v) ).clearTime();
17047 if(isNaN(d.getTime())){
17048 this.date = this.viewDate = '';
17049 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17053 v = this.formatDate(d);
17055 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17057 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17061 this.fireEvent('select', this, this.date);
17065 getValue: function()
17067 return this.formatDate(this.date);
17070 fireKey: function(e)
17072 if (!this.picker().isVisible()){
17073 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17079 var dateChanged = false,
17081 newDate, newViewDate;
17086 e.preventDefault();
17090 if (!this.keyboardNavigation) {
17093 dir = e.keyCode == 37 ? -1 : 1;
17096 newDate = this.moveYear(this.date, dir);
17097 newViewDate = this.moveYear(this.viewDate, dir);
17098 } else if (e.shiftKey){
17099 newDate = this.moveMonth(this.date, dir);
17100 newViewDate = this.moveMonth(this.viewDate, dir);
17102 newDate = new Date(this.date);
17103 newDate.setUTCDate(this.date.getUTCDate() + dir);
17104 newViewDate = new Date(this.viewDate);
17105 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17107 if (this.dateWithinRange(newDate)){
17108 this.date = newDate;
17109 this.viewDate = newViewDate;
17110 this.setValue(this.formatDate(this.date));
17112 e.preventDefault();
17113 dateChanged = true;
17118 if (!this.keyboardNavigation) {
17121 dir = e.keyCode == 38 ? -1 : 1;
17123 newDate = this.moveYear(this.date, dir);
17124 newViewDate = this.moveYear(this.viewDate, dir);
17125 } else if (e.shiftKey){
17126 newDate = this.moveMonth(this.date, dir);
17127 newViewDate = this.moveMonth(this.viewDate, dir);
17129 newDate = new Date(this.date);
17130 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17131 newViewDate = new Date(this.viewDate);
17132 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17134 if (this.dateWithinRange(newDate)){
17135 this.date = newDate;
17136 this.viewDate = newViewDate;
17137 this.setValue(this.formatDate(this.date));
17139 e.preventDefault();
17140 dateChanged = true;
17144 this.setValue(this.formatDate(this.date));
17146 e.preventDefault();
17149 this.setValue(this.formatDate(this.date));
17163 onClick: function(e)
17165 e.stopPropagation();
17166 e.preventDefault();
17168 var target = e.getTarget();
17170 if(target.nodeName.toLowerCase() === 'i'){
17171 target = Roo.get(target).dom.parentNode;
17174 var nodeName = target.nodeName;
17175 var className = target.className;
17176 var html = target.innerHTML;
17177 //Roo.log(nodeName);
17179 switch(nodeName.toLowerCase()) {
17181 switch(className) {
17187 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17188 switch(this.viewMode){
17190 this.viewDate = this.moveMonth(this.viewDate, dir);
17194 this.viewDate = this.moveYear(this.viewDate, dir);
17200 var date = new Date();
17201 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17203 this.setValue(this.formatDate(this.date));
17210 if (className.indexOf('disabled') < 0) {
17211 this.viewDate.setUTCDate(1);
17212 if (className.indexOf('month') > -1) {
17213 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17215 var year = parseInt(html, 10) || 0;
17216 this.viewDate.setUTCFullYear(year);
17220 if(this.singleMode){
17221 this.setValue(this.formatDate(this.viewDate));
17232 //Roo.log(className);
17233 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17234 var day = parseInt(html, 10) || 1;
17235 var year = this.viewDate.getUTCFullYear(),
17236 month = this.viewDate.getUTCMonth();
17238 if (className.indexOf('old') > -1) {
17245 } else if (className.indexOf('new') > -1) {
17253 //Roo.log([year,month,day]);
17254 this.date = this.UTCDate(year, month, day,0,0,0,0);
17255 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17257 //Roo.log(this.formatDate(this.date));
17258 this.setValue(this.formatDate(this.date));
17265 setStartDate: function(startDate)
17267 this.startDate = startDate || -Infinity;
17268 if (this.startDate !== -Infinity) {
17269 this.startDate = this.parseDate(this.startDate);
17272 this.updateNavArrows();
17275 setEndDate: function(endDate)
17277 this.endDate = endDate || Infinity;
17278 if (this.endDate !== Infinity) {
17279 this.endDate = this.parseDate(this.endDate);
17282 this.updateNavArrows();
17285 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17287 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17288 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17289 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17291 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17292 return parseInt(d, 10);
17295 this.updateNavArrows();
17298 updateNavArrows: function()
17300 if(this.singleMode){
17304 var d = new Date(this.viewDate),
17305 year = d.getUTCFullYear(),
17306 month = d.getUTCMonth();
17308 Roo.each(this.picker().select('.prev', true).elements, function(v){
17310 switch (this.viewMode) {
17313 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17319 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17326 Roo.each(this.picker().select('.next', true).elements, function(v){
17328 switch (this.viewMode) {
17331 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17337 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17345 moveMonth: function(date, dir)
17350 var new_date = new Date(date.valueOf()),
17351 day = new_date.getUTCDate(),
17352 month = new_date.getUTCMonth(),
17353 mag = Math.abs(dir),
17355 dir = dir > 0 ? 1 : -1;
17358 // If going back one month, make sure month is not current month
17359 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17361 return new_date.getUTCMonth() == month;
17363 // If going forward one month, make sure month is as expected
17364 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17366 return new_date.getUTCMonth() != new_month;
17368 new_month = month + dir;
17369 new_date.setUTCMonth(new_month);
17370 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17371 if (new_month < 0 || new_month > 11) {
17372 new_month = (new_month + 12) % 12;
17375 // For magnitudes >1, move one month at a time...
17376 for (var i=0; i<mag; i++) {
17377 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17378 new_date = this.moveMonth(new_date, dir);
17380 // ...then reset the day, keeping it in the new month
17381 new_month = new_date.getUTCMonth();
17382 new_date.setUTCDate(day);
17384 return new_month != new_date.getUTCMonth();
17387 // Common date-resetting loop -- if date is beyond end of month, make it
17390 new_date.setUTCDate(--day);
17391 new_date.setUTCMonth(new_month);
17396 moveYear: function(date, dir)
17398 return this.moveMonth(date, dir*12);
17401 dateWithinRange: function(date)
17403 return date >= this.startDate && date <= this.endDate;
17409 this.picker().remove();
17414 Roo.apply(Roo.bootstrap.DateField, {
17425 html: '<i class="fa fa-arrow-left"/>'
17435 html: '<i class="fa fa-arrow-right"/>'
17477 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17478 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17479 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17480 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17481 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17494 navFnc: 'FullYear',
17499 navFnc: 'FullYear',
17504 Roo.apply(Roo.bootstrap.DateField, {
17508 cls: 'datepicker dropdown-menu roo-dynamic',
17512 cls: 'datepicker-days',
17516 cls: 'table-condensed',
17518 Roo.bootstrap.DateField.head,
17522 Roo.bootstrap.DateField.footer
17529 cls: 'datepicker-months',
17533 cls: 'table-condensed',
17535 Roo.bootstrap.DateField.head,
17536 Roo.bootstrap.DateField.content,
17537 Roo.bootstrap.DateField.footer
17544 cls: 'datepicker-years',
17548 cls: 'table-condensed',
17550 Roo.bootstrap.DateField.head,
17551 Roo.bootstrap.DateField.content,
17552 Roo.bootstrap.DateField.footer
17571 * @class Roo.bootstrap.TimeField
17572 * @extends Roo.bootstrap.Input
17573 * Bootstrap DateField class
17577 * Create a new TimeField
17578 * @param {Object} config The config object
17581 Roo.bootstrap.TimeField = function(config){
17582 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17586 * Fires when this field show.
17587 * @param {Roo.bootstrap.DateField} thisthis
17588 * @param {Mixed} date The date value
17593 * Fires when this field hide.
17594 * @param {Roo.bootstrap.DateField} this
17595 * @param {Mixed} date The date value
17600 * Fires when select a date.
17601 * @param {Roo.bootstrap.DateField} this
17602 * @param {Mixed} date The date value
17608 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17611 * @cfg {String} format
17612 * The default time format string which can be overriden for localization support. The format must be
17613 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17617 onRender: function(ct, position)
17620 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17622 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17624 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17626 this.pop = this.picker().select('>.datepicker-time',true).first();
17627 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17629 this.picker().on('mousedown', this.onMousedown, this);
17630 this.picker().on('click', this.onClick, this);
17632 this.picker().addClass('datepicker-dropdown');
17637 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17638 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17639 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17640 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17641 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17642 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17646 fireKey: function(e){
17647 if (!this.picker().isVisible()){
17648 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17654 e.preventDefault();
17662 this.onTogglePeriod();
17665 this.onIncrementMinutes();
17668 this.onDecrementMinutes();
17677 onClick: function(e) {
17678 e.stopPropagation();
17679 e.preventDefault();
17682 picker : function()
17684 return this.el.select('.datepicker', true).first();
17687 fillTime: function()
17689 var time = this.pop.select('tbody', true).first();
17691 time.dom.innerHTML = '';
17706 cls: 'hours-up glyphicon glyphicon-chevron-up'
17726 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17747 cls: 'timepicker-hour',
17762 cls: 'timepicker-minute',
17777 cls: 'btn btn-primary period',
17799 cls: 'hours-down glyphicon glyphicon-chevron-down'
17819 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17837 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17844 var hours = this.time.getHours();
17845 var minutes = this.time.getMinutes();
17858 hours = hours - 12;
17862 hours = '0' + hours;
17866 minutes = '0' + minutes;
17869 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17870 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17871 this.pop.select('button', true).first().dom.innerHTML = period;
17877 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17879 var cls = ['bottom'];
17881 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17888 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17893 this.picker().addClass(cls.join('-'));
17897 Roo.each(cls, function(c){
17899 _this.picker().setTop(_this.inputEl().getHeight());
17903 _this.picker().setTop(0 - _this.picker().getHeight());
17908 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17912 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17919 onFocus : function()
17921 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17925 onBlur : function()
17927 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17933 this.picker().show();
17938 this.fireEvent('show', this, this.date);
17943 this.picker().hide();
17946 this.fireEvent('hide', this, this.date);
17949 setTime : function()
17952 this.setValue(this.time.format(this.format));
17954 this.fireEvent('select', this, this.date);
17959 onMousedown: function(e){
17960 e.stopPropagation();
17961 e.preventDefault();
17964 onIncrementHours: function()
17966 Roo.log('onIncrementHours');
17967 this.time = this.time.add(Date.HOUR, 1);
17972 onDecrementHours: function()
17974 Roo.log('onDecrementHours');
17975 this.time = this.time.add(Date.HOUR, -1);
17979 onIncrementMinutes: function()
17981 Roo.log('onIncrementMinutes');
17982 this.time = this.time.add(Date.MINUTE, 1);
17986 onDecrementMinutes: function()
17988 Roo.log('onDecrementMinutes');
17989 this.time = this.time.add(Date.MINUTE, -1);
17993 onTogglePeriod: function()
17995 Roo.log('onTogglePeriod');
17996 this.time = this.time.add(Date.HOUR, 12);
18003 Roo.apply(Roo.bootstrap.TimeField, {
18033 cls: 'btn btn-info ok',
18045 Roo.apply(Roo.bootstrap.TimeField, {
18049 cls: 'datepicker dropdown-menu',
18053 cls: 'datepicker-time',
18057 cls: 'table-condensed',
18059 Roo.bootstrap.TimeField.content,
18060 Roo.bootstrap.TimeField.footer
18079 * @class Roo.bootstrap.MonthField
18080 * @extends Roo.bootstrap.Input
18081 * Bootstrap MonthField class
18083 * @cfg {String} language default en
18086 * Create a new MonthField
18087 * @param {Object} config The config object
18090 Roo.bootstrap.MonthField = function(config){
18091 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18096 * Fires when this field show.
18097 * @param {Roo.bootstrap.MonthField} this
18098 * @param {Mixed} date The date value
18103 * Fires when this field hide.
18104 * @param {Roo.bootstrap.MonthField} this
18105 * @param {Mixed} date The date value
18110 * Fires when select a date.
18111 * @param {Roo.bootstrap.MonthField} this
18112 * @param {String} oldvalue The old value
18113 * @param {String} newvalue The new value
18119 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18121 onRender: function(ct, position)
18124 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18126 this.language = this.language || 'en';
18127 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18128 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18130 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18131 this.isInline = false;
18132 this.isInput = true;
18133 this.component = this.el.select('.add-on', true).first() || false;
18134 this.component = (this.component && this.component.length === 0) ? false : this.component;
18135 this.hasInput = this.component && this.inputEL().length;
18137 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18139 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18141 this.picker().on('mousedown', this.onMousedown, this);
18142 this.picker().on('click', this.onClick, this);
18144 this.picker().addClass('datepicker-dropdown');
18146 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18147 v.setStyle('width', '189px');
18154 if(this.isInline) {
18160 setValue: function(v, suppressEvent)
18162 var o = this.getValue();
18164 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18168 if(suppressEvent !== true){
18169 this.fireEvent('select', this, o, v);
18174 getValue: function()
18179 onClick: function(e)
18181 e.stopPropagation();
18182 e.preventDefault();
18184 var target = e.getTarget();
18186 if(target.nodeName.toLowerCase() === 'i'){
18187 target = Roo.get(target).dom.parentNode;
18190 var nodeName = target.nodeName;
18191 var className = target.className;
18192 var html = target.innerHTML;
18194 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18198 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18200 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18206 picker : function()
18208 return this.pickerEl;
18211 fillMonths: function()
18214 var months = this.picker().select('>.datepicker-months td', true).first();
18216 months.dom.innerHTML = '';
18222 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18225 months.createChild(month);
18234 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18235 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18238 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18239 e.removeClass('active');
18241 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18242 e.addClass('active');
18249 if(this.isInline) {
18253 this.picker().removeClass(['bottom', 'top']);
18255 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18257 * place to the top of element!
18261 this.picker().addClass('top');
18262 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18267 this.picker().addClass('bottom');
18269 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18272 onFocus : function()
18274 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18278 onBlur : function()
18280 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18282 var d = this.inputEl().getValue();
18291 this.picker().show();
18292 this.picker().select('>.datepicker-months', true).first().show();
18296 this.fireEvent('show', this, this.date);
18301 if(this.isInline) {
18304 this.picker().hide();
18305 this.fireEvent('hide', this, this.date);
18309 onMousedown: function(e)
18311 e.stopPropagation();
18312 e.preventDefault();
18317 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18321 fireKey: function(e)
18323 if (!this.picker().isVisible()){
18324 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18335 e.preventDefault();
18339 dir = e.keyCode == 37 ? -1 : 1;
18341 this.vIndex = this.vIndex + dir;
18343 if(this.vIndex < 0){
18347 if(this.vIndex > 11){
18351 if(isNaN(this.vIndex)){
18355 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18361 dir = e.keyCode == 38 ? -1 : 1;
18363 this.vIndex = this.vIndex + dir * 4;
18365 if(this.vIndex < 0){
18369 if(this.vIndex > 11){
18373 if(isNaN(this.vIndex)){
18377 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18382 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18383 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18387 e.preventDefault();
18390 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18391 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18407 this.picker().remove();
18412 Roo.apply(Roo.bootstrap.MonthField, {
18431 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18432 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18437 Roo.apply(Roo.bootstrap.MonthField, {
18441 cls: 'datepicker dropdown-menu roo-dynamic',
18445 cls: 'datepicker-months',
18449 cls: 'table-condensed',
18451 Roo.bootstrap.DateField.content
18471 * @class Roo.bootstrap.CheckBox
18472 * @extends Roo.bootstrap.Input
18473 * Bootstrap CheckBox class
18475 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18476 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18477 * @cfg {String} boxLabel The text that appears beside the checkbox
18478 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18479 * @cfg {Boolean} checked initnal the element
18480 * @cfg {Boolean} inline inline the element (default false)
18481 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18484 * Create a new CheckBox
18485 * @param {Object} config The config object
18488 Roo.bootstrap.CheckBox = function(config){
18489 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18494 * Fires when the element is checked or unchecked.
18495 * @param {Roo.bootstrap.CheckBox} this This input
18496 * @param {Boolean} checked The new checked value
18503 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18505 inputType: 'checkbox',
18513 getAutoCreate : function()
18515 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18521 cfg.cls = 'form-group ' + this.inputType; //input-group
18524 cfg.cls += ' ' + this.inputType + '-inline';
18530 type : this.inputType,
18531 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18532 cls : 'roo-' + this.inputType, //'form-box',
18533 placeholder : this.placeholder || ''
18537 if (this.weight) { // Validity check?
18538 cfg.cls += " " + this.inputType + "-" + this.weight;
18541 if (this.disabled) {
18542 input.disabled=true;
18546 input.checked = this.checked;
18550 input.name = this.name;
18554 input.cls += ' input-' + this.size;
18559 ['xs','sm','md','lg'].map(function(size){
18560 if (settings[size]) {
18561 cfg.cls += ' col-' + size + '-' + settings[size];
18565 var inputblock = input;
18567 if (this.before || this.after) {
18570 cls : 'input-group',
18575 inputblock.cn.push({
18577 cls : 'input-group-addon',
18582 inputblock.cn.push(input);
18585 inputblock.cn.push({
18587 cls : 'input-group-addon',
18594 if (align ==='left' && this.fieldLabel.length) {
18595 Roo.log("left and has label");
18601 cls : 'control-label col-md-' + this.labelWidth,
18602 html : this.fieldLabel
18606 cls : "col-md-" + (12 - this.labelWidth),
18613 } else if ( this.fieldLabel.length) {
18618 tag: this.boxLabel ? 'span' : 'label',
18620 cls: 'control-label box-input-label',
18621 //cls : 'input-group-addon',
18622 html : this.fieldLabel
18632 Roo.log(" no label && no align");
18633 cfg.cn = [ inputblock ] ;
18638 var boxLabelCfg = {
18640 //'for': id, // box label is handled by onclick - so no for...
18642 html: this.boxLabel
18646 boxLabelCfg.tooltip = this.tooltip;
18649 cfg.cn.push(boxLabelCfg);
18659 * return the real input element.
18661 inputEl: function ()
18663 return this.el.select('input.roo-' + this.inputType,true).first();
18666 labelEl: function()
18668 return this.el.select('label.control-label',true).first();
18670 /* depricated... */
18674 return this.labelEl();
18677 initEvents : function()
18679 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18681 this.inputEl().on('click', this.onClick, this);
18683 if (this.boxLabel) {
18684 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18687 this.startValue = this.getValue();
18690 Roo.bootstrap.CheckBox.register(this);
18694 onClick : function()
18696 this.setChecked(!this.checked);
18699 setChecked : function(state,suppressEvent)
18701 this.startValue = this.getValue();
18703 if(this.inputType == 'radio'){
18705 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18706 e.dom.checked = false;
18709 this.inputEl().dom.checked = true;
18711 this.inputEl().dom.value = this.inputValue;
18713 if(suppressEvent !== true){
18714 this.fireEvent('check', this, true);
18722 this.checked = state;
18724 this.inputEl().dom.checked = state;
18726 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18728 if(suppressEvent !== true){
18729 this.fireEvent('check', this, state);
18735 getValue : function()
18737 if(this.inputType == 'radio'){
18738 return this.getGroupValue();
18741 return this.inputEl().getValue();
18745 getGroupValue : function()
18747 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18751 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18754 setValue : function(v,suppressEvent)
18756 if(this.inputType == 'radio'){
18757 this.setGroupValue(v, suppressEvent);
18761 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18766 setGroupValue : function(v, suppressEvent)
18768 this.startValue = this.getValue();
18770 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18771 e.dom.checked = false;
18773 if(e.dom.value == v){
18774 e.dom.checked = true;
18778 if(suppressEvent !== true){
18779 this.fireEvent('check', this, true);
18787 validate : function()
18791 (this.inputType == 'radio' && this.validateRadio()) ||
18792 (this.inputType == 'checkbox' && this.validateCheckbox())
18798 this.markInvalid();
18802 validateRadio : function()
18806 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18807 if(!e.dom.checked){
18819 validateCheckbox : function()
18822 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18825 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18833 for(var i in group){
18838 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18845 * Mark this field as valid
18847 markValid : function()
18849 if(this.allowBlank){
18855 this.fireEvent('valid', this);
18857 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18860 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18867 if(this.inputType == 'radio'){
18868 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18869 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18870 e.findParent('.form-group', false, true).addClass(_this.validClass);
18877 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18878 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18882 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18888 for(var i in group){
18889 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18890 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18895 * Mark this field as invalid
18896 * @param {String} msg The validation message
18898 markInvalid : function(msg)
18900 if(this.allowBlank){
18906 this.fireEvent('invalid', this, msg);
18908 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18911 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18915 label.markInvalid();
18918 if(this.inputType == 'radio'){
18919 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18920 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18921 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18928 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18929 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18933 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18939 for(var i in group){
18940 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18941 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18948 Roo.apply(Roo.bootstrap.CheckBox, {
18953 * register a CheckBox Group
18954 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18956 register : function(checkbox)
18958 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18959 this.groups[checkbox.groupId] = {};
18962 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18966 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18970 * fetch a CheckBox Group based on the group ID
18971 * @param {string} the group ID
18972 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18974 get: function(groupId) {
18975 if (typeof(this.groups[groupId]) == 'undefined') {
18979 return this.groups[groupId] ;
18991 *<div class="radio">
18993 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18994 Option one is this and that—be sure to include why it's great
19001 *<label class="radio-inline">fieldLabel</label>
19002 *<label class="radio-inline">
19003 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19011 * @class Roo.bootstrap.Radio
19012 * @extends Roo.bootstrap.CheckBox
19013 * Bootstrap Radio class
19016 * Create a new Radio
19017 * @param {Object} config The config object
19020 Roo.bootstrap.Radio = function(config){
19021 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19025 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19027 inputType: 'radio',
19031 getAutoCreate : function()
19033 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19034 align = align || 'left'; // default...
19041 tag : this.inline ? 'span' : 'div',
19046 var inline = this.inline ? ' radio-inline' : '';
19050 // does not need for, as we wrap the input with it..
19052 cls : 'control-label box-label' + inline,
19055 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19059 //cls : 'control-label' + inline,
19060 html : this.fieldLabel,
19061 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19070 type : this.inputType,
19071 //value : (!this.checked) ? this.valueOff : this.inputValue,
19072 value : this.inputValue,
19074 placeholder : this.placeholder || '' // ?? needed????
19077 if (this.weight) { // Validity check?
19078 input.cls += " radio-" + this.weight;
19080 if (this.disabled) {
19081 input.disabled=true;
19085 input.checked = this.checked;
19089 input.name = this.name;
19093 input.cls += ' input-' + this.size;
19096 //?? can span's inline have a width??
19099 ['xs','sm','md','lg'].map(function(size){
19100 if (settings[size]) {
19101 cfg.cls += ' col-' + size + '-' + settings[size];
19105 var inputblock = input;
19107 if (this.before || this.after) {
19110 cls : 'input-group',
19115 inputblock.cn.push({
19117 cls : 'input-group-addon',
19121 inputblock.cn.push(input);
19123 inputblock.cn.push({
19125 cls : 'input-group-addon',
19133 if (this.fieldLabel && this.fieldLabel.length) {
19134 cfg.cn.push(fieldLabel);
19137 // normal bootstrap puts the input inside the label.
19138 // however with our styled version - it has to go after the input.
19140 //lbl.cn.push(inputblock);
19144 cls: 'radio' + inline,
19151 cfg.cn.push( lblwrap);
19156 html: this.boxLabel
19165 initEvents : function()
19167 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19169 this.inputEl().on('click', this.onClick, this);
19170 if (this.boxLabel) {
19171 //Roo.log('find label');
19172 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19177 inputEl: function ()
19179 return this.el.select('input.roo-radio',true).first();
19181 onClick : function()
19184 this.setChecked(true);
19187 setChecked : function(state,suppressEvent)
19190 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19191 v.dom.checked = false;
19194 Roo.log(this.inputEl().dom);
19195 this.checked = state;
19196 this.inputEl().dom.checked = state;
19198 if(suppressEvent !== true){
19199 this.fireEvent('check', this, state);
19202 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19206 getGroupValue : function()
19209 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19210 if(v.dom.checked == true){
19211 value = v.dom.value;
19219 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19220 * @return {Mixed} value The field value
19222 getValue : function(){
19223 return this.getGroupValue();
19229 //<script type="text/javascript">
19232 * Based Ext JS Library 1.1.1
19233 * Copyright(c) 2006-2007, Ext JS, LLC.
19239 * @class Roo.HtmlEditorCore
19240 * @extends Roo.Component
19241 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19243 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19246 Roo.HtmlEditorCore = function(config){
19249 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19254 * @event initialize
19255 * Fires when the editor is fully initialized (including the iframe)
19256 * @param {Roo.HtmlEditorCore} this
19261 * Fires when the editor is first receives the focus. Any insertion must wait
19262 * until after this event.
19263 * @param {Roo.HtmlEditorCore} this
19267 * @event beforesync
19268 * Fires before the textarea is updated with content from the editor iframe. Return false
19269 * to cancel the sync.
19270 * @param {Roo.HtmlEditorCore} this
19271 * @param {String} html
19275 * @event beforepush
19276 * Fires before the iframe editor is updated with content from the textarea. Return false
19277 * to cancel the push.
19278 * @param {Roo.HtmlEditorCore} this
19279 * @param {String} html
19284 * Fires when the textarea is updated with content from the editor iframe.
19285 * @param {Roo.HtmlEditorCore} this
19286 * @param {String} html
19291 * Fires when the iframe editor is updated with content from the textarea.
19292 * @param {Roo.HtmlEditorCore} this
19293 * @param {String} html
19298 * @event editorevent
19299 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19300 * @param {Roo.HtmlEditorCore} this
19306 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19308 // defaults : white / black...
19309 this.applyBlacklists();
19316 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19320 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19326 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19331 * @cfg {Number} height (in pixels)
19335 * @cfg {Number} width (in pixels)
19340 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19343 stylesheets: false,
19348 // private properties
19349 validationEvent : false,
19351 initialized : false,
19353 sourceEditMode : false,
19354 onFocus : Roo.emptyFn,
19356 hideMode:'offsets',
19360 // blacklist + whitelisted elements..
19367 * Protected method that will not generally be called directly. It
19368 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19369 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19371 getDocMarkup : function(){
19375 // inherit styels from page...??
19376 if (this.stylesheets === false) {
19378 Roo.get(document.head).select('style').each(function(node) {
19379 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19382 Roo.get(document.head).select('link').each(function(node) {
19383 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19386 } else if (!this.stylesheets.length) {
19388 st = '<style type="text/css">' +
19389 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19395 st += '<style type="text/css">' +
19396 'IMG { cursor: pointer } ' +
19400 return '<html><head>' + st +
19401 //<style type="text/css">' +
19402 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19404 ' </head><body class="roo-htmleditor-body"></body></html>';
19408 onRender : function(ct, position)
19411 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19412 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19415 this.el.dom.style.border = '0 none';
19416 this.el.dom.setAttribute('tabIndex', -1);
19417 this.el.addClass('x-hidden hide');
19421 if(Roo.isIE){ // fix IE 1px bogus margin
19422 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19426 this.frameId = Roo.id();
19430 var iframe = this.owner.wrap.createChild({
19432 cls: 'form-control', // bootstrap..
19434 name: this.frameId,
19435 frameBorder : 'no',
19436 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19441 this.iframe = iframe.dom;
19443 this.assignDocWin();
19445 this.doc.designMode = 'on';
19448 this.doc.write(this.getDocMarkup());
19452 var task = { // must defer to wait for browser to be ready
19454 //console.log("run task?" + this.doc.readyState);
19455 this.assignDocWin();
19456 if(this.doc.body || this.doc.readyState == 'complete'){
19458 this.doc.designMode="on";
19462 Roo.TaskMgr.stop(task);
19463 this.initEditor.defer(10, this);
19470 Roo.TaskMgr.start(task);
19475 onResize : function(w, h)
19477 Roo.log('resize: ' +w + ',' + h );
19478 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19482 if(typeof w == 'number'){
19484 this.iframe.style.width = w + 'px';
19486 if(typeof h == 'number'){
19488 this.iframe.style.height = h + 'px';
19490 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19497 * Toggles the editor between standard and source edit mode.
19498 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19500 toggleSourceEdit : function(sourceEditMode){
19502 this.sourceEditMode = sourceEditMode === true;
19504 if(this.sourceEditMode){
19506 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19509 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19510 //this.iframe.className = '';
19513 //this.setSize(this.owner.wrap.getSize());
19514 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19521 * Protected method that will not generally be called directly. If you need/want
19522 * custom HTML cleanup, this is the method you should override.
19523 * @param {String} html The HTML to be cleaned
19524 * return {String} The cleaned HTML
19526 cleanHtml : function(html){
19527 html = String(html);
19528 if(html.length > 5){
19529 if(Roo.isSafari){ // strip safari nonsense
19530 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19533 if(html == ' '){
19540 * HTML Editor -> Textarea
19541 * Protected method that will not generally be called directly. Syncs the contents
19542 * of the editor iframe with the textarea.
19544 syncValue : function(){
19545 if(this.initialized){
19546 var bd = (this.doc.body || this.doc.documentElement);
19547 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19548 var html = bd.innerHTML;
19550 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19551 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19553 html = '<div style="'+m[0]+'">' + html + '</div>';
19556 html = this.cleanHtml(html);
19557 // fix up the special chars.. normaly like back quotes in word...
19558 // however we do not want to do this with chinese..
19559 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19560 var cc = b.charCodeAt();
19562 (cc >= 0x4E00 && cc < 0xA000 ) ||
19563 (cc >= 0x3400 && cc < 0x4E00 ) ||
19564 (cc >= 0xf900 && cc < 0xfb00 )
19570 if(this.owner.fireEvent('beforesync', this, html) !== false){
19571 this.el.dom.value = html;
19572 this.owner.fireEvent('sync', this, html);
19578 * Protected method that will not generally be called directly. Pushes the value of the textarea
19579 * into the iframe editor.
19581 pushValue : function(){
19582 if(this.initialized){
19583 var v = this.el.dom.value.trim();
19585 // if(v.length < 1){
19589 if(this.owner.fireEvent('beforepush', this, v) !== false){
19590 var d = (this.doc.body || this.doc.documentElement);
19592 this.cleanUpPaste();
19593 this.el.dom.value = d.innerHTML;
19594 this.owner.fireEvent('push', this, v);
19600 deferFocus : function(){
19601 this.focus.defer(10, this);
19605 focus : function(){
19606 if(this.win && !this.sourceEditMode){
19613 assignDocWin: function()
19615 var iframe = this.iframe;
19618 this.doc = iframe.contentWindow.document;
19619 this.win = iframe.contentWindow;
19621 // if (!Roo.get(this.frameId)) {
19624 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19625 // this.win = Roo.get(this.frameId).dom.contentWindow;
19627 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19631 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19632 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19637 initEditor : function(){
19638 //console.log("INIT EDITOR");
19639 this.assignDocWin();
19643 this.doc.designMode="on";
19645 this.doc.write(this.getDocMarkup());
19648 var dbody = (this.doc.body || this.doc.documentElement);
19649 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19650 // this copies styles from the containing element into thsi one..
19651 // not sure why we need all of this..
19652 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19654 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19655 //ss['background-attachment'] = 'fixed'; // w3c
19656 dbody.bgProperties = 'fixed'; // ie
19657 //Roo.DomHelper.applyStyles(dbody, ss);
19658 Roo.EventManager.on(this.doc, {
19659 //'mousedown': this.onEditorEvent,
19660 'mouseup': this.onEditorEvent,
19661 'dblclick': this.onEditorEvent,
19662 'click': this.onEditorEvent,
19663 'keyup': this.onEditorEvent,
19668 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19670 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19671 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19673 this.initialized = true;
19675 this.owner.fireEvent('initialize', this);
19680 onDestroy : function(){
19686 //for (var i =0; i < this.toolbars.length;i++) {
19687 // // fixme - ask toolbars for heights?
19688 // this.toolbars[i].onDestroy();
19691 //this.wrap.dom.innerHTML = '';
19692 //this.wrap.remove();
19697 onFirstFocus : function(){
19699 this.assignDocWin();
19702 this.activated = true;
19705 if(Roo.isGecko){ // prevent silly gecko errors
19707 var s = this.win.getSelection();
19708 if(!s.focusNode || s.focusNode.nodeType != 3){
19709 var r = s.getRangeAt(0);
19710 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19715 this.execCmd('useCSS', true);
19716 this.execCmd('styleWithCSS', false);
19719 this.owner.fireEvent('activate', this);
19723 adjustFont: function(btn){
19724 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19725 //if(Roo.isSafari){ // safari
19728 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19729 if(Roo.isSafari){ // safari
19730 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19731 v = (v < 10) ? 10 : v;
19732 v = (v > 48) ? 48 : v;
19733 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19738 v = Math.max(1, v+adjust);
19740 this.execCmd('FontSize', v );
19743 onEditorEvent : function(e)
19745 this.owner.fireEvent('editorevent', this, e);
19746 // this.updateToolbar();
19747 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19750 insertTag : function(tg)
19752 // could be a bit smarter... -> wrap the current selected tRoo..
19753 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19755 range = this.createRange(this.getSelection());
19756 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19757 wrappingNode.appendChild(range.extractContents());
19758 range.insertNode(wrappingNode);
19765 this.execCmd("formatblock", tg);
19769 insertText : function(txt)
19773 var range = this.createRange();
19774 range.deleteContents();
19775 //alert(Sender.getAttribute('label'));
19777 range.insertNode(this.doc.createTextNode(txt));
19783 * Executes a Midas editor command on the editor document and performs necessary focus and
19784 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19785 * @param {String} cmd The Midas command
19786 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19788 relayCmd : function(cmd, value){
19790 this.execCmd(cmd, value);
19791 this.owner.fireEvent('editorevent', this);
19792 //this.updateToolbar();
19793 this.owner.deferFocus();
19797 * Executes a Midas editor command directly on the editor document.
19798 * For visual commands, you should use {@link #relayCmd} instead.
19799 * <b>This should only be called after the editor is initialized.</b>
19800 * @param {String} cmd The Midas command
19801 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19803 execCmd : function(cmd, value){
19804 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19811 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19813 * @param {String} text | dom node..
19815 insertAtCursor : function(text)
19820 if(!this.activated){
19826 var r = this.doc.selection.createRange();
19837 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19841 // from jquery ui (MIT licenced)
19843 var win = this.win;
19845 if (win.getSelection && win.getSelection().getRangeAt) {
19846 range = win.getSelection().getRangeAt(0);
19847 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19848 range.insertNode(node);
19849 } else if (win.document.selection && win.document.selection.createRange) {
19850 // no firefox support
19851 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19852 win.document.selection.createRange().pasteHTML(txt);
19854 // no firefox support
19855 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19856 this.execCmd('InsertHTML', txt);
19865 mozKeyPress : function(e){
19867 var c = e.getCharCode(), cmd;
19870 c = String.fromCharCode(c).toLowerCase();
19884 this.cleanUpPaste.defer(100, this);
19892 e.preventDefault();
19900 fixKeys : function(){ // load time branching for fastest keydown performance
19902 return function(e){
19903 var k = e.getKey(), r;
19906 r = this.doc.selection.createRange();
19909 r.pasteHTML('    ');
19916 r = this.doc.selection.createRange();
19918 var target = r.parentElement();
19919 if(!target || target.tagName.toLowerCase() != 'li'){
19921 r.pasteHTML('<br />');
19927 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19928 this.cleanUpPaste.defer(100, this);
19934 }else if(Roo.isOpera){
19935 return function(e){
19936 var k = e.getKey();
19940 this.execCmd('InsertHTML','    ');
19943 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19944 this.cleanUpPaste.defer(100, this);
19949 }else if(Roo.isSafari){
19950 return function(e){
19951 var k = e.getKey();
19955 this.execCmd('InsertText','\t');
19959 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19960 this.cleanUpPaste.defer(100, this);
19968 getAllAncestors: function()
19970 var p = this.getSelectedNode();
19973 a.push(p); // push blank onto stack..
19974 p = this.getParentElement();
19978 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19982 a.push(this.doc.body);
19986 lastSelNode : false,
19989 getSelection : function()
19991 this.assignDocWin();
19992 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19995 getSelectedNode: function()
19997 // this may only work on Gecko!!!
19999 // should we cache this!!!!
20004 var range = this.createRange(this.getSelection()).cloneRange();
20007 var parent = range.parentElement();
20009 var testRange = range.duplicate();
20010 testRange.moveToElementText(parent);
20011 if (testRange.inRange(range)) {
20014 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20017 parent = parent.parentElement;
20022 // is ancestor a text element.
20023 var ac = range.commonAncestorContainer;
20024 if (ac.nodeType == 3) {
20025 ac = ac.parentNode;
20028 var ar = ac.childNodes;
20031 var other_nodes = [];
20032 var has_other_nodes = false;
20033 for (var i=0;i<ar.length;i++) {
20034 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20037 // fullly contained node.
20039 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20044 // probably selected..
20045 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20046 other_nodes.push(ar[i]);
20050 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20055 has_other_nodes = true;
20057 if (!nodes.length && other_nodes.length) {
20058 nodes= other_nodes;
20060 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20066 createRange: function(sel)
20068 // this has strange effects when using with
20069 // top toolbar - not sure if it's a great idea.
20070 //this.editor.contentWindow.focus();
20071 if (typeof sel != "undefined") {
20073 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20075 return this.doc.createRange();
20078 return this.doc.createRange();
20081 getParentElement: function()
20084 this.assignDocWin();
20085 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20087 var range = this.createRange(sel);
20090 var p = range.commonAncestorContainer;
20091 while (p.nodeType == 3) { // text node
20102 * Range intersection.. the hard stuff...
20106 * [ -- selected range --- ]
20110 * if end is before start or hits it. fail.
20111 * if start is after end or hits it fail.
20113 * if either hits (but other is outside. - then it's not
20119 // @see http://www.thismuchiknow.co.uk/?p=64.
20120 rangeIntersectsNode : function(range, node)
20122 var nodeRange = node.ownerDocument.createRange();
20124 nodeRange.selectNode(node);
20126 nodeRange.selectNodeContents(node);
20129 var rangeStartRange = range.cloneRange();
20130 rangeStartRange.collapse(true);
20132 var rangeEndRange = range.cloneRange();
20133 rangeEndRange.collapse(false);
20135 var nodeStartRange = nodeRange.cloneRange();
20136 nodeStartRange.collapse(true);
20138 var nodeEndRange = nodeRange.cloneRange();
20139 nodeEndRange.collapse(false);
20141 return rangeStartRange.compareBoundaryPoints(
20142 Range.START_TO_START, nodeEndRange) == -1 &&
20143 rangeEndRange.compareBoundaryPoints(
20144 Range.START_TO_START, nodeStartRange) == 1;
20148 rangeCompareNode : function(range, node)
20150 var nodeRange = node.ownerDocument.createRange();
20152 nodeRange.selectNode(node);
20154 nodeRange.selectNodeContents(node);
20158 range.collapse(true);
20160 nodeRange.collapse(true);
20162 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20163 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20165 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20167 var nodeIsBefore = ss == 1;
20168 var nodeIsAfter = ee == -1;
20170 if (nodeIsBefore && nodeIsAfter) {
20173 if (!nodeIsBefore && nodeIsAfter) {
20174 return 1; //right trailed.
20177 if (nodeIsBefore && !nodeIsAfter) {
20178 return 2; // left trailed.
20184 // private? - in a new class?
20185 cleanUpPaste : function()
20187 // cleans up the whole document..
20188 Roo.log('cleanuppaste');
20190 this.cleanUpChildren(this.doc.body);
20191 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20192 if (clean != this.doc.body.innerHTML) {
20193 this.doc.body.innerHTML = clean;
20198 cleanWordChars : function(input) {// change the chars to hex code
20199 var he = Roo.HtmlEditorCore;
20201 var output = input;
20202 Roo.each(he.swapCodes, function(sw) {
20203 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20205 output = output.replace(swapper, sw[1]);
20212 cleanUpChildren : function (n)
20214 if (!n.childNodes.length) {
20217 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20218 this.cleanUpChild(n.childNodes[i]);
20225 cleanUpChild : function (node)
20228 //console.log(node);
20229 if (node.nodeName == "#text") {
20230 // clean up silly Windows -- stuff?
20233 if (node.nodeName == "#comment") {
20234 node.parentNode.removeChild(node);
20235 // clean up silly Windows -- stuff?
20238 var lcname = node.tagName.toLowerCase();
20239 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20240 // whitelist of tags..
20242 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20244 node.parentNode.removeChild(node);
20249 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20251 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20252 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20254 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20255 // remove_keep_children = true;
20258 if (remove_keep_children) {
20259 this.cleanUpChildren(node);
20260 // inserts everything just before this node...
20261 while (node.childNodes.length) {
20262 var cn = node.childNodes[0];
20263 node.removeChild(cn);
20264 node.parentNode.insertBefore(cn, node);
20266 node.parentNode.removeChild(node);
20270 if (!node.attributes || !node.attributes.length) {
20271 this.cleanUpChildren(node);
20275 function cleanAttr(n,v)
20278 if (v.match(/^\./) || v.match(/^\//)) {
20281 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20284 if (v.match(/^#/)) {
20287 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20288 node.removeAttribute(n);
20292 var cwhite = this.cwhite;
20293 var cblack = this.cblack;
20295 function cleanStyle(n,v)
20297 if (v.match(/expression/)) { //XSS?? should we even bother..
20298 node.removeAttribute(n);
20302 var parts = v.split(/;/);
20305 Roo.each(parts, function(p) {
20306 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20310 var l = p.split(':').shift().replace(/\s+/g,'');
20311 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20313 if ( cwhite.length && cblack.indexOf(l) > -1) {
20314 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20315 //node.removeAttribute(n);
20319 // only allow 'c whitelisted system attributes'
20320 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20321 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20322 //node.removeAttribute(n);
20332 if (clean.length) {
20333 node.setAttribute(n, clean.join(';'));
20335 node.removeAttribute(n);
20341 for (var i = node.attributes.length-1; i > -1 ; i--) {
20342 var a = node.attributes[i];
20345 if (a.name.toLowerCase().substr(0,2)=='on') {
20346 node.removeAttribute(a.name);
20349 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20350 node.removeAttribute(a.name);
20353 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20354 cleanAttr(a.name,a.value); // fixme..
20357 if (a.name == 'style') {
20358 cleanStyle(a.name,a.value);
20361 /// clean up MS crap..
20362 // tecnically this should be a list of valid class'es..
20365 if (a.name == 'class') {
20366 if (a.value.match(/^Mso/)) {
20367 node.className = '';
20370 if (a.value.match(/body/)) {
20371 node.className = '';
20382 this.cleanUpChildren(node);
20388 * Clean up MS wordisms...
20390 cleanWord : function(node)
20395 this.cleanWord(this.doc.body);
20398 if (node.nodeName == "#text") {
20399 // clean up silly Windows -- stuff?
20402 if (node.nodeName == "#comment") {
20403 node.parentNode.removeChild(node);
20404 // clean up silly Windows -- stuff?
20408 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20409 node.parentNode.removeChild(node);
20413 // remove - but keep children..
20414 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20415 while (node.childNodes.length) {
20416 var cn = node.childNodes[0];
20417 node.removeChild(cn);
20418 node.parentNode.insertBefore(cn, node);
20420 node.parentNode.removeChild(node);
20421 this.iterateChildren(node, this.cleanWord);
20425 if (node.className.length) {
20427 var cn = node.className.split(/\W+/);
20429 Roo.each(cn, function(cls) {
20430 if (cls.match(/Mso[a-zA-Z]+/)) {
20435 node.className = cna.length ? cna.join(' ') : '';
20437 node.removeAttribute("class");
20441 if (node.hasAttribute("lang")) {
20442 node.removeAttribute("lang");
20445 if (node.hasAttribute("style")) {
20447 var styles = node.getAttribute("style").split(";");
20449 Roo.each(styles, function(s) {
20450 if (!s.match(/:/)) {
20453 var kv = s.split(":");
20454 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20457 // what ever is left... we allow.
20460 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20461 if (!nstyle.length) {
20462 node.removeAttribute('style');
20465 this.iterateChildren(node, this.cleanWord);
20471 * iterateChildren of a Node, calling fn each time, using this as the scole..
20472 * @param {DomNode} node node to iterate children of.
20473 * @param {Function} fn method of this class to call on each item.
20475 iterateChildren : function(node, fn)
20477 if (!node.childNodes.length) {
20480 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20481 fn.call(this, node.childNodes[i])
20487 * cleanTableWidths.
20489 * Quite often pasting from word etc.. results in tables with column and widths.
20490 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20493 cleanTableWidths : function(node)
20498 this.cleanTableWidths(this.doc.body);
20503 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20506 Roo.log(node.tagName);
20507 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20508 this.iterateChildren(node, this.cleanTableWidths);
20511 if (node.hasAttribute('width')) {
20512 node.removeAttribute('width');
20516 if (node.hasAttribute("style")) {
20519 var styles = node.getAttribute("style").split(";");
20521 Roo.each(styles, function(s) {
20522 if (!s.match(/:/)) {
20525 var kv = s.split(":");
20526 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20529 // what ever is left... we allow.
20532 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20533 if (!nstyle.length) {
20534 node.removeAttribute('style');
20538 this.iterateChildren(node, this.cleanTableWidths);
20546 domToHTML : function(currentElement, depth, nopadtext) {
20548 depth = depth || 0;
20549 nopadtext = nopadtext || false;
20551 if (!currentElement) {
20552 return this.domToHTML(this.doc.body);
20555 //Roo.log(currentElement);
20557 var allText = false;
20558 var nodeName = currentElement.nodeName;
20559 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20561 if (nodeName == '#text') {
20563 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20568 if (nodeName != 'BODY') {
20571 // Prints the node tagName, such as <A>, <IMG>, etc
20574 for(i = 0; i < currentElement.attributes.length;i++) {
20576 var aname = currentElement.attributes.item(i).name;
20577 if (!currentElement.attributes.item(i).value.length) {
20580 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20583 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20592 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20595 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20600 // Traverse the tree
20602 var currentElementChild = currentElement.childNodes.item(i);
20603 var allText = true;
20604 var innerHTML = '';
20606 while (currentElementChild) {
20607 // Formatting code (indent the tree so it looks nice on the screen)
20608 var nopad = nopadtext;
20609 if (lastnode == 'SPAN') {
20613 if (currentElementChild.nodeName == '#text') {
20614 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20615 toadd = nopadtext ? toadd : toadd.trim();
20616 if (!nopad && toadd.length > 80) {
20617 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20619 innerHTML += toadd;
20622 currentElementChild = currentElement.childNodes.item(i);
20628 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20630 // Recursively traverse the tree structure of the child node
20631 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20632 lastnode = currentElementChild.nodeName;
20634 currentElementChild=currentElement.childNodes.item(i);
20640 // The remaining code is mostly for formatting the tree
20641 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20646 ret+= "</"+tagName+">";
20652 applyBlacklists : function()
20654 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20655 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20659 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20660 if (b.indexOf(tag) > -1) {
20663 this.white.push(tag);
20667 Roo.each(w, function(tag) {
20668 if (b.indexOf(tag) > -1) {
20671 if (this.white.indexOf(tag) > -1) {
20674 this.white.push(tag);
20679 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20680 if (w.indexOf(tag) > -1) {
20683 this.black.push(tag);
20687 Roo.each(b, function(tag) {
20688 if (w.indexOf(tag) > -1) {
20691 if (this.black.indexOf(tag) > -1) {
20694 this.black.push(tag);
20699 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20700 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20704 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20705 if (b.indexOf(tag) > -1) {
20708 this.cwhite.push(tag);
20712 Roo.each(w, function(tag) {
20713 if (b.indexOf(tag) > -1) {
20716 if (this.cwhite.indexOf(tag) > -1) {
20719 this.cwhite.push(tag);
20724 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20725 if (w.indexOf(tag) > -1) {
20728 this.cblack.push(tag);
20732 Roo.each(b, function(tag) {
20733 if (w.indexOf(tag) > -1) {
20736 if (this.cblack.indexOf(tag) > -1) {
20739 this.cblack.push(tag);
20744 setStylesheets : function(stylesheets)
20746 if(typeof(stylesheets) == 'string'){
20747 Roo.get(this.iframe.contentDocument.head).createChild({
20749 rel : 'stylesheet',
20758 Roo.each(stylesheets, function(s) {
20763 Roo.get(_this.iframe.contentDocument.head).createChild({
20765 rel : 'stylesheet',
20774 removeStylesheets : function()
20778 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20783 // hide stuff that is not compatible
20797 * @event specialkey
20801 * @cfg {String} fieldClass @hide
20804 * @cfg {String} focusClass @hide
20807 * @cfg {String} autoCreate @hide
20810 * @cfg {String} inputType @hide
20813 * @cfg {String} invalidClass @hide
20816 * @cfg {String} invalidText @hide
20819 * @cfg {String} msgFx @hide
20822 * @cfg {String} validateOnBlur @hide
20826 Roo.HtmlEditorCore.white = [
20827 'area', 'br', 'img', 'input', 'hr', 'wbr',
20829 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20830 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20831 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20832 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20833 'table', 'ul', 'xmp',
20835 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20838 'dir', 'menu', 'ol', 'ul', 'dl',
20844 Roo.HtmlEditorCore.black = [
20845 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20847 'base', 'basefont', 'bgsound', 'blink', 'body',
20848 'frame', 'frameset', 'head', 'html', 'ilayer',
20849 'iframe', 'layer', 'link', 'meta', 'object',
20850 'script', 'style' ,'title', 'xml' // clean later..
20852 Roo.HtmlEditorCore.clean = [
20853 'script', 'style', 'title', 'xml'
20855 Roo.HtmlEditorCore.remove = [
20860 Roo.HtmlEditorCore.ablack = [
20864 Roo.HtmlEditorCore.aclean = [
20865 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20869 Roo.HtmlEditorCore.pwhite= [
20870 'http', 'https', 'mailto'
20873 // white listed style attributes.
20874 Roo.HtmlEditorCore.cwhite= [
20875 // 'text-align', /// default is to allow most things..
20881 // black listed style attributes.
20882 Roo.HtmlEditorCore.cblack= [
20883 // 'font-size' -- this can be set by the project
20887 Roo.HtmlEditorCore.swapCodes =[
20906 * @class Roo.bootstrap.HtmlEditor
20907 * @extends Roo.bootstrap.TextArea
20908 * Bootstrap HtmlEditor class
20911 * Create a new HtmlEditor
20912 * @param {Object} config The config object
20915 Roo.bootstrap.HtmlEditor = function(config){
20916 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20917 if (!this.toolbars) {
20918 this.toolbars = [];
20920 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20923 * @event initialize
20924 * Fires when the editor is fully initialized (including the iframe)
20925 * @param {HtmlEditor} this
20930 * Fires when the editor is first receives the focus. Any insertion must wait
20931 * until after this event.
20932 * @param {HtmlEditor} this
20936 * @event beforesync
20937 * Fires before the textarea is updated with content from the editor iframe. Return false
20938 * to cancel the sync.
20939 * @param {HtmlEditor} this
20940 * @param {String} html
20944 * @event beforepush
20945 * Fires before the iframe editor is updated with content from the textarea. Return false
20946 * to cancel the push.
20947 * @param {HtmlEditor} this
20948 * @param {String} html
20953 * Fires when the textarea is updated with content from the editor iframe.
20954 * @param {HtmlEditor} this
20955 * @param {String} html
20960 * Fires when the iframe editor is updated with content from the textarea.
20961 * @param {HtmlEditor} this
20962 * @param {String} html
20966 * @event editmodechange
20967 * Fires when the editor switches edit modes
20968 * @param {HtmlEditor} this
20969 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20971 editmodechange: true,
20973 * @event editorevent
20974 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20975 * @param {HtmlEditor} this
20979 * @event firstfocus
20980 * Fires when on first focus - needed by toolbars..
20981 * @param {HtmlEditor} this
20986 * Auto save the htmlEditor value as a file into Events
20987 * @param {HtmlEditor} this
20991 * @event savedpreview
20992 * preview the saved version of htmlEditor
20993 * @param {HtmlEditor} this
21000 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21004 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21009 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21014 * @cfg {Number} height (in pixels)
21018 * @cfg {Number} width (in pixels)
21023 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21026 stylesheets: false,
21031 // private properties
21032 validationEvent : false,
21034 initialized : false,
21037 onFocus : Roo.emptyFn,
21039 hideMode:'offsets',
21042 tbContainer : false,
21044 toolbarContainer :function() {
21045 return this.wrap.select('.x-html-editor-tb',true).first();
21049 * Protected method that will not generally be called directly. It
21050 * is called when the editor creates its toolbar. Override this method if you need to
21051 * add custom toolbar buttons.
21052 * @param {HtmlEditor} editor
21054 createToolbar : function(){
21056 Roo.log("create toolbars");
21058 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21059 this.toolbars[0].render(this.toolbarContainer());
21063 // if (!editor.toolbars || !editor.toolbars.length) {
21064 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21067 // for (var i =0 ; i < editor.toolbars.length;i++) {
21068 // editor.toolbars[i] = Roo.factory(
21069 // typeof(editor.toolbars[i]) == 'string' ?
21070 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21071 // Roo.bootstrap.HtmlEditor);
21072 // editor.toolbars[i].init(editor);
21078 onRender : function(ct, position)
21080 // Roo.log("Call onRender: " + this.xtype);
21082 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21084 this.wrap = this.inputEl().wrap({
21085 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21088 this.editorcore.onRender(ct, position);
21090 if (this.resizable) {
21091 this.resizeEl = new Roo.Resizable(this.wrap, {
21095 minHeight : this.height,
21096 height: this.height,
21097 handles : this.resizable,
21100 resize : function(r, w, h) {
21101 _t.onResize(w,h); // -something
21107 this.createToolbar(this);
21110 if(!this.width && this.resizable){
21111 this.setSize(this.wrap.getSize());
21113 if (this.resizeEl) {
21114 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21115 // should trigger onReize..
21121 onResize : function(w, h)
21123 Roo.log('resize: ' +w + ',' + h );
21124 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21128 if(this.inputEl() ){
21129 if(typeof w == 'number'){
21130 var aw = w - this.wrap.getFrameWidth('lr');
21131 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21134 if(typeof h == 'number'){
21135 var tbh = -11; // fixme it needs to tool bar size!
21136 for (var i =0; i < this.toolbars.length;i++) {
21137 // fixme - ask toolbars for heights?
21138 tbh += this.toolbars[i].el.getHeight();
21139 //if (this.toolbars[i].footer) {
21140 // tbh += this.toolbars[i].footer.el.getHeight();
21148 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21149 ah -= 5; // knock a few pixes off for look..
21150 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21154 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21155 this.editorcore.onResize(ew,eh);
21160 * Toggles the editor between standard and source edit mode.
21161 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21163 toggleSourceEdit : function(sourceEditMode)
21165 this.editorcore.toggleSourceEdit(sourceEditMode);
21167 if(this.editorcore.sourceEditMode){
21168 Roo.log('editor - showing textarea');
21171 // Roo.log(this.syncValue());
21173 this.inputEl().removeClass(['hide', 'x-hidden']);
21174 this.inputEl().dom.removeAttribute('tabIndex');
21175 this.inputEl().focus();
21177 Roo.log('editor - hiding textarea');
21179 // Roo.log(this.pushValue());
21182 this.inputEl().addClass(['hide', 'x-hidden']);
21183 this.inputEl().dom.setAttribute('tabIndex', -1);
21184 //this.deferFocus();
21187 if(this.resizable){
21188 this.setSize(this.wrap.getSize());
21191 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21194 // private (for BoxComponent)
21195 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21197 // private (for BoxComponent)
21198 getResizeEl : function(){
21202 // private (for BoxComponent)
21203 getPositionEl : function(){
21208 initEvents : function(){
21209 this.originalValue = this.getValue();
21213 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21216 // markInvalid : Roo.emptyFn,
21218 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21221 // clearInvalid : Roo.emptyFn,
21223 setValue : function(v){
21224 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21225 this.editorcore.pushValue();
21230 deferFocus : function(){
21231 this.focus.defer(10, this);
21235 focus : function(){
21236 this.editorcore.focus();
21242 onDestroy : function(){
21248 for (var i =0; i < this.toolbars.length;i++) {
21249 // fixme - ask toolbars for heights?
21250 this.toolbars[i].onDestroy();
21253 this.wrap.dom.innerHTML = '';
21254 this.wrap.remove();
21259 onFirstFocus : function(){
21260 //Roo.log("onFirstFocus");
21261 this.editorcore.onFirstFocus();
21262 for (var i =0; i < this.toolbars.length;i++) {
21263 this.toolbars[i].onFirstFocus();
21269 syncValue : function()
21271 this.editorcore.syncValue();
21274 pushValue : function()
21276 this.editorcore.pushValue();
21280 // hide stuff that is not compatible
21294 * @event specialkey
21298 * @cfg {String} fieldClass @hide
21301 * @cfg {String} focusClass @hide
21304 * @cfg {String} autoCreate @hide
21307 * @cfg {String} inputType @hide
21310 * @cfg {String} invalidClass @hide
21313 * @cfg {String} invalidText @hide
21316 * @cfg {String} msgFx @hide
21319 * @cfg {String} validateOnBlur @hide
21328 Roo.namespace('Roo.bootstrap.htmleditor');
21330 * @class Roo.bootstrap.HtmlEditorToolbar1
21335 new Roo.bootstrap.HtmlEditor({
21338 new Roo.bootstrap.HtmlEditorToolbar1({
21339 disable : { fonts: 1 , format: 1, ..., ... , ...],
21345 * @cfg {Object} disable List of elements to disable..
21346 * @cfg {Array} btns List of additional buttons.
21350 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21353 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21356 Roo.apply(this, config);
21358 // default disabled, based on 'good practice'..
21359 this.disable = this.disable || {};
21360 Roo.applyIf(this.disable, {
21363 specialElements : true
21365 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21367 this.editor = config.editor;
21368 this.editorcore = config.editor.editorcore;
21370 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21372 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21373 // dont call parent... till later.
21375 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21380 editorcore : false,
21385 "h1","h2","h3","h4","h5","h6",
21387 "abbr", "acronym", "address", "cite", "samp", "var",
21391 onRender : function(ct, position)
21393 // Roo.log("Call onRender: " + this.xtype);
21395 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21397 this.el.dom.style.marginBottom = '0';
21399 var editorcore = this.editorcore;
21400 var editor= this.editor;
21403 var btn = function(id,cmd , toggle, handler){
21405 var event = toggle ? 'toggle' : 'click';
21410 xns: Roo.bootstrap,
21413 enableToggle:toggle !== false,
21415 pressed : toggle ? false : null,
21418 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21419 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21428 xns: Roo.bootstrap,
21429 glyphicon : 'font',
21433 xns: Roo.bootstrap,
21437 Roo.each(this.formats, function(f) {
21438 style.menu.items.push({
21440 xns: Roo.bootstrap,
21441 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21446 editorcore.insertTag(this.tagname);
21453 children.push(style);
21456 btn('bold',false,true);
21457 btn('italic',false,true);
21458 btn('align-left', 'justifyleft',true);
21459 btn('align-center', 'justifycenter',true);
21460 btn('align-right' , 'justifyright',true);
21461 btn('link', false, false, function(btn) {
21462 //Roo.log("create link?");
21463 var url = prompt(this.createLinkText, this.defaultLinkValue);
21464 if(url && url != 'http:/'+'/'){
21465 this.editorcore.relayCmd('createlink', url);
21468 btn('list','insertunorderedlist',true);
21469 btn('pencil', false,true, function(btn){
21472 this.toggleSourceEdit(btn.pressed);
21478 xns: Roo.bootstrap,
21483 xns: Roo.bootstrap,
21488 cog.menu.items.push({
21490 xns: Roo.bootstrap,
21491 html : Clean styles,
21496 editorcore.insertTag(this.tagname);
21505 this.xtype = 'NavSimplebar';
21507 for(var i=0;i< children.length;i++) {
21509 this.buttons.add(this.addxtypeChild(children[i]));
21513 editor.on('editorevent', this.updateToolbar, this);
21515 onBtnClick : function(id)
21517 this.editorcore.relayCmd(id);
21518 this.editorcore.focus();
21522 * Protected method that will not generally be called directly. It triggers
21523 * a toolbar update by reading the markup state of the current selection in the editor.
21525 updateToolbar: function(){
21527 if(!this.editorcore.activated){
21528 this.editor.onFirstFocus(); // is this neeed?
21532 var btns = this.buttons;
21533 var doc = this.editorcore.doc;
21534 btns.get('bold').setActive(doc.queryCommandState('bold'));
21535 btns.get('italic').setActive(doc.queryCommandState('italic'));
21536 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21538 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21539 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21540 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21542 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21543 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21546 var ans = this.editorcore.getAllAncestors();
21547 if (this.formatCombo) {
21550 var store = this.formatCombo.store;
21551 this.formatCombo.setValue("");
21552 for (var i =0; i < ans.length;i++) {
21553 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21555 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21563 // hides menus... - so this cant be on a menu...
21564 Roo.bootstrap.MenuMgr.hideAll();
21566 Roo.bootstrap.MenuMgr.hideAll();
21567 //this.editorsyncValue();
21569 onFirstFocus: function() {
21570 this.buttons.each(function(item){
21574 toggleSourceEdit : function(sourceEditMode){
21577 if(sourceEditMode){
21578 Roo.log("disabling buttons");
21579 this.buttons.each( function(item){
21580 if(item.cmd != 'pencil'){
21586 Roo.log("enabling buttons");
21587 if(this.editorcore.initialized){
21588 this.buttons.each( function(item){
21594 Roo.log("calling toggole on editor");
21595 // tell the editor that it's been pressed..
21596 this.editor.toggleSourceEdit(sourceEditMode);
21606 * @class Roo.bootstrap.Table.AbstractSelectionModel
21607 * @extends Roo.util.Observable
21608 * Abstract base class for grid SelectionModels. It provides the interface that should be
21609 * implemented by descendant classes. This class should not be directly instantiated.
21612 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21613 this.locked = false;
21614 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21618 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21619 /** @ignore Called by the grid automatically. Do not call directly. */
21620 init : function(grid){
21626 * Locks the selections.
21629 this.locked = true;
21633 * Unlocks the selections.
21635 unlock : function(){
21636 this.locked = false;
21640 * Returns true if the selections are locked.
21641 * @return {Boolean}
21643 isLocked : function(){
21644 return this.locked;
21648 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21649 * @class Roo.bootstrap.Table.RowSelectionModel
21650 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21651 * It supports multiple selections and keyboard selection/navigation.
21653 * @param {Object} config
21656 Roo.bootstrap.Table.RowSelectionModel = function(config){
21657 Roo.apply(this, config);
21658 this.selections = new Roo.util.MixedCollection(false, function(o){
21663 this.lastActive = false;
21667 * @event selectionchange
21668 * Fires when the selection changes
21669 * @param {SelectionModel} this
21671 "selectionchange" : true,
21673 * @event afterselectionchange
21674 * Fires after the selection changes (eg. by key press or clicking)
21675 * @param {SelectionModel} this
21677 "afterselectionchange" : true,
21679 * @event beforerowselect
21680 * Fires when a row is selected being selected, return false to cancel.
21681 * @param {SelectionModel} this
21682 * @param {Number} rowIndex The selected index
21683 * @param {Boolean} keepExisting False if other selections will be cleared
21685 "beforerowselect" : true,
21688 * Fires when a row is selected.
21689 * @param {SelectionModel} this
21690 * @param {Number} rowIndex The selected index
21691 * @param {Roo.data.Record} r The record
21693 "rowselect" : true,
21695 * @event rowdeselect
21696 * Fires when a row is deselected.
21697 * @param {SelectionModel} this
21698 * @param {Number} rowIndex The selected index
21700 "rowdeselect" : true
21702 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21703 this.locked = false;
21706 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21708 * @cfg {Boolean} singleSelect
21709 * True to allow selection of only one row at a time (defaults to false)
21711 singleSelect : false,
21714 initEvents : function(){
21716 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21717 this.grid.on("mousedown", this.handleMouseDown, this);
21718 }else{ // allow click to work like normal
21719 this.grid.on("rowclick", this.handleDragableRowClick, this);
21722 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21723 "up" : function(e){
21725 this.selectPrevious(e.shiftKey);
21726 }else if(this.last !== false && this.lastActive !== false){
21727 var last = this.last;
21728 this.selectRange(this.last, this.lastActive-1);
21729 this.grid.getView().focusRow(this.lastActive);
21730 if(last !== false){
21734 this.selectFirstRow();
21736 this.fireEvent("afterselectionchange", this);
21738 "down" : function(e){
21740 this.selectNext(e.shiftKey);
21741 }else if(this.last !== false && this.lastActive !== false){
21742 var last = this.last;
21743 this.selectRange(this.last, this.lastActive+1);
21744 this.grid.getView().focusRow(this.lastActive);
21745 if(last !== false){
21749 this.selectFirstRow();
21751 this.fireEvent("afterselectionchange", this);
21756 var view = this.grid.view;
21757 view.on("refresh", this.onRefresh, this);
21758 view.on("rowupdated", this.onRowUpdated, this);
21759 view.on("rowremoved", this.onRemove, this);
21763 onRefresh : function(){
21764 var ds = this.grid.dataSource, i, v = this.grid.view;
21765 var s = this.selections;
21766 s.each(function(r){
21767 if((i = ds.indexOfId(r.id)) != -1){
21776 onRemove : function(v, index, r){
21777 this.selections.remove(r);
21781 onRowUpdated : function(v, index, r){
21782 if(this.isSelected(r)){
21783 v.onRowSelect(index);
21789 * @param {Array} records The records to select
21790 * @param {Boolean} keepExisting (optional) True to keep existing selections
21792 selectRecords : function(records, keepExisting){
21794 this.clearSelections();
21796 var ds = this.grid.dataSource;
21797 for(var i = 0, len = records.length; i < len; i++){
21798 this.selectRow(ds.indexOf(records[i]), true);
21803 * Gets the number of selected rows.
21806 getCount : function(){
21807 return this.selections.length;
21811 * Selects the first row in the grid.
21813 selectFirstRow : function(){
21818 * Select the last row.
21819 * @param {Boolean} keepExisting (optional) True to keep existing selections
21821 selectLastRow : function(keepExisting){
21822 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21826 * Selects the row immediately following the last selected row.
21827 * @param {Boolean} keepExisting (optional) True to keep existing selections
21829 selectNext : function(keepExisting){
21830 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21831 this.selectRow(this.last+1, keepExisting);
21832 this.grid.getView().focusRow(this.last);
21837 * Selects the row that precedes the last selected row.
21838 * @param {Boolean} keepExisting (optional) True to keep existing selections
21840 selectPrevious : function(keepExisting){
21842 this.selectRow(this.last-1, keepExisting);
21843 this.grid.getView().focusRow(this.last);
21848 * Returns the selected records
21849 * @return {Array} Array of selected records
21851 getSelections : function(){
21852 return [].concat(this.selections.items);
21856 * Returns the first selected record.
21859 getSelected : function(){
21860 return this.selections.itemAt(0);
21865 * Clears all selections.
21867 clearSelections : function(fast){
21872 var ds = this.grid.dataSource;
21873 var s = this.selections;
21874 s.each(function(r){
21875 this.deselectRow(ds.indexOfId(r.id));
21879 this.selections.clear();
21886 * Selects all rows.
21888 selectAll : function(){
21892 this.selections.clear();
21893 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21894 this.selectRow(i, true);
21899 * Returns True if there is a selection.
21900 * @return {Boolean}
21902 hasSelection : function(){
21903 return this.selections.length > 0;
21907 * Returns True if the specified row is selected.
21908 * @param {Number/Record} record The record or index of the record to check
21909 * @return {Boolean}
21911 isSelected : function(index){
21912 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21913 return (r && this.selections.key(r.id) ? true : false);
21917 * Returns True if the specified record id is selected.
21918 * @param {String} id The id of record to check
21919 * @return {Boolean}
21921 isIdSelected : function(id){
21922 return (this.selections.key(id) ? true : false);
21926 handleMouseDown : function(e, t){
21927 var view = this.grid.getView(), rowIndex;
21928 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21931 if(e.shiftKey && this.last !== false){
21932 var last = this.last;
21933 this.selectRange(last, rowIndex, e.ctrlKey);
21934 this.last = last; // reset the last
21935 view.focusRow(rowIndex);
21937 var isSelected = this.isSelected(rowIndex);
21938 if(e.button !== 0 && isSelected){
21939 view.focusRow(rowIndex);
21940 }else if(e.ctrlKey && isSelected){
21941 this.deselectRow(rowIndex);
21942 }else if(!isSelected){
21943 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21944 view.focusRow(rowIndex);
21947 this.fireEvent("afterselectionchange", this);
21950 handleDragableRowClick : function(grid, rowIndex, e)
21952 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21953 this.selectRow(rowIndex, false);
21954 grid.view.focusRow(rowIndex);
21955 this.fireEvent("afterselectionchange", this);
21960 * Selects multiple rows.
21961 * @param {Array} rows Array of the indexes of the row to select
21962 * @param {Boolean} keepExisting (optional) True to keep existing selections
21964 selectRows : function(rows, keepExisting){
21966 this.clearSelections();
21968 for(var i = 0, len = rows.length; i < len; i++){
21969 this.selectRow(rows[i], true);
21974 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21975 * @param {Number} startRow The index of the first row in the range
21976 * @param {Number} endRow The index of the last row in the range
21977 * @param {Boolean} keepExisting (optional) True to retain existing selections
21979 selectRange : function(startRow, endRow, keepExisting){
21984 this.clearSelections();
21986 if(startRow <= endRow){
21987 for(var i = startRow; i <= endRow; i++){
21988 this.selectRow(i, true);
21991 for(var i = startRow; i >= endRow; i--){
21992 this.selectRow(i, true);
21998 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21999 * @param {Number} startRow The index of the first row in the range
22000 * @param {Number} endRow The index of the last row in the range
22002 deselectRange : function(startRow, endRow, preventViewNotify){
22006 for(var i = startRow; i <= endRow; i++){
22007 this.deselectRow(i, preventViewNotify);
22013 * @param {Number} row The index of the row to select
22014 * @param {Boolean} keepExisting (optional) True to keep existing selections
22016 selectRow : function(index, keepExisting, preventViewNotify){
22017 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22020 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22021 if(!keepExisting || this.singleSelect){
22022 this.clearSelections();
22024 var r = this.grid.dataSource.getAt(index);
22025 this.selections.add(r);
22026 this.last = this.lastActive = index;
22027 if(!preventViewNotify){
22028 this.grid.getView().onRowSelect(index);
22030 this.fireEvent("rowselect", this, index, r);
22031 this.fireEvent("selectionchange", this);
22037 * @param {Number} row The index of the row to deselect
22039 deselectRow : function(index, preventViewNotify){
22043 if(this.last == index){
22046 if(this.lastActive == index){
22047 this.lastActive = false;
22049 var r = this.grid.dataSource.getAt(index);
22050 this.selections.remove(r);
22051 if(!preventViewNotify){
22052 this.grid.getView().onRowDeselect(index);
22054 this.fireEvent("rowdeselect", this, index);
22055 this.fireEvent("selectionchange", this);
22059 restoreLast : function(){
22061 this.last = this._last;
22066 acceptsNav : function(row, col, cm){
22067 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22071 onEditorKey : function(field, e){
22072 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22077 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22079 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22081 }else if(k == e.ENTER && !e.ctrlKey){
22085 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22087 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22089 }else if(k == e.ESC){
22093 g.startEditing(newCell[0], newCell[1]);
22098 * Ext JS Library 1.1.1
22099 * Copyright(c) 2006-2007, Ext JS, LLC.
22101 * Originally Released Under LGPL - original licence link has changed is not relivant.
22104 * <script type="text/javascript">
22108 * @class Roo.bootstrap.PagingToolbar
22109 * @extends Roo.bootstrap.NavSimplebar
22110 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22112 * Create a new PagingToolbar
22113 * @param {Object} config The config object
22114 * @param {Roo.data.Store} store
22116 Roo.bootstrap.PagingToolbar = function(config)
22118 // old args format still supported... - xtype is prefered..
22119 // created from xtype...
22121 this.ds = config.dataSource;
22123 if (config.store && !this.ds) {
22124 this.store= Roo.factory(config.store, Roo.data);
22125 this.ds = this.store;
22126 this.ds.xmodule = this.xmodule || false;
22129 this.toolbarItems = [];
22130 if (config.items) {
22131 this.toolbarItems = config.items;
22134 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22139 this.bind(this.ds);
22142 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22146 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22148 * @cfg {Roo.data.Store} dataSource
22149 * The underlying data store providing the paged data
22152 * @cfg {String/HTMLElement/Element} container
22153 * container The id or element that will contain the toolbar
22156 * @cfg {Boolean} displayInfo
22157 * True to display the displayMsg (defaults to false)
22160 * @cfg {Number} pageSize
22161 * The number of records to display per page (defaults to 20)
22165 * @cfg {String} displayMsg
22166 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22168 displayMsg : 'Displaying {0} - {1} of {2}',
22170 * @cfg {String} emptyMsg
22171 * The message to display when no records are found (defaults to "No data to display")
22173 emptyMsg : 'No data to display',
22175 * Customizable piece of the default paging text (defaults to "Page")
22178 beforePageText : "Page",
22180 * Customizable piece of the default paging text (defaults to "of %0")
22183 afterPageText : "of {0}",
22185 * Customizable piece of the default paging text (defaults to "First Page")
22188 firstText : "First Page",
22190 * Customizable piece of the default paging text (defaults to "Previous Page")
22193 prevText : "Previous Page",
22195 * Customizable piece of the default paging text (defaults to "Next Page")
22198 nextText : "Next Page",
22200 * Customizable piece of the default paging text (defaults to "Last Page")
22203 lastText : "Last Page",
22205 * Customizable piece of the default paging text (defaults to "Refresh")
22208 refreshText : "Refresh",
22212 onRender : function(ct, position)
22214 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22215 this.navgroup.parentId = this.id;
22216 this.navgroup.onRender(this.el, null);
22217 // add the buttons to the navgroup
22219 if(this.displayInfo){
22220 Roo.log(this.el.select('ul.navbar-nav',true).first());
22221 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22222 this.displayEl = this.el.select('.x-paging-info', true).first();
22223 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22224 // this.displayEl = navel.el.select('span',true).first();
22230 Roo.each(_this.buttons, function(e){ // this might need to use render????
22231 Roo.factory(e).onRender(_this.el, null);
22235 Roo.each(_this.toolbarItems, function(e) {
22236 _this.navgroup.addItem(e);
22240 this.first = this.navgroup.addItem({
22241 tooltip: this.firstText,
22243 icon : 'fa fa-backward',
22245 preventDefault: true,
22246 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22249 this.prev = this.navgroup.addItem({
22250 tooltip: this.prevText,
22252 icon : 'fa fa-step-backward',
22254 preventDefault: true,
22255 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22257 //this.addSeparator();
22260 var field = this.navgroup.addItem( {
22262 cls : 'x-paging-position',
22264 html : this.beforePageText +
22265 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22266 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22269 this.field = field.el.select('input', true).first();
22270 this.field.on("keydown", this.onPagingKeydown, this);
22271 this.field.on("focus", function(){this.dom.select();});
22274 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22275 //this.field.setHeight(18);
22276 //this.addSeparator();
22277 this.next = this.navgroup.addItem({
22278 tooltip: this.nextText,
22280 html : ' <i class="fa fa-step-forward">',
22282 preventDefault: true,
22283 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22285 this.last = this.navgroup.addItem({
22286 tooltip: this.lastText,
22287 icon : 'fa fa-forward',
22290 preventDefault: true,
22291 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22293 //this.addSeparator();
22294 this.loading = this.navgroup.addItem({
22295 tooltip: this.refreshText,
22296 icon: 'fa fa-refresh',
22297 preventDefault: true,
22298 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22304 updateInfo : function(){
22305 if(this.displayEl){
22306 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22307 var msg = count == 0 ?
22311 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22313 this.displayEl.update(msg);
22318 onLoad : function(ds, r, o){
22319 this.cursor = o.params ? o.params.start : 0;
22320 var d = this.getPageData(),
22324 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22325 this.field.dom.value = ap;
22326 this.first.setDisabled(ap == 1);
22327 this.prev.setDisabled(ap == 1);
22328 this.next.setDisabled(ap == ps);
22329 this.last.setDisabled(ap == ps);
22330 this.loading.enable();
22335 getPageData : function(){
22336 var total = this.ds.getTotalCount();
22339 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22340 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22345 onLoadError : function(){
22346 this.loading.enable();
22350 onPagingKeydown : function(e){
22351 var k = e.getKey();
22352 var d = this.getPageData();
22354 var v = this.field.dom.value, pageNum;
22355 if(!v || isNaN(pageNum = parseInt(v, 10))){
22356 this.field.dom.value = d.activePage;
22359 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22360 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22363 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))
22365 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22366 this.field.dom.value = pageNum;
22367 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22370 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22372 var v = this.field.dom.value, pageNum;
22373 var increment = (e.shiftKey) ? 10 : 1;
22374 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22377 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22378 this.field.dom.value = d.activePage;
22381 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22383 this.field.dom.value = parseInt(v, 10) + increment;
22384 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22385 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22392 beforeLoad : function(){
22394 this.loading.disable();
22399 onClick : function(which){
22408 ds.load({params:{start: 0, limit: this.pageSize}});
22411 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22414 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22417 var total = ds.getTotalCount();
22418 var extra = total % this.pageSize;
22419 var lastStart = extra ? (total - extra) : total-this.pageSize;
22420 ds.load({params:{start: lastStart, limit: this.pageSize}});
22423 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22429 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22430 * @param {Roo.data.Store} store The data store to unbind
22432 unbind : function(ds){
22433 ds.un("beforeload", this.beforeLoad, this);
22434 ds.un("load", this.onLoad, this);
22435 ds.un("loadexception", this.onLoadError, this);
22436 ds.un("remove", this.updateInfo, this);
22437 ds.un("add", this.updateInfo, this);
22438 this.ds = undefined;
22442 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22443 * @param {Roo.data.Store} store The data store to bind
22445 bind : function(ds){
22446 ds.on("beforeload", this.beforeLoad, this);
22447 ds.on("load", this.onLoad, this);
22448 ds.on("loadexception", this.onLoadError, this);
22449 ds.on("remove", this.updateInfo, this);
22450 ds.on("add", this.updateInfo, this);
22461 * @class Roo.bootstrap.MessageBar
22462 * @extends Roo.bootstrap.Component
22463 * Bootstrap MessageBar class
22464 * @cfg {String} html contents of the MessageBar
22465 * @cfg {String} weight (info | success | warning | danger) default info
22466 * @cfg {String} beforeClass insert the bar before the given class
22467 * @cfg {Boolean} closable (true | false) default false
22468 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22471 * Create a new Element
22472 * @param {Object} config The config object
22475 Roo.bootstrap.MessageBar = function(config){
22476 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22479 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22485 beforeClass: 'bootstrap-sticky-wrap',
22487 getAutoCreate : function(){
22491 cls: 'alert alert-dismissable alert-' + this.weight,
22496 html: this.html || ''
22502 cfg.cls += ' alert-messages-fixed';
22516 onRender : function(ct, position)
22518 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22521 var cfg = Roo.apply({}, this.getAutoCreate());
22525 cfg.cls += ' ' + this.cls;
22528 cfg.style = this.style;
22530 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22532 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22535 this.el.select('>button.close').on('click', this.hide, this);
22541 if (!this.rendered) {
22547 this.fireEvent('show', this);
22553 if (!this.rendered) {
22559 this.fireEvent('hide', this);
22562 update : function()
22564 // var e = this.el.dom.firstChild;
22566 // if(this.closable){
22567 // e = e.nextSibling;
22570 // e.data = this.html || '';
22572 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22588 * @class Roo.bootstrap.Graph
22589 * @extends Roo.bootstrap.Component
22590 * Bootstrap Graph class
22594 @cfg {String} graphtype bar | vbar | pie
22595 @cfg {number} g_x coodinator | centre x (pie)
22596 @cfg {number} g_y coodinator | centre y (pie)
22597 @cfg {number} g_r radius (pie)
22598 @cfg {number} g_height height of the chart (respected by all elements in the set)
22599 @cfg {number} g_width width of the chart (respected by all elements in the set)
22600 @cfg {Object} title The title of the chart
22603 -opts (object) options for the chart
22605 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22606 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22608 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.
22609 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22611 o stretch (boolean)
22613 -opts (object) options for the pie
22616 o startAngle (number)
22617 o endAngle (number)
22621 * Create a new Input
22622 * @param {Object} config The config object
22625 Roo.bootstrap.Graph = function(config){
22626 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22632 * The img click event for the img.
22633 * @param {Roo.EventObject} e
22639 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22650 //g_colors: this.colors,
22657 getAutoCreate : function(){
22668 onRender : function(ct,position){
22669 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22670 this.raphael = Raphael(this.el.dom);
22672 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22673 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22674 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22675 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22677 r.text(160, 10, "Single Series Chart").attr(txtattr);
22678 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22679 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22680 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22682 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22683 r.barchart(330, 10, 300, 220, data1);
22684 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22685 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22688 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22689 // r.barchart(30, 30, 560, 250, xdata, {
22690 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22691 // axis : "0 0 1 1",
22692 // axisxlabels : xdata
22693 // //yvalues : cols,
22696 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22698 // this.load(null,xdata,{
22699 // axis : "0 0 1 1",
22700 // axisxlabels : xdata
22705 load : function(graphtype,xdata,opts){
22706 this.raphael.clear();
22708 graphtype = this.graphtype;
22713 var r = this.raphael,
22714 fin = function () {
22715 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22717 fout = function () {
22718 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22720 pfin = function() {
22721 this.sector.stop();
22722 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22725 this.label[0].stop();
22726 this.label[0].attr({ r: 7.5 });
22727 this.label[1].attr({ "font-weight": 800 });
22730 pfout = function() {
22731 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22734 this.label[0].animate({ r: 5 }, 500, "bounce");
22735 this.label[1].attr({ "font-weight": 400 });
22741 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22744 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22747 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22748 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22750 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22757 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22762 setTitle: function(o)
22767 initEvents: function() {
22770 this.el.on('click', this.onClick, this);
22774 onClick : function(e)
22776 Roo.log('img onclick');
22777 this.fireEvent('click', this, e);
22789 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22792 * @class Roo.bootstrap.dash.NumberBox
22793 * @extends Roo.bootstrap.Component
22794 * Bootstrap NumberBox class
22795 * @cfg {String} headline Box headline
22796 * @cfg {String} content Box content
22797 * @cfg {String} icon Box icon
22798 * @cfg {String} footer Footer text
22799 * @cfg {String} fhref Footer href
22802 * Create a new NumberBox
22803 * @param {Object} config The config object
22807 Roo.bootstrap.dash.NumberBox = function(config){
22808 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22812 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22821 getAutoCreate : function(){
22825 cls : 'small-box ',
22833 cls : 'roo-headline',
22834 html : this.headline
22838 cls : 'roo-content',
22839 html : this.content
22853 cls : 'ion ' + this.icon
22862 cls : 'small-box-footer',
22863 href : this.fhref || '#',
22867 cfg.cn.push(footer);
22874 onRender : function(ct,position){
22875 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22882 setHeadline: function (value)
22884 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22887 setFooter: function (value, href)
22889 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22892 this.el.select('a.small-box-footer',true).first().attr('href', href);
22897 setContent: function (value)
22899 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22902 initEvents: function()
22916 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22919 * @class Roo.bootstrap.dash.TabBox
22920 * @extends Roo.bootstrap.Component
22921 * Bootstrap TabBox class
22922 * @cfg {String} title Title of the TabBox
22923 * @cfg {String} icon Icon of the TabBox
22924 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22925 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22928 * Create a new TabBox
22929 * @param {Object} config The config object
22933 Roo.bootstrap.dash.TabBox = function(config){
22934 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22939 * When a pane is added
22940 * @param {Roo.bootstrap.dash.TabPane} pane
22944 * @event activatepane
22945 * When a pane is activated
22946 * @param {Roo.bootstrap.dash.TabPane} pane
22948 "activatepane" : true
22956 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22961 tabScrollable : false,
22963 getChildContainer : function()
22965 return this.el.select('.tab-content', true).first();
22968 getAutoCreate : function(){
22972 cls: 'pull-left header',
22980 cls: 'fa ' + this.icon
22986 cls: 'nav nav-tabs pull-right',
22992 if(this.tabScrollable){
22999 cls: 'nav nav-tabs pull-right',
23010 cls: 'nav-tabs-custom',
23015 cls: 'tab-content no-padding',
23023 initEvents : function()
23025 //Roo.log('add add pane handler');
23026 this.on('addpane', this.onAddPane, this);
23029 * Updates the box title
23030 * @param {String} html to set the title to.
23032 setTitle : function(value)
23034 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23036 onAddPane : function(pane)
23038 this.panes.push(pane);
23039 //Roo.log('addpane');
23041 // tabs are rendere left to right..
23042 if(!this.showtabs){
23046 var ctr = this.el.select('.nav-tabs', true).first();
23049 var existing = ctr.select('.nav-tab',true);
23050 var qty = existing.getCount();;
23053 var tab = ctr.createChild({
23055 cls : 'nav-tab' + (qty ? '' : ' active'),
23063 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23066 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23068 pane.el.addClass('active');
23073 onTabClick : function(ev,un,ob,pane)
23075 //Roo.log('tab - prev default');
23076 ev.preventDefault();
23079 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23080 pane.tab.addClass('active');
23081 //Roo.log(pane.title);
23082 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23083 // technically we should have a deactivate event.. but maybe add later.
23084 // and it should not de-activate the selected tab...
23085 this.fireEvent('activatepane', pane);
23086 pane.el.addClass('active');
23087 pane.fireEvent('activate');
23092 getActivePane : function()
23095 Roo.each(this.panes, function(p) {
23096 if(p.el.hasClass('active')){
23117 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23119 * @class Roo.bootstrap.TabPane
23120 * @extends Roo.bootstrap.Component
23121 * Bootstrap TabPane class
23122 * @cfg {Boolean} active (false | true) Default false
23123 * @cfg {String} title title of panel
23127 * Create a new TabPane
23128 * @param {Object} config The config object
23131 Roo.bootstrap.dash.TabPane = function(config){
23132 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23138 * When a pane is activated
23139 * @param {Roo.bootstrap.dash.TabPane} pane
23146 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23151 // the tabBox that this is attached to.
23154 getAutoCreate : function()
23162 cfg.cls += ' active';
23167 initEvents : function()
23169 //Roo.log('trigger add pane handler');
23170 this.parent().fireEvent('addpane', this)
23174 * Updates the tab title
23175 * @param {String} html to set the title to.
23177 setTitle: function(str)
23183 this.tab.select('a', true).first().dom.innerHTML = str;
23200 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23203 * @class Roo.bootstrap.menu.Menu
23204 * @extends Roo.bootstrap.Component
23205 * Bootstrap Menu class - container for Menu
23206 * @cfg {String} html Text of the menu
23207 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23208 * @cfg {String} icon Font awesome icon
23209 * @cfg {String} pos Menu align to (top | bottom) default bottom
23213 * Create a new Menu
23214 * @param {Object} config The config object
23218 Roo.bootstrap.menu.Menu = function(config){
23219 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23223 * @event beforeshow
23224 * Fires before this menu is displayed
23225 * @param {Roo.bootstrap.menu.Menu} this
23229 * @event beforehide
23230 * Fires before this menu is hidden
23231 * @param {Roo.bootstrap.menu.Menu} this
23236 * Fires after this menu is displayed
23237 * @param {Roo.bootstrap.menu.Menu} this
23242 * Fires after this menu is hidden
23243 * @param {Roo.bootstrap.menu.Menu} this
23248 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23249 * @param {Roo.bootstrap.menu.Menu} this
23250 * @param {Roo.EventObject} e
23257 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23261 weight : 'default',
23266 getChildContainer : function() {
23267 if(this.isSubMenu){
23271 return this.el.select('ul.dropdown-menu', true).first();
23274 getAutoCreate : function()
23279 cls : 'roo-menu-text',
23287 cls : 'fa ' + this.icon
23298 cls : 'dropdown-button btn btn-' + this.weight,
23303 cls : 'dropdown-toggle btn btn-' + this.weight,
23313 cls : 'dropdown-menu'
23319 if(this.pos == 'top'){
23320 cfg.cls += ' dropup';
23323 if(this.isSubMenu){
23326 cls : 'dropdown-menu'
23333 onRender : function(ct, position)
23335 this.isSubMenu = ct.hasClass('dropdown-submenu');
23337 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23340 initEvents : function()
23342 if(this.isSubMenu){
23346 this.hidden = true;
23348 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23349 this.triggerEl.on('click', this.onTriggerPress, this);
23351 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23352 this.buttonEl.on('click', this.onClick, this);
23358 if(this.isSubMenu){
23362 return this.el.select('ul.dropdown-menu', true).first();
23365 onClick : function(e)
23367 this.fireEvent("click", this, e);
23370 onTriggerPress : function(e)
23372 if (this.isVisible()) {
23379 isVisible : function(){
23380 return !this.hidden;
23385 this.fireEvent("beforeshow", this);
23387 this.hidden = false;
23388 this.el.addClass('open');
23390 Roo.get(document).on("mouseup", this.onMouseUp, this);
23392 this.fireEvent("show", this);
23399 this.fireEvent("beforehide", this);
23401 this.hidden = true;
23402 this.el.removeClass('open');
23404 Roo.get(document).un("mouseup", this.onMouseUp);
23406 this.fireEvent("hide", this);
23409 onMouseUp : function()
23423 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23426 * @class Roo.bootstrap.menu.Item
23427 * @extends Roo.bootstrap.Component
23428 * Bootstrap MenuItem class
23429 * @cfg {Boolean} submenu (true | false) default false
23430 * @cfg {String} html text of the item
23431 * @cfg {String} href the link
23432 * @cfg {Boolean} disable (true | false) default false
23433 * @cfg {Boolean} preventDefault (true | false) default true
23434 * @cfg {String} icon Font awesome icon
23435 * @cfg {String} pos Submenu align to (left | right) default right
23439 * Create a new Item
23440 * @param {Object} config The config object
23444 Roo.bootstrap.menu.Item = function(config){
23445 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23449 * Fires when the mouse is hovering over this menu
23450 * @param {Roo.bootstrap.menu.Item} this
23451 * @param {Roo.EventObject} e
23456 * Fires when the mouse exits this menu
23457 * @param {Roo.bootstrap.menu.Item} this
23458 * @param {Roo.EventObject} e
23464 * The raw click event for the entire grid.
23465 * @param {Roo.EventObject} e
23471 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23476 preventDefault: true,
23481 getAutoCreate : function()
23486 cls : 'roo-menu-item-text',
23494 cls : 'fa ' + this.icon
23503 href : this.href || '#',
23510 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23514 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23516 if(this.pos == 'left'){
23517 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23524 initEvents : function()
23526 this.el.on('mouseover', this.onMouseOver, this);
23527 this.el.on('mouseout', this.onMouseOut, this);
23529 this.el.select('a', true).first().on('click', this.onClick, this);
23533 onClick : function(e)
23535 if(this.preventDefault){
23536 e.preventDefault();
23539 this.fireEvent("click", this, e);
23542 onMouseOver : function(e)
23544 if(this.submenu && this.pos == 'left'){
23545 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23548 this.fireEvent("mouseover", this, e);
23551 onMouseOut : function(e)
23553 this.fireEvent("mouseout", this, e);
23565 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23568 * @class Roo.bootstrap.menu.Separator
23569 * @extends Roo.bootstrap.Component
23570 * Bootstrap Separator class
23573 * Create a new Separator
23574 * @param {Object} config The config object
23578 Roo.bootstrap.menu.Separator = function(config){
23579 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23582 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23584 getAutoCreate : function(){
23605 * @class Roo.bootstrap.Tooltip
23606 * Bootstrap Tooltip class
23607 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23608 * to determine which dom element triggers the tooltip.
23610 * It needs to add support for additional attributes like tooltip-position
23613 * Create a new Toolti
23614 * @param {Object} config The config object
23617 Roo.bootstrap.Tooltip = function(config){
23618 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23621 Roo.apply(Roo.bootstrap.Tooltip, {
23623 * @function init initialize tooltip monitoring.
23627 currentTip : false,
23628 currentRegion : false,
23634 Roo.get(document).on('mouseover', this.enter ,this);
23635 Roo.get(document).on('mouseout', this.leave, this);
23638 this.currentTip = new Roo.bootstrap.Tooltip();
23641 enter : function(ev)
23643 var dom = ev.getTarget();
23645 //Roo.log(['enter',dom]);
23646 var el = Roo.fly(dom);
23647 if (this.currentEl) {
23649 //Roo.log(this.currentEl);
23650 //Roo.log(this.currentEl.contains(dom));
23651 if (this.currentEl == el) {
23654 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23660 if (this.currentTip.el) {
23661 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23666 // you can not look for children, as if el is the body.. then everythign is the child..
23667 if (!el.attr('tooltip')) { //
23668 if (!el.select("[tooltip]").elements.length) {
23671 // is the mouse over this child...?
23672 bindEl = el.select("[tooltip]").first();
23673 var xy = ev.getXY();
23674 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23675 //Roo.log("not in region.");
23678 //Roo.log("child element over..");
23681 this.currentEl = bindEl;
23682 this.currentTip.bind(bindEl);
23683 this.currentRegion = Roo.lib.Region.getRegion(dom);
23684 this.currentTip.enter();
23687 leave : function(ev)
23689 var dom = ev.getTarget();
23690 //Roo.log(['leave',dom]);
23691 if (!this.currentEl) {
23696 if (dom != this.currentEl.dom) {
23699 var xy = ev.getXY();
23700 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23703 // only activate leave if mouse cursor is outside... bounding box..
23708 if (this.currentTip) {
23709 this.currentTip.leave();
23711 //Roo.log('clear currentEl');
23712 this.currentEl = false;
23717 'left' : ['r-l', [-2,0], 'right'],
23718 'right' : ['l-r', [2,0], 'left'],
23719 'bottom' : ['t-b', [0,2], 'top'],
23720 'top' : [ 'b-t', [0,-2], 'bottom']
23726 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23731 delay : null, // can be { show : 300 , hide: 500}
23735 hoverState : null, //???
23737 placement : 'bottom',
23739 getAutoCreate : function(){
23746 cls : 'tooltip-arrow'
23749 cls : 'tooltip-inner'
23756 bind : function(el)
23762 enter : function () {
23764 if (this.timeout != null) {
23765 clearTimeout(this.timeout);
23768 this.hoverState = 'in';
23769 //Roo.log("enter - show");
23770 if (!this.delay || !this.delay.show) {
23775 this.timeout = setTimeout(function () {
23776 if (_t.hoverState == 'in') {
23779 }, this.delay.show);
23783 clearTimeout(this.timeout);
23785 this.hoverState = 'out';
23786 if (!this.delay || !this.delay.hide) {
23792 this.timeout = setTimeout(function () {
23793 //Roo.log("leave - timeout");
23795 if (_t.hoverState == 'out') {
23797 Roo.bootstrap.Tooltip.currentEl = false;
23805 this.render(document.body);
23808 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23810 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23812 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23814 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23816 var placement = typeof this.placement == 'function' ?
23817 this.placement.call(this, this.el, on_el) :
23820 var autoToken = /\s?auto?\s?/i;
23821 var autoPlace = autoToken.test(placement);
23823 placement = placement.replace(autoToken, '') || 'top';
23827 //this.el.setXY([0,0]);
23829 //this.el.dom.style.display='block';
23831 //this.el.appendTo(on_el);
23833 var p = this.getPosition();
23834 var box = this.el.getBox();
23840 var align = Roo.bootstrap.Tooltip.alignment[placement];
23842 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23844 if(placement == 'top' || placement == 'bottom'){
23846 placement = 'right';
23849 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23850 placement = 'left';
23854 align = Roo.bootstrap.Tooltip.alignment[placement];
23856 this.el.alignTo(this.bindEl, align[0],align[1]);
23857 //var arrow = this.el.select('.arrow',true).first();
23858 //arrow.set(align[2],
23860 this.el.addClass(placement);
23862 this.el.addClass('in fade');
23864 this.hoverState = null;
23866 if (this.el.hasClass('fade')) {
23877 //this.el.setXY([0,0]);
23878 this.el.removeClass('in');
23894 * @class Roo.bootstrap.LocationPicker
23895 * @extends Roo.bootstrap.Component
23896 * Bootstrap LocationPicker class
23897 * @cfg {Number} latitude Position when init default 0
23898 * @cfg {Number} longitude Position when init default 0
23899 * @cfg {Number} zoom default 15
23900 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23901 * @cfg {Boolean} mapTypeControl default false
23902 * @cfg {Boolean} disableDoubleClickZoom default false
23903 * @cfg {Boolean} scrollwheel default true
23904 * @cfg {Boolean} streetViewControl default false
23905 * @cfg {Number} radius default 0
23906 * @cfg {String} locationName
23907 * @cfg {Boolean} draggable default true
23908 * @cfg {Boolean} enableAutocomplete default false
23909 * @cfg {Boolean} enableReverseGeocode default true
23910 * @cfg {String} markerTitle
23913 * Create a new LocationPicker
23914 * @param {Object} config The config object
23918 Roo.bootstrap.LocationPicker = function(config){
23920 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23925 * Fires when the picker initialized.
23926 * @param {Roo.bootstrap.LocationPicker} this
23927 * @param {Google Location} location
23931 * @event positionchanged
23932 * Fires when the picker position changed.
23933 * @param {Roo.bootstrap.LocationPicker} this
23934 * @param {Google Location} location
23936 positionchanged : true,
23939 * Fires when the map resize.
23940 * @param {Roo.bootstrap.LocationPicker} this
23945 * Fires when the map show.
23946 * @param {Roo.bootstrap.LocationPicker} this
23951 * Fires when the map hide.
23952 * @param {Roo.bootstrap.LocationPicker} this
23957 * Fires when click the map.
23958 * @param {Roo.bootstrap.LocationPicker} this
23959 * @param {Map event} e
23963 * @event mapRightClick
23964 * Fires when right click the map.
23965 * @param {Roo.bootstrap.LocationPicker} this
23966 * @param {Map event} e
23968 mapRightClick : true,
23970 * @event markerClick
23971 * Fires when click the marker.
23972 * @param {Roo.bootstrap.LocationPicker} this
23973 * @param {Map event} e
23975 markerClick : true,
23977 * @event markerRightClick
23978 * Fires when right click the marker.
23979 * @param {Roo.bootstrap.LocationPicker} this
23980 * @param {Map event} e
23982 markerRightClick : true,
23984 * @event OverlayViewDraw
23985 * Fires when OverlayView Draw
23986 * @param {Roo.bootstrap.LocationPicker} this
23988 OverlayViewDraw : true,
23990 * @event OverlayViewOnAdd
23991 * Fires when OverlayView Draw
23992 * @param {Roo.bootstrap.LocationPicker} this
23994 OverlayViewOnAdd : true,
23996 * @event OverlayViewOnRemove
23997 * Fires when OverlayView Draw
23998 * @param {Roo.bootstrap.LocationPicker} this
24000 OverlayViewOnRemove : true,
24002 * @event OverlayViewShow
24003 * Fires when OverlayView Draw
24004 * @param {Roo.bootstrap.LocationPicker} this
24005 * @param {Pixel} cpx
24007 OverlayViewShow : true,
24009 * @event OverlayViewHide
24010 * Fires when OverlayView Draw
24011 * @param {Roo.bootstrap.LocationPicker} this
24013 OverlayViewHide : true,
24015 * @event loadexception
24016 * Fires when load google lib failed.
24017 * @param {Roo.bootstrap.LocationPicker} this
24019 loadexception : true
24024 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24026 gMapContext: false,
24032 mapTypeControl: false,
24033 disableDoubleClickZoom: false,
24035 streetViewControl: false,
24039 enableAutocomplete: false,
24040 enableReverseGeocode: true,
24043 getAutoCreate: function()
24048 cls: 'roo-location-picker'
24054 initEvents: function(ct, position)
24056 if(!this.el.getWidth() || this.isApplied()){
24060 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24065 initial: function()
24067 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24068 this.fireEvent('loadexception', this);
24072 if(!this.mapTypeId){
24073 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24076 this.gMapContext = this.GMapContext();
24078 this.initOverlayView();
24080 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24084 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24085 _this.setPosition(_this.gMapContext.marker.position);
24088 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24089 _this.fireEvent('mapClick', this, event);
24093 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24094 _this.fireEvent('mapRightClick', this, event);
24098 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24099 _this.fireEvent('markerClick', this, event);
24103 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24104 _this.fireEvent('markerRightClick', this, event);
24108 this.setPosition(this.gMapContext.location);
24110 this.fireEvent('initial', this, this.gMapContext.location);
24113 initOverlayView: function()
24117 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24121 _this.fireEvent('OverlayViewDraw', _this);
24126 _this.fireEvent('OverlayViewOnAdd', _this);
24129 onRemove: function()
24131 _this.fireEvent('OverlayViewOnRemove', _this);
24134 show: function(cpx)
24136 _this.fireEvent('OverlayViewShow', _this, cpx);
24141 _this.fireEvent('OverlayViewHide', _this);
24147 fromLatLngToContainerPixel: function(event)
24149 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24152 isApplied: function()
24154 return this.getGmapContext() == false ? false : true;
24157 getGmapContext: function()
24159 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24162 GMapContext: function()
24164 var position = new google.maps.LatLng(this.latitude, this.longitude);
24166 var _map = new google.maps.Map(this.el.dom, {
24169 mapTypeId: this.mapTypeId,
24170 mapTypeControl: this.mapTypeControl,
24171 disableDoubleClickZoom: this.disableDoubleClickZoom,
24172 scrollwheel: this.scrollwheel,
24173 streetViewControl: this.streetViewControl,
24174 locationName: this.locationName,
24175 draggable: this.draggable,
24176 enableAutocomplete: this.enableAutocomplete,
24177 enableReverseGeocode: this.enableReverseGeocode
24180 var _marker = new google.maps.Marker({
24181 position: position,
24183 title: this.markerTitle,
24184 draggable: this.draggable
24191 location: position,
24192 radius: this.radius,
24193 locationName: this.locationName,
24194 addressComponents: {
24195 formatted_address: null,
24196 addressLine1: null,
24197 addressLine2: null,
24199 streetNumber: null,
24203 stateOrProvince: null
24206 domContainer: this.el.dom,
24207 geodecoder: new google.maps.Geocoder()
24211 drawCircle: function(center, radius, options)
24213 if (this.gMapContext.circle != null) {
24214 this.gMapContext.circle.setMap(null);
24218 options = Roo.apply({}, options, {
24219 strokeColor: "#0000FF",
24220 strokeOpacity: .35,
24222 fillColor: "#0000FF",
24226 options.map = this.gMapContext.map;
24227 options.radius = radius;
24228 options.center = center;
24229 this.gMapContext.circle = new google.maps.Circle(options);
24230 return this.gMapContext.circle;
24236 setPosition: function(location)
24238 this.gMapContext.location = location;
24239 this.gMapContext.marker.setPosition(location);
24240 this.gMapContext.map.panTo(location);
24241 this.drawCircle(location, this.gMapContext.radius, {});
24245 if (this.gMapContext.settings.enableReverseGeocode) {
24246 this.gMapContext.geodecoder.geocode({
24247 latLng: this.gMapContext.location
24248 }, function(results, status) {
24250 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24251 _this.gMapContext.locationName = results[0].formatted_address;
24252 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24254 _this.fireEvent('positionchanged', this, location);
24261 this.fireEvent('positionchanged', this, location);
24266 google.maps.event.trigger(this.gMapContext.map, "resize");
24268 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24270 this.fireEvent('resize', this);
24273 setPositionByLatLng: function(latitude, longitude)
24275 this.setPosition(new google.maps.LatLng(latitude, longitude));
24278 getCurrentPosition: function()
24281 latitude: this.gMapContext.location.lat(),
24282 longitude: this.gMapContext.location.lng()
24286 getAddressName: function()
24288 return this.gMapContext.locationName;
24291 getAddressComponents: function()
24293 return this.gMapContext.addressComponents;
24296 address_component_from_google_geocode: function(address_components)
24300 for (var i = 0; i < address_components.length; i++) {
24301 var component = address_components[i];
24302 if (component.types.indexOf("postal_code") >= 0) {
24303 result.postalCode = component.short_name;
24304 } else if (component.types.indexOf("street_number") >= 0) {
24305 result.streetNumber = component.short_name;
24306 } else if (component.types.indexOf("route") >= 0) {
24307 result.streetName = component.short_name;
24308 } else if (component.types.indexOf("neighborhood") >= 0) {
24309 result.city = component.short_name;
24310 } else if (component.types.indexOf("locality") >= 0) {
24311 result.city = component.short_name;
24312 } else if (component.types.indexOf("sublocality") >= 0) {
24313 result.district = component.short_name;
24314 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24315 result.stateOrProvince = component.short_name;
24316 } else if (component.types.indexOf("country") >= 0) {
24317 result.country = component.short_name;
24321 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24322 result.addressLine2 = "";
24326 setZoomLevel: function(zoom)
24328 this.gMapContext.map.setZoom(zoom);
24341 this.fireEvent('show', this);
24352 this.fireEvent('hide', this);
24357 Roo.apply(Roo.bootstrap.LocationPicker, {
24359 OverlayView : function(map, options)
24361 options = options || {};
24375 * @class Roo.bootstrap.Alert
24376 * @extends Roo.bootstrap.Component
24377 * Bootstrap Alert class
24378 * @cfg {String} title The title of alert
24379 * @cfg {String} html The content of alert
24380 * @cfg {String} weight ( success | info | warning | danger )
24381 * @cfg {String} faicon font-awesomeicon
24384 * Create a new alert
24385 * @param {Object} config The config object
24389 Roo.bootstrap.Alert = function(config){
24390 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24394 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24401 getAutoCreate : function()
24410 cls : 'roo-alert-icon'
24415 cls : 'roo-alert-title',
24420 cls : 'roo-alert-text',
24427 cfg.cn[0].cls += ' fa ' + this.faicon;
24431 cfg.cls += ' alert-' + this.weight;
24437 initEvents: function()
24439 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24442 setTitle : function(str)
24444 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24447 setText : function(str)
24449 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24452 setWeight : function(weight)
24455 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24458 this.weight = weight;
24460 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24463 setIcon : function(icon)
24466 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24469 this.faicon = icon;
24471 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24492 * @class Roo.bootstrap.UploadCropbox
24493 * @extends Roo.bootstrap.Component
24494 * Bootstrap UploadCropbox class
24495 * @cfg {String} emptyText show when image has been loaded
24496 * @cfg {String} rotateNotify show when image too small to rotate
24497 * @cfg {Number} errorTimeout default 3000
24498 * @cfg {Number} minWidth default 300
24499 * @cfg {Number} minHeight default 300
24500 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24501 * @cfg {Boolean} isDocument (true|false) default false
24502 * @cfg {String} url action url
24503 * @cfg {String} paramName default 'imageUpload'
24504 * @cfg {String} method default POST
24505 * @cfg {Boolean} loadMask (true|false) default true
24506 * @cfg {Boolean} loadingText default 'Loading...'
24509 * Create a new UploadCropbox
24510 * @param {Object} config The config object
24513 Roo.bootstrap.UploadCropbox = function(config){
24514 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24518 * @event beforeselectfile
24519 * Fire before select file
24520 * @param {Roo.bootstrap.UploadCropbox} this
24522 "beforeselectfile" : true,
24525 * Fire after initEvent
24526 * @param {Roo.bootstrap.UploadCropbox} this
24531 * Fire after initEvent
24532 * @param {Roo.bootstrap.UploadCropbox} this
24533 * @param {String} data
24538 * Fire when preparing the file data
24539 * @param {Roo.bootstrap.UploadCropbox} this
24540 * @param {Object} file
24545 * Fire when get exception
24546 * @param {Roo.bootstrap.UploadCropbox} this
24547 * @param {XMLHttpRequest} xhr
24549 "exception" : true,
24551 * @event beforeloadcanvas
24552 * Fire before load the canvas
24553 * @param {Roo.bootstrap.UploadCropbox} this
24554 * @param {String} src
24556 "beforeloadcanvas" : true,
24559 * Fire when trash image
24560 * @param {Roo.bootstrap.UploadCropbox} this
24565 * Fire when download the image
24566 * @param {Roo.bootstrap.UploadCropbox} this
24570 * @event footerbuttonclick
24571 * Fire when footerbuttonclick
24572 * @param {Roo.bootstrap.UploadCropbox} this
24573 * @param {String} type
24575 "footerbuttonclick" : true,
24579 * @param {Roo.bootstrap.UploadCropbox} this
24584 * Fire when rotate the image
24585 * @param {Roo.bootstrap.UploadCropbox} this
24586 * @param {String} pos
24591 * Fire when inspect the file
24592 * @param {Roo.bootstrap.UploadCropbox} this
24593 * @param {Object} file
24598 * Fire when xhr upload the file
24599 * @param {Roo.bootstrap.UploadCropbox} this
24600 * @param {Object} data
24605 * Fire when arrange the file data
24606 * @param {Roo.bootstrap.UploadCropbox} this
24607 * @param {Object} formData
24612 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24615 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24617 emptyText : 'Click to upload image',
24618 rotateNotify : 'Image is too small to rotate',
24619 errorTimeout : 3000,
24633 cropType : 'image/jpeg',
24635 canvasLoaded : false,
24636 isDocument : false,
24638 paramName : 'imageUpload',
24640 loadingText : 'Loading...',
24643 getAutoCreate : function()
24647 cls : 'roo-upload-cropbox',
24651 cls : 'roo-upload-cropbox-selector',
24656 cls : 'roo-upload-cropbox-body',
24657 style : 'cursor:pointer',
24661 cls : 'roo-upload-cropbox-preview'
24665 cls : 'roo-upload-cropbox-thumb'
24669 cls : 'roo-upload-cropbox-empty-notify',
24670 html : this.emptyText
24674 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24675 html : this.rotateNotify
24681 cls : 'roo-upload-cropbox-footer',
24684 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24694 onRender : function(ct, position)
24696 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24698 if (this.buttons.length) {
24700 Roo.each(this.buttons, function(bb) {
24702 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24704 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24710 this.maskEl = this.el;
24714 initEvents : function()
24716 this.urlAPI = (window.createObjectURL && window) ||
24717 (window.URL && URL.revokeObjectURL && URL) ||
24718 (window.webkitURL && webkitURL);
24720 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24721 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24723 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24724 this.selectorEl.hide();
24726 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24727 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24729 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24730 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24731 this.thumbEl.hide();
24733 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24734 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24736 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24737 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24738 this.errorEl.hide();
24740 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24741 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24742 this.footerEl.hide();
24744 this.setThumbBoxSize();
24750 this.fireEvent('initial', this);
24757 window.addEventListener("resize", function() { _this.resize(); } );
24759 this.bodyEl.on('click', this.beforeSelectFile, this);
24762 this.bodyEl.on('touchstart', this.onTouchStart, this);
24763 this.bodyEl.on('touchmove', this.onTouchMove, this);
24764 this.bodyEl.on('touchend', this.onTouchEnd, this);
24768 this.bodyEl.on('mousedown', this.onMouseDown, this);
24769 this.bodyEl.on('mousemove', this.onMouseMove, this);
24770 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24771 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24772 Roo.get(document).on('mouseup', this.onMouseUp, this);
24775 this.selectorEl.on('change', this.onFileSelected, this);
24781 this.baseScale = 1;
24783 this.baseRotate = 1;
24784 this.dragable = false;
24785 this.pinching = false;
24788 this.cropData = false;
24789 this.notifyEl.dom.innerHTML = this.emptyText;
24791 this.selectorEl.dom.value = '';
24795 resize : function()
24797 if(this.fireEvent('resize', this) != false){
24798 this.setThumbBoxPosition();
24799 this.setCanvasPosition();
24803 onFooterButtonClick : function(e, el, o, type)
24806 case 'rotate-left' :
24807 this.onRotateLeft(e);
24809 case 'rotate-right' :
24810 this.onRotateRight(e);
24813 this.beforeSelectFile(e);
24828 this.fireEvent('footerbuttonclick', this, type);
24831 beforeSelectFile : function(e)
24833 e.preventDefault();
24835 if(this.fireEvent('beforeselectfile', this) != false){
24836 this.selectorEl.dom.click();
24840 onFileSelected : function(e)
24842 e.preventDefault();
24844 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24848 var file = this.selectorEl.dom.files[0];
24850 if(this.fireEvent('inspect', this, file) != false){
24851 this.prepare(file);
24856 trash : function(e)
24858 this.fireEvent('trash', this);
24861 download : function(e)
24863 this.fireEvent('download', this);
24866 loadCanvas : function(src)
24868 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24872 this.imageEl = document.createElement('img');
24876 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24878 this.imageEl.src = src;
24882 onLoadCanvas : function()
24884 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24885 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24887 this.bodyEl.un('click', this.beforeSelectFile, this);
24889 this.notifyEl.hide();
24890 this.thumbEl.show();
24891 this.footerEl.show();
24893 this.baseRotateLevel();
24895 if(this.isDocument){
24896 this.setThumbBoxSize();
24899 this.setThumbBoxPosition();
24901 this.baseScaleLevel();
24907 this.canvasLoaded = true;
24910 this.maskEl.unmask();
24915 setCanvasPosition : function()
24917 if(!this.canvasEl){
24921 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24922 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24924 this.previewEl.setLeft(pw);
24925 this.previewEl.setTop(ph);
24929 onMouseDown : function(e)
24933 this.dragable = true;
24934 this.pinching = false;
24936 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24937 this.dragable = false;
24941 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24942 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24946 onMouseMove : function(e)
24950 if(!this.canvasLoaded){
24954 if (!this.dragable){
24958 var minX = Math.ceil(this.thumbEl.getLeft(true));
24959 var minY = Math.ceil(this.thumbEl.getTop(true));
24961 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24962 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24964 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24965 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24967 x = x - this.mouseX;
24968 y = y - this.mouseY;
24970 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24971 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24973 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24974 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24976 this.previewEl.setLeft(bgX);
24977 this.previewEl.setTop(bgY);
24979 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24980 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24983 onMouseUp : function(e)
24987 this.dragable = false;
24990 onMouseWheel : function(e)
24994 this.startScale = this.scale;
24996 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24998 if(!this.zoomable()){
24999 this.scale = this.startScale;
25008 zoomable : function()
25010 var minScale = this.thumbEl.getWidth() / this.minWidth;
25012 if(this.minWidth < this.minHeight){
25013 minScale = this.thumbEl.getHeight() / this.minHeight;
25016 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25017 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25021 (this.rotate == 0 || this.rotate == 180) &&
25023 width > this.imageEl.OriginWidth ||
25024 height > this.imageEl.OriginHeight ||
25025 (width < this.minWidth && height < this.minHeight)
25033 (this.rotate == 90 || this.rotate == 270) &&
25035 width > this.imageEl.OriginWidth ||
25036 height > this.imageEl.OriginHeight ||
25037 (width < this.minHeight && height < this.minWidth)
25044 !this.isDocument &&
25045 (this.rotate == 0 || this.rotate == 180) &&
25047 width < this.minWidth ||
25048 width > this.imageEl.OriginWidth ||
25049 height < this.minHeight ||
25050 height > this.imageEl.OriginHeight
25057 !this.isDocument &&
25058 (this.rotate == 90 || this.rotate == 270) &&
25060 width < this.minHeight ||
25061 width > this.imageEl.OriginWidth ||
25062 height < this.minWidth ||
25063 height > this.imageEl.OriginHeight
25073 onRotateLeft : function(e)
25075 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25077 var minScale = this.thumbEl.getWidth() / this.minWidth;
25079 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25080 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25082 this.startScale = this.scale;
25084 while (this.getScaleLevel() < minScale){
25086 this.scale = this.scale + 1;
25088 if(!this.zoomable()){
25093 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25094 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25099 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25106 this.scale = this.startScale;
25108 this.onRotateFail();
25113 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25115 if(this.isDocument){
25116 this.setThumbBoxSize();
25117 this.setThumbBoxPosition();
25118 this.setCanvasPosition();
25123 this.fireEvent('rotate', this, 'left');
25127 onRotateRight : function(e)
25129 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25131 var minScale = this.thumbEl.getWidth() / this.minWidth;
25133 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25134 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25136 this.startScale = this.scale;
25138 while (this.getScaleLevel() < minScale){
25140 this.scale = this.scale + 1;
25142 if(!this.zoomable()){
25147 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25148 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25153 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25160 this.scale = this.startScale;
25162 this.onRotateFail();
25167 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25169 if(this.isDocument){
25170 this.setThumbBoxSize();
25171 this.setThumbBoxPosition();
25172 this.setCanvasPosition();
25177 this.fireEvent('rotate', this, 'right');
25180 onRotateFail : function()
25182 this.errorEl.show(true);
25186 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25191 this.previewEl.dom.innerHTML = '';
25193 var canvasEl = document.createElement("canvas");
25195 var contextEl = canvasEl.getContext("2d");
25197 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25198 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25199 var center = this.imageEl.OriginWidth / 2;
25201 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25202 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25203 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25204 center = this.imageEl.OriginHeight / 2;
25207 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25209 contextEl.translate(center, center);
25210 contextEl.rotate(this.rotate * Math.PI / 180);
25212 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25214 this.canvasEl = document.createElement("canvas");
25216 this.contextEl = this.canvasEl.getContext("2d");
25218 switch (this.rotate) {
25221 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25222 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25224 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25229 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25230 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25232 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25233 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);
25237 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25242 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25243 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25245 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25246 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);
25250 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);
25255 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25256 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25258 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25259 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25263 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);
25270 this.previewEl.appendChild(this.canvasEl);
25272 this.setCanvasPosition();
25277 if(!this.canvasLoaded){
25281 var imageCanvas = document.createElement("canvas");
25283 var imageContext = imageCanvas.getContext("2d");
25285 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25286 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25288 var center = imageCanvas.width / 2;
25290 imageContext.translate(center, center);
25292 imageContext.rotate(this.rotate * Math.PI / 180);
25294 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25296 var canvas = document.createElement("canvas");
25298 var context = canvas.getContext("2d");
25300 canvas.width = this.minWidth;
25301 canvas.height = this.minHeight;
25303 switch (this.rotate) {
25306 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25307 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25309 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25310 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25312 var targetWidth = this.minWidth - 2 * x;
25313 var targetHeight = this.minHeight - 2 * y;
25317 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25318 scale = targetWidth / width;
25321 if(x > 0 && y == 0){
25322 scale = targetHeight / height;
25325 if(x > 0 && y > 0){
25326 scale = targetWidth / width;
25328 if(width < height){
25329 scale = targetHeight / height;
25333 context.scale(scale, scale);
25335 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25336 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25338 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25339 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25341 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25346 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25347 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25349 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25350 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25352 var targetWidth = this.minWidth - 2 * x;
25353 var targetHeight = this.minHeight - 2 * y;
25357 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25358 scale = targetWidth / width;
25361 if(x > 0 && y == 0){
25362 scale = targetHeight / height;
25365 if(x > 0 && y > 0){
25366 scale = targetWidth / width;
25368 if(width < height){
25369 scale = targetHeight / height;
25373 context.scale(scale, scale);
25375 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25376 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25378 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25379 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25381 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25383 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25388 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25389 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25391 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25392 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25394 var targetWidth = this.minWidth - 2 * x;
25395 var targetHeight = this.minHeight - 2 * y;
25399 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25400 scale = targetWidth / width;
25403 if(x > 0 && y == 0){
25404 scale = targetHeight / height;
25407 if(x > 0 && y > 0){
25408 scale = targetWidth / width;
25410 if(width < height){
25411 scale = targetHeight / height;
25415 context.scale(scale, scale);
25417 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25418 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25420 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25421 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25423 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25424 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25426 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25431 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25432 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25434 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25435 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25437 var targetWidth = this.minWidth - 2 * x;
25438 var targetHeight = this.minHeight - 2 * y;
25442 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25443 scale = targetWidth / width;
25446 if(x > 0 && y == 0){
25447 scale = targetHeight / height;
25450 if(x > 0 && y > 0){
25451 scale = targetWidth / width;
25453 if(width < height){
25454 scale = targetHeight / height;
25458 context.scale(scale, scale);
25460 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25461 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25463 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25464 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25466 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25468 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25475 this.cropData = canvas.toDataURL(this.cropType);
25477 if(this.fireEvent('crop', this, this.cropData) !== false){
25478 this.process(this.file, this.cropData);
25485 setThumbBoxSize : function()
25489 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25490 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25491 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25493 this.minWidth = width;
25494 this.minHeight = height;
25496 if(this.rotate == 90 || this.rotate == 270){
25497 this.minWidth = height;
25498 this.minHeight = width;
25503 width = Math.ceil(this.minWidth * height / this.minHeight);
25505 if(this.minWidth > this.minHeight){
25507 height = Math.ceil(this.minHeight * width / this.minWidth);
25510 this.thumbEl.setStyle({
25511 width : width + 'px',
25512 height : height + 'px'
25519 setThumbBoxPosition : function()
25521 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25522 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25524 this.thumbEl.setLeft(x);
25525 this.thumbEl.setTop(y);
25529 baseRotateLevel : function()
25531 this.baseRotate = 1;
25534 typeof(this.exif) != 'undefined' &&
25535 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25536 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25538 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25541 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25545 baseScaleLevel : function()
25549 if(this.isDocument){
25551 if(this.baseRotate == 6 || this.baseRotate == 8){
25553 height = this.thumbEl.getHeight();
25554 this.baseScale = height / this.imageEl.OriginWidth;
25556 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25557 width = this.thumbEl.getWidth();
25558 this.baseScale = width / this.imageEl.OriginHeight;
25564 height = this.thumbEl.getHeight();
25565 this.baseScale = height / this.imageEl.OriginHeight;
25567 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25568 width = this.thumbEl.getWidth();
25569 this.baseScale = width / this.imageEl.OriginWidth;
25575 if(this.baseRotate == 6 || this.baseRotate == 8){
25577 width = this.thumbEl.getHeight();
25578 this.baseScale = width / this.imageEl.OriginHeight;
25580 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25581 height = this.thumbEl.getWidth();
25582 this.baseScale = height / this.imageEl.OriginHeight;
25585 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25586 height = this.thumbEl.getWidth();
25587 this.baseScale = height / this.imageEl.OriginHeight;
25589 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25590 width = this.thumbEl.getHeight();
25591 this.baseScale = width / this.imageEl.OriginWidth;
25598 width = this.thumbEl.getWidth();
25599 this.baseScale = width / this.imageEl.OriginWidth;
25601 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25602 height = this.thumbEl.getHeight();
25603 this.baseScale = height / this.imageEl.OriginHeight;
25606 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25608 height = this.thumbEl.getHeight();
25609 this.baseScale = height / this.imageEl.OriginHeight;
25611 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25612 width = this.thumbEl.getWidth();
25613 this.baseScale = width / this.imageEl.OriginWidth;
25621 getScaleLevel : function()
25623 return this.baseScale * Math.pow(1.1, this.scale);
25626 onTouchStart : function(e)
25628 if(!this.canvasLoaded){
25629 this.beforeSelectFile(e);
25633 var touches = e.browserEvent.touches;
25639 if(touches.length == 1){
25640 this.onMouseDown(e);
25644 if(touches.length != 2){
25650 for(var i = 0, finger; finger = touches[i]; i++){
25651 coords.push(finger.pageX, finger.pageY);
25654 var x = Math.pow(coords[0] - coords[2], 2);
25655 var y = Math.pow(coords[1] - coords[3], 2);
25657 this.startDistance = Math.sqrt(x + y);
25659 this.startScale = this.scale;
25661 this.pinching = true;
25662 this.dragable = false;
25666 onTouchMove : function(e)
25668 if(!this.pinching && !this.dragable){
25672 var touches = e.browserEvent.touches;
25679 this.onMouseMove(e);
25685 for(var i = 0, finger; finger = touches[i]; i++){
25686 coords.push(finger.pageX, finger.pageY);
25689 var x = Math.pow(coords[0] - coords[2], 2);
25690 var y = Math.pow(coords[1] - coords[3], 2);
25692 this.endDistance = Math.sqrt(x + y);
25694 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25696 if(!this.zoomable()){
25697 this.scale = this.startScale;
25705 onTouchEnd : function(e)
25707 this.pinching = false;
25708 this.dragable = false;
25712 process : function(file, crop)
25715 this.maskEl.mask(this.loadingText);
25718 this.xhr = new XMLHttpRequest();
25720 file.xhr = this.xhr;
25722 this.xhr.open(this.method, this.url, true);
25725 "Accept": "application/json",
25726 "Cache-Control": "no-cache",
25727 "X-Requested-With": "XMLHttpRequest"
25730 for (var headerName in headers) {
25731 var headerValue = headers[headerName];
25733 this.xhr.setRequestHeader(headerName, headerValue);
25739 this.xhr.onload = function()
25741 _this.xhrOnLoad(_this.xhr);
25744 this.xhr.onerror = function()
25746 _this.xhrOnError(_this.xhr);
25749 var formData = new FormData();
25751 formData.append('returnHTML', 'NO');
25754 formData.append('crop', crop);
25757 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25758 formData.append(this.paramName, file, file.name);
25761 if(typeof(file.filename) != 'undefined'){
25762 formData.append('filename', file.filename);
25765 if(typeof(file.mimetype) != 'undefined'){
25766 formData.append('mimetype', file.mimetype);
25769 if(this.fireEvent('arrange', this, formData) != false){
25770 this.xhr.send(formData);
25774 xhrOnLoad : function(xhr)
25777 this.maskEl.unmask();
25780 if (xhr.readyState !== 4) {
25781 this.fireEvent('exception', this, xhr);
25785 var response = Roo.decode(xhr.responseText);
25787 if(!response.success){
25788 this.fireEvent('exception', this, xhr);
25792 var response = Roo.decode(xhr.responseText);
25794 this.fireEvent('upload', this, response);
25798 xhrOnError : function()
25801 this.maskEl.unmask();
25804 Roo.log('xhr on error');
25806 var response = Roo.decode(xhr.responseText);
25812 prepare : function(file)
25815 this.maskEl.mask(this.loadingText);
25821 if(typeof(file) === 'string'){
25822 this.loadCanvas(file);
25826 if(!file || !this.urlAPI){
25831 this.cropType = file.type;
25835 if(this.fireEvent('prepare', this, this.file) != false){
25837 var reader = new FileReader();
25839 reader.onload = function (e) {
25840 if (e.target.error) {
25841 Roo.log(e.target.error);
25845 var buffer = e.target.result,
25846 dataView = new DataView(buffer),
25848 maxOffset = dataView.byteLength - 4,
25852 if (dataView.getUint16(0) === 0xffd8) {
25853 while (offset < maxOffset) {
25854 markerBytes = dataView.getUint16(offset);
25856 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25857 markerLength = dataView.getUint16(offset + 2) + 2;
25858 if (offset + markerLength > dataView.byteLength) {
25859 Roo.log('Invalid meta data: Invalid segment size.');
25863 if(markerBytes == 0xffe1){
25864 _this.parseExifData(
25871 offset += markerLength;
25881 var url = _this.urlAPI.createObjectURL(_this.file);
25883 _this.loadCanvas(url);
25888 reader.readAsArrayBuffer(this.file);
25894 parseExifData : function(dataView, offset, length)
25896 var tiffOffset = offset + 10,
25900 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25901 // No Exif data, might be XMP data instead
25905 // Check for the ASCII code for "Exif" (0x45786966):
25906 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25907 // No Exif data, might be XMP data instead
25910 if (tiffOffset + 8 > dataView.byteLength) {
25911 Roo.log('Invalid Exif data: Invalid segment size.');
25914 // Check for the two null bytes:
25915 if (dataView.getUint16(offset + 8) !== 0x0000) {
25916 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25919 // Check the byte alignment:
25920 switch (dataView.getUint16(tiffOffset)) {
25922 littleEndian = true;
25925 littleEndian = false;
25928 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25931 // Check for the TIFF tag marker (0x002A):
25932 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25933 Roo.log('Invalid Exif data: Missing TIFF marker.');
25936 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25937 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25939 this.parseExifTags(
25942 tiffOffset + dirOffset,
25947 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25952 if (dirOffset + 6 > dataView.byteLength) {
25953 Roo.log('Invalid Exif data: Invalid directory offset.');
25956 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25957 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25958 if (dirEndOffset + 4 > dataView.byteLength) {
25959 Roo.log('Invalid Exif data: Invalid directory size.');
25962 for (i = 0; i < tagsNumber; i += 1) {
25966 dirOffset + 2 + 12 * i, // tag offset
25970 // Return the offset to the next directory:
25971 return dataView.getUint32(dirEndOffset, littleEndian);
25974 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25976 var tag = dataView.getUint16(offset, littleEndian);
25978 this.exif[tag] = this.getExifValue(
25982 dataView.getUint16(offset + 2, littleEndian), // tag type
25983 dataView.getUint32(offset + 4, littleEndian), // tag length
25988 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25990 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25999 Roo.log('Invalid Exif data: Invalid tag type.');
26003 tagSize = tagType.size * length;
26004 // Determine if the value is contained in the dataOffset bytes,
26005 // or if the value at the dataOffset is a pointer to the actual data:
26006 dataOffset = tagSize > 4 ?
26007 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26008 if (dataOffset + tagSize > dataView.byteLength) {
26009 Roo.log('Invalid Exif data: Invalid data offset.');
26012 if (length === 1) {
26013 return tagType.getValue(dataView, dataOffset, littleEndian);
26016 for (i = 0; i < length; i += 1) {
26017 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26020 if (tagType.ascii) {
26022 // Concatenate the chars:
26023 for (i = 0; i < values.length; i += 1) {
26025 // Ignore the terminating NULL byte(s):
26026 if (c === '\u0000') {
26038 Roo.apply(Roo.bootstrap.UploadCropbox, {
26040 'Orientation': 0x0112
26044 1: 0, //'top-left',
26046 3: 180, //'bottom-right',
26047 // 4: 'bottom-left',
26049 6: 90, //'right-top',
26050 // 7: 'right-bottom',
26051 8: 270 //'left-bottom'
26055 // byte, 8-bit unsigned int:
26057 getValue: function (dataView, dataOffset) {
26058 return dataView.getUint8(dataOffset);
26062 // ascii, 8-bit byte:
26064 getValue: function (dataView, dataOffset) {
26065 return String.fromCharCode(dataView.getUint8(dataOffset));
26070 // short, 16 bit int:
26072 getValue: function (dataView, dataOffset, littleEndian) {
26073 return dataView.getUint16(dataOffset, littleEndian);
26077 // long, 32 bit int:
26079 getValue: function (dataView, dataOffset, littleEndian) {
26080 return dataView.getUint32(dataOffset, littleEndian);
26084 // rational = two long values, first is numerator, second is denominator:
26086 getValue: function (dataView, dataOffset, littleEndian) {
26087 return dataView.getUint32(dataOffset, littleEndian) /
26088 dataView.getUint32(dataOffset + 4, littleEndian);
26092 // slong, 32 bit signed int:
26094 getValue: function (dataView, dataOffset, littleEndian) {
26095 return dataView.getInt32(dataOffset, littleEndian);
26099 // srational, two slongs, first is numerator, second is denominator:
26101 getValue: function (dataView, dataOffset, littleEndian) {
26102 return dataView.getInt32(dataOffset, littleEndian) /
26103 dataView.getInt32(dataOffset + 4, littleEndian);
26113 cls : 'btn-group roo-upload-cropbox-rotate-left',
26114 action : 'rotate-left',
26118 cls : 'btn btn-default',
26119 html : '<i class="fa fa-undo"></i>'
26125 cls : 'btn-group roo-upload-cropbox-picture',
26126 action : 'picture',
26130 cls : 'btn btn-default',
26131 html : '<i class="fa fa-picture-o"></i>'
26137 cls : 'btn-group roo-upload-cropbox-rotate-right',
26138 action : 'rotate-right',
26142 cls : 'btn btn-default',
26143 html : '<i class="fa fa-repeat"></i>'
26151 cls : 'btn-group roo-upload-cropbox-rotate-left',
26152 action : 'rotate-left',
26156 cls : 'btn btn-default',
26157 html : '<i class="fa fa-undo"></i>'
26163 cls : 'btn-group roo-upload-cropbox-download',
26164 action : 'download',
26168 cls : 'btn btn-default',
26169 html : '<i class="fa fa-download"></i>'
26175 cls : 'btn-group roo-upload-cropbox-crop',
26180 cls : 'btn btn-default',
26181 html : '<i class="fa fa-crop"></i>'
26187 cls : 'btn-group roo-upload-cropbox-trash',
26192 cls : 'btn btn-default',
26193 html : '<i class="fa fa-trash"></i>'
26199 cls : 'btn-group roo-upload-cropbox-rotate-right',
26200 action : 'rotate-right',
26204 cls : 'btn btn-default',
26205 html : '<i class="fa fa-repeat"></i>'
26213 cls : 'btn-group roo-upload-cropbox-rotate-left',
26214 action : 'rotate-left',
26218 cls : 'btn btn-default',
26219 html : '<i class="fa fa-undo"></i>'
26225 cls : 'btn-group roo-upload-cropbox-rotate-right',
26226 action : 'rotate-right',
26230 cls : 'btn btn-default',
26231 html : '<i class="fa fa-repeat"></i>'
26244 * @class Roo.bootstrap.DocumentManager
26245 * @extends Roo.bootstrap.Component
26246 * Bootstrap DocumentManager class
26247 * @cfg {String} paramName default 'imageUpload'
26248 * @cfg {String} method default POST
26249 * @cfg {String} url action url
26250 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26251 * @cfg {Boolean} multiple multiple upload default true
26252 * @cfg {Number} thumbSize default 300
26253 * @cfg {String} fieldLabel
26254 * @cfg {Number} labelWidth default 4
26255 * @cfg {String} labelAlign (left|top) default left
26256 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26259 * Create a new DocumentManager
26260 * @param {Object} config The config object
26263 Roo.bootstrap.DocumentManager = function(config){
26264 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26269 * Fire when initial the DocumentManager
26270 * @param {Roo.bootstrap.DocumentManager} this
26275 * inspect selected file
26276 * @param {Roo.bootstrap.DocumentManager} this
26277 * @param {File} file
26282 * Fire when xhr load exception
26283 * @param {Roo.bootstrap.DocumentManager} this
26284 * @param {XMLHttpRequest} xhr
26286 "exception" : true,
26289 * prepare the form data
26290 * @param {Roo.bootstrap.DocumentManager} this
26291 * @param {Object} formData
26296 * Fire when remove the file
26297 * @param {Roo.bootstrap.DocumentManager} this
26298 * @param {Object} file
26303 * Fire after refresh the file
26304 * @param {Roo.bootstrap.DocumentManager} this
26309 * Fire after click the image
26310 * @param {Roo.bootstrap.DocumentManager} this
26311 * @param {Object} file
26316 * Fire when upload a image and editable set to true
26317 * @param {Roo.bootstrap.DocumentManager} this
26318 * @param {Object} file
26322 * @event beforeselectfile
26323 * Fire before select file
26324 * @param {Roo.bootstrap.DocumentManager} this
26326 "beforeselectfile" : true,
26329 * Fire before process file
26330 * @param {Roo.bootstrap.DocumentManager} this
26331 * @param {Object} file
26338 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26347 paramName : 'imageUpload',
26350 labelAlign : 'left',
26357 getAutoCreate : function()
26359 var managerWidget = {
26361 cls : 'roo-document-manager',
26365 cls : 'roo-document-manager-selector',
26370 cls : 'roo-document-manager-uploader',
26374 cls : 'roo-document-manager-upload-btn',
26375 html : '<i class="fa fa-plus"></i>'
26386 cls : 'column col-md-12',
26391 if(this.fieldLabel.length){
26396 cls : 'column col-md-12',
26397 html : this.fieldLabel
26401 cls : 'column col-md-12',
26406 if(this.labelAlign == 'left'){
26410 cls : 'column col-md-' + this.labelWidth,
26411 html : this.fieldLabel
26415 cls : 'column col-md-' + (12 - this.labelWidth),
26425 cls : 'row clearfix',
26433 initEvents : function()
26435 this.managerEl = this.el.select('.roo-document-manager', true).first();
26436 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26438 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26439 this.selectorEl.hide();
26442 this.selectorEl.attr('multiple', 'multiple');
26445 this.selectorEl.on('change', this.onFileSelected, this);
26447 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26448 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26450 this.uploader.on('click', this.onUploaderClick, this);
26452 this.renderProgressDialog();
26456 window.addEventListener("resize", function() { _this.refresh(); } );
26458 this.fireEvent('initial', this);
26461 renderProgressDialog : function()
26465 this.progressDialog = new Roo.bootstrap.Modal({
26466 cls : 'roo-document-manager-progress-dialog',
26467 allow_close : false,
26477 btnclick : function() {
26478 _this.uploadCancel();
26484 this.progressDialog.render(Roo.get(document.body));
26486 this.progress = new Roo.bootstrap.Progress({
26487 cls : 'roo-document-manager-progress',
26492 this.progress.render(this.progressDialog.getChildContainer());
26494 this.progressBar = new Roo.bootstrap.ProgressBar({
26495 cls : 'roo-document-manager-progress-bar',
26498 aria_valuemax : 12,
26502 this.progressBar.render(this.progress.getChildContainer());
26505 onUploaderClick : function(e)
26507 e.preventDefault();
26509 if(this.fireEvent('beforeselectfile', this) != false){
26510 this.selectorEl.dom.click();
26515 onFileSelected : function(e)
26517 e.preventDefault();
26519 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26523 Roo.each(this.selectorEl.dom.files, function(file){
26524 if(this.fireEvent('inspect', this, file) != false){
26525 this.files.push(file);
26535 this.selectorEl.dom.value = '';
26537 if(!this.files.length){
26541 if(this.boxes > 0 && this.files.length > this.boxes){
26542 this.files = this.files.slice(0, this.boxes);
26545 this.uploader.show();
26547 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26548 this.uploader.hide();
26557 Roo.each(this.files, function(file){
26559 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26560 var f = this.renderPreview(file);
26565 if(file.type.indexOf('image') != -1){
26566 this.delegates.push(
26568 _this.process(file);
26569 }).createDelegate(this)
26577 _this.process(file);
26578 }).createDelegate(this)
26583 this.files = files;
26585 this.delegates = this.delegates.concat(docs);
26587 if(!this.delegates.length){
26592 this.progressBar.aria_valuemax = this.delegates.length;
26599 arrange : function()
26601 if(!this.delegates.length){
26602 this.progressDialog.hide();
26607 var delegate = this.delegates.shift();
26609 this.progressDialog.show();
26611 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26613 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26618 refresh : function()
26620 this.uploader.show();
26622 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26623 this.uploader.hide();
26626 Roo.isTouch ? this.closable(false) : this.closable(true);
26628 this.fireEvent('refresh', this);
26631 onRemove : function(e, el, o)
26633 e.preventDefault();
26635 this.fireEvent('remove', this, o);
26639 remove : function(o)
26643 Roo.each(this.files, function(file){
26644 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26653 this.files = files;
26660 Roo.each(this.files, function(file){
26665 file.target.remove();
26674 onClick : function(e, el, o)
26676 e.preventDefault();
26678 this.fireEvent('click', this, o);
26682 closable : function(closable)
26684 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26686 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26698 xhrOnLoad : function(xhr)
26700 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26704 if (xhr.readyState !== 4) {
26706 this.fireEvent('exception', this, xhr);
26710 var response = Roo.decode(xhr.responseText);
26712 if(!response.success){
26714 this.fireEvent('exception', this, xhr);
26718 var file = this.renderPreview(response.data);
26720 this.files.push(file);
26726 xhrOnError : function()
26728 Roo.log('xhr on error');
26730 var response = Roo.decode(xhr.responseText);
26737 process : function(file)
26739 if(this.fireEvent('process', this, file) !== false){
26740 if(this.editable && file.type.indexOf('image') != -1){
26741 this.fireEvent('edit', this, file);
26745 this.uploadStart(file, false);
26752 uploadStart : function(file, crop)
26754 this.xhr = new XMLHttpRequest();
26756 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26761 file.xhr = this.xhr;
26763 this.managerEl.createChild({
26765 cls : 'roo-document-manager-loading',
26769 tooltip : file.name,
26770 cls : 'roo-document-manager-thumb',
26771 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26777 this.xhr.open(this.method, this.url, true);
26780 "Accept": "application/json",
26781 "Cache-Control": "no-cache",
26782 "X-Requested-With": "XMLHttpRequest"
26785 for (var headerName in headers) {
26786 var headerValue = headers[headerName];
26788 this.xhr.setRequestHeader(headerName, headerValue);
26794 this.xhr.onload = function()
26796 _this.xhrOnLoad(_this.xhr);
26799 this.xhr.onerror = function()
26801 _this.xhrOnError(_this.xhr);
26804 var formData = new FormData();
26806 formData.append('returnHTML', 'NO');
26809 formData.append('crop', crop);
26812 formData.append(this.paramName, file, file.name);
26814 if(this.fireEvent('prepare', this, formData) != false){
26815 this.xhr.send(formData);
26819 uploadCancel : function()
26826 this.delegates = [];
26828 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26835 renderPreview : function(file)
26837 if(typeof(file.target) != 'undefined' && file.target){
26841 var previewEl = this.managerEl.createChild({
26843 cls : 'roo-document-manager-preview',
26847 tooltip : file.filename,
26848 cls : 'roo-document-manager-thumb',
26849 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26854 html : '<i class="fa fa-times-circle"></i>'
26859 var close = previewEl.select('button.close', true).first();
26861 close.on('click', this.onRemove, this, file);
26863 file.target = previewEl;
26865 var image = previewEl.select('img', true).first();
26869 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26871 image.on('click', this.onClick, this, file);
26877 onPreviewLoad : function(file, image)
26879 if(typeof(file.target) == 'undefined' || !file.target){
26883 var width = image.dom.naturalWidth || image.dom.width;
26884 var height = image.dom.naturalHeight || image.dom.height;
26886 if(width > height){
26887 file.target.addClass('wide');
26891 file.target.addClass('tall');
26896 uploadFromSource : function(file, crop)
26898 this.xhr = new XMLHttpRequest();
26900 this.managerEl.createChild({
26902 cls : 'roo-document-manager-loading',
26906 tooltip : file.name,
26907 cls : 'roo-document-manager-thumb',
26908 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26914 this.xhr.open(this.method, this.url, true);
26917 "Accept": "application/json",
26918 "Cache-Control": "no-cache",
26919 "X-Requested-With": "XMLHttpRequest"
26922 for (var headerName in headers) {
26923 var headerValue = headers[headerName];
26925 this.xhr.setRequestHeader(headerName, headerValue);
26931 this.xhr.onload = function()
26933 _this.xhrOnLoad(_this.xhr);
26936 this.xhr.onerror = function()
26938 _this.xhrOnError(_this.xhr);
26941 var formData = new FormData();
26943 formData.append('returnHTML', 'NO');
26945 formData.append('crop', crop);
26947 if(typeof(file.filename) != 'undefined'){
26948 formData.append('filename', file.filename);
26951 if(typeof(file.mimetype) != 'undefined'){
26952 formData.append('mimetype', file.mimetype);
26955 if(this.fireEvent('prepare', this, formData) != false){
26956 this.xhr.send(formData);
26966 * @class Roo.bootstrap.DocumentViewer
26967 * @extends Roo.bootstrap.Component
26968 * Bootstrap DocumentViewer class
26971 * Create a new DocumentViewer
26972 * @param {Object} config The config object
26975 Roo.bootstrap.DocumentViewer = function(config){
26976 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26981 * Fire after initEvent
26982 * @param {Roo.bootstrap.DocumentViewer} this
26988 * @param {Roo.bootstrap.DocumentViewer} this
26993 * Fire after trash button
26994 * @param {Roo.bootstrap.DocumentViewer} this
27001 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27003 getAutoCreate : function()
27007 cls : 'roo-document-viewer',
27011 cls : 'roo-document-viewer-body',
27015 cls : 'roo-document-viewer-thumb',
27019 cls : 'roo-document-viewer-image'
27027 cls : 'roo-document-viewer-footer',
27030 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27038 cls : 'btn btn-default roo-document-viewer-trash',
27039 html : '<i class="fa fa-trash"></i>'
27052 initEvents : function()
27055 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27056 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27058 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27059 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27061 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27062 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27064 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27065 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27067 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27068 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.bodyEl.on('click', this.onClick, this);
27072 this.trashBtn.on('click', this.onTrash, this);
27076 initial : function()
27078 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27081 this.fireEvent('initial', this);
27085 onClick : function(e)
27087 e.preventDefault();
27089 this.fireEvent('click', this);
27092 onTrash : function(e)
27094 e.preventDefault();
27096 this.fireEvent('trash', this);
27108 * @class Roo.bootstrap.NavProgressBar
27109 * @extends Roo.bootstrap.Component
27110 * Bootstrap NavProgressBar class
27113 * Create a new nav progress bar
27114 * @param {Object} config The config object
27117 Roo.bootstrap.NavProgressBar = function(config){
27118 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27120 this.bullets = this.bullets || [];
27122 // Roo.bootstrap.NavProgressBar.register(this);
27126 * Fires when the active item changes
27127 * @param {Roo.bootstrap.NavProgressBar} this
27128 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27129 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27136 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27141 getAutoCreate : function()
27143 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27147 cls : 'roo-navigation-bar-group',
27151 cls : 'roo-navigation-top-bar'
27155 cls : 'roo-navigation-bullets-bar',
27159 cls : 'roo-navigation-bar'
27166 cls : 'roo-navigation-bottom-bar'
27176 initEvents: function()
27181 onRender : function(ct, position)
27183 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27185 if(this.bullets.length){
27186 Roo.each(this.bullets, function(b){
27195 addItem : function(cfg)
27197 var item = new Roo.bootstrap.NavProgressItem(cfg);
27199 item.parentId = this.id;
27200 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27203 var top = new Roo.bootstrap.Element({
27205 cls : 'roo-navigation-bar-text'
27208 var bottom = new Roo.bootstrap.Element({
27210 cls : 'roo-navigation-bar-text'
27213 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27214 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27216 var topText = new Roo.bootstrap.Element({
27218 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27221 var bottomText = new Roo.bootstrap.Element({
27223 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27226 topText.onRender(top.el, null);
27227 bottomText.onRender(bottom.el, null);
27230 item.bottomEl = bottom;
27233 this.barItems.push(item);
27238 getActive : function()
27240 var active = false;
27242 Roo.each(this.barItems, function(v){
27244 if (!v.isActive()) {
27256 setActiveItem : function(item)
27260 Roo.each(this.barItems, function(v){
27261 if (v.rid == item.rid) {
27265 if (v.isActive()) {
27266 v.setActive(false);
27271 item.setActive(true);
27273 this.fireEvent('changed', this, item, prev);
27276 getBarItem: function(rid)
27280 Roo.each(this.barItems, function(e) {
27281 if (e.rid != rid) {
27292 indexOfItem : function(item)
27296 Roo.each(this.barItems, function(v, i){
27298 if (v.rid != item.rid) {
27309 setActiveNext : function()
27311 var i = this.indexOfItem(this.getActive());
27313 if (i > this.barItems.length) {
27317 this.setActiveItem(this.barItems[i+1]);
27320 setActivePrev : function()
27322 var i = this.indexOfItem(this.getActive());
27328 this.setActiveItem(this.barItems[i-1]);
27331 format : function()
27333 if(!this.barItems.length){
27337 var width = 100 / this.barItems.length;
27339 Roo.each(this.barItems, function(i){
27340 i.el.setStyle('width', width + '%');
27341 i.topEl.el.setStyle('width', width + '%');
27342 i.bottomEl.el.setStyle('width', width + '%');
27351 * Nav Progress Item
27356 * @class Roo.bootstrap.NavProgressItem
27357 * @extends Roo.bootstrap.Component
27358 * Bootstrap NavProgressItem class
27359 * @cfg {String} rid the reference id
27360 * @cfg {Boolean} active (true|false) Is item active default false
27361 * @cfg {Boolean} disabled (true|false) Is item active default false
27362 * @cfg {String} html
27363 * @cfg {String} position (top|bottom) text position default bottom
27364 * @cfg {String} icon show icon instead of number
27367 * Create a new NavProgressItem
27368 * @param {Object} config The config object
27370 Roo.bootstrap.NavProgressItem = function(config){
27371 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27376 * The raw click event for the entire grid.
27377 * @param {Roo.bootstrap.NavProgressItem} this
27378 * @param {Roo.EventObject} e
27385 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27391 position : 'bottom',
27394 getAutoCreate : function()
27396 var iconCls = 'roo-navigation-bar-item-icon';
27398 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27402 cls: 'roo-navigation-bar-item',
27412 cfg.cls += ' active';
27415 cfg.cls += ' disabled';
27421 disable : function()
27423 this.setDisabled(true);
27426 enable : function()
27428 this.setDisabled(false);
27431 initEvents: function()
27433 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27435 this.iconEl.on('click', this.onClick, this);
27438 onClick : function(e)
27440 e.preventDefault();
27446 if(this.fireEvent('click', this, e) === false){
27450 this.parent().setActiveItem(this);
27453 isActive: function ()
27455 return this.active;
27458 setActive : function(state)
27460 if(this.active == state){
27464 this.active = state;
27467 this.el.addClass('active');
27471 this.el.removeClass('active');
27476 setDisabled : function(state)
27478 if(this.disabled == state){
27482 this.disabled = state;
27485 this.el.addClass('disabled');
27489 this.el.removeClass('disabled');
27492 tooltipEl : function()
27494 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27507 * @class Roo.bootstrap.FieldLabel
27508 * @extends Roo.bootstrap.Component
27509 * Bootstrap FieldLabel class
27510 * @cfg {String} html contents of the element
27511 * @cfg {String} tag tag of the element default label
27512 * @cfg {String} cls class of the element
27513 * @cfg {String} target label target
27514 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27515 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27516 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27517 * @cfg {String} iconTooltip default "This field is required"
27520 * Create a new FieldLabel
27521 * @param {Object} config The config object
27524 Roo.bootstrap.FieldLabel = function(config){
27525 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27530 * Fires after the field has been marked as invalid.
27531 * @param {Roo.form.FieldLabel} this
27532 * @param {String} msg The validation message
27537 * Fires after the field has been validated with no errors.
27538 * @param {Roo.form.FieldLabel} this
27544 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27551 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27552 validClass : 'text-success fa fa-lg fa-check',
27553 iconTooltip : 'This field is required',
27555 getAutoCreate : function(){
27559 cls : 'roo-bootstrap-field-label ' + this.cls,
27565 tooltip : this.iconTooltip
27577 initEvents: function()
27579 Roo.bootstrap.Element.superclass.initEvents.call(this);
27581 this.iconEl = this.el.select('i', true).first();
27583 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27585 Roo.bootstrap.FieldLabel.register(this);
27589 * Mark this field as valid
27591 markValid : function()
27593 this.iconEl.show();
27595 this.iconEl.removeClass(this.invalidClass);
27597 this.iconEl.addClass(this.validClass);
27599 this.fireEvent('valid', this);
27603 * Mark this field as invalid
27604 * @param {String} msg The validation message
27606 markInvalid : function(msg)
27608 this.iconEl.show();
27610 this.iconEl.removeClass(this.validClass);
27612 this.iconEl.addClass(this.invalidClass);
27614 this.fireEvent('invalid', this, msg);
27620 Roo.apply(Roo.bootstrap.FieldLabel, {
27625 * register a FieldLabel Group
27626 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27628 register : function(label)
27630 if(this.groups.hasOwnProperty(label.target)){
27634 this.groups[label.target] = label;
27638 * fetch a FieldLabel Group based on the target
27639 * @param {string} target
27640 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27642 get: function(target) {
27643 if (typeof(this.groups[target]) == 'undefined') {
27647 return this.groups[target] ;
27656 * page DateSplitField.
27662 * @class Roo.bootstrap.DateSplitField
27663 * @extends Roo.bootstrap.Component
27664 * Bootstrap DateSplitField class
27665 * @cfg {string} fieldLabel - the label associated
27666 * @cfg {Number} labelWidth set the width of label (0-12)
27667 * @cfg {String} labelAlign (top|left)
27668 * @cfg {Boolean} dayAllowBlank (true|false) default false
27669 * @cfg {Boolean} monthAllowBlank (true|false) default false
27670 * @cfg {Boolean} yearAllowBlank (true|false) default false
27671 * @cfg {string} dayPlaceholder
27672 * @cfg {string} monthPlaceholder
27673 * @cfg {string} yearPlaceholder
27674 * @cfg {string} dayFormat default 'd'
27675 * @cfg {string} monthFormat default 'm'
27676 * @cfg {string} yearFormat default 'Y'
27680 * Create a new DateSplitField
27681 * @param {Object} config The config object
27684 Roo.bootstrap.DateSplitField = function(config){
27685 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27691 * getting the data of years
27692 * @param {Roo.bootstrap.DateSplitField} this
27693 * @param {Object} years
27698 * getting the data of days
27699 * @param {Roo.bootstrap.DateSplitField} this
27700 * @param {Object} days
27705 * Fires after the field has been marked as invalid.
27706 * @param {Roo.form.Field} this
27707 * @param {String} msg The validation message
27712 * Fires after the field has been validated with no errors.
27713 * @param {Roo.form.Field} this
27719 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27722 labelAlign : 'top',
27724 dayAllowBlank : false,
27725 monthAllowBlank : false,
27726 yearAllowBlank : false,
27727 dayPlaceholder : '',
27728 monthPlaceholder : '',
27729 yearPlaceholder : '',
27733 isFormField : true,
27735 getAutoCreate : function()
27739 cls : 'row roo-date-split-field-group',
27744 cls : 'form-hidden-field roo-date-split-field-group-value',
27750 if(this.fieldLabel){
27753 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27757 html : this.fieldLabel
27763 Roo.each(['day', 'month', 'year'], function(t){
27766 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27773 inputEl: function ()
27775 return this.el.select('.roo-date-split-field-group-value', true).first();
27778 onRender : function(ct, position)
27782 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27784 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27786 this.dayField = new Roo.bootstrap.ComboBox({
27787 allowBlank : this.dayAllowBlank,
27788 alwaysQuery : true,
27789 displayField : 'value',
27792 forceSelection : true,
27794 placeholder : this.dayPlaceholder,
27795 selectOnFocus : true,
27796 tpl : '<div class="select2-result"><b>{value}</b></div>',
27797 triggerAction : 'all',
27799 valueField : 'value',
27800 store : new Roo.data.SimpleStore({
27801 data : (function() {
27803 _this.fireEvent('days', _this, days);
27806 fields : [ 'value' ]
27809 select : function (_self, record, index)
27811 _this.setValue(_this.getValue());
27816 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27818 this.monthField = new Roo.bootstrap.MonthField({
27819 after : '<i class=\"fa fa-calendar\"></i>',
27820 allowBlank : this.monthAllowBlank,
27821 placeholder : this.monthPlaceholder,
27824 render : function (_self)
27826 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27827 e.preventDefault();
27831 select : function (_self, oldvalue, newvalue)
27833 _this.setValue(_this.getValue());
27838 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27840 this.yearField = new Roo.bootstrap.ComboBox({
27841 allowBlank : this.yearAllowBlank,
27842 alwaysQuery : true,
27843 displayField : 'value',
27846 forceSelection : true,
27848 placeholder : this.yearPlaceholder,
27849 selectOnFocus : true,
27850 tpl : '<div class="select2-result"><b>{value}</b></div>',
27851 triggerAction : 'all',
27853 valueField : 'value',
27854 store : new Roo.data.SimpleStore({
27855 data : (function() {
27857 _this.fireEvent('years', _this, years);
27860 fields : [ 'value' ]
27863 select : function (_self, record, index)
27865 _this.setValue(_this.getValue());
27870 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27873 setValue : function(v, format)
27875 this.inputEl.dom.value = v;
27877 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27879 var d = Date.parseDate(v, f);
27886 this.setDay(d.format(this.dayFormat));
27887 this.setMonth(d.format(this.monthFormat));
27888 this.setYear(d.format(this.yearFormat));
27895 setDay : function(v)
27897 this.dayField.setValue(v);
27898 this.inputEl.dom.value = this.getValue();
27903 setMonth : function(v)
27905 this.monthField.setValue(v, true);
27906 this.inputEl.dom.value = this.getValue();
27911 setYear : function(v)
27913 this.yearField.setValue(v);
27914 this.inputEl.dom.value = this.getValue();
27919 getDay : function()
27921 return this.dayField.getValue();
27924 getMonth : function()
27926 return this.monthField.getValue();
27929 getYear : function()
27931 return this.yearField.getValue();
27934 getValue : function()
27936 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27938 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27948 this.inputEl.dom.value = '';
27953 validate : function()
27955 var d = this.dayField.validate();
27956 var m = this.monthField.validate();
27957 var y = this.yearField.validate();
27962 (!this.dayAllowBlank && !d) ||
27963 (!this.monthAllowBlank && !m) ||
27964 (!this.yearAllowBlank && !y)
27969 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27978 this.markInvalid();
27983 markValid : function()
27986 var label = this.el.select('label', true).first();
27987 var icon = this.el.select('i.fa-star', true).first();
27993 this.fireEvent('valid', this);
27997 * Mark this field as invalid
27998 * @param {String} msg The validation message
28000 markInvalid : function(msg)
28003 var label = this.el.select('label', true).first();
28004 var icon = this.el.select('i.fa-star', true).first();
28006 if(label && !icon){
28007 this.el.select('.roo-date-split-field-label', true).createChild({
28009 cls : 'text-danger fa fa-lg fa-star',
28010 tooltip : 'This field is required',
28011 style : 'margin-right:5px;'
28015 this.fireEvent('invalid', this, msg);
28018 clearInvalid : function()
28020 var label = this.el.select('label', true).first();
28021 var icon = this.el.select('i.fa-star', true).first();
28027 this.fireEvent('valid', this);
28030 getName: function()