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');
1987 this.el.on('touchstart' , this.onTouch, this);
1989 this.el.on('click' , this.onClick, this);
1991 this.el.on("mouseover", this.onMouseOver, this);
1992 this.el.on("mouseout", this.onMouseOut, this);
1996 findTargetItem : function(e)
1998 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2002 //Roo.log(t); Roo.log(t.id);
2004 //Roo.log(this.menuitems);
2005 return this.menuitems.get(t.id);
2007 //return this.items.get(t.menuItemId);
2013 onTouch : function(e)
2015 //e.stopEvent(); this make the user popdown broken
2019 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');
2601 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2604 this.el.addClass('in');
2607 this.el.addClass('in');
2611 // not sure how we can show data in here..
2613 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2616 Roo.get(document.body).addClass("x-body-masked");
2617 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2619 this.el.setStyle('zIndex', '10001');
2621 this.fireEvent('show', this);
2629 Roo.get(document.body).removeClass("x-body-masked");
2630 this.el.removeClass('in');
2631 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2633 if(this.animate){ // why
2635 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2637 this.el.setStyle('display', 'none');
2640 this.fireEvent('hide', this);
2643 addButton : function(str, cb)
2647 var b = Roo.apply({}, { html : str } );
2648 b.xns = b.xns || Roo.bootstrap;
2649 b.xtype = b.xtype || 'Button';
2650 if (typeof(b.listeners) == 'undefined') {
2651 b.listeners = { click : cb.createDelegate(this) };
2654 var btn = Roo.factory(b);
2656 btn.onRender(this.el.select('.modal-footer div').first());
2662 setDefaultButton : function(btn)
2664 //this.el.select('.modal-footer').()
2666 resizeTo: function(w,h)
2670 setContentSize : function(w, h)
2674 onButtonClick: function(btn,e)
2677 this.fireEvent('btnclick', btn.name, e);
2680 * Set the title of the Dialog
2681 * @param {String} str new Title
2683 setTitle: function(str) {
2684 this.titleEl.dom.innerHTML = str;
2687 * Set the body of the Dialog
2688 * @param {String} str new Title
2690 setBody: function(str) {
2691 this.bodyEl.dom.innerHTML = str;
2694 * Set the body of the Dialog using the template
2695 * @param {Obj} data - apply this data to the template and replace the body contents.
2697 applyBody: function(obj)
2700 Roo.log("Error - using apply Body without a template");
2703 this.tmpl.overwrite(this.bodyEl, obj);
2709 Roo.apply(Roo.bootstrap.Modal, {
2711 * Button config that displays a single OK button
2720 * Button config that displays Yes and No buttons
2736 * Button config that displays OK and Cancel buttons
2751 * Button config that displays Yes, No and Cancel buttons
2774 * messagebox - can be used as a replace
2778 * @class Roo.MessageBox
2779 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2783 Roo.Msg.alert('Status', 'Changes saved successfully.');
2785 // Prompt for user data:
2786 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2788 // process text value...
2792 // Show a dialog using config options:
2794 title:'Save Changes?',
2795 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2796 buttons: Roo.Msg.YESNOCANCEL,
2803 Roo.bootstrap.MessageBox = function(){
2804 var dlg, opt, mask, waitTimer;
2805 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2806 var buttons, activeTextEl, bwidth;
2810 var handleButton = function(button){
2812 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2816 var handleHide = function(){
2818 dlg.el.removeClass(opt.cls);
2821 // Roo.TaskMgr.stop(waitTimer);
2822 // waitTimer = null;
2827 var updateButtons = function(b){
2830 buttons["ok"].hide();
2831 buttons["cancel"].hide();
2832 buttons["yes"].hide();
2833 buttons["no"].hide();
2834 //dlg.footer.dom.style.display = 'none';
2837 dlg.footerEl.dom.style.display = '';
2838 for(var k in buttons){
2839 if(typeof buttons[k] != "function"){
2842 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2843 width += buttons[k].el.getWidth()+15;
2853 var handleEsc = function(d, k, e){
2854 if(opt && opt.closable !== false){
2864 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2865 * @return {Roo.BasicDialog} The BasicDialog element
2867 getDialog : function(){
2869 dlg = new Roo.bootstrap.Modal( {
2872 //constraintoviewport:false,
2874 //collapsible : false,
2879 //buttonAlign:"center",
2880 closeClick : function(){
2881 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2884 handleButton("cancel");
2889 dlg.on("hide", handleHide);
2891 //dlg.addKeyListener(27, handleEsc);
2893 this.buttons = buttons;
2894 var bt = this.buttonText;
2895 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2896 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2897 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2898 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2900 bodyEl = dlg.bodyEl.createChild({
2902 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2903 '<textarea class="roo-mb-textarea"></textarea>' +
2904 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2906 msgEl = bodyEl.dom.firstChild;
2907 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2908 textboxEl.enableDisplayMode();
2909 textboxEl.addKeyListener([10,13], function(){
2910 if(dlg.isVisible() && opt && opt.buttons){
2913 }else if(opt.buttons.yes){
2914 handleButton("yes");
2918 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2919 textareaEl.enableDisplayMode();
2920 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2921 progressEl.enableDisplayMode();
2922 var pf = progressEl.dom.firstChild;
2924 pp = Roo.get(pf.firstChild);
2925 pp.setHeight(pf.offsetHeight);
2933 * Updates the message box body text
2934 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2935 * the XHTML-compliant non-breaking space character '&#160;')
2936 * @return {Roo.MessageBox} This message box
2938 updateText : function(text){
2939 if(!dlg.isVisible() && !opt.width){
2940 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2942 msgEl.innerHTML = text || ' ';
2944 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2945 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2947 Math.min(opt.width || cw , this.maxWidth),
2948 Math.max(opt.minWidth || this.minWidth, bwidth)
2951 activeTextEl.setWidth(w);
2953 if(dlg.isVisible()){
2954 dlg.fixedcenter = false;
2956 // to big, make it scroll. = But as usual stupid IE does not support
2959 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2960 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2961 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2963 bodyEl.dom.style.height = '';
2964 bodyEl.dom.style.overflowY = '';
2967 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2969 bodyEl.dom.style.overflowX = '';
2972 dlg.setContentSize(w, bodyEl.getHeight());
2973 if(dlg.isVisible()){
2974 dlg.fixedcenter = true;
2980 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2981 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2982 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2983 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2984 * @return {Roo.MessageBox} This message box
2986 updateProgress : function(value, text){
2988 this.updateText(text);
2990 if (pp) { // weird bug on my firefox - for some reason this is not defined
2991 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2997 * Returns true if the message box is currently displayed
2998 * @return {Boolean} True if the message box is visible, else false
3000 isVisible : function(){
3001 return dlg && dlg.isVisible();
3005 * Hides the message box if it is displayed
3008 if(this.isVisible()){
3014 * Displays a new message box, or reinitializes an existing message box, based on the config options
3015 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3016 * The following config object properties are supported:
3018 Property Type Description
3019 ---------- --------------- ------------------------------------------------------------------------------------
3020 animEl String/Element An id or Element from which the message box should animate as it opens and
3021 closes (defaults to undefined)
3022 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3023 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3024 closable Boolean False to hide the top-right close button (defaults to true). Note that
3025 progress and wait dialogs will ignore this property and always hide the
3026 close button as they can only be closed programmatically.
3027 cls String A custom CSS class to apply to the message box element
3028 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3029 displayed (defaults to 75)
3030 fn Function A callback function to execute after closing the dialog. The arguments to the
3031 function will be btn (the name of the button that was clicked, if applicable,
3032 e.g. "ok"), and text (the value of the active text field, if applicable).
3033 Progress and wait dialogs will ignore this option since they do not respond to
3034 user actions and can only be closed programmatically, so any required function
3035 should be called by the same code after it closes the dialog.
3036 icon String A CSS class that provides a background image to be used as an icon for
3037 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3038 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3039 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3040 modal Boolean False to allow user interaction with the page while the message box is
3041 displayed (defaults to true)
3042 msg String A string that will replace the existing message box body text (defaults
3043 to the XHTML-compliant non-breaking space character ' ')
3044 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3045 progress Boolean True to display a progress bar (defaults to false)
3046 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3047 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3048 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3049 title String The title text
3050 value String The string value to set into the active textbox element if displayed
3051 wait Boolean True to display a progress bar (defaults to false)
3052 width Number The width of the dialog in pixels
3059 msg: 'Please enter your address:',
3061 buttons: Roo.MessageBox.OKCANCEL,
3064 animEl: 'addAddressBtn'
3067 * @param {Object} config Configuration options
3068 * @return {Roo.MessageBox} This message box
3070 show : function(options)
3073 // this causes nightmares if you show one dialog after another
3074 // especially on callbacks..
3076 if(this.isVisible()){
3079 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3080 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3081 Roo.log("New Dialog Message:" + options.msg )
3082 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3083 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3086 var d = this.getDialog();
3088 d.setTitle(opt.title || " ");
3089 d.closeEl.setDisplayed(opt.closable !== false);
3090 activeTextEl = textboxEl;
3091 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3096 textareaEl.setHeight(typeof opt.multiline == "number" ?
3097 opt.multiline : this.defaultTextHeight);
3098 activeTextEl = textareaEl;
3107 progressEl.setDisplayed(opt.progress === true);
3108 this.updateProgress(0);
3109 activeTextEl.dom.value = opt.value || "";
3111 dlg.setDefaultButton(activeTextEl);
3113 var bs = opt.buttons;
3117 }else if(bs && bs.yes){
3118 db = buttons["yes"];
3120 dlg.setDefaultButton(db);
3122 bwidth = updateButtons(opt.buttons);
3123 this.updateText(opt.msg);
3125 d.el.addClass(opt.cls);
3127 d.proxyDrag = opt.proxyDrag === true;
3128 d.modal = opt.modal !== false;
3129 d.mask = opt.modal !== false ? mask : false;
3131 // force it to the end of the z-index stack so it gets a cursor in FF
3132 document.body.appendChild(dlg.el.dom);
3133 d.animateTarget = null;
3134 d.show(options.animEl);
3140 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3141 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3142 * and closing the message box when the process is complete.
3143 * @param {String} title The title bar text
3144 * @param {String} msg The message box body text
3145 * @return {Roo.MessageBox} This message box
3147 progress : function(title, msg){
3154 minWidth: this.minProgressWidth,
3161 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3162 * If a callback function is passed it will be called after the user clicks the button, and the
3163 * id of the button that was clicked will be passed as the only parameter to the callback
3164 * (could also be the top-right close button).
3165 * @param {String} title The title bar text
3166 * @param {String} msg The message box body text
3167 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3168 * @param {Object} scope (optional) The scope of the callback function
3169 * @return {Roo.MessageBox} This message box
3171 alert : function(title, msg, fn, scope){
3184 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3185 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3186 * You are responsible for closing the message box when the process is complete.
3187 * @param {String} msg The message box body text
3188 * @param {String} title (optional) The title bar text
3189 * @return {Roo.MessageBox} This message box
3191 wait : function(msg, title){
3202 waitTimer = Roo.TaskMgr.start({
3204 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3212 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3213 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3214 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3215 * @param {String} title The title bar text
3216 * @param {String} msg The message box body text
3217 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3218 * @param {Object} scope (optional) The scope of the callback function
3219 * @return {Roo.MessageBox} This message box
3221 confirm : function(title, msg, fn, scope){
3225 buttons: this.YESNO,
3234 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3235 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3236 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3237 * (could also be the top-right close button) and the text that was entered will be passed as the two
3238 * parameters to the callback.
3239 * @param {String} title The title bar text
3240 * @param {String} msg The message box body text
3241 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3242 * @param {Object} scope (optional) The scope of the callback function
3243 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3244 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3245 * @return {Roo.MessageBox} This message box
3247 prompt : function(title, msg, fn, scope, multiline){
3251 buttons: this.OKCANCEL,
3256 multiline: multiline,
3263 * Button config that displays a single OK button
3268 * Button config that displays Yes and No buttons
3271 YESNO : {yes:true, no:true},
3273 * Button config that displays OK and Cancel buttons
3276 OKCANCEL : {ok:true, cancel:true},
3278 * Button config that displays Yes, No and Cancel buttons
3281 YESNOCANCEL : {yes:true, no:true, cancel:true},
3284 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3287 defaultTextHeight : 75,
3289 * The maximum width in pixels of the message box (defaults to 600)
3294 * The minimum width in pixels of the message box (defaults to 100)
3299 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3300 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3303 minProgressWidth : 250,
3305 * An object containing the default button text strings that can be overriden for localized language support.
3306 * Supported properties are: ok, cancel, yes and no.
3307 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3320 * Shorthand for {@link Roo.MessageBox}
3322 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3323 Roo.Msg = Roo.Msg || Roo.MessageBox;
3332 * @class Roo.bootstrap.Navbar
3333 * @extends Roo.bootstrap.Component
3334 * Bootstrap Navbar class
3337 * Create a new Navbar
3338 * @param {Object} config The config object
3342 Roo.bootstrap.Navbar = function(config){
3343 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3347 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3356 getAutoCreate : function(){
3359 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3363 initEvents :function ()
3365 //Roo.log(this.el.select('.navbar-toggle',true));
3366 this.el.select('.navbar-toggle',true).on('click', function() {
3367 // Roo.log('click');
3368 this.el.select('.navbar-collapse',true).toggleClass('in');
3376 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3378 var size = this.el.getSize();
3379 this.maskEl.setSize(size.width, size.height);
3380 this.maskEl.enableDisplayMode("block");
3389 getChildContainer : function()
3391 if (this.el.select('.collapse').getCount()) {
3392 return this.el.select('.collapse',true).first();
3425 * @class Roo.bootstrap.NavSimplebar
3426 * @extends Roo.bootstrap.Navbar
3427 * Bootstrap Sidebar class
3429 * @cfg {Boolean} inverse is inverted color
3431 * @cfg {String} type (nav | pills | tabs)
3432 * @cfg {Boolean} arrangement stacked | justified
3433 * @cfg {String} align (left | right) alignment
3435 * @cfg {Boolean} main (true|false) main nav bar? default false
3436 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3438 * @cfg {String} tag (header|footer|nav|div) default is nav
3444 * Create a new Sidebar
3445 * @param {Object} config The config object
3449 Roo.bootstrap.NavSimplebar = function(config){
3450 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3453 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3469 getAutoCreate : function(){
3473 tag : this.tag || 'div',
3486 this.type = this.type || 'nav';
3487 if (['tabs','pills'].indexOf(this.type)!==-1) {
3488 cfg.cn[0].cls += ' nav-' + this.type
3492 if (this.type!=='nav') {
3493 Roo.log('nav type must be nav/tabs/pills')
3495 cfg.cn[0].cls += ' navbar-nav'
3501 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3502 cfg.cn[0].cls += ' nav-' + this.arrangement;
3506 if (this.align === 'right') {
3507 cfg.cn[0].cls += ' navbar-right';
3511 cfg.cls += ' navbar-inverse';
3538 * @class Roo.bootstrap.NavHeaderbar
3539 * @extends Roo.bootstrap.NavSimplebar
3540 * Bootstrap Sidebar class
3542 * @cfg {String} brand what is brand
3543 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3544 * @cfg {String} brand_href href of the brand
3545 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3546 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3547 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3548 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3551 * Create a new Sidebar
3552 * @param {Object} config The config object
3556 Roo.bootstrap.NavHeaderbar = function(config){
3557 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3561 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3568 desktopCenter : false,
3571 getAutoCreate : function(){
3574 tag: this.nav || 'nav',
3581 if (this.desktopCenter) {
3582 cn.push({cls : 'container', cn : []});
3589 cls: 'navbar-header',
3594 cls: 'navbar-toggle',
3595 'data-toggle': 'collapse',
3600 html: 'Toggle navigation'
3622 cls: 'collapse navbar-collapse',
3626 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3628 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3629 cfg.cls += ' navbar-' + this.position;
3631 // tag can override this..
3633 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3636 if (this.brand !== '') {
3639 href: this.brand_href ? this.brand_href : '#',
3640 cls: 'navbar-brand',
3648 cfg.cls += ' main-nav';
3656 getHeaderChildContainer : function()
3658 if (this.el.select('.navbar-header').getCount()) {
3659 return this.el.select('.navbar-header',true).first();
3662 return this.getChildContainer();
3666 initEvents : function()
3668 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3670 if (this.autohide) {
3675 Roo.get(document).on('scroll',function(e) {
3676 var ns = Roo.get(document).getScroll().top;
3677 var os = prevScroll;
3681 ft.removeClass('slideDown');
3682 ft.addClass('slideUp');
3685 ft.removeClass('slideUp');
3686 ft.addClass('slideDown');
3707 * @class Roo.bootstrap.NavSidebar
3708 * @extends Roo.bootstrap.Navbar
3709 * Bootstrap Sidebar class
3712 * Create a new Sidebar
3713 * @param {Object} config The config object
3717 Roo.bootstrap.NavSidebar = function(config){
3718 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3721 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3723 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3725 getAutoCreate : function(){
3730 cls: 'sidebar sidebar-nav'
3752 * @class Roo.bootstrap.NavGroup
3753 * @extends Roo.bootstrap.Component
3754 * Bootstrap NavGroup class
3755 * @cfg {String} align (left|right)
3756 * @cfg {Boolean} inverse
3757 * @cfg {String} type (nav|pills|tab) default nav
3758 * @cfg {String} navId - reference Id for navbar.
3762 * Create a new nav group
3763 * @param {Object} config The config object
3766 Roo.bootstrap.NavGroup = function(config){
3767 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3770 Roo.bootstrap.NavGroup.register(this);
3774 * Fires when the active item changes
3775 * @param {Roo.bootstrap.NavGroup} this
3776 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3777 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3784 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3795 getAutoCreate : function()
3797 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3804 if (['tabs','pills'].indexOf(this.type)!==-1) {
3805 cfg.cls += ' nav-' + this.type
3807 if (this.type!=='nav') {
3808 Roo.log('nav type must be nav/tabs/pills')
3810 cfg.cls += ' navbar-nav'
3813 if (this.parent().sidebar) {
3816 cls: 'dashboard-menu sidebar-menu'
3822 if (this.form === true) {
3828 if (this.align === 'right') {
3829 cfg.cls += ' navbar-right';
3831 cfg.cls += ' navbar-left';
3835 if (this.align === 'right') {
3836 cfg.cls += ' navbar-right';
3840 cfg.cls += ' navbar-inverse';
3848 * sets the active Navigation item
3849 * @param {Roo.bootstrap.NavItem} the new current navitem
3851 setActiveItem : function(item)
3854 Roo.each(this.navItems, function(v){
3859 v.setActive(false, true);
3866 item.setActive(true, true);
3867 this.fireEvent('changed', this, item, prev);
3872 * gets the active Navigation item
3873 * @return {Roo.bootstrap.NavItem} the current navitem
3875 getActive : function()
3879 Roo.each(this.navItems, function(v){
3890 indexOfNav : function()
3894 Roo.each(this.navItems, function(v,i){
3905 * adds a Navigation item
3906 * @param {Roo.bootstrap.NavItem} the navitem to add
3908 addItem : function(cfg)
3910 var cn = new Roo.bootstrap.NavItem(cfg);
3912 cn.parentId = this.id;
3913 cn.onRender(this.el, null);
3917 * register a Navigation item
3918 * @param {Roo.bootstrap.NavItem} the navitem to add
3920 register : function(item)
3922 this.navItems.push( item);
3923 item.navId = this.navId;
3928 * clear all the Navigation item
3931 clearAll : function()
3934 this.el.dom.innerHTML = '';
3937 getNavItem: function(tabId)
3940 Roo.each(this.navItems, function(e) {
3941 if (e.tabId == tabId) {
3951 setActiveNext : function()
3953 var i = this.indexOfNav(this.getActive());
3954 if (i > this.navItems.length) {
3957 this.setActiveItem(this.navItems[i+1]);
3959 setActivePrev : function()
3961 var i = this.indexOfNav(this.getActive());
3965 this.setActiveItem(this.navItems[i-1]);
3967 clearWasActive : function(except) {
3968 Roo.each(this.navItems, function(e) {
3969 if (e.tabId != except.tabId && e.was_active) {
3970 e.was_active = false;
3977 getWasActive : function ()
3980 Roo.each(this.navItems, function(e) {
3995 Roo.apply(Roo.bootstrap.NavGroup, {
3999 * register a Navigation Group
4000 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4002 register : function(navgrp)
4004 this.groups[navgrp.navId] = navgrp;
4008 * fetch a Navigation Group based on the navigation ID
4009 * @param {string} the navgroup to add
4010 * @returns {Roo.bootstrap.NavGroup} the navgroup
4012 get: function(navId) {
4013 if (typeof(this.groups[navId]) == 'undefined') {
4015 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4017 return this.groups[navId] ;
4032 * @class Roo.bootstrap.NavItem
4033 * @extends Roo.bootstrap.Component
4034 * Bootstrap Navbar.NavItem class
4035 * @cfg {String} href link to
4036 * @cfg {String} html content of button
4037 * @cfg {String} badge text inside badge
4038 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4039 * @cfg {String} glyphicon name of glyphicon
4040 * @cfg {String} icon name of font awesome icon
4041 * @cfg {Boolean} active Is item active
4042 * @cfg {Boolean} disabled Is item disabled
4044 * @cfg {Boolean} preventDefault (true | false) default false
4045 * @cfg {String} tabId the tab that this item activates.
4046 * @cfg {String} tagtype (a|span) render as a href or span?
4047 * @cfg {Boolean} animateRef (true|false) link to element default false
4050 * Create a new Navbar Item
4051 * @param {Object} config The config object
4053 Roo.bootstrap.NavItem = function(config){
4054 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4059 * The raw click event for the entire grid.
4060 * @param {Roo.EventObject} e
4065 * Fires when the active item active state changes
4066 * @param {Roo.bootstrap.NavItem} this
4067 * @param {boolean} state the new state
4073 * Fires when scroll to element
4074 * @param {Roo.bootstrap.NavItem} this
4075 * @param {Object} options
4076 * @param {Roo.EventObject} e
4084 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4092 preventDefault : false,
4099 getAutoCreate : function(){
4108 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4110 if (this.disabled) {
4111 cfg.cls += ' disabled';
4114 if (this.href || this.html || this.glyphicon || this.icon) {
4118 href : this.href || "#",
4119 html: this.html || ''
4124 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4127 if(this.glyphicon) {
4128 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4133 cfg.cn[0].html += " <span class='caret'></span>";
4137 if (this.badge !== '') {
4139 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4147 initEvents: function()
4149 if (typeof (this.menu) != 'undefined') {
4150 this.menu.parentType = this.xtype;
4151 this.menu.triggerEl = this.el;
4152 this.menu = this.addxtype(Roo.apply({}, this.menu));
4155 this.el.select('a',true).on('click', this.onClick, this);
4157 if(this.tagtype == 'span'){
4158 this.el.select('span',true).on('click', this.onClick, this);
4161 // at this point parent should be available..
4162 this.parent().register(this);
4165 onClick : function(e)
4168 this.preventDefault ||
4175 if (this.disabled) {
4179 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4180 if (tg && tg.transition) {
4181 Roo.log("waiting for the transitionend");
4187 //Roo.log("fire event clicked");
4188 if(this.fireEvent('click', this, e) === false){
4192 if(this.tagtype == 'span'){
4196 //Roo.log(this.href);
4197 var ael = this.el.select('a',true).first();
4200 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4201 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4202 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4203 return; // ignore... - it's a 'hash' to another page.
4207 this.scrollToElement(e);
4211 var p = this.parent();
4213 if (['tabs','pills'].indexOf(p.type)!==-1) {
4214 if (typeof(p.setActiveItem) !== 'undefined') {
4215 p.setActiveItem(this);
4219 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4220 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4221 // remove the collapsed menu expand...
4222 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4226 isActive: function () {
4229 setActive : function(state, fire, is_was_active)
4231 if (this.active && !state && this.navId) {
4232 this.was_active = true;
4233 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4235 nv.clearWasActive(this);
4239 this.active = state;
4242 this.el.removeClass('active');
4243 } else if (!this.el.hasClass('active')) {
4244 this.el.addClass('active');
4247 this.fireEvent('changed', this, state);
4250 // show a panel if it's registered and related..
4252 if (!this.navId || !this.tabId || !state || is_was_active) {
4256 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4260 var pan = tg.getPanelByName(this.tabId);
4264 // if we can not flip to new panel - go back to old nav highlight..
4265 if (false == tg.showPanel(pan)) {
4266 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4268 var onav = nv.getWasActive();
4270 onav.setActive(true, false, true);
4279 // this should not be here...
4280 setDisabled : function(state)
4282 this.disabled = state;
4284 this.el.removeClass('disabled');
4285 } else if (!this.el.hasClass('disabled')) {
4286 this.el.addClass('disabled');
4292 * Fetch the element to display the tooltip on.
4293 * @return {Roo.Element} defaults to this.el
4295 tooltipEl : function()
4297 return this.el.select('' + this.tagtype + '', true).first();
4300 scrollToElement : function(e)
4302 var c = document.body;
4305 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4307 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4308 c = document.documentElement;
4311 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4317 var o = target.calcOffsetsTo(c);
4324 this.fireEvent('scrollto', this, options, e);
4326 Roo.get(c).scrollTo('top', options.value, true);
4339 * <span> icon </span>
4340 * <span> text </span>
4341 * <span>badge </span>
4345 * @class Roo.bootstrap.NavSidebarItem
4346 * @extends Roo.bootstrap.NavItem
4347 * Bootstrap Navbar.NavSidebarItem class
4348 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4350 * Create a new Navbar Button
4351 * @param {Object} config The config object
4353 Roo.bootstrap.NavSidebarItem = function(config){
4354 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4359 * The raw click event for the entire grid.
4360 * @param {Roo.EventObject} e
4365 * Fires when the active item active state changes
4366 * @param {Roo.bootstrap.NavSidebarItem} this
4367 * @param {boolean} state the new state
4375 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4377 badgeWeight : 'default',
4379 getAutoCreate : function(){
4384 href : this.href || '#',
4396 html : this.html || ''
4401 cfg.cls += ' active';
4404 if (this.disabled) {
4405 cfg.cls += ' disabled';
4409 if (this.glyphicon || this.icon) {
4410 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4411 a.cn.push({ tag : 'i', cls : c }) ;
4416 if (this.badge !== '') {
4418 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4422 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4423 a.cls += 'dropdown-toggle treeview' ;
4434 initEvents : function()
4436 this.el.on('click', this.onClick, this);
4439 if(this.badge !== ''){
4441 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4446 onClick : function(e)
4453 if(this.preventDefault){
4457 this.fireEvent('click', this);
4460 disable : function()
4462 this.setDisabled(true);
4467 this.setDisabled(false);
4470 setDisabled : function(state)
4472 if(this.disabled == state){
4476 this.disabled = state;
4479 this.el.addClass('disabled');
4483 this.el.removeClass('disabled');
4488 setActive : function(state)
4490 if(this.active == state){
4494 this.active = state;
4497 this.el.addClass('active');
4501 this.el.removeClass('active');
4506 isActive: function ()
4511 setBadge : function(str)
4517 this.badgeEl.dom.innerHTML = str;
4534 * @class Roo.bootstrap.Row
4535 * @extends Roo.bootstrap.Component
4536 * Bootstrap Row class (contains columns...)
4540 * @param {Object} config The config object
4543 Roo.bootstrap.Row = function(config){
4544 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4547 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4549 getAutoCreate : function(){
4568 * @class Roo.bootstrap.Element
4569 * @extends Roo.bootstrap.Component
4570 * Bootstrap Element class
4571 * @cfg {String} html contents of the element
4572 * @cfg {String} tag tag of the element
4573 * @cfg {String} cls class of the element
4574 * @cfg {Boolean} preventDefault (true|false) default false
4575 * @cfg {Boolean} clickable (true|false) default false
4578 * Create a new Element
4579 * @param {Object} config The config object
4582 Roo.bootstrap.Element = function(config){
4583 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4589 * When a element is chick
4590 * @param {Roo.bootstrap.Element} this
4591 * @param {Roo.EventObject} e
4597 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4602 preventDefault: false,
4605 getAutoCreate : function(){
4616 initEvents: function()
4618 Roo.bootstrap.Element.superclass.initEvents.call(this);
4621 this.el.on('click', this.onClick, this);
4626 onClick : function(e)
4628 if(this.preventDefault){
4632 this.fireEvent('click', this, e);
4635 getValue : function()
4637 return this.el.dom.innerHTML;
4640 setValue : function(value)
4642 this.el.dom.innerHTML = value;
4657 * @class Roo.bootstrap.Pagination
4658 * @extends Roo.bootstrap.Component
4659 * Bootstrap Pagination class
4660 * @cfg {String} size xs | sm | md | lg
4661 * @cfg {Boolean} inverse false | true
4664 * Create a new Pagination
4665 * @param {Object} config The config object
4668 Roo.bootstrap.Pagination = function(config){
4669 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4672 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4678 getAutoCreate : function(){
4684 cfg.cls += ' inverse';
4690 cfg.cls += " " + this.cls;
4708 * @class Roo.bootstrap.PaginationItem
4709 * @extends Roo.bootstrap.Component
4710 * Bootstrap PaginationItem class
4711 * @cfg {String} html text
4712 * @cfg {String} href the link
4713 * @cfg {Boolean} preventDefault (true | false) default true
4714 * @cfg {Boolean} active (true | false) default false
4715 * @cfg {Boolean} disabled default false
4719 * Create a new PaginationItem
4720 * @param {Object} config The config object
4724 Roo.bootstrap.PaginationItem = function(config){
4725 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4730 * The raw click event for the entire grid.
4731 * @param {Roo.EventObject} e
4737 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4741 preventDefault: true,
4746 getAutoCreate : function(){
4752 href : this.href ? this.href : '#',
4753 html : this.html ? this.html : ''
4763 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4767 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4773 initEvents: function() {
4775 this.el.on('click', this.onClick, this);
4778 onClick : function(e)
4780 Roo.log('PaginationItem on click ');
4781 if(this.preventDefault){
4789 this.fireEvent('click', this, e);
4805 * @class Roo.bootstrap.Slider
4806 * @extends Roo.bootstrap.Component
4807 * Bootstrap Slider class
4810 * Create a new Slider
4811 * @param {Object} config The config object
4814 Roo.bootstrap.Slider = function(config){
4815 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4818 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4820 getAutoCreate : function(){
4824 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4828 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4840 * Ext JS Library 1.1.1
4841 * Copyright(c) 2006-2007, Ext JS, LLC.
4843 * Originally Released Under LGPL - original licence link has changed is not relivant.
4846 * <script type="text/javascript">
4851 * @class Roo.grid.ColumnModel
4852 * @extends Roo.util.Observable
4853 * This is the default implementation of a ColumnModel used by the Grid. It defines
4854 * the columns in the grid.
4857 var colModel = new Roo.grid.ColumnModel([
4858 {header: "Ticker", width: 60, sortable: true, locked: true},
4859 {header: "Company Name", width: 150, sortable: true},
4860 {header: "Market Cap.", width: 100, sortable: true},
4861 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4862 {header: "Employees", width: 100, sortable: true, resizable: false}
4867 * The config options listed for this class are options which may appear in each
4868 * individual column definition.
4869 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4871 * @param {Object} config An Array of column config objects. See this class's
4872 * config objects for details.
4874 Roo.grid.ColumnModel = function(config){
4876 * The config passed into the constructor
4878 this.config = config;
4881 // if no id, create one
4882 // if the column does not have a dataIndex mapping,
4883 // map it to the order it is in the config
4884 for(var i = 0, len = config.length; i < len; i++){
4886 if(typeof c.dataIndex == "undefined"){
4889 if(typeof c.renderer == "string"){
4890 c.renderer = Roo.util.Format[c.renderer];
4892 if(typeof c.id == "undefined"){
4895 if(c.editor && c.editor.xtype){
4896 c.editor = Roo.factory(c.editor, Roo.grid);
4898 if(c.editor && c.editor.isFormField){
4899 c.editor = new Roo.grid.GridEditor(c.editor);
4901 this.lookup[c.id] = c;
4905 * The width of columns which have no width specified (defaults to 100)
4908 this.defaultWidth = 100;
4911 * Default sortable of columns which have no sortable specified (defaults to false)
4914 this.defaultSortable = false;
4918 * @event widthchange
4919 * Fires when the width of a column changes.
4920 * @param {ColumnModel} this
4921 * @param {Number} columnIndex The column index
4922 * @param {Number} newWidth The new width
4924 "widthchange": true,
4926 * @event headerchange
4927 * Fires when the text of a header changes.
4928 * @param {ColumnModel} this
4929 * @param {Number} columnIndex The column index
4930 * @param {Number} newText The new header text
4932 "headerchange": true,
4934 * @event hiddenchange
4935 * Fires when a column is hidden or "unhidden".
4936 * @param {ColumnModel} this
4937 * @param {Number} columnIndex The column index
4938 * @param {Boolean} hidden true if hidden, false otherwise
4940 "hiddenchange": true,
4942 * @event columnmoved
4943 * Fires when a column is moved.
4944 * @param {ColumnModel} this
4945 * @param {Number} oldIndex
4946 * @param {Number} newIndex
4948 "columnmoved" : true,
4950 * @event columlockchange
4951 * Fires when a column's locked state is changed
4952 * @param {ColumnModel} this
4953 * @param {Number} colIndex
4954 * @param {Boolean} locked true if locked
4956 "columnlockchange" : true
4958 Roo.grid.ColumnModel.superclass.constructor.call(this);
4960 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4962 * @cfg {String} header The header text to display in the Grid view.
4965 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4966 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4967 * specified, the column's index is used as an index into the Record's data Array.
4970 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4971 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4974 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4975 * Defaults to the value of the {@link #defaultSortable} property.
4976 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4979 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4982 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4985 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4988 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4991 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4992 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4993 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4994 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4997 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5000 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5003 * @cfg {String} cursor (Optional)
5006 * @cfg {String} tooltip (Optional)
5009 * @cfg {Number} xs (Optional)
5012 * @cfg {Number} sm (Optional)
5015 * @cfg {Number} md (Optional)
5018 * @cfg {Number} lg (Optional)
5021 * Returns the id of the column at the specified index.
5022 * @param {Number} index The column index
5023 * @return {String} the id
5025 getColumnId : function(index){
5026 return this.config[index].id;
5030 * Returns the column for a specified id.
5031 * @param {String} id The column id
5032 * @return {Object} the column
5034 getColumnById : function(id){
5035 return this.lookup[id];
5040 * Returns the column for a specified dataIndex.
5041 * @param {String} dataIndex The column dataIndex
5042 * @return {Object|Boolean} the column or false if not found
5044 getColumnByDataIndex: function(dataIndex){
5045 var index = this.findColumnIndex(dataIndex);
5046 return index > -1 ? this.config[index] : false;
5050 * Returns the index for a specified column id.
5051 * @param {String} id The column id
5052 * @return {Number} the index, or -1 if not found
5054 getIndexById : function(id){
5055 for(var i = 0, len = this.config.length; i < len; i++){
5056 if(this.config[i].id == id){
5064 * Returns the index for a specified column dataIndex.
5065 * @param {String} dataIndex The column dataIndex
5066 * @return {Number} the index, or -1 if not found
5069 findColumnIndex : function(dataIndex){
5070 for(var i = 0, len = this.config.length; i < len; i++){
5071 if(this.config[i].dataIndex == dataIndex){
5079 moveColumn : function(oldIndex, newIndex){
5080 var c = this.config[oldIndex];
5081 this.config.splice(oldIndex, 1);
5082 this.config.splice(newIndex, 0, c);
5083 this.dataMap = null;
5084 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5087 isLocked : function(colIndex){
5088 return this.config[colIndex].locked === true;
5091 setLocked : function(colIndex, value, suppressEvent){
5092 if(this.isLocked(colIndex) == value){
5095 this.config[colIndex].locked = value;
5097 this.fireEvent("columnlockchange", this, colIndex, value);
5101 getTotalLockedWidth : function(){
5103 for(var i = 0; i < this.config.length; i++){
5104 if(this.isLocked(i) && !this.isHidden(i)){
5105 this.totalWidth += this.getColumnWidth(i);
5111 getLockedCount : function(){
5112 for(var i = 0, len = this.config.length; i < len; i++){
5113 if(!this.isLocked(i)){
5120 * Returns the number of columns.
5123 getColumnCount : function(visibleOnly){
5124 if(visibleOnly === true){
5126 for(var i = 0, len = this.config.length; i < len; i++){
5127 if(!this.isHidden(i)){
5133 return this.config.length;
5137 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5138 * @param {Function} fn
5139 * @param {Object} scope (optional)
5140 * @return {Array} result
5142 getColumnsBy : function(fn, scope){
5144 for(var i = 0, len = this.config.length; i < len; i++){
5145 var c = this.config[i];
5146 if(fn.call(scope||this, c, i) === true){
5154 * Returns true if the specified column is sortable.
5155 * @param {Number} col The column index
5158 isSortable : function(col){
5159 if(typeof this.config[col].sortable == "undefined"){
5160 return this.defaultSortable;
5162 return this.config[col].sortable;
5166 * Returns the rendering (formatting) function defined for the column.
5167 * @param {Number} col The column index.
5168 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5170 getRenderer : function(col){
5171 if(!this.config[col].renderer){
5172 return Roo.grid.ColumnModel.defaultRenderer;
5174 return this.config[col].renderer;
5178 * Sets the rendering (formatting) function for a column.
5179 * @param {Number} col The column index
5180 * @param {Function} fn The function to use to process the cell's raw data
5181 * to return HTML markup for the grid view. The render function is called with
5182 * the following parameters:<ul>
5183 * <li>Data value.</li>
5184 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5185 * <li>css A CSS style string to apply to the table cell.</li>
5186 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5187 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5188 * <li>Row index</li>
5189 * <li>Column index</li>
5190 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5192 setRenderer : function(col, fn){
5193 this.config[col].renderer = fn;
5197 * Returns the width for the specified column.
5198 * @param {Number} col The column index
5201 getColumnWidth : function(col){
5202 return this.config[col].width * 1 || this.defaultWidth;
5206 * Sets the width for a column.
5207 * @param {Number} col The column index
5208 * @param {Number} width The new width
5210 setColumnWidth : function(col, width, suppressEvent){
5211 this.config[col].width = width;
5212 this.totalWidth = null;
5214 this.fireEvent("widthchange", this, col, width);
5219 * Returns the total width of all columns.
5220 * @param {Boolean} includeHidden True to include hidden column widths
5223 getTotalWidth : function(includeHidden){
5224 if(!this.totalWidth){
5225 this.totalWidth = 0;
5226 for(var i = 0, len = this.config.length; i < len; i++){
5227 if(includeHidden || !this.isHidden(i)){
5228 this.totalWidth += this.getColumnWidth(i);
5232 return this.totalWidth;
5236 * Returns the header for the specified column.
5237 * @param {Number} col The column index
5240 getColumnHeader : function(col){
5241 return this.config[col].header;
5245 * Sets the header for a column.
5246 * @param {Number} col The column index
5247 * @param {String} header The new header
5249 setColumnHeader : function(col, header){
5250 this.config[col].header = header;
5251 this.fireEvent("headerchange", this, col, header);
5255 * Returns the tooltip for the specified column.
5256 * @param {Number} col The column index
5259 getColumnTooltip : function(col){
5260 return this.config[col].tooltip;
5263 * Sets the tooltip for a column.
5264 * @param {Number} col The column index
5265 * @param {String} tooltip The new tooltip
5267 setColumnTooltip : function(col, tooltip){
5268 this.config[col].tooltip = tooltip;
5272 * Returns the dataIndex for the specified column.
5273 * @param {Number} col The column index
5276 getDataIndex : function(col){
5277 return this.config[col].dataIndex;
5281 * Sets the dataIndex for a column.
5282 * @param {Number} col The column index
5283 * @param {Number} dataIndex The new dataIndex
5285 setDataIndex : function(col, dataIndex){
5286 this.config[col].dataIndex = dataIndex;
5292 * Returns true if the cell is editable.
5293 * @param {Number} colIndex The column index
5294 * @param {Number} rowIndex The row index
5297 isCellEditable : function(colIndex, rowIndex){
5298 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5302 * Returns the editor defined for the cell/column.
5303 * return false or null to disable editing.
5304 * @param {Number} colIndex The column index
5305 * @param {Number} rowIndex The row index
5308 getCellEditor : function(colIndex, rowIndex){
5309 return this.config[colIndex].editor;
5313 * Sets if a column is editable.
5314 * @param {Number} col The column index
5315 * @param {Boolean} editable True if the column is editable
5317 setEditable : function(col, editable){
5318 this.config[col].editable = editable;
5323 * Returns true if the column is hidden.
5324 * @param {Number} colIndex The column index
5327 isHidden : function(colIndex){
5328 return this.config[colIndex].hidden;
5333 * Returns true if the column width cannot be changed
5335 isFixed : function(colIndex){
5336 return this.config[colIndex].fixed;
5340 * Returns true if the column can be resized
5343 isResizable : function(colIndex){
5344 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5347 * Sets if a column is hidden.
5348 * @param {Number} colIndex The column index
5349 * @param {Boolean} hidden True if the column is hidden
5351 setHidden : function(colIndex, hidden){
5352 this.config[colIndex].hidden = hidden;
5353 this.totalWidth = null;
5354 this.fireEvent("hiddenchange", this, colIndex, hidden);
5358 * Sets the editor for a column.
5359 * @param {Number} col The column index
5360 * @param {Object} editor The editor object
5362 setEditor : function(col, editor){
5363 this.config[col].editor = editor;
5367 Roo.grid.ColumnModel.defaultRenderer = function(value){
5368 if(typeof value == "string" && value.length < 1){
5374 // Alias for backwards compatibility
5375 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5378 * Ext JS Library 1.1.1
5379 * Copyright(c) 2006-2007, Ext JS, LLC.
5381 * Originally Released Under LGPL - original licence link has changed is not relivant.
5384 * <script type="text/javascript">
5388 * @class Roo.LoadMask
5389 * A simple utility class for generically masking elements while loading data. If the element being masked has
5390 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5391 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5392 * element's UpdateManager load indicator and will be destroyed after the initial load.
5394 * Create a new LoadMask
5395 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5396 * @param {Object} config The config object
5398 Roo.LoadMask = function(el, config){
5399 this.el = Roo.get(el);
5400 Roo.apply(this, config);
5402 this.store.on('beforeload', this.onBeforeLoad, this);
5403 this.store.on('load', this.onLoad, this);
5404 this.store.on('loadexception', this.onLoadException, this);
5405 this.removeMask = false;
5407 var um = this.el.getUpdateManager();
5408 um.showLoadIndicator = false; // disable the default indicator
5409 um.on('beforeupdate', this.onBeforeLoad, this);
5410 um.on('update', this.onLoad, this);
5411 um.on('failure', this.onLoad, this);
5412 this.removeMask = true;
5416 Roo.LoadMask.prototype = {
5418 * @cfg {Boolean} removeMask
5419 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5420 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5424 * The text to display in a centered loading message box (defaults to 'Loading...')
5428 * @cfg {String} msgCls
5429 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5431 msgCls : 'x-mask-loading',
5434 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5440 * Disables the mask to prevent it from being displayed
5442 disable : function(){
5443 this.disabled = true;
5447 * Enables the mask so that it can be displayed
5449 enable : function(){
5450 this.disabled = false;
5453 onLoadException : function()
5457 if (typeof(arguments[3]) != 'undefined') {
5458 Roo.MessageBox.alert("Error loading",arguments[3]);
5462 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5463 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5472 this.el.unmask(this.removeMask);
5477 this.el.unmask(this.removeMask);
5481 onBeforeLoad : function(){
5483 this.el.mask(this.msg, this.msgCls);
5488 destroy : function(){
5490 this.store.un('beforeload', this.onBeforeLoad, this);
5491 this.store.un('load', this.onLoad, this);
5492 this.store.un('loadexception', this.onLoadException, this);
5494 var um = this.el.getUpdateManager();
5495 um.un('beforeupdate', this.onBeforeLoad, this);
5496 um.un('update', this.onLoad, this);
5497 um.un('failure', this.onLoad, this);
5508 * @class Roo.bootstrap.Table
5509 * @extends Roo.bootstrap.Component
5510 * Bootstrap Table class
5511 * @cfg {String} cls table class
5512 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5513 * @cfg {String} bgcolor Specifies the background color for a table
5514 * @cfg {Number} border Specifies whether the table cells should have borders or not
5515 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5516 * @cfg {Number} cellspacing Specifies the space between cells
5517 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5518 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5519 * @cfg {String} sortable Specifies that the table should be sortable
5520 * @cfg {String} summary Specifies a summary of the content of a table
5521 * @cfg {Number} width Specifies the width of a table
5522 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5524 * @cfg {boolean} striped Should the rows be alternative striped
5525 * @cfg {boolean} bordered Add borders to the table
5526 * @cfg {boolean} hover Add hover highlighting
5527 * @cfg {boolean} condensed Format condensed
5528 * @cfg {boolean} responsive Format condensed
5529 * @cfg {Boolean} loadMask (true|false) default false
5530 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5531 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5532 * @cfg {Boolean} rowSelection (true|false) default false
5533 * @cfg {Boolean} cellSelection (true|false) default false
5534 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5538 * Create a new Table
5539 * @param {Object} config The config object
5542 Roo.bootstrap.Table = function(config){
5543 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5546 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5547 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5548 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5549 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5553 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5554 this.sm = this.selModel;
5555 this.sm.xmodule = this.xmodule || false;
5557 if (this.cm && typeof(this.cm.config) == 'undefined') {
5558 this.colModel = new Roo.grid.ColumnModel(this.cm);
5559 this.cm = this.colModel;
5560 this.cm.xmodule = this.xmodule || false;
5563 this.store= Roo.factory(this.store, Roo.data);
5564 this.ds = this.store;
5565 this.ds.xmodule = this.xmodule || false;
5568 if (this.footer && this.store) {
5569 this.footer.dataSource = this.ds;
5570 this.footer = Roo.factory(this.footer);
5577 * Fires when a cell is clicked
5578 * @param {Roo.bootstrap.Table} this
5579 * @param {Roo.Element} el
5580 * @param {Number} rowIndex
5581 * @param {Number} columnIndex
5582 * @param {Roo.EventObject} e
5586 * @event celldblclick
5587 * Fires when a cell is double clicked
5588 * @param {Roo.bootstrap.Table} this
5589 * @param {Roo.Element} el
5590 * @param {Number} rowIndex
5591 * @param {Number} columnIndex
5592 * @param {Roo.EventObject} e
5594 "celldblclick" : true,
5597 * Fires when a row is clicked
5598 * @param {Roo.bootstrap.Table} this
5599 * @param {Roo.Element} el
5600 * @param {Number} rowIndex
5601 * @param {Roo.EventObject} e
5605 * @event rowdblclick
5606 * Fires when a row is double clicked
5607 * @param {Roo.bootstrap.Table} this
5608 * @param {Roo.Element} el
5609 * @param {Number} rowIndex
5610 * @param {Roo.EventObject} e
5612 "rowdblclick" : true,
5615 * Fires when a mouseover occur
5616 * @param {Roo.bootstrap.Table} this
5617 * @param {Roo.Element} el
5618 * @param {Number} rowIndex
5619 * @param {Number} columnIndex
5620 * @param {Roo.EventObject} e
5625 * Fires when a mouseout occur
5626 * @param {Roo.bootstrap.Table} this
5627 * @param {Roo.Element} el
5628 * @param {Number} rowIndex
5629 * @param {Number} columnIndex
5630 * @param {Roo.EventObject} e
5635 * Fires when a row is rendered, so you can change add a style to it.
5636 * @param {Roo.bootstrap.Table} this
5637 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5641 * @event rowsrendered
5642 * Fires when all the rows have been rendered
5643 * @param {Roo.bootstrap.Table} this
5645 'rowsrendered' : true
5650 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5675 rowSelection : false,
5676 cellSelection : false,
5679 // Roo.Element - the tbody
5682 getAutoCreate : function(){
5683 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5692 cfg.cls += ' table-striped';
5696 cfg.cls += ' table-hover';
5698 if (this.bordered) {
5699 cfg.cls += ' table-bordered';
5701 if (this.condensed) {
5702 cfg.cls += ' table-condensed';
5704 if (this.responsive) {
5705 cfg.cls += ' table-responsive';
5709 cfg.cls+= ' ' +this.cls;
5712 // this lot should be simplifed...
5715 cfg.align=this.align;
5718 cfg.bgcolor=this.bgcolor;
5721 cfg.border=this.border;
5723 if (this.cellpadding) {
5724 cfg.cellpadding=this.cellpadding;
5726 if (this.cellspacing) {
5727 cfg.cellspacing=this.cellspacing;
5730 cfg.frame=this.frame;
5733 cfg.rules=this.rules;
5735 if (this.sortable) {
5736 cfg.sortable=this.sortable;
5739 cfg.summary=this.summary;
5742 cfg.width=this.width;
5745 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5748 if(this.store || this.cm){
5749 if(this.headerShow){
5750 cfg.cn.push(this.renderHeader());
5753 cfg.cn.push(this.renderBody());
5755 if(this.footerShow){
5756 cfg.cn.push(this.renderFooter());
5759 cfg.cls+= ' TableGrid';
5762 return { cn : [ cfg ] };
5765 initEvents : function()
5767 if(!this.store || !this.cm){
5771 //Roo.log('initEvents with ds!!!!');
5773 this.mainBody = this.el.select('tbody', true).first();
5778 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5779 e.on('click', _this.sort, _this);
5782 this.el.on("click", this.onClick, this);
5783 this.el.on("dblclick", this.onDblClick, this);
5785 // why is this done????? = it breaks dialogs??
5786 //this.parent().el.setStyle('position', 'relative');
5790 this.footer.parentId = this.id;
5791 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5794 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5796 this.store.on('load', this.onLoad, this);
5797 this.store.on('beforeload', this.onBeforeLoad, this);
5798 this.store.on('update', this.onUpdate, this);
5799 this.store.on('add', this.onAdd, this);
5803 onMouseover : function(e, el)
5805 var cell = Roo.get(el);
5811 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5812 cell = cell.findParent('td', false, true);
5815 var row = cell.findParent('tr', false, true);
5816 var cellIndex = cell.dom.cellIndex;
5817 var rowIndex = row.dom.rowIndex - 1; // start from 0
5819 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5823 onMouseout : function(e, el)
5825 var cell = Roo.get(el);
5831 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5832 cell = cell.findParent('td', false, true);
5835 var row = cell.findParent('tr', false, true);
5836 var cellIndex = cell.dom.cellIndex;
5837 var rowIndex = row.dom.rowIndex - 1; // start from 0
5839 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5843 onClick : function(e, el)
5845 var cell = Roo.get(el);
5847 if(!cell || (!this.cellSelection && !this.rowSelection)){
5851 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5852 cell = cell.findParent('td', false, true);
5855 if(!cell || typeof(cell) == 'undefined'){
5859 var row = cell.findParent('tr', false, true);
5861 if(!row || typeof(row) == 'undefined'){
5865 var cellIndex = cell.dom.cellIndex;
5866 var rowIndex = this.getRowIndex(row);
5868 // why??? - should these not be based on SelectionModel?
5869 if(this.cellSelection){
5870 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5873 if(this.rowSelection){
5874 this.fireEvent('rowclick', this, row, rowIndex, e);
5880 onDblClick : function(e,el)
5882 var cell = Roo.get(el);
5884 if(!cell || (!this.CellSelection && !this.RowSelection)){
5888 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5889 cell = cell.findParent('td', false, true);
5892 if(!cell || typeof(cell) == 'undefined'){
5896 var row = cell.findParent('tr', false, true);
5898 if(!row || typeof(row) == 'undefined'){
5902 var cellIndex = cell.dom.cellIndex;
5903 var rowIndex = this.getRowIndex(row);
5905 if(this.CellSelection){
5906 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5909 if(this.RowSelection){
5910 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5914 sort : function(e,el)
5916 var col = Roo.get(el);
5918 if(!col.hasClass('sortable')){
5922 var sort = col.attr('sort');
5925 if(col.hasClass('glyphicon-arrow-up')){
5929 this.store.sortInfo = {field : sort, direction : dir};
5932 Roo.log("calling footer first");
5933 this.footer.onClick('first');
5936 this.store.load({ params : { start : 0 } });
5940 renderHeader : function()
5949 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5951 var config = cm.config[i];
5956 html: cm.getColumnHeader(i)
5961 if(typeof(config.lgHeader) != 'undefined'){
5962 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5965 if(typeof(config.mdHeader) != 'undefined'){
5966 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5969 if(typeof(config.smHeader) != 'undefined'){
5970 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5973 if(typeof(config.xsHeader) != 'undefined'){
5974 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5981 if(typeof(config.tooltip) != 'undefined'){
5982 c.tooltip = config.tooltip;
5985 if(typeof(config.colspan) != 'undefined'){
5986 c.colspan = config.colspan;
5989 if(typeof(config.hidden) != 'undefined' && config.hidden){
5990 c.style += ' display:none;';
5993 if(typeof(config.dataIndex) != 'undefined'){
5994 c.sort = config.dataIndex;
5997 if(typeof(config.sortable) != 'undefined' && config.sortable){
6001 if(typeof(config.align) != 'undefined' && config.align.length){
6002 c.style += ' text-align:' + config.align + ';';
6005 if(typeof(config.width) != 'undefined'){
6006 c.style += ' width:' + config.width + 'px;';
6009 if(typeof(config.cls) != 'undefined'){
6010 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6013 ['xs','sm','md','lg'].map(function(size){
6015 if(typeof(config[size]) == 'undefined'){
6019 if (!config[size]) { // 0 = hidden
6020 cfg.cls += ' hidden-' + size;
6024 cfg.cls += ' col-' + size + '-' + config[size];
6034 renderBody : function()
6044 colspan : this.cm.getColumnCount()
6054 renderFooter : function()
6064 colspan : this.cm.getColumnCount()
6078 Roo.log('ds onload');
6083 var ds = this.store;
6085 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6086 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6088 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6089 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6092 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6093 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6097 var tbody = this.mainBody;
6099 if(ds.getCount() > 0){
6100 ds.data.each(function(d,rowIndex){
6101 var row = this.renderRow(cm, ds, rowIndex);
6103 tbody.createChild(row);
6107 if(row.cellObjects.length){
6108 Roo.each(row.cellObjects, function(r){
6109 _this.renderCellObject(r);
6116 Roo.each(this.el.select('tbody td', true).elements, function(e){
6117 e.on('mouseover', _this.onMouseover, _this);
6120 Roo.each(this.el.select('tbody td', true).elements, function(e){
6121 e.on('mouseout', _this.onMouseout, _this);
6123 this.fireEvent('rowsrendered', this);
6124 //if(this.loadMask){
6125 // this.maskEl.hide();
6130 onUpdate : function(ds,record)
6132 this.refreshRow(record);
6135 onRemove : function(ds, record, index, isUpdate){
6136 if(isUpdate !== true){
6137 this.fireEvent("beforerowremoved", this, index, record);
6139 var bt = this.mainBody.dom;
6141 var rows = this.el.select('tbody > tr', true).elements;
6143 if(typeof(rows[index]) != 'undefined'){
6144 bt.removeChild(rows[index].dom);
6147 // if(bt.rows[index]){
6148 // bt.removeChild(bt.rows[index]);
6151 if(isUpdate !== true){
6152 //this.stripeRows(index);
6153 //this.syncRowHeights(index, index);
6155 this.fireEvent("rowremoved", this, index, record);
6159 onAdd : function(ds, records, rowIndex)
6161 //Roo.log('on Add called');
6162 // - note this does not handle multiple adding very well..
6163 var bt = this.mainBody.dom;
6164 for (var i =0 ; i < records.length;i++) {
6165 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6166 //Roo.log(records[i]);
6167 //Roo.log(this.store.getAt(rowIndex+i));
6168 this.insertRow(this.store, rowIndex + i, false);
6175 refreshRow : function(record){
6176 var ds = this.store, index;
6177 if(typeof record == 'number'){
6179 record = ds.getAt(index);
6181 index = ds.indexOf(record);
6183 this.insertRow(ds, index, true);
6184 this.onRemove(ds, record, index+1, true);
6185 //this.syncRowHeights(index, index);
6187 this.fireEvent("rowupdated", this, index, record);
6190 insertRow : function(dm, rowIndex, isUpdate){
6193 this.fireEvent("beforerowsinserted", this, rowIndex);
6195 //var s = this.getScrollState();
6196 var row = this.renderRow(this.cm, this.store, rowIndex);
6197 // insert before rowIndex..
6198 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6202 if(row.cellObjects.length){
6203 Roo.each(row.cellObjects, function(r){
6204 _this.renderCellObject(r);
6209 this.fireEvent("rowsinserted", this, rowIndex);
6210 //this.syncRowHeights(firstRow, lastRow);
6211 //this.stripeRows(firstRow);
6218 getRowDom : function(rowIndex)
6220 var rows = this.el.select('tbody > tr', true).elements;
6222 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6225 // returns the object tree for a tr..
6228 renderRow : function(cm, ds, rowIndex)
6231 var d = ds.getAt(rowIndex);
6238 var cellObjects = [];
6240 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6241 var config = cm.config[i];
6243 var renderer = cm.getRenderer(i);
6247 if(typeof(renderer) !== 'undefined'){
6248 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6250 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6251 // and are rendered into the cells after the row is rendered - using the id for the element.
6253 if(typeof(value) === 'object'){
6263 rowIndex : rowIndex,
6268 this.fireEvent('rowclass', this, rowcfg);
6272 cls : rowcfg.rowClass,
6274 html: (typeof(value) === 'object') ? '' : value
6281 if(typeof(config.colspan) != 'undefined'){
6282 td.colspan = config.colspan;
6285 if(typeof(config.hidden) != 'undefined' && config.hidden){
6286 td.style += ' display:none;';
6289 if(typeof(config.align) != 'undefined' && config.align.length){
6290 td.style += ' text-align:' + config.align + ';';
6293 if(typeof(config.width) != 'undefined'){
6294 td.style += ' width:' + config.width + 'px;';
6297 if(typeof(config.cursor) != 'undefined'){
6298 td.style += ' cursor:' + config.cursor + ';';
6301 if(typeof(config.cls) != 'undefined'){
6302 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6305 ['xs','sm','md','lg'].map(function(size){
6307 if(typeof(config[size]) == 'undefined'){
6311 if (!config[size]) { // 0 = hidden
6312 td.cls += ' hidden-' + size;
6316 td.cls += ' col-' + size + '-' + config[size];
6324 row.cellObjects = cellObjects;
6332 onBeforeLoad : function()
6334 //Roo.log('ds onBeforeLoad');
6338 //if(this.loadMask){
6339 // this.maskEl.show();
6347 this.el.select('tbody', true).first().dom.innerHTML = '';
6350 * Show or hide a row.
6351 * @param {Number} rowIndex to show or hide
6352 * @param {Boolean} state hide
6354 setRowVisibility : function(rowIndex, state)
6356 var bt = this.mainBody.dom;
6358 var rows = this.el.select('tbody > tr', true).elements;
6360 if(typeof(rows[rowIndex]) == 'undefined'){
6363 rows[rowIndex].dom.style.display = state ? '' : 'none';
6367 getSelectionModel : function(){
6369 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6371 return this.selModel;
6374 * Render the Roo.bootstrap object from renderder
6376 renderCellObject : function(r)
6380 var t = r.cfg.render(r.container);
6383 Roo.each(r.cfg.cn, function(c){
6385 container: t.getChildContainer(),
6388 _this.renderCellObject(child);
6393 getRowIndex : function(row)
6397 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6420 * @class Roo.bootstrap.TableCell
6421 * @extends Roo.bootstrap.Component
6422 * Bootstrap TableCell class
6423 * @cfg {String} html cell contain text
6424 * @cfg {String} cls cell class
6425 * @cfg {String} tag cell tag (td|th) default td
6426 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6427 * @cfg {String} align Aligns the content in a cell
6428 * @cfg {String} axis Categorizes cells
6429 * @cfg {String} bgcolor Specifies the background color of a cell
6430 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6431 * @cfg {Number} colspan Specifies the number of columns a cell should span
6432 * @cfg {String} headers Specifies one or more header cells a cell is related to
6433 * @cfg {Number} height Sets the height of a cell
6434 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6435 * @cfg {Number} rowspan Sets the number of rows a cell should span
6436 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6437 * @cfg {String} valign Vertical aligns the content in a cell
6438 * @cfg {Number} width Specifies the width of a cell
6441 * Create a new TableCell
6442 * @param {Object} config The config object
6445 Roo.bootstrap.TableCell = function(config){
6446 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6449 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6469 getAutoCreate : function(){
6470 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6490 cfg.align=this.align
6496 cfg.bgcolor=this.bgcolor
6499 cfg.charoff=this.charoff
6502 cfg.colspan=this.colspan
6505 cfg.headers=this.headers
6508 cfg.height=this.height
6511 cfg.nowrap=this.nowrap
6514 cfg.rowspan=this.rowspan
6517 cfg.scope=this.scope
6520 cfg.valign=this.valign
6523 cfg.width=this.width
6542 * @class Roo.bootstrap.TableRow
6543 * @extends Roo.bootstrap.Component
6544 * Bootstrap TableRow class
6545 * @cfg {String} cls row class
6546 * @cfg {String} align Aligns the content in a table row
6547 * @cfg {String} bgcolor Specifies a background color for a table row
6548 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6549 * @cfg {String} valign Vertical aligns the content in a table row
6552 * Create a new TableRow
6553 * @param {Object} config The config object
6556 Roo.bootstrap.TableRow = function(config){
6557 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6560 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6568 getAutoCreate : function(){
6569 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6579 cfg.align = this.align;
6582 cfg.bgcolor = this.bgcolor;
6585 cfg.charoff = this.charoff;
6588 cfg.valign = this.valign;
6606 * @class Roo.bootstrap.TableBody
6607 * @extends Roo.bootstrap.Component
6608 * Bootstrap TableBody class
6609 * @cfg {String} cls element class
6610 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6611 * @cfg {String} align Aligns the content inside the element
6612 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6613 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6616 * Create a new TableBody
6617 * @param {Object} config The config object
6620 Roo.bootstrap.TableBody = function(config){
6621 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6624 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6632 getAutoCreate : function(){
6633 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6647 cfg.align = this.align;
6650 cfg.charoff = this.charoff;
6653 cfg.valign = this.valign;
6660 // initEvents : function()
6667 // this.store = Roo.factory(this.store, Roo.data);
6668 // this.store.on('load', this.onLoad, this);
6670 // this.store.load();
6674 // onLoad: function ()
6676 // this.fireEvent('load', this);
6686 * Ext JS Library 1.1.1
6687 * Copyright(c) 2006-2007, Ext JS, LLC.
6689 * Originally Released Under LGPL - original licence link has changed is not relivant.
6692 * <script type="text/javascript">
6695 // as we use this in bootstrap.
6696 Roo.namespace('Roo.form');
6698 * @class Roo.form.Action
6699 * Internal Class used to handle form actions
6701 * @param {Roo.form.BasicForm} el The form element or its id
6702 * @param {Object} config Configuration options
6707 // define the action interface
6708 Roo.form.Action = function(form, options){
6710 this.options = options || {};
6713 * Client Validation Failed
6716 Roo.form.Action.CLIENT_INVALID = 'client';
6718 * Server Validation Failed
6721 Roo.form.Action.SERVER_INVALID = 'server';
6723 * Connect to Server Failed
6726 Roo.form.Action.CONNECT_FAILURE = 'connect';
6728 * Reading Data from Server Failed
6731 Roo.form.Action.LOAD_FAILURE = 'load';
6733 Roo.form.Action.prototype = {
6735 failureType : undefined,
6736 response : undefined,
6740 run : function(options){
6745 success : function(response){
6750 handleResponse : function(response){
6754 // default connection failure
6755 failure : function(response){
6757 this.response = response;
6758 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6759 this.form.afterAction(this, false);
6762 processResponse : function(response){
6763 this.response = response;
6764 if(!response.responseText){
6767 this.result = this.handleResponse(response);
6771 // utility functions used internally
6772 getUrl : function(appendParams){
6773 var url = this.options.url || this.form.url || this.form.el.dom.action;
6775 var p = this.getParams();
6777 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6783 getMethod : function(){
6784 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6787 getParams : function(){
6788 var bp = this.form.baseParams;
6789 var p = this.options.params;
6791 if(typeof p == "object"){
6792 p = Roo.urlEncode(Roo.applyIf(p, bp));
6793 }else if(typeof p == 'string' && bp){
6794 p += '&' + Roo.urlEncode(bp);
6797 p = Roo.urlEncode(bp);
6802 createCallback : function(){
6804 success: this.success,
6805 failure: this.failure,
6807 timeout: (this.form.timeout*1000),
6808 upload: this.form.fileUpload ? this.success : undefined
6813 Roo.form.Action.Submit = function(form, options){
6814 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6817 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6820 haveProgress : false,
6821 uploadComplete : false,
6823 // uploadProgress indicator.
6824 uploadProgress : function()
6826 if (!this.form.progressUrl) {
6830 if (!this.haveProgress) {
6831 Roo.MessageBox.progress("Uploading", "Uploading");
6833 if (this.uploadComplete) {
6834 Roo.MessageBox.hide();
6838 this.haveProgress = true;
6840 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6842 var c = new Roo.data.Connection();
6844 url : this.form.progressUrl,
6849 success : function(req){
6850 //console.log(data);
6854 rdata = Roo.decode(req.responseText)
6856 Roo.log("Invalid data from server..");
6860 if (!rdata || !rdata.success) {
6862 Roo.MessageBox.alert(Roo.encode(rdata));
6865 var data = rdata.data;
6867 if (this.uploadComplete) {
6868 Roo.MessageBox.hide();
6873 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6874 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6877 this.uploadProgress.defer(2000,this);
6880 failure: function(data) {
6881 Roo.log('progress url failed ');
6892 // run get Values on the form, so it syncs any secondary forms.
6893 this.form.getValues();
6895 var o = this.options;
6896 var method = this.getMethod();
6897 var isPost = method == 'POST';
6898 if(o.clientValidation === false || this.form.isValid()){
6900 if (this.form.progressUrl) {
6901 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6902 (new Date() * 1) + '' + Math.random());
6907 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6908 form:this.form.el.dom,
6909 url:this.getUrl(!isPost),
6911 params:isPost ? this.getParams() : null,
6912 isUpload: this.form.fileUpload
6915 this.uploadProgress();
6917 }else if (o.clientValidation !== false){ // client validation failed
6918 this.failureType = Roo.form.Action.CLIENT_INVALID;
6919 this.form.afterAction(this, false);
6923 success : function(response)
6925 this.uploadComplete= true;
6926 if (this.haveProgress) {
6927 Roo.MessageBox.hide();
6931 var result = this.processResponse(response);
6932 if(result === true || result.success){
6933 this.form.afterAction(this, true);
6937 this.form.markInvalid(result.errors);
6938 this.failureType = Roo.form.Action.SERVER_INVALID;
6940 this.form.afterAction(this, false);
6942 failure : function(response)
6944 this.uploadComplete= true;
6945 if (this.haveProgress) {
6946 Roo.MessageBox.hide();
6949 this.response = response;
6950 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6951 this.form.afterAction(this, false);
6954 handleResponse : function(response){
6955 if(this.form.errorReader){
6956 var rs = this.form.errorReader.read(response);
6959 for(var i = 0, len = rs.records.length; i < len; i++) {
6960 var r = rs.records[i];
6964 if(errors.length < 1){
6968 success : rs.success,
6974 ret = Roo.decode(response.responseText);
6978 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6988 Roo.form.Action.Load = function(form, options){
6989 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6990 this.reader = this.form.reader;
6993 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6998 Roo.Ajax.request(Roo.apply(
6999 this.createCallback(), {
7000 method:this.getMethod(),
7001 url:this.getUrl(false),
7002 params:this.getParams()
7006 success : function(response){
7008 var result = this.processResponse(response);
7009 if(result === true || !result.success || !result.data){
7010 this.failureType = Roo.form.Action.LOAD_FAILURE;
7011 this.form.afterAction(this, false);
7014 this.form.clearInvalid();
7015 this.form.setValues(result.data);
7016 this.form.afterAction(this, true);
7019 handleResponse : function(response){
7020 if(this.form.reader){
7021 var rs = this.form.reader.read(response);
7022 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7024 success : rs.success,
7028 return Roo.decode(response.responseText);
7032 Roo.form.Action.ACTION_TYPES = {
7033 'load' : Roo.form.Action.Load,
7034 'submit' : Roo.form.Action.Submit
7043 * @class Roo.bootstrap.Form
7044 * @extends Roo.bootstrap.Component
7045 * Bootstrap Form class
7046 * @cfg {String} method GET | POST (default POST)
7047 * @cfg {String} labelAlign top | left (default top)
7048 * @cfg {String} align left | right - for navbars
7049 * @cfg {Boolean} loadMask load mask when submit (default true)
7054 * @param {Object} config The config object
7058 Roo.bootstrap.Form = function(config){
7059 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7062 * @event clientvalidation
7063 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7064 * @param {Form} this
7065 * @param {Boolean} valid true if the form has passed client-side validation
7067 clientvalidation: true,
7069 * @event beforeaction
7070 * Fires before any action is performed. Return false to cancel the action.
7071 * @param {Form} this
7072 * @param {Action} action The action to be performed
7076 * @event actionfailed
7077 * Fires when an action fails.
7078 * @param {Form} this
7079 * @param {Action} action The action that failed
7081 actionfailed : true,
7083 * @event actioncomplete
7084 * Fires when an action is completed.
7085 * @param {Form} this
7086 * @param {Action} action The action that completed
7088 actioncomplete : true
7093 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7096 * @cfg {String} method
7097 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7102 * The URL to use for form actions if one isn't supplied in the action options.
7105 * @cfg {Boolean} fileUpload
7106 * Set to true if this form is a file upload.
7110 * @cfg {Object} baseParams
7111 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7115 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7119 * @cfg {Sting} align (left|right) for navbar forms
7124 activeAction : null,
7127 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7128 * element by passing it or its id or mask the form itself by passing in true.
7131 waitMsgTarget : false,
7135 getAutoCreate : function(){
7139 method : this.method || 'POST',
7140 id : this.id || Roo.id(),
7143 if (this.parent().xtype.match(/^Nav/)) {
7144 cfg.cls = 'navbar-form navbar-' + this.align;
7148 if (this.labelAlign == 'left' ) {
7149 cfg.cls += ' form-horizontal';
7155 initEvents : function()
7157 this.el.on('submit', this.onSubmit, this);
7158 // this was added as random key presses on the form where triggering form submit.
7159 this.el.on('keypress', function(e) {
7160 if (e.getCharCode() != 13) {
7163 // we might need to allow it for textareas.. and some other items.
7164 // check e.getTarget().
7166 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7170 Roo.log("keypress blocked");
7178 onSubmit : function(e){
7183 * Returns true if client-side validation on the form is successful.
7186 isValid : function(){
7187 var items = this.getItems();
7189 items.each(function(f){
7198 * Returns true if any fields in this form have changed since their original load.
7201 isDirty : function(){
7203 var items = this.getItems();
7204 items.each(function(f){
7214 * Performs a predefined action (submit or load) or custom actions you define on this form.
7215 * @param {String} actionName The name of the action type
7216 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7217 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7218 * accept other config options):
7220 Property Type Description
7221 ---------------- --------------- ----------------------------------------------------------------------------------
7222 url String The url for the action (defaults to the form's url)
7223 method String The form method to use (defaults to the form's method, or POST if not defined)
7224 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7225 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7226 validate the form on the client (defaults to false)
7228 * @return {BasicForm} this
7230 doAction : function(action, options){
7231 if(typeof action == 'string'){
7232 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7234 if(this.fireEvent('beforeaction', this, action) !== false){
7235 this.beforeAction(action);
7236 action.run.defer(100, action);
7242 beforeAction : function(action){
7243 var o = action.options;
7246 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7248 // not really supported yet.. ??
7250 //if(this.waitMsgTarget === true){
7251 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7252 //}else if(this.waitMsgTarget){
7253 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7254 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7256 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7262 afterAction : function(action, success){
7263 this.activeAction = null;
7264 var o = action.options;
7266 //if(this.waitMsgTarget === true){
7268 //}else if(this.waitMsgTarget){
7269 // this.waitMsgTarget.unmask();
7271 // Roo.MessageBox.updateProgress(1);
7272 // Roo.MessageBox.hide();
7279 Roo.callback(o.success, o.scope, [this, action]);
7280 this.fireEvent('actioncomplete', this, action);
7284 // failure condition..
7285 // we have a scenario where updates need confirming.
7286 // eg. if a locking scenario exists..
7287 // we look for { errors : { needs_confirm : true }} in the response.
7289 (typeof(action.result) != 'undefined') &&
7290 (typeof(action.result.errors) != 'undefined') &&
7291 (typeof(action.result.errors.needs_confirm) != 'undefined')
7294 Roo.log("not supported yet");
7297 Roo.MessageBox.confirm(
7298 "Change requires confirmation",
7299 action.result.errorMsg,
7304 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7314 Roo.callback(o.failure, o.scope, [this, action]);
7315 // show an error message if no failed handler is set..
7316 if (!this.hasListener('actionfailed')) {
7317 Roo.log("need to add dialog support");
7319 Roo.MessageBox.alert("Error",
7320 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7321 action.result.errorMsg :
7322 "Saving Failed, please check your entries or try again"
7327 this.fireEvent('actionfailed', this, action);
7332 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7333 * @param {String} id The value to search for
7336 findField : function(id){
7337 var items = this.getItems();
7338 var field = items.get(id);
7340 items.each(function(f){
7341 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7348 return field || null;
7351 * Mark fields in this form invalid in bulk.
7352 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7353 * @return {BasicForm} this
7355 markInvalid : function(errors){
7356 if(errors instanceof Array){
7357 for(var i = 0, len = errors.length; i < len; i++){
7358 var fieldError = errors[i];
7359 var f = this.findField(fieldError.id);
7361 f.markInvalid(fieldError.msg);
7367 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7368 field.markInvalid(errors[id]);
7372 //Roo.each(this.childForms || [], function (f) {
7373 // f.markInvalid(errors);
7380 * Set values for fields in this form in bulk.
7381 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7382 * @return {BasicForm} this
7384 setValues : function(values){
7385 if(values instanceof Array){ // array of objects
7386 for(var i = 0, len = values.length; i < len; i++){
7388 var f = this.findField(v.id);
7390 f.setValue(v.value);
7391 if(this.trackResetOnLoad){
7392 f.originalValue = f.getValue();
7396 }else{ // object hash
7399 if(typeof values[id] != 'function' && (field = this.findField(id))){
7401 if (field.setFromData &&
7403 field.displayField &&
7404 // combos' with local stores can
7405 // be queried via setValue()
7406 // to set their value..
7407 (field.store && !field.store.isLocal)
7411 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7412 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7413 field.setFromData(sd);
7416 field.setValue(values[id]);
7420 if(this.trackResetOnLoad){
7421 field.originalValue = field.getValue();
7427 //Roo.each(this.childForms || [], function (f) {
7428 // f.setValues(values);
7435 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7436 * they are returned as an array.
7437 * @param {Boolean} asString
7440 getValues : function(asString){
7441 //if (this.childForms) {
7442 // copy values from the child forms
7443 // Roo.each(this.childForms, function (f) {
7444 // this.setValues(f.getValues());
7450 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7451 if(asString === true){
7454 return Roo.urlDecode(fs);
7458 * Returns the fields in this form as an object with key/value pairs.
7459 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7462 getFieldValues : function(with_hidden)
7464 var items = this.getItems();
7466 items.each(function(f){
7470 var v = f.getValue();
7471 if (f.inputType =='radio') {
7472 if (typeof(ret[f.getName()]) == 'undefined') {
7473 ret[f.getName()] = ''; // empty..
7476 if (!f.el.dom.checked) {
7484 // not sure if this supported any more..
7485 if ((typeof(v) == 'object') && f.getRawValue) {
7486 v = f.getRawValue() ; // dates..
7488 // combo boxes where name != hiddenName...
7489 if (f.name != f.getName()) {
7490 ret[f.name] = f.getRawValue();
7492 ret[f.getName()] = v;
7499 * Clears all invalid messages in this form.
7500 * @return {BasicForm} this
7502 clearInvalid : function(){
7503 var items = this.getItems();
7505 items.each(function(f){
7516 * @return {BasicForm} this
7519 var items = this.getItems();
7520 items.each(function(f){
7524 Roo.each(this.childForms || [], function (f) {
7531 getItems : function()
7533 var r=new Roo.util.MixedCollection(false, function(o){
7534 return o.id || (o.id = Roo.id());
7536 var iter = function(el) {
7543 Roo.each(el.items,function(e) {
7563 * Ext JS Library 1.1.1
7564 * Copyright(c) 2006-2007, Ext JS, LLC.
7566 * Originally Released Under LGPL - original licence link has changed is not relivant.
7569 * <script type="text/javascript">
7572 * @class Roo.form.VTypes
7573 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7576 Roo.form.VTypes = function(){
7577 // closure these in so they are only created once.
7578 var alpha = /^[a-zA-Z_]+$/;
7579 var alphanum = /^[a-zA-Z0-9_]+$/;
7580 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7581 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7583 // All these messages and functions are configurable
7586 * The function used to validate email addresses
7587 * @param {String} value The email address
7589 'email' : function(v){
7590 return email.test(v);
7593 * The error text to display when the email validation function returns false
7596 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7598 * The keystroke filter mask to be applied on email input
7601 'emailMask' : /[a-z0-9_\.\-@]/i,
7604 * The function used to validate URLs
7605 * @param {String} value The URL
7607 'url' : function(v){
7611 * The error text to display when the url validation function returns false
7614 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7617 * The function used to validate alpha values
7618 * @param {String} value The value
7620 'alpha' : function(v){
7621 return alpha.test(v);
7624 * The error text to display when the alpha validation function returns false
7627 'alphaText' : 'This field should only contain letters and _',
7629 * The keystroke filter mask to be applied on alpha input
7632 'alphaMask' : /[a-z_]/i,
7635 * The function used to validate alphanumeric values
7636 * @param {String} value The value
7638 'alphanum' : function(v){
7639 return alphanum.test(v);
7642 * The error text to display when the alphanumeric validation function returns false
7645 'alphanumText' : 'This field should only contain letters, numbers and _',
7647 * The keystroke filter mask to be applied on alphanumeric input
7650 'alphanumMask' : /[a-z0-9_]/i
7660 * @class Roo.bootstrap.Input
7661 * @extends Roo.bootstrap.Component
7662 * Bootstrap Input class
7663 * @cfg {Boolean} disabled is it disabled
7664 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7665 * @cfg {String} name name of the input
7666 * @cfg {string} fieldLabel - the label associated
7667 * @cfg {string} placeholder - placeholder to put in text.
7668 * @cfg {string} before - input group add on before
7669 * @cfg {string} after - input group add on after
7670 * @cfg {string} size - (lg|sm) or leave empty..
7671 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7672 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7673 * @cfg {Number} md colspan out of 12 for computer-sized screens
7674 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7675 * @cfg {string} value default value of the input
7676 * @cfg {Number} labelWidth set the width of label (0-12)
7677 * @cfg {String} labelAlign (top|left)
7678 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7679 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7681 * @cfg {String} align (left|center|right) Default left
7682 * @cfg {Boolean} forceFeedback (true|false) Default false
7688 * Create a new Input
7689 * @param {Object} config The config object
7692 Roo.bootstrap.Input = function(config){
7693 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7698 * Fires when this field receives input focus.
7699 * @param {Roo.form.Field} this
7704 * Fires when this field loses input focus.
7705 * @param {Roo.form.Field} this
7710 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7711 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7712 * @param {Roo.form.Field} this
7713 * @param {Roo.EventObject} e The event object
7718 * Fires just before the field blurs if the field value has changed.
7719 * @param {Roo.form.Field} this
7720 * @param {Mixed} newValue The new value
7721 * @param {Mixed} oldValue The original value
7726 * Fires after the field has been marked as invalid.
7727 * @param {Roo.form.Field} this
7728 * @param {String} msg The validation message
7733 * Fires after the field has been validated with no errors.
7734 * @param {Roo.form.Field} this
7739 * Fires after the key up
7740 * @param {Roo.form.Field} this
7741 * @param {Roo.EventObject} e The event Object
7747 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7749 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7750 automatic validation (defaults to "keyup").
7752 validationEvent : "keyup",
7754 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7756 validateOnBlur : true,
7758 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7760 validationDelay : 250,
7762 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7764 focusClass : "x-form-focus", // not needed???
7768 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7770 invalidClass : "has-warning",
7773 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7775 validClass : "has-success",
7778 * @cfg {Boolean} hasFeedback (true|false) default true
7783 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7785 invalidFeedbackClass : "glyphicon-warning-sign",
7788 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7790 validFeedbackClass : "glyphicon-ok",
7793 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7795 selectOnFocus : false,
7798 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7802 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7807 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7809 disableKeyFilter : false,
7812 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7816 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7820 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7822 blankText : "This field is required",
7825 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7829 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7831 maxLength : Number.MAX_VALUE,
7833 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7835 minLengthText : "The minimum length for this field is {0}",
7837 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7839 maxLengthText : "The maximum length for this field is {0}",
7843 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7844 * If available, this function will be called only after the basic validators all return true, and will be passed the
7845 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7849 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7850 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7851 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7855 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7859 autocomplete: false,
7878 formatedValue : false,
7879 forceFeedback : false,
7881 parentLabelAlign : function()
7884 while (parent.parent()) {
7885 parent = parent.parent();
7886 if (typeof(parent.labelAlign) !='undefined') {
7887 return parent.labelAlign;
7894 getAutoCreate : function(){
7896 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7902 if(this.inputType != 'hidden'){
7903 cfg.cls = 'form-group' //input-group
7909 type : this.inputType,
7911 cls : 'form-control',
7912 placeholder : this.placeholder || '',
7913 autocomplete : this.autocomplete || 'new-password'
7918 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7921 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7922 input.maxLength = this.maxLength;
7925 if (this.disabled) {
7926 input.disabled=true;
7929 if (this.readOnly) {
7930 input.readonly=true;
7934 input.name = this.name;
7937 input.cls += ' input-' + this.size;
7940 ['xs','sm','md','lg'].map(function(size){
7941 if (settings[size]) {
7942 cfg.cls += ' col-' + size + '-' + settings[size];
7946 var inputblock = input;
7950 cls: 'glyphicon form-control-feedback'
7953 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7956 cls : 'has-feedback',
7964 if (this.before || this.after) {
7967 cls : 'input-group',
7971 if (this.before && typeof(this.before) == 'string') {
7973 inputblock.cn.push({
7975 cls : 'roo-input-before input-group-addon',
7979 if (this.before && typeof(this.before) == 'object') {
7980 this.before = Roo.factory(this.before);
7981 Roo.log(this.before);
7982 inputblock.cn.push({
7984 cls : 'roo-input-before input-group-' +
7985 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7989 inputblock.cn.push(input);
7991 if (this.after && typeof(this.after) == 'string') {
7992 inputblock.cn.push({
7994 cls : 'roo-input-after input-group-addon',
7998 if (this.after && typeof(this.after) == 'object') {
7999 this.after = Roo.factory(this.after);
8000 Roo.log(this.after);
8001 inputblock.cn.push({
8003 cls : 'roo-input-after input-group-' +
8004 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8008 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8009 inputblock.cls += ' has-feedback';
8010 inputblock.cn.push(feedback);
8014 if (align ==='left' && this.fieldLabel.length) {
8015 Roo.log("left and has label");
8021 cls : 'control-label col-sm-' + this.labelWidth,
8022 html : this.fieldLabel
8026 cls : "col-sm-" + (12 - this.labelWidth),
8033 } else if ( this.fieldLabel.length) {
8039 //cls : 'input-group-addon',
8040 html : this.fieldLabel
8050 Roo.log(" no label && no align");
8059 Roo.log('input-parentType: ' + this.parentType);
8061 if (this.parentType === 'Navbar' && this.parent().bar) {
8062 cfg.cls += ' navbar-form';
8070 * return the real input element.
8072 inputEl: function ()
8074 return this.el.select('input.form-control',true).first();
8077 tooltipEl : function()
8079 return this.inputEl();
8082 setDisabled : function(v)
8084 var i = this.inputEl().dom;
8086 i.removeAttribute('disabled');
8090 i.setAttribute('disabled','true');
8092 initEvents : function()
8095 this.inputEl().on("keydown" , this.fireKey, this);
8096 this.inputEl().on("focus", this.onFocus, this);
8097 this.inputEl().on("blur", this.onBlur, this);
8099 this.inputEl().relayEvent('keyup', this);
8101 // reference to original value for reset
8102 this.originalValue = this.getValue();
8103 //Roo.form.TextField.superclass.initEvents.call(this);
8104 if(this.validationEvent == 'keyup'){
8105 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8106 this.inputEl().on('keyup', this.filterValidation, this);
8108 else if(this.validationEvent !== false){
8109 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8112 if(this.selectOnFocus){
8113 this.on("focus", this.preFocus, this);
8116 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8117 this.inputEl().on("keypress", this.filterKeys, this);
8120 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8121 this.el.on("click", this.autoSize, this);
8124 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8125 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8128 if (typeof(this.before) == 'object') {
8129 this.before.render(this.el.select('.roo-input-before',true).first());
8131 if (typeof(this.after) == 'object') {
8132 this.after.render(this.el.select('.roo-input-after',true).first());
8137 filterValidation : function(e){
8138 if(!e.isNavKeyPress()){
8139 this.validationTask.delay(this.validationDelay);
8143 * Validates the field value
8144 * @return {Boolean} True if the value is valid, else false
8146 validate : function(){
8147 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8148 if(this.disabled || this.validateValue(this.getRawValue())){
8159 * Validates a value according to the field's validation rules and marks the field as invalid
8160 * if the validation fails
8161 * @param {Mixed} value The value to validate
8162 * @return {Boolean} True if the value is valid, else false
8164 validateValue : function(value){
8165 if(value.length < 1) { // if it's blank
8166 if(this.allowBlank){
8172 if(value.length < this.minLength){
8175 if(value.length > this.maxLength){
8179 var vt = Roo.form.VTypes;
8180 if(!vt[this.vtype](value, this)){
8184 if(typeof this.validator == "function"){
8185 var msg = this.validator(value);
8191 if(this.regex && !this.regex.test(value)){
8201 fireKey : function(e){
8202 //Roo.log('field ' + e.getKey());
8203 if(e.isNavKeyPress()){
8204 this.fireEvent("specialkey", this, e);
8207 focus : function (selectText){
8209 this.inputEl().focus();
8210 if(selectText === true){
8211 this.inputEl().dom.select();
8217 onFocus : function(){
8218 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8219 // this.el.addClass(this.focusClass);
8222 this.hasFocus = true;
8223 this.startValue = this.getValue();
8224 this.fireEvent("focus", this);
8228 beforeBlur : Roo.emptyFn,
8232 onBlur : function(){
8234 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8235 //this.el.removeClass(this.focusClass);
8237 this.hasFocus = false;
8238 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8241 var v = this.getValue();
8242 if(String(v) !== String(this.startValue)){
8243 this.fireEvent('change', this, v, this.startValue);
8245 this.fireEvent("blur", this);
8249 * Resets the current field value to the originally loaded value and clears any validation messages
8252 this.setValue(this.originalValue);
8256 * Returns the name of the field
8257 * @return {Mixed} name The name field
8259 getName: function(){
8263 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8264 * @return {Mixed} value The field value
8266 getValue : function(){
8268 var v = this.inputEl().getValue();
8273 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8274 * @return {Mixed} value The field value
8276 getRawValue : function(){
8277 var v = this.inputEl().getValue();
8283 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8284 * @param {Mixed} value The value to set
8286 setRawValue : function(v){
8287 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8290 selectText : function(start, end){
8291 var v = this.getRawValue();
8293 start = start === undefined ? 0 : start;
8294 end = end === undefined ? v.length : end;
8295 var d = this.inputEl().dom;
8296 if(d.setSelectionRange){
8297 d.setSelectionRange(start, end);
8298 }else if(d.createTextRange){
8299 var range = d.createTextRange();
8300 range.moveStart("character", start);
8301 range.moveEnd("character", v.length-end);
8308 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8309 * @param {Mixed} value The value to set
8311 setValue : function(v){
8314 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8320 processValue : function(value){
8321 if(this.stripCharsRe){
8322 var newValue = value.replace(this.stripCharsRe, '');
8323 if(newValue !== value){
8324 this.setRawValue(newValue);
8331 preFocus : function(){
8333 if(this.selectOnFocus){
8334 this.inputEl().dom.select();
8337 filterKeys : function(e){
8339 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8342 var c = e.getCharCode(), cc = String.fromCharCode(c);
8343 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8346 if(!this.maskRe.test(cc)){
8351 * Clear any invalid styles/messages for this field
8353 clearInvalid : function(){
8355 if(!this.el || this.preventMark){ // not rendered
8358 this.el.removeClass(this.invalidClass);
8360 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8362 var feedback = this.el.select('.form-control-feedback', true).first();
8365 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8370 this.fireEvent('valid', this);
8374 * Mark this field as valid
8376 markValid : function()
8378 if(!this.el || this.preventMark){ // not rendered
8382 this.el.removeClass([this.invalidClass, this.validClass]);
8384 var feedback = this.el.select('.form-control-feedback', true).first();
8387 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8390 if(this.disabled || this.allowBlank){
8394 var formGroup = this.el.findParent('.form-group', false, true);
8398 var label = formGroup.select('label', true).first();
8399 var icon = formGroup.select('i.fa-star', true).first();
8406 this.el.addClass(this.validClass);
8408 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8410 var feedback = this.el.select('.form-control-feedback', true).first();
8413 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8414 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8419 this.fireEvent('valid', this);
8423 * Mark this field as invalid
8424 * @param {String} msg The validation message
8426 markInvalid : function(msg)
8428 if(!this.el || this.preventMark){ // not rendered
8432 this.el.removeClass([this.invalidClass, this.validClass]);
8434 var feedback = this.el.select('.form-control-feedback', true).first();
8437 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8440 if(this.disabled || this.allowBlank){
8444 var formGroup = this.el.findParent('.form-group', false, true);
8447 var label = formGroup.select('label', true).first();
8448 var icon = formGroup.select('i.fa-star', true).first();
8450 if(!this.getValue().length && label && !icon){
8451 this.el.findParent('.form-group', false, true).createChild({
8453 cls : 'text-danger fa fa-lg fa-star',
8454 tooltip : 'This field is required',
8455 style : 'margin-right:5px;'
8461 this.el.addClass(this.invalidClass);
8463 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8465 var feedback = this.el.select('.form-control-feedback', true).first();
8468 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8470 if(this.getValue().length || this.forceFeedback){
8471 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8478 this.fireEvent('invalid', this, msg);
8481 SafariOnKeyDown : function(event)
8483 // this is a workaround for a password hang bug on chrome/ webkit.
8485 var isSelectAll = false;
8487 if(this.inputEl().dom.selectionEnd > 0){
8488 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8490 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8491 event.preventDefault();
8496 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8498 event.preventDefault();
8499 // this is very hacky as keydown always get's upper case.
8501 var cc = String.fromCharCode(event.getCharCode());
8502 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8506 adjustWidth : function(tag, w){
8507 tag = tag.toLowerCase();
8508 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8509 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8513 if(tag == 'textarea'){
8516 }else if(Roo.isOpera){
8520 if(tag == 'textarea'){
8539 * @class Roo.bootstrap.TextArea
8540 * @extends Roo.bootstrap.Input
8541 * Bootstrap TextArea class
8542 * @cfg {Number} cols Specifies the visible width of a text area
8543 * @cfg {Number} rows Specifies the visible number of lines in a text area
8544 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8545 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8546 * @cfg {string} html text
8549 * Create a new TextArea
8550 * @param {Object} config The config object
8553 Roo.bootstrap.TextArea = function(config){
8554 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8558 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8568 getAutoCreate : function(){
8570 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8581 value : this.value || '',
8582 html: this.html || '',
8583 cls : 'form-control',
8584 placeholder : this.placeholder || ''
8588 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8589 input.maxLength = this.maxLength;
8593 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8597 input.cols = this.cols;
8600 if (this.readOnly) {
8601 input.readonly = true;
8605 input.name = this.name;
8609 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8613 ['xs','sm','md','lg'].map(function(size){
8614 if (settings[size]) {
8615 cfg.cls += ' col-' + size + '-' + settings[size];
8619 var inputblock = input;
8621 if(this.hasFeedback && !this.allowBlank){
8625 cls: 'glyphicon form-control-feedback'
8629 cls : 'has-feedback',
8638 if (this.before || this.after) {
8641 cls : 'input-group',
8645 inputblock.cn.push({
8647 cls : 'input-group-addon',
8652 inputblock.cn.push(input);
8654 if(this.hasFeedback && !this.allowBlank){
8655 inputblock.cls += ' has-feedback';
8656 inputblock.cn.push(feedback);
8660 inputblock.cn.push({
8662 cls : 'input-group-addon',
8669 if (align ==='left' && this.fieldLabel.length) {
8670 Roo.log("left and has label");
8676 cls : 'control-label col-sm-' + this.labelWidth,
8677 html : this.fieldLabel
8681 cls : "col-sm-" + (12 - this.labelWidth),
8688 } else if ( this.fieldLabel.length) {
8694 //cls : 'input-group-addon',
8695 html : this.fieldLabel
8705 Roo.log(" no label && no align");
8715 if (this.disabled) {
8716 input.disabled=true;
8723 * return the real textarea element.
8725 inputEl: function ()
8727 return this.el.select('textarea.form-control',true).first();
8731 * Clear any invalid styles/messages for this field
8733 clearInvalid : function()
8736 if(!this.el || this.preventMark){ // not rendered
8740 var label = this.el.select('label', true).first();
8741 var icon = this.el.select('i.fa-star', true).first();
8747 this.el.removeClass(this.invalidClass);
8749 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8751 var feedback = this.el.select('.form-control-feedback', true).first();
8754 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8759 this.fireEvent('valid', this);
8763 * Mark this field as valid
8765 markValid : function()
8767 if(!this.el || this.preventMark){ // not rendered
8771 this.el.removeClass([this.invalidClass, this.validClass]);
8773 var feedback = this.el.select('.form-control-feedback', true).first();
8776 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8779 if(this.disabled || this.allowBlank){
8783 var label = this.el.select('label', true).first();
8784 var icon = this.el.select('i.fa-star', true).first();
8790 this.el.addClass(this.validClass);
8792 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8794 var feedback = this.el.select('.form-control-feedback', true).first();
8797 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8798 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8803 this.fireEvent('valid', this);
8807 * Mark this field as invalid
8808 * @param {String} msg The validation message
8810 markInvalid : function(msg)
8812 if(!this.el || this.preventMark){ // not rendered
8816 this.el.removeClass([this.invalidClass, this.validClass]);
8818 var feedback = this.el.select('.form-control-feedback', true).first();
8821 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8824 if(this.disabled || this.allowBlank){
8828 var label = this.el.select('label', true).first();
8829 var icon = this.el.select('i.fa-star', true).first();
8831 if(!this.getValue().length && label && !icon){
8832 this.el.createChild({
8834 cls : 'text-danger fa fa-lg fa-star',
8835 tooltip : 'This field is required',
8836 style : 'margin-right:5px;'
8840 this.el.addClass(this.invalidClass);
8842 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8844 var feedback = this.el.select('.form-control-feedback', true).first();
8847 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8849 if(this.getValue().length || this.forceFeedback){
8850 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8857 this.fireEvent('invalid', this, msg);
8865 * trigger field - base class for combo..
8870 * @class Roo.bootstrap.TriggerField
8871 * @extends Roo.bootstrap.Input
8872 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8873 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8874 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8875 * for which you can provide a custom implementation. For example:
8877 var trigger = new Roo.bootstrap.TriggerField();
8878 trigger.onTriggerClick = myTriggerFn;
8879 trigger.applyTo('my-field');
8882 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8883 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8884 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8885 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8886 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8889 * Create a new TriggerField.
8890 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8891 * to the base TextField)
8893 Roo.bootstrap.TriggerField = function(config){
8894 this.mimicing = false;
8895 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8898 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8900 * @cfg {String} triggerClass A CSS class to apply to the trigger
8903 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8908 * @cfg {Boolean} removable (true|false) special filter default false
8912 /** @cfg {Boolean} grow @hide */
8913 /** @cfg {Number} growMin @hide */
8914 /** @cfg {Number} growMax @hide */
8920 autoSize: Roo.emptyFn,
8927 actionMode : 'wrap',
8932 getAutoCreate : function(){
8934 var align = this.labelAlign || this.parentLabelAlign();
8939 cls: 'form-group' //input-group
8946 type : this.inputType,
8947 cls : 'form-control',
8948 autocomplete: 'new-password',
8949 placeholder : this.placeholder || ''
8953 input.name = this.name;
8956 input.cls += ' input-' + this.size;
8959 if (this.disabled) {
8960 input.disabled=true;
8963 var inputblock = input;
8965 if(this.hasFeedback && !this.allowBlank){
8969 cls: 'glyphicon form-control-feedback'
8972 if(this.removable && !this.editable && !this.tickable){
8974 cls : 'has-feedback',
8980 cls : 'roo-combo-removable-btn close'
8987 cls : 'has-feedback',
8996 if(this.removable && !this.editable && !this.tickable){
8998 cls : 'roo-removable',
9004 cls : 'roo-combo-removable-btn close'
9011 if (this.before || this.after) {
9014 cls : 'input-group',
9018 inputblock.cn.push({
9020 cls : 'input-group-addon',
9025 inputblock.cn.push(input);
9027 if(this.hasFeedback && !this.allowBlank){
9028 inputblock.cls += ' has-feedback';
9029 inputblock.cn.push(feedback);
9033 inputblock.cn.push({
9035 cls : 'input-group-addon',
9048 cls: 'form-hidden-field'
9056 Roo.log('multiple');
9064 cls: 'form-hidden-field'
9068 cls: 'select2-choices',
9072 cls: 'select2-search-field',
9085 cls: 'select2-container input-group',
9090 // cls: 'typeahead typeahead-long dropdown-menu',
9091 // style: 'display:none'
9096 if(!this.multiple && this.showToggleBtn){
9102 if (this.caret != false) {
9105 cls: 'fa fa-' + this.caret
9112 cls : 'input-group-addon btn dropdown-toggle',
9117 cls: 'combobox-clear',
9131 combobox.cls += ' select2-container-multi';
9134 if (align ==='left' && this.fieldLabel.length) {
9136 Roo.log("left and has label");
9142 cls : 'control-label col-sm-' + this.labelWidth,
9143 html : this.fieldLabel
9147 cls : "col-sm-" + (12 - this.labelWidth),
9154 } else if ( this.fieldLabel.length) {
9160 //cls : 'input-group-addon',
9161 html : this.fieldLabel
9171 Roo.log(" no label && no align");
9178 ['xs','sm','md','lg'].map(function(size){
9179 if (settings[size]) {
9180 cfg.cls += ' col-' + size + '-' + settings[size];
9191 onResize : function(w, h){
9192 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9193 // if(typeof w == 'number'){
9194 // var x = w - this.trigger.getWidth();
9195 // this.inputEl().setWidth(this.adjustWidth('input', x));
9196 // this.trigger.setStyle('left', x+'px');
9201 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9204 getResizeEl : function(){
9205 return this.inputEl();
9209 getPositionEl : function(){
9210 return this.inputEl();
9214 alignErrorIcon : function(){
9215 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9219 initEvents : function(){
9223 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9224 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9225 if(!this.multiple && this.showToggleBtn){
9226 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9227 if(this.hideTrigger){
9228 this.trigger.setDisplayed(false);
9230 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9234 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9237 if(this.removable && !this.editable && !this.tickable){
9238 var close = this.closeTriggerEl();
9241 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9242 close.on('click', this.removeBtnClick, this, close);
9246 //this.trigger.addClassOnOver('x-form-trigger-over');
9247 //this.trigger.addClassOnClick('x-form-trigger-click');
9250 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9254 closeTriggerEl : function()
9256 var close = this.el.select('.roo-combo-removable-btn', true).first();
9257 return close ? close : false;
9260 removeBtnClick : function(e, h, el)
9264 if(this.fireEvent("remove", this) !== false){
9269 createList : function()
9271 this.list = Roo.get(document.body).createChild({
9273 cls: 'typeahead typeahead-long dropdown-menu',
9274 style: 'display:none'
9277 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9282 initTrigger : function(){
9287 onDestroy : function(){
9289 this.trigger.removeAllListeners();
9290 // this.trigger.remove();
9293 // this.wrap.remove();
9295 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9299 onFocus : function(){
9300 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9303 this.wrap.addClass('x-trigger-wrap-focus');
9304 this.mimicing = true;
9305 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9306 if(this.monitorTab){
9307 this.el.on("keydown", this.checkTab, this);
9314 checkTab : function(e){
9315 if(e.getKey() == e.TAB){
9321 onBlur : function(){
9326 mimicBlur : function(e, t){
9328 if(!this.wrap.contains(t) && this.validateBlur()){
9335 triggerBlur : function(){
9336 this.mimicing = false;
9337 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9338 if(this.monitorTab){
9339 this.el.un("keydown", this.checkTab, this);
9341 //this.wrap.removeClass('x-trigger-wrap-focus');
9342 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9346 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9347 validateBlur : function(e, t){
9352 onDisable : function(){
9353 this.inputEl().dom.disabled = true;
9354 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9356 // this.wrap.addClass('x-item-disabled');
9361 onEnable : function(){
9362 this.inputEl().dom.disabled = false;
9363 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9365 // this.el.removeClass('x-item-disabled');
9370 onShow : function(){
9371 var ae = this.getActionEl();
9374 ae.dom.style.display = '';
9375 ae.dom.style.visibility = 'visible';
9381 onHide : function(){
9382 var ae = this.getActionEl();
9383 ae.dom.style.display = 'none';
9387 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9388 * by an implementing function.
9390 * @param {EventObject} e
9392 onTriggerClick : Roo.emptyFn
9396 * Ext JS Library 1.1.1
9397 * Copyright(c) 2006-2007, Ext JS, LLC.
9399 * Originally Released Under LGPL - original licence link has changed is not relivant.
9402 * <script type="text/javascript">
9407 * @class Roo.data.SortTypes
9409 * Defines the default sorting (casting?) comparison functions used when sorting data.
9411 Roo.data.SortTypes = {
9413 * Default sort that does nothing
9414 * @param {Mixed} s The value being converted
9415 * @return {Mixed} The comparison value
9422 * The regular expression used to strip tags
9426 stripTagsRE : /<\/?[^>]+>/gi,
9429 * Strips all HTML tags to sort on text only
9430 * @param {Mixed} s The value being converted
9431 * @return {String} The comparison value
9433 asText : function(s){
9434 return String(s).replace(this.stripTagsRE, "");
9438 * Strips all HTML tags to sort on text only - Case insensitive
9439 * @param {Mixed} s The value being converted
9440 * @return {String} The comparison value
9442 asUCText : function(s){
9443 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9447 * Case insensitive string
9448 * @param {Mixed} s The value being converted
9449 * @return {String} The comparison value
9451 asUCString : function(s) {
9452 return String(s).toUpperCase();
9457 * @param {Mixed} s The value being converted
9458 * @return {Number} The comparison value
9460 asDate : function(s) {
9464 if(s instanceof Date){
9467 return Date.parse(String(s));
9472 * @param {Mixed} s The value being converted
9473 * @return {Float} The comparison value
9475 asFloat : function(s) {
9476 var val = parseFloat(String(s).replace(/,/g, ""));
9485 * @param {Mixed} s The value being converted
9486 * @return {Number} The comparison value
9488 asInt : function(s) {
9489 var val = parseInt(String(s).replace(/,/g, ""));
9497 * Ext JS Library 1.1.1
9498 * Copyright(c) 2006-2007, Ext JS, LLC.
9500 * Originally Released Under LGPL - original licence link has changed is not relivant.
9503 * <script type="text/javascript">
9507 * @class Roo.data.Record
9508 * Instances of this class encapsulate both record <em>definition</em> information, and record
9509 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9510 * to access Records cached in an {@link Roo.data.Store} object.<br>
9512 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9513 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9516 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9518 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9519 * {@link #create}. The parameters are the same.
9520 * @param {Array} data An associative Array of data values keyed by the field name.
9521 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9522 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9523 * not specified an integer id is generated.
9525 Roo.data.Record = function(data, id){
9526 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9531 * Generate a constructor for a specific record layout.
9532 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9533 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9534 * Each field definition object may contain the following properties: <ul>
9535 * <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,
9536 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9537 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9538 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9539 * is being used, then this is a string containing the javascript expression to reference the data relative to
9540 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9541 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9542 * this may be omitted.</p></li>
9543 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9544 * <ul><li>auto (Default, implies no conversion)</li>
9549 * <li>date</li></ul></p></li>
9550 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9551 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9552 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9553 * by the Reader into an object that will be stored in the Record. It is passed the
9554 * following parameters:<ul>
9555 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9557 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9559 * <br>usage:<br><pre><code>
9560 var TopicRecord = Roo.data.Record.create(
9561 {name: 'title', mapping: 'topic_title'},
9562 {name: 'author', mapping: 'username'},
9563 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9564 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9565 {name: 'lastPoster', mapping: 'user2'},
9566 {name: 'excerpt', mapping: 'post_text'}
9569 var myNewRecord = new TopicRecord({
9570 title: 'Do my job please',
9573 lastPost: new Date(),
9574 lastPoster: 'Animal',
9575 excerpt: 'No way dude!'
9577 myStore.add(myNewRecord);
9582 Roo.data.Record.create = function(o){
9584 f.superclass.constructor.apply(this, arguments);
9586 Roo.extend(f, Roo.data.Record);
9587 var p = f.prototype;
9588 p.fields = new Roo.util.MixedCollection(false, function(field){
9591 for(var i = 0, len = o.length; i < len; i++){
9592 p.fields.add(new Roo.data.Field(o[i]));
9594 f.getField = function(name){
9595 return p.fields.get(name);
9600 Roo.data.Record.AUTO_ID = 1000;
9601 Roo.data.Record.EDIT = 'edit';
9602 Roo.data.Record.REJECT = 'reject';
9603 Roo.data.Record.COMMIT = 'commit';
9605 Roo.data.Record.prototype = {
9607 * Readonly flag - true if this record has been modified.
9616 join : function(store){
9621 * Set the named field to the specified value.
9622 * @param {String} name The name of the field to set.
9623 * @param {Object} value The value to set the field to.
9625 set : function(name, value){
9626 if(this.data[name] == value){
9633 if(typeof this.modified[name] == 'undefined'){
9634 this.modified[name] = this.data[name];
9636 this.data[name] = value;
9637 if(!this.editing && this.store){
9638 this.store.afterEdit(this);
9643 * Get the value of the named field.
9644 * @param {String} name The name of the field to get the value of.
9645 * @return {Object} The value of the field.
9647 get : function(name){
9648 return this.data[name];
9652 beginEdit : function(){
9653 this.editing = true;
9658 cancelEdit : function(){
9659 this.editing = false;
9660 delete this.modified;
9664 endEdit : function(){
9665 this.editing = false;
9666 if(this.dirty && this.store){
9667 this.store.afterEdit(this);
9672 * Usually called by the {@link Roo.data.Store} which owns the Record.
9673 * Rejects all changes made to the Record since either creation, or the last commit operation.
9674 * Modified fields are reverted to their original values.
9676 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9677 * of reject operations.
9679 reject : function(){
9680 var m = this.modified;
9682 if(typeof m[n] != "function"){
9683 this.data[n] = m[n];
9687 delete this.modified;
9688 this.editing = false;
9690 this.store.afterReject(this);
9695 * Usually called by the {@link Roo.data.Store} which owns the Record.
9696 * Commits all changes made to the Record since either creation, or the last commit operation.
9698 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9699 * of commit operations.
9701 commit : function(){
9703 delete this.modified;
9704 this.editing = false;
9706 this.store.afterCommit(this);
9711 hasError : function(){
9712 return this.error != null;
9716 clearError : function(){
9721 * Creates a copy of this record.
9722 * @param {String} id (optional) A new record id if you don't want to use this record's id
9725 copy : function(newId) {
9726 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9730 * Ext JS Library 1.1.1
9731 * Copyright(c) 2006-2007, Ext JS, LLC.
9733 * Originally Released Under LGPL - original licence link has changed is not relivant.
9736 * <script type="text/javascript">
9742 * @class Roo.data.Store
9743 * @extends Roo.util.Observable
9744 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9745 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9747 * 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
9748 * has no knowledge of the format of the data returned by the Proxy.<br>
9750 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9751 * instances from the data object. These records are cached and made available through accessor functions.
9753 * Creates a new Store.
9754 * @param {Object} config A config object containing the objects needed for the Store to access data,
9755 * and read the data into Records.
9757 Roo.data.Store = function(config){
9758 this.data = new Roo.util.MixedCollection(false);
9759 this.data.getKey = function(o){
9762 this.baseParams = {};
9769 "multisort" : "_multisort"
9772 if(config && config.data){
9773 this.inlineData = config.data;
9777 Roo.apply(this, config);
9779 if(this.reader){ // reader passed
9780 this.reader = Roo.factory(this.reader, Roo.data);
9781 this.reader.xmodule = this.xmodule || false;
9782 if(!this.recordType){
9783 this.recordType = this.reader.recordType;
9785 if(this.reader.onMetaChange){
9786 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9790 if(this.recordType){
9791 this.fields = this.recordType.prototype.fields;
9797 * @event datachanged
9798 * Fires when the data cache has changed, and a widget which is using this Store
9799 * as a Record cache should refresh its view.
9800 * @param {Store} this
9805 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9806 * @param {Store} this
9807 * @param {Object} meta The JSON metadata
9812 * Fires when Records have been added to the Store
9813 * @param {Store} this
9814 * @param {Roo.data.Record[]} records The array of Records added
9815 * @param {Number} index The index at which the record(s) were added
9820 * Fires when a Record has been removed from the Store
9821 * @param {Store} this
9822 * @param {Roo.data.Record} record The Record that was removed
9823 * @param {Number} index The index at which the record was removed
9828 * Fires when a Record has been updated
9829 * @param {Store} this
9830 * @param {Roo.data.Record} record The Record that was updated
9831 * @param {String} operation The update operation being performed. Value may be one of:
9833 Roo.data.Record.EDIT
9834 Roo.data.Record.REJECT
9835 Roo.data.Record.COMMIT
9841 * Fires when the data cache has been cleared.
9842 * @param {Store} this
9847 * Fires before a request is made for a new data object. If the beforeload handler returns false
9848 * the load action will be canceled.
9849 * @param {Store} this
9850 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9854 * @event beforeloadadd
9855 * Fires after a new set of Records has been loaded.
9856 * @param {Store} this
9857 * @param {Roo.data.Record[]} records The Records that were loaded
9858 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9860 beforeloadadd : true,
9863 * Fires after a new set of Records has been loaded, before they are added to the store.
9864 * @param {Store} this
9865 * @param {Roo.data.Record[]} records The Records that were loaded
9866 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9867 * @params {Object} return from reader
9871 * @event loadexception
9872 * Fires if an exception occurs in the Proxy during loading.
9873 * Called with the signature of the Proxy's "loadexception" event.
9874 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9877 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9878 * @param {Object} load options
9879 * @param {Object} jsonData from your request (normally this contains the Exception)
9881 loadexception : true
9885 this.proxy = Roo.factory(this.proxy, Roo.data);
9886 this.proxy.xmodule = this.xmodule || false;
9887 this.relayEvents(this.proxy, ["loadexception"]);
9889 this.sortToggle = {};
9890 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9892 Roo.data.Store.superclass.constructor.call(this);
9894 if(this.inlineData){
9895 this.loadData(this.inlineData);
9896 delete this.inlineData;
9900 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9902 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9903 * without a remote query - used by combo/forms at present.
9907 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9910 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9913 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9914 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9917 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9918 * on any HTTP request
9921 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9924 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9928 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9929 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9934 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9935 * loaded or when a record is removed. (defaults to false).
9937 pruneModifiedRecords : false,
9943 * Add Records to the Store and fires the add event.
9944 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9946 add : function(records){
9947 records = [].concat(records);
9948 for(var i = 0, len = records.length; i < len; i++){
9949 records[i].join(this);
9951 var index = this.data.length;
9952 this.data.addAll(records);
9953 this.fireEvent("add", this, records, index);
9957 * Remove a Record from the Store and fires the remove event.
9958 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9960 remove : function(record){
9961 var index = this.data.indexOf(record);
9962 this.data.removeAt(index);
9963 if(this.pruneModifiedRecords){
9964 this.modified.remove(record);
9966 this.fireEvent("remove", this, record, index);
9970 * Remove all Records from the Store and fires the clear event.
9972 removeAll : function(){
9974 if(this.pruneModifiedRecords){
9977 this.fireEvent("clear", this);
9981 * Inserts Records to the Store at the given index and fires the add event.
9982 * @param {Number} index The start index at which to insert the passed Records.
9983 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9985 insert : function(index, records){
9986 records = [].concat(records);
9987 for(var i = 0, len = records.length; i < len; i++){
9988 this.data.insert(index, records[i]);
9989 records[i].join(this);
9991 this.fireEvent("add", this, records, index);
9995 * Get the index within the cache of the passed Record.
9996 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9997 * @return {Number} The index of the passed Record. Returns -1 if not found.
9999 indexOf : function(record){
10000 return this.data.indexOf(record);
10004 * Get the index within the cache of the Record with the passed id.
10005 * @param {String} id The id of the Record to find.
10006 * @return {Number} The index of the Record. Returns -1 if not found.
10008 indexOfId : function(id){
10009 return this.data.indexOfKey(id);
10013 * Get the Record with the specified id.
10014 * @param {String} id The id of the Record to find.
10015 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10017 getById : function(id){
10018 return this.data.key(id);
10022 * Get the Record at the specified index.
10023 * @param {Number} index The index of the Record to find.
10024 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10026 getAt : function(index){
10027 return this.data.itemAt(index);
10031 * Returns a range of Records between specified indices.
10032 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10033 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10034 * @return {Roo.data.Record[]} An array of Records
10036 getRange : function(start, end){
10037 return this.data.getRange(start, end);
10041 storeOptions : function(o){
10042 o = Roo.apply({}, o);
10045 this.lastOptions = o;
10049 * Loads the Record cache from the configured Proxy using the configured Reader.
10051 * If using remote paging, then the first load call must specify the <em>start</em>
10052 * and <em>limit</em> properties in the options.params property to establish the initial
10053 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10055 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10056 * and this call will return before the new data has been loaded. Perform any post-processing
10057 * in a callback function, or in a "load" event handler.</strong>
10059 * @param {Object} options An object containing properties which control loading options:<ul>
10060 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10061 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10062 * passed the following arguments:<ul>
10063 * <li>r : Roo.data.Record[]</li>
10064 * <li>options: Options object from the load call</li>
10065 * <li>success: Boolean success indicator</li></ul></li>
10066 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10067 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10070 load : function(options){
10071 options = options || {};
10072 if(this.fireEvent("beforeload", this, options) !== false){
10073 this.storeOptions(options);
10074 var p = Roo.apply(options.params || {}, this.baseParams);
10075 // if meta was not loaded from remote source.. try requesting it.
10076 if (!this.reader.metaFromRemote) {
10077 p._requestMeta = 1;
10079 if(this.sortInfo && this.remoteSort){
10080 var pn = this.paramNames;
10081 p[pn["sort"]] = this.sortInfo.field;
10082 p[pn["dir"]] = this.sortInfo.direction;
10084 if (this.multiSort) {
10085 var pn = this.paramNames;
10086 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10089 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10094 * Reloads the Record cache from the configured Proxy using the configured Reader and
10095 * the options from the last load operation performed.
10096 * @param {Object} options (optional) An object containing properties which may override the options
10097 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10098 * the most recently used options are reused).
10100 reload : function(options){
10101 this.load(Roo.applyIf(options||{}, this.lastOptions));
10105 // Called as a callback by the Reader during a load operation.
10106 loadRecords : function(o, options, success){
10107 if(!o || success === false){
10108 if(success !== false){
10109 this.fireEvent("load", this, [], options, o);
10111 if(options.callback){
10112 options.callback.call(options.scope || this, [], options, false);
10116 // if data returned failure - throw an exception.
10117 if (o.success === false) {
10118 // show a message if no listener is registered.
10119 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10120 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10122 // loadmask wil be hooked into this..
10123 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10126 var r = o.records, t = o.totalRecords || r.length;
10128 this.fireEvent("beforeloadadd", this, r, options, o);
10130 if(!options || options.add !== true){
10131 if(this.pruneModifiedRecords){
10132 this.modified = [];
10134 for(var i = 0, len = r.length; i < len; i++){
10138 this.data = this.snapshot;
10139 delete this.snapshot;
10142 this.data.addAll(r);
10143 this.totalLength = t;
10145 this.fireEvent("datachanged", this);
10147 this.totalLength = Math.max(t, this.data.length+r.length);
10150 this.fireEvent("load", this, r, options, o);
10151 if(options.callback){
10152 options.callback.call(options.scope || this, r, options, true);
10158 * Loads data from a passed data block. A Reader which understands the format of the data
10159 * must have been configured in the constructor.
10160 * @param {Object} data The data block from which to read the Records. The format of the data expected
10161 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10162 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10164 loadData : function(o, append){
10165 var r = this.reader.readRecords(o);
10166 this.loadRecords(r, {add: append}, true);
10170 * Gets the number of cached records.
10172 * <em>If using paging, this may not be the total size of the dataset. If the data object
10173 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10174 * the data set size</em>
10176 getCount : function(){
10177 return this.data.length || 0;
10181 * Gets the total number of records in the dataset as returned by the server.
10183 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10184 * the dataset size</em>
10186 getTotalCount : function(){
10187 return this.totalLength || 0;
10191 * Returns the sort state of the Store as an object with two properties:
10193 field {String} The name of the field by which the Records are sorted
10194 direction {String} The sort order, "ASC" or "DESC"
10197 getSortState : function(){
10198 return this.sortInfo;
10202 applySort : function(){
10203 if(this.sortInfo && !this.remoteSort){
10204 var s = this.sortInfo, f = s.field;
10205 var st = this.fields.get(f).sortType;
10206 var fn = function(r1, r2){
10207 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10208 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10210 this.data.sort(s.direction, fn);
10211 if(this.snapshot && this.snapshot != this.data){
10212 this.snapshot.sort(s.direction, fn);
10218 * Sets the default sort column and order to be used by the next load operation.
10219 * @param {String} fieldName The name of the field to sort by.
10220 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10222 setDefaultSort : function(field, dir){
10223 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10227 * Sort the Records.
10228 * If remote sorting is used, the sort is performed on the server, and the cache is
10229 * reloaded. If local sorting is used, the cache is sorted internally.
10230 * @param {String} fieldName The name of the field to sort by.
10231 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10233 sort : function(fieldName, dir){
10234 var f = this.fields.get(fieldName);
10236 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10238 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10239 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10244 this.sortToggle[f.name] = dir;
10245 this.sortInfo = {field: f.name, direction: dir};
10246 if(!this.remoteSort){
10248 this.fireEvent("datachanged", this);
10250 this.load(this.lastOptions);
10255 * Calls the specified function for each of the Records in the cache.
10256 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10257 * Returning <em>false</em> aborts and exits the iteration.
10258 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10260 each : function(fn, scope){
10261 this.data.each(fn, scope);
10265 * Gets all records modified since the last commit. Modified records are persisted across load operations
10266 * (e.g., during paging).
10267 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10269 getModifiedRecords : function(){
10270 return this.modified;
10274 createFilterFn : function(property, value, anyMatch){
10275 if(!value.exec){ // not a regex
10276 value = String(value);
10277 if(value.length == 0){
10280 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10282 return function(r){
10283 return value.test(r.data[property]);
10288 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10289 * @param {String} property A field on your records
10290 * @param {Number} start The record index to start at (defaults to 0)
10291 * @param {Number} end The last record index to include (defaults to length - 1)
10292 * @return {Number} The sum
10294 sum : function(property, start, end){
10295 var rs = this.data.items, v = 0;
10296 start = start || 0;
10297 end = (end || end === 0) ? end : rs.length-1;
10299 for(var i = start; i <= end; i++){
10300 v += (rs[i].data[property] || 0);
10306 * Filter the records by a specified property.
10307 * @param {String} field A field on your records
10308 * @param {String/RegExp} value Either a string that the field
10309 * should start with or a RegExp to test against the field
10310 * @param {Boolean} anyMatch True to match any part not just the beginning
10312 filter : function(property, value, anyMatch){
10313 var fn = this.createFilterFn(property, value, anyMatch);
10314 return fn ? this.filterBy(fn) : this.clearFilter();
10318 * Filter by a function. The specified function will be called with each
10319 * record in this data source. If the function returns true the record is included,
10320 * otherwise it is filtered.
10321 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10322 * @param {Object} scope (optional) The scope of the function (defaults to this)
10324 filterBy : function(fn, scope){
10325 this.snapshot = this.snapshot || this.data;
10326 this.data = this.queryBy(fn, scope||this);
10327 this.fireEvent("datachanged", this);
10331 * Query the records by a specified property.
10332 * @param {String} field A field on your records
10333 * @param {String/RegExp} value Either a string that the field
10334 * should start with or a RegExp to test against the field
10335 * @param {Boolean} anyMatch True to match any part not just the beginning
10336 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10338 query : function(property, value, anyMatch){
10339 var fn = this.createFilterFn(property, value, anyMatch);
10340 return fn ? this.queryBy(fn) : this.data.clone();
10344 * Query by a function. The specified function will be called with each
10345 * record in this data source. If the function returns true the record is included
10347 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10348 * @param {Object} scope (optional) The scope of the function (defaults to this)
10349 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10351 queryBy : function(fn, scope){
10352 var data = this.snapshot || this.data;
10353 return data.filterBy(fn, scope||this);
10357 * Collects unique values for a particular dataIndex from this store.
10358 * @param {String} dataIndex The property to collect
10359 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10360 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10361 * @return {Array} An array of the unique values
10363 collect : function(dataIndex, allowNull, bypassFilter){
10364 var d = (bypassFilter === true && this.snapshot) ?
10365 this.snapshot.items : this.data.items;
10366 var v, sv, r = [], l = {};
10367 for(var i = 0, len = d.length; i < len; i++){
10368 v = d[i].data[dataIndex];
10370 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10379 * Revert to a view of the Record cache with no filtering applied.
10380 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10382 clearFilter : function(suppressEvent){
10383 if(this.snapshot && this.snapshot != this.data){
10384 this.data = this.snapshot;
10385 delete this.snapshot;
10386 if(suppressEvent !== true){
10387 this.fireEvent("datachanged", this);
10393 afterEdit : function(record){
10394 if(this.modified.indexOf(record) == -1){
10395 this.modified.push(record);
10397 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10401 afterReject : function(record){
10402 this.modified.remove(record);
10403 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10407 afterCommit : function(record){
10408 this.modified.remove(record);
10409 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10413 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10414 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10416 commitChanges : function(){
10417 var m = this.modified.slice(0);
10418 this.modified = [];
10419 for(var i = 0, len = m.length; i < len; i++){
10425 * Cancel outstanding changes on all changed records.
10427 rejectChanges : function(){
10428 var m = this.modified.slice(0);
10429 this.modified = [];
10430 for(var i = 0, len = m.length; i < len; i++){
10435 onMetaChange : function(meta, rtype, o){
10436 this.recordType = rtype;
10437 this.fields = rtype.prototype.fields;
10438 delete this.snapshot;
10439 this.sortInfo = meta.sortInfo || this.sortInfo;
10440 this.modified = [];
10441 this.fireEvent('metachange', this, this.reader.meta);
10444 moveIndex : function(data, type)
10446 var index = this.indexOf(data);
10448 var newIndex = index + type;
10452 this.insert(newIndex, data);
10457 * Ext JS Library 1.1.1
10458 * Copyright(c) 2006-2007, Ext JS, LLC.
10460 * Originally Released Under LGPL - original licence link has changed is not relivant.
10463 * <script type="text/javascript">
10467 * @class Roo.data.SimpleStore
10468 * @extends Roo.data.Store
10469 * Small helper class to make creating Stores from Array data easier.
10470 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10471 * @cfg {Array} fields An array of field definition objects, or field name strings.
10472 * @cfg {Array} data The multi-dimensional array of data
10474 * @param {Object} config
10476 Roo.data.SimpleStore = function(config){
10477 Roo.data.SimpleStore.superclass.constructor.call(this, {
10479 reader: new Roo.data.ArrayReader({
10482 Roo.data.Record.create(config.fields)
10484 proxy : new Roo.data.MemoryProxy(config.data)
10488 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10490 * Ext JS Library 1.1.1
10491 * Copyright(c) 2006-2007, Ext JS, LLC.
10493 * Originally Released Under LGPL - original licence link has changed is not relivant.
10496 * <script type="text/javascript">
10501 * @extends Roo.data.Store
10502 * @class Roo.data.JsonStore
10503 * Small helper class to make creating Stores for JSON data easier. <br/>
10505 var store = new Roo.data.JsonStore({
10506 url: 'get-images.php',
10508 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10511 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10512 * JsonReader and HttpProxy (unless inline data is provided).</b>
10513 * @cfg {Array} fields An array of field definition objects, or field name strings.
10515 * @param {Object} config
10517 Roo.data.JsonStore = function(c){
10518 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10519 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10520 reader: new Roo.data.JsonReader(c, c.fields)
10523 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10525 * Ext JS Library 1.1.1
10526 * Copyright(c) 2006-2007, Ext JS, LLC.
10528 * Originally Released Under LGPL - original licence link has changed is not relivant.
10531 * <script type="text/javascript">
10535 Roo.data.Field = function(config){
10536 if(typeof config == "string"){
10537 config = {name: config};
10539 Roo.apply(this, config);
10542 this.type = "auto";
10545 var st = Roo.data.SortTypes;
10546 // named sortTypes are supported, here we look them up
10547 if(typeof this.sortType == "string"){
10548 this.sortType = st[this.sortType];
10551 // set default sortType for strings and dates
10552 if(!this.sortType){
10555 this.sortType = st.asUCString;
10558 this.sortType = st.asDate;
10561 this.sortType = st.none;
10566 var stripRe = /[\$,%]/g;
10568 // prebuilt conversion function for this field, instead of
10569 // switching every time we're reading a value
10571 var cv, dateFormat = this.dateFormat;
10576 cv = function(v){ return v; };
10579 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10583 return v !== undefined && v !== null && v !== '' ?
10584 parseInt(String(v).replace(stripRe, ""), 10) : '';
10589 return v !== undefined && v !== null && v !== '' ?
10590 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10595 cv = function(v){ return v === true || v === "true" || v == 1; };
10602 if(v instanceof Date){
10606 if(dateFormat == "timestamp"){
10607 return new Date(v*1000);
10609 return Date.parseDate(v, dateFormat);
10611 var parsed = Date.parse(v);
10612 return parsed ? new Date(parsed) : null;
10621 Roo.data.Field.prototype = {
10629 * Ext JS Library 1.1.1
10630 * Copyright(c) 2006-2007, Ext JS, LLC.
10632 * Originally Released Under LGPL - original licence link has changed is not relivant.
10635 * <script type="text/javascript">
10638 // Base class for reading structured data from a data source. This class is intended to be
10639 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10642 * @class Roo.data.DataReader
10643 * Base class for reading structured data from a data source. This class is intended to be
10644 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10647 Roo.data.DataReader = function(meta, recordType){
10651 this.recordType = recordType instanceof Array ?
10652 Roo.data.Record.create(recordType) : recordType;
10655 Roo.data.DataReader.prototype = {
10657 * Create an empty record
10658 * @param {Object} data (optional) - overlay some values
10659 * @return {Roo.data.Record} record created.
10661 newRow : function(d) {
10663 this.recordType.prototype.fields.each(function(c) {
10665 case 'int' : da[c.name] = 0; break;
10666 case 'date' : da[c.name] = new Date(); break;
10667 case 'float' : da[c.name] = 0.0; break;
10668 case 'boolean' : da[c.name] = false; break;
10669 default : da[c.name] = ""; break;
10673 return new this.recordType(Roo.apply(da, d));
10678 * Ext JS Library 1.1.1
10679 * Copyright(c) 2006-2007, Ext JS, LLC.
10681 * Originally Released Under LGPL - original licence link has changed is not relivant.
10684 * <script type="text/javascript">
10688 * @class Roo.data.DataProxy
10689 * @extends Roo.data.Observable
10690 * This class is an abstract base class for implementations which provide retrieval of
10691 * unformatted data objects.<br>
10693 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10694 * (of the appropriate type which knows how to parse the data object) to provide a block of
10695 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10697 * Custom implementations must implement the load method as described in
10698 * {@link Roo.data.HttpProxy#load}.
10700 Roo.data.DataProxy = function(){
10703 * @event beforeload
10704 * Fires before a network request is made to retrieve a data object.
10705 * @param {Object} This DataProxy object.
10706 * @param {Object} params The params parameter to the load function.
10711 * Fires before the load method's callback is called.
10712 * @param {Object} This DataProxy object.
10713 * @param {Object} o The data object.
10714 * @param {Object} arg The callback argument object passed to the load function.
10718 * @event loadexception
10719 * Fires if an Exception occurs during data retrieval.
10720 * @param {Object} This DataProxy object.
10721 * @param {Object} o The data object.
10722 * @param {Object} arg The callback argument object passed to the load function.
10723 * @param {Object} e The Exception.
10725 loadexception : true
10727 Roo.data.DataProxy.superclass.constructor.call(this);
10730 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10733 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10737 * Ext JS Library 1.1.1
10738 * Copyright(c) 2006-2007, Ext JS, LLC.
10740 * Originally Released Under LGPL - original licence link has changed is not relivant.
10743 * <script type="text/javascript">
10746 * @class Roo.data.MemoryProxy
10747 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10748 * to the Reader when its load method is called.
10750 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10752 Roo.data.MemoryProxy = function(data){
10756 Roo.data.MemoryProxy.superclass.constructor.call(this);
10760 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10762 * Load data from the requested source (in this case an in-memory
10763 * data object passed to the constructor), read the data object into
10764 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10765 * process that block using the passed callback.
10766 * @param {Object} params This parameter is not used by the MemoryProxy class.
10767 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10768 * object into a block of Roo.data.Records.
10769 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10770 * The function must be passed <ul>
10771 * <li>The Record block object</li>
10772 * <li>The "arg" argument from the load function</li>
10773 * <li>A boolean success indicator</li>
10775 * @param {Object} scope The scope in which to call the callback
10776 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10778 load : function(params, reader, callback, scope, arg){
10779 params = params || {};
10782 result = reader.readRecords(this.data);
10784 this.fireEvent("loadexception", this, arg, null, e);
10785 callback.call(scope, null, arg, false);
10788 callback.call(scope, result, arg, true);
10792 update : function(params, records){
10797 * Ext JS Library 1.1.1
10798 * Copyright(c) 2006-2007, Ext JS, LLC.
10800 * Originally Released Under LGPL - original licence link has changed is not relivant.
10803 * <script type="text/javascript">
10806 * @class Roo.data.HttpProxy
10807 * @extends Roo.data.DataProxy
10808 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10809 * configured to reference a certain URL.<br><br>
10811 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10812 * from which the running page was served.<br><br>
10814 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10816 * Be aware that to enable the browser to parse an XML document, the server must set
10817 * the Content-Type header in the HTTP response to "text/xml".
10819 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10820 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10821 * will be used to make the request.
10823 Roo.data.HttpProxy = function(conn){
10824 Roo.data.HttpProxy.superclass.constructor.call(this);
10825 // is conn a conn config or a real conn?
10827 this.useAjax = !conn || !conn.events;
10831 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10832 // thse are take from connection...
10835 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10838 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10839 * extra parameters to each request made by this object. (defaults to undefined)
10842 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10843 * to each request made by this object. (defaults to undefined)
10846 * @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)
10849 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10852 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10858 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10862 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10863 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10864 * a finer-grained basis than the DataProxy events.
10866 getConnection : function(){
10867 return this.useAjax ? Roo.Ajax : this.conn;
10871 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10872 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10873 * process that block using the passed callback.
10874 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10875 * for the request to the remote server.
10876 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10877 * object into a block of Roo.data.Records.
10878 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10879 * The function must be passed <ul>
10880 * <li>The Record block object</li>
10881 * <li>The "arg" argument from the load function</li>
10882 * <li>A boolean success indicator</li>
10884 * @param {Object} scope The scope in which to call the callback
10885 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10887 load : function(params, reader, callback, scope, arg){
10888 if(this.fireEvent("beforeload", this, params) !== false){
10890 params : params || {},
10892 callback : callback,
10897 callback : this.loadResponse,
10901 Roo.applyIf(o, this.conn);
10902 if(this.activeRequest){
10903 Roo.Ajax.abort(this.activeRequest);
10905 this.activeRequest = Roo.Ajax.request(o);
10907 this.conn.request(o);
10910 callback.call(scope||this, null, arg, false);
10915 loadResponse : function(o, success, response){
10916 delete this.activeRequest;
10918 this.fireEvent("loadexception", this, o, response);
10919 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10924 result = o.reader.read(response);
10926 this.fireEvent("loadexception", this, o, response, e);
10927 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10931 this.fireEvent("load", this, o, o.request.arg);
10932 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10936 update : function(dataSet){
10941 updateResponse : function(dataSet){
10946 * Ext JS Library 1.1.1
10947 * Copyright(c) 2006-2007, Ext JS, LLC.
10949 * Originally Released Under LGPL - original licence link has changed is not relivant.
10952 * <script type="text/javascript">
10956 * @class Roo.data.ScriptTagProxy
10957 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10958 * other than the originating domain of the running page.<br><br>
10960 * <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
10961 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10963 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10964 * source code that is used as the source inside a <script> tag.<br><br>
10966 * In order for the browser to process the returned data, the server must wrap the data object
10967 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10968 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10969 * depending on whether the callback name was passed:
10972 boolean scriptTag = false;
10973 String cb = request.getParameter("callback");
10976 response.setContentType("text/javascript");
10978 response.setContentType("application/x-json");
10980 Writer out = response.getWriter();
10982 out.write(cb + "(");
10984 out.print(dataBlock.toJsonString());
10991 * @param {Object} config A configuration object.
10993 Roo.data.ScriptTagProxy = function(config){
10994 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10995 Roo.apply(this, config);
10996 this.head = document.getElementsByTagName("head")[0];
10999 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11001 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11003 * @cfg {String} url The URL from which to request the data object.
11006 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11010 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11011 * the server the name of the callback function set up by the load call to process the returned data object.
11012 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11013 * javascript output which calls this named function passing the data object as its only parameter.
11015 callbackParam : "callback",
11017 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11018 * name to the request.
11023 * Load data from the configured URL, read the data object into
11024 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11025 * process that block using the passed callback.
11026 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11027 * for the request to the remote server.
11028 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11029 * object into a block of Roo.data.Records.
11030 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11031 * The function must be passed <ul>
11032 * <li>The Record block object</li>
11033 * <li>The "arg" argument from the load function</li>
11034 * <li>A boolean success indicator</li>
11036 * @param {Object} scope The scope in which to call the callback
11037 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11039 load : function(params, reader, callback, scope, arg){
11040 if(this.fireEvent("beforeload", this, params) !== false){
11042 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11044 var url = this.url;
11045 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11047 url += "&_dc=" + (new Date().getTime());
11049 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11052 cb : "stcCallback"+transId,
11053 scriptId : "stcScript"+transId,
11057 callback : callback,
11063 window[trans.cb] = function(o){
11064 conn.handleResponse(o, trans);
11067 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11069 if(this.autoAbort !== false){
11073 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11075 var script = document.createElement("script");
11076 script.setAttribute("src", url);
11077 script.setAttribute("type", "text/javascript");
11078 script.setAttribute("id", trans.scriptId);
11079 this.head.appendChild(script);
11081 this.trans = trans;
11083 callback.call(scope||this, null, arg, false);
11088 isLoading : function(){
11089 return this.trans ? true : false;
11093 * Abort the current server request.
11095 abort : function(){
11096 if(this.isLoading()){
11097 this.destroyTrans(this.trans);
11102 destroyTrans : function(trans, isLoaded){
11103 this.head.removeChild(document.getElementById(trans.scriptId));
11104 clearTimeout(trans.timeoutId);
11106 window[trans.cb] = undefined;
11108 delete window[trans.cb];
11111 // if hasn't been loaded, wait for load to remove it to prevent script error
11112 window[trans.cb] = function(){
11113 window[trans.cb] = undefined;
11115 delete window[trans.cb];
11122 handleResponse : function(o, trans){
11123 this.trans = false;
11124 this.destroyTrans(trans, true);
11127 result = trans.reader.readRecords(o);
11129 this.fireEvent("loadexception", this, o, trans.arg, e);
11130 trans.callback.call(trans.scope||window, null, trans.arg, false);
11133 this.fireEvent("load", this, o, trans.arg);
11134 trans.callback.call(trans.scope||window, result, trans.arg, true);
11138 handleFailure : function(trans){
11139 this.trans = false;
11140 this.destroyTrans(trans, false);
11141 this.fireEvent("loadexception", this, null, trans.arg);
11142 trans.callback.call(trans.scope||window, null, trans.arg, false);
11146 * Ext JS Library 1.1.1
11147 * Copyright(c) 2006-2007, Ext JS, LLC.
11149 * Originally Released Under LGPL - original licence link has changed is not relivant.
11152 * <script type="text/javascript">
11156 * @class Roo.data.JsonReader
11157 * @extends Roo.data.DataReader
11158 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11159 * based on mappings in a provided Roo.data.Record constructor.
11161 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11162 * in the reply previously.
11167 var RecordDef = Roo.data.Record.create([
11168 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11169 {name: 'occupation'} // This field will use "occupation" as the mapping.
11171 var myReader = new Roo.data.JsonReader({
11172 totalProperty: "results", // The property which contains the total dataset size (optional)
11173 root: "rows", // The property which contains an Array of row objects
11174 id: "id" // The property within each row object that provides an ID for the record (optional)
11178 * This would consume a JSON file like this:
11180 { 'results': 2, 'rows': [
11181 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11182 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11185 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11186 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11187 * paged from the remote server.
11188 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11189 * @cfg {String} root name of the property which contains the Array of row objects.
11190 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11191 * @cfg {Array} fields Array of field definition objects
11193 * Create a new JsonReader
11194 * @param {Object} meta Metadata configuration options
11195 * @param {Object} recordType Either an Array of field definition objects,
11196 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11198 Roo.data.JsonReader = function(meta, recordType){
11201 // set some defaults:
11202 Roo.applyIf(meta, {
11203 totalProperty: 'total',
11204 successProperty : 'success',
11209 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11211 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11214 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11215 * Used by Store query builder to append _requestMeta to params.
11218 metaFromRemote : false,
11220 * This method is only used by a DataProxy which has retrieved data from a remote server.
11221 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11222 * @return {Object} data A data block which is used by an Roo.data.Store object as
11223 * a cache of Roo.data.Records.
11225 read : function(response){
11226 var json = response.responseText;
11228 var o = /* eval:var:o */ eval("("+json+")");
11230 throw {message: "JsonReader.read: Json object not found"};
11236 this.metaFromRemote = true;
11237 this.meta = o.metaData;
11238 this.recordType = Roo.data.Record.create(o.metaData.fields);
11239 this.onMetaChange(this.meta, this.recordType, o);
11241 return this.readRecords(o);
11244 // private function a store will implement
11245 onMetaChange : function(meta, recordType, o){
11252 simpleAccess: function(obj, subsc) {
11259 getJsonAccessor: function(){
11261 return function(expr) {
11263 return(re.test(expr))
11264 ? new Function("obj", "return obj." + expr)
11269 return Roo.emptyFn;
11274 * Create a data block containing Roo.data.Records from an XML document.
11275 * @param {Object} o An object which contains an Array of row objects in the property specified
11276 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11277 * which contains the total size of the dataset.
11278 * @return {Object} data A data block which is used by an Roo.data.Store object as
11279 * a cache of Roo.data.Records.
11281 readRecords : function(o){
11283 * After any data loads, the raw JSON data is available for further custom processing.
11287 var s = this.meta, Record = this.recordType,
11288 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11290 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11292 if(s.totalProperty) {
11293 this.getTotal = this.getJsonAccessor(s.totalProperty);
11295 if(s.successProperty) {
11296 this.getSuccess = this.getJsonAccessor(s.successProperty);
11298 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11300 var g = this.getJsonAccessor(s.id);
11301 this.getId = function(rec) {
11303 return (r === undefined || r === "") ? null : r;
11306 this.getId = function(){return null;};
11309 for(var jj = 0; jj < fl; jj++){
11311 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11312 this.ef[jj] = this.getJsonAccessor(map);
11316 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11317 if(s.totalProperty){
11318 var vt = parseInt(this.getTotal(o), 10);
11323 if(s.successProperty){
11324 var vs = this.getSuccess(o);
11325 if(vs === false || vs === 'false'){
11330 for(var i = 0; i < c; i++){
11333 var id = this.getId(n);
11334 for(var j = 0; j < fl; j++){
11336 var v = this.ef[j](n);
11338 Roo.log('missing convert for ' + f.name);
11342 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11344 var record = new Record(values, id);
11346 records[i] = record;
11352 totalRecords : totalRecords
11357 * Ext JS Library 1.1.1
11358 * Copyright(c) 2006-2007, Ext JS, LLC.
11360 * Originally Released Under LGPL - original licence link has changed is not relivant.
11363 * <script type="text/javascript">
11367 * @class Roo.data.ArrayReader
11368 * @extends Roo.data.DataReader
11369 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11370 * Each element of that Array represents a row of data fields. The
11371 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11372 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11376 var RecordDef = Roo.data.Record.create([
11377 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11378 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11380 var myReader = new Roo.data.ArrayReader({
11381 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11385 * This would consume an Array like this:
11387 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11389 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11391 * Create a new JsonReader
11392 * @param {Object} meta Metadata configuration options.
11393 * @param {Object} recordType Either an Array of field definition objects
11394 * as specified to {@link Roo.data.Record#create},
11395 * or an {@link Roo.data.Record} object
11396 * created using {@link Roo.data.Record#create}.
11398 Roo.data.ArrayReader = function(meta, recordType){
11399 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11402 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11404 * Create a data block containing Roo.data.Records from an XML document.
11405 * @param {Object} o An Array of row objects which represents the dataset.
11406 * @return {Object} data A data block which is used by an Roo.data.Store object as
11407 * a cache of Roo.data.Records.
11409 readRecords : function(o){
11410 var sid = this.meta ? this.meta.id : null;
11411 var recordType = this.recordType, fields = recordType.prototype.fields;
11414 for(var i = 0; i < root.length; i++){
11417 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11418 for(var j = 0, jlen = fields.length; j < jlen; j++){
11419 var f = fields.items[j];
11420 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11421 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11423 values[f.name] = v;
11425 var record = new recordType(values, id);
11427 records[records.length] = record;
11431 totalRecords : records.length
11440 * @class Roo.bootstrap.ComboBox
11441 * @extends Roo.bootstrap.TriggerField
11442 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11443 * @cfg {Boolean} append (true|false) default false
11444 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11445 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11446 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11447 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11448 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11449 * @cfg {Boolean} animate default true
11450 * @cfg {Boolean} emptyResultText only for touch device
11451 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11453 * Create a new ComboBox.
11454 * @param {Object} config Configuration options
11456 Roo.bootstrap.ComboBox = function(config){
11457 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11461 * Fires when the dropdown list is expanded
11462 * @param {Roo.bootstrap.ComboBox} combo This combo box
11467 * Fires when the dropdown list is collapsed
11468 * @param {Roo.bootstrap.ComboBox} combo This combo box
11472 * @event beforeselect
11473 * Fires before a list item is selected. Return false to cancel the selection.
11474 * @param {Roo.bootstrap.ComboBox} combo This combo box
11475 * @param {Roo.data.Record} record The data record returned from the underlying store
11476 * @param {Number} index The index of the selected item in the dropdown list
11478 'beforeselect' : true,
11481 * Fires when a list item is selected
11482 * @param {Roo.bootstrap.ComboBox} combo This combo box
11483 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11484 * @param {Number} index The index of the selected item in the dropdown list
11488 * @event beforequery
11489 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11490 * The event object passed has these properties:
11491 * @param {Roo.bootstrap.ComboBox} combo This combo box
11492 * @param {String} query The query
11493 * @param {Boolean} forceAll true to force "all" query
11494 * @param {Boolean} cancel true to cancel the query
11495 * @param {Object} e The query event object
11497 'beforequery': true,
11500 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11501 * @param {Roo.bootstrap.ComboBox} combo This combo box
11506 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11507 * @param {Roo.bootstrap.ComboBox} combo This combo box
11508 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11513 * Fires when the remove value from the combobox array
11514 * @param {Roo.bootstrap.ComboBox} combo This combo box
11518 * @event specialfilter
11519 * Fires when specialfilter
11520 * @param {Roo.bootstrap.ComboBox} combo This combo box
11522 'specialfilter' : true,
11525 * Fires when tick the element
11526 * @param {Roo.bootstrap.ComboBox} combo This combo box
11530 * @event touchviewdisplay
11531 * Fires when touch view require special display (default is using displayField)
11532 * @param {Roo.bootstrap.ComboBox} combo This combo box
11533 * @param {Object} cfg set html .
11535 'touchviewdisplay' : true
11540 this.tickItems = [];
11542 this.selectedIndex = -1;
11543 if(this.mode == 'local'){
11544 if(config.queryDelay === undefined){
11545 this.queryDelay = 10;
11547 if(config.minChars === undefined){
11553 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11556 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11557 * rendering into an Roo.Editor, defaults to false)
11560 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11561 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11564 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11567 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11568 * the dropdown list (defaults to undefined, with no header element)
11572 * @cfg {String/Roo.Template} tpl The template to use to render the output
11576 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11578 listWidth: undefined,
11580 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11581 * mode = 'remote' or 'text' if mode = 'local')
11583 displayField: undefined,
11586 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11587 * mode = 'remote' or 'value' if mode = 'local').
11588 * Note: use of a valueField requires the user make a selection
11589 * in order for a value to be mapped.
11591 valueField: undefined,
11595 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11596 * field's data value (defaults to the underlying DOM element's name)
11598 hiddenName: undefined,
11600 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11604 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11606 selectedClass: 'active',
11609 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11613 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11614 * anchor positions (defaults to 'tl-bl')
11616 listAlign: 'tl-bl?',
11618 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11622 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11623 * query specified by the allQuery config option (defaults to 'query')
11625 triggerAction: 'query',
11627 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11628 * (defaults to 4, does not apply if editable = false)
11632 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11633 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11637 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11638 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11642 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11643 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11647 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11648 * when editable = true (defaults to false)
11650 selectOnFocus:false,
11652 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11654 queryParam: 'query',
11656 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11657 * when mode = 'remote' (defaults to 'Loading...')
11659 loadingText: 'Loading...',
11661 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11665 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11669 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11670 * traditional select (defaults to true)
11674 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11678 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11682 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11683 * listWidth has a higher value)
11687 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11688 * allow the user to set arbitrary text into the field (defaults to false)
11690 forceSelection:false,
11692 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11693 * if typeAhead = true (defaults to 250)
11695 typeAheadDelay : 250,
11697 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11698 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11700 valueNotFoundText : undefined,
11702 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11704 blockFocus : false,
11707 * @cfg {Boolean} disableClear Disable showing of clear button.
11709 disableClear : false,
11711 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11713 alwaysQuery : false,
11716 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11721 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11723 invalidClass : "has-warning",
11726 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11728 validClass : "has-success",
11731 * @cfg {Boolean} specialFilter (true|false) special filter default false
11733 specialFilter : false,
11736 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11738 mobileTouchView : true,
11750 btnPosition : 'right',
11751 triggerList : true,
11752 showToggleBtn : true,
11754 emptyResultText: 'Empty',
11755 triggerText : 'Select',
11757 // element that contains real text value.. (when hidden is used..)
11759 getAutoCreate : function()
11767 if(Roo.isTouch && this.mobileTouchView){
11768 cfg = this.getAutoCreateTouchView();
11775 if(!this.tickable){
11776 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11781 * ComboBox with tickable selections
11784 var align = this.labelAlign || this.parentLabelAlign();
11787 cls : 'form-group roo-combobox-tickable' //input-group
11792 cls : 'tickable-buttons',
11797 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11798 html : this.triggerText
11804 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11811 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11818 buttons.cn.unshift({
11820 cls: 'select2-search-field-input'
11826 Roo.each(buttons.cn, function(c){
11828 c.cls += ' btn-' + _this.size;
11831 if (_this.disabled) {
11842 cls: 'form-hidden-field'
11846 cls: 'select2-choices',
11850 cls: 'select2-search-field',
11862 cls: 'select2-container input-group select2-container-multi',
11867 // cls: 'typeahead typeahead-long dropdown-menu',
11868 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11873 if(this.hasFeedback && !this.allowBlank){
11877 cls: 'glyphicon form-control-feedback'
11880 combobox.cn.push(feedback);
11883 if (align ==='left' && this.fieldLabel.length) {
11885 Roo.log("left and has label");
11891 cls : 'control-label col-sm-' + this.labelWidth,
11892 html : this.fieldLabel
11896 cls : "col-sm-" + (12 - this.labelWidth),
11903 } else if ( this.fieldLabel.length) {
11909 //cls : 'input-group-addon',
11910 html : this.fieldLabel
11920 Roo.log(" no label && no align");
11927 ['xs','sm','md','lg'].map(function(size){
11928 if (settings[size]) {
11929 cfg.cls += ' col-' + size + '-' + settings[size];
11937 _initEventsCalled : false,
11940 initEvents: function()
11943 if (this._initEventsCalled) { // as we call render... prevent looping...
11946 this._initEventsCalled = true;
11949 throw "can not find store for combo";
11952 this.store = Roo.factory(this.store, Roo.data);
11954 // if we are building from html. then this element is so complex, that we can not really
11955 // use the rendered HTML.
11956 // so we have to trash and replace the previous code.
11957 if (Roo.XComponent.build_from_html) {
11959 // remove this element....
11960 var e = this.el.dom, k=0;
11961 while (e ) { e = e.previousSibling; ++k;}
11966 this.rendered = false;
11968 this.render(this.parent().getChildContainer(true), k);
11979 if(Roo.isTouch && this.mobileTouchView){
11980 this.initTouchView();
11985 this.initTickableEvents();
11989 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11991 if(this.hiddenName){
11993 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11995 this.hiddenField.dom.value =
11996 this.hiddenValue !== undefined ? this.hiddenValue :
11997 this.value !== undefined ? this.value : '';
11999 // prevent input submission
12000 this.el.dom.removeAttribute('name');
12001 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12006 // this.el.dom.setAttribute('autocomplete', 'off');
12009 var cls = 'x-combo-list';
12011 //this.list = new Roo.Layer({
12012 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12018 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12019 _this.list.setWidth(lw);
12022 this.list.on('mouseover', this.onViewOver, this);
12023 this.list.on('mousemove', this.onViewMove, this);
12025 this.list.on('scroll', this.onViewScroll, this);
12028 this.list.swallowEvent('mousewheel');
12029 this.assetHeight = 0;
12032 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12033 this.assetHeight += this.header.getHeight();
12036 this.innerList = this.list.createChild({cls:cls+'-inner'});
12037 this.innerList.on('mouseover', this.onViewOver, this);
12038 this.innerList.on('mousemove', this.onViewMove, this);
12039 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12041 if(this.allowBlank && !this.pageSize && !this.disableClear){
12042 this.footer = this.list.createChild({cls:cls+'-ft'});
12043 this.pageTb = new Roo.Toolbar(this.footer);
12047 this.footer = this.list.createChild({cls:cls+'-ft'});
12048 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12049 {pageSize: this.pageSize});
12053 if (this.pageTb && this.allowBlank && !this.disableClear) {
12055 this.pageTb.add(new Roo.Toolbar.Fill(), {
12056 cls: 'x-btn-icon x-btn-clear',
12058 handler: function()
12061 _this.clearValue();
12062 _this.onSelect(false, -1);
12067 this.assetHeight += this.footer.getHeight();
12072 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12075 this.view = new Roo.View(this.list, this.tpl, {
12076 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12078 //this.view.wrapEl.setDisplayed(false);
12079 this.view.on('click', this.onViewClick, this);
12083 this.store.on('beforeload', this.onBeforeLoad, this);
12084 this.store.on('load', this.onLoad, this);
12085 this.store.on('loadexception', this.onLoadException, this);
12087 if(this.resizable){
12088 this.resizer = new Roo.Resizable(this.list, {
12089 pinned:true, handles:'se'
12091 this.resizer.on('resize', function(r, w, h){
12092 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12093 this.listWidth = w;
12094 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12095 this.restrictHeight();
12097 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12100 if(!this.editable){
12101 this.editable = true;
12102 this.setEditable(false);
12107 if (typeof(this.events.add.listeners) != 'undefined') {
12109 this.addicon = this.wrap.createChild(
12110 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12112 this.addicon.on('click', function(e) {
12113 this.fireEvent('add', this);
12116 if (typeof(this.events.edit.listeners) != 'undefined') {
12118 this.editicon = this.wrap.createChild(
12119 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12120 if (this.addicon) {
12121 this.editicon.setStyle('margin-left', '40px');
12123 this.editicon.on('click', function(e) {
12125 // we fire even if inothing is selected..
12126 this.fireEvent('edit', this, this.lastData );
12132 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12133 "up" : function(e){
12134 this.inKeyMode = true;
12138 "down" : function(e){
12139 if(!this.isExpanded()){
12140 this.onTriggerClick();
12142 this.inKeyMode = true;
12147 "enter" : function(e){
12148 // this.onViewClick();
12152 if(this.fireEvent("specialkey", this, e)){
12153 this.onViewClick(false);
12159 "esc" : function(e){
12163 "tab" : function(e){
12166 if(this.fireEvent("specialkey", this, e)){
12167 this.onViewClick(false);
12175 doRelay : function(foo, bar, hname){
12176 if(hname == 'down' || this.scope.isExpanded()){
12177 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12186 this.queryDelay = Math.max(this.queryDelay || 10,
12187 this.mode == 'local' ? 10 : 250);
12190 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12192 if(this.typeAhead){
12193 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12195 if(this.editable !== false){
12196 this.inputEl().on("keyup", this.onKeyUp, this);
12198 if(this.forceSelection){
12199 this.inputEl().on('blur', this.doForce, this);
12203 this.choices = this.el.select('ul.select2-choices', true).first();
12204 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12208 initTickableEvents: function()
12212 if(this.hiddenName){
12214 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12216 this.hiddenField.dom.value =
12217 this.hiddenValue !== undefined ? this.hiddenValue :
12218 this.value !== undefined ? this.value : '';
12220 // prevent input submission
12221 this.el.dom.removeAttribute('name');
12222 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12227 // this.list = this.el.select('ul.dropdown-menu',true).first();
12229 this.choices = this.el.select('ul.select2-choices', true).first();
12230 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12231 if(this.triggerList){
12232 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12235 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12236 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12238 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12239 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12241 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12242 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12244 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12245 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12246 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12249 this.cancelBtn.hide();
12254 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12255 _this.list.setWidth(lw);
12258 this.list.on('mouseover', this.onViewOver, this);
12259 this.list.on('mousemove', this.onViewMove, this);
12261 this.list.on('scroll', this.onViewScroll, this);
12264 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>';
12267 this.view = new Roo.View(this.list, this.tpl, {
12268 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12271 //this.view.wrapEl.setDisplayed(false);
12272 this.view.on('click', this.onViewClick, this);
12276 this.store.on('beforeload', this.onBeforeLoad, this);
12277 this.store.on('load', this.onLoad, this);
12278 this.store.on('loadexception', this.onLoadException, this);
12281 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12282 "up" : function(e){
12283 this.inKeyMode = true;
12287 "down" : function(e){
12288 this.inKeyMode = true;
12292 "enter" : function(e){
12293 if(this.fireEvent("specialkey", this, e)){
12294 this.onViewClick(false);
12300 "esc" : function(e){
12301 this.onTickableFooterButtonClick(e, false, false);
12304 "tab" : function(e){
12305 this.fireEvent("specialkey", this, e);
12307 this.onTickableFooterButtonClick(e, false, false);
12314 doRelay : function(e, fn, key){
12315 if(this.scope.isExpanded()){
12316 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12325 this.queryDelay = Math.max(this.queryDelay || 10,
12326 this.mode == 'local' ? 10 : 250);
12329 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12331 if(this.typeAhead){
12332 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12335 if(this.editable !== false){
12336 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12341 onDestroy : function(){
12343 this.view.setStore(null);
12344 this.view.el.removeAllListeners();
12345 this.view.el.remove();
12346 this.view.purgeListeners();
12349 this.list.dom.innerHTML = '';
12353 this.store.un('beforeload', this.onBeforeLoad, this);
12354 this.store.un('load', this.onLoad, this);
12355 this.store.un('loadexception', this.onLoadException, this);
12357 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12361 fireKey : function(e){
12362 if(e.isNavKeyPress() && !this.list.isVisible()){
12363 this.fireEvent("specialkey", this, e);
12368 onResize: function(w, h){
12369 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12371 // if(typeof w != 'number'){
12372 // // we do not handle it!?!?
12375 // var tw = this.trigger.getWidth();
12376 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12377 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12379 // this.inputEl().setWidth( this.adjustWidth('input', x));
12381 // //this.trigger.setStyle('left', x+'px');
12383 // if(this.list && this.listWidth === undefined){
12384 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12385 // this.list.setWidth(lw);
12386 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12394 * Allow or prevent the user from directly editing the field text. If false is passed,
12395 * the user will only be able to select from the items defined in the dropdown list. This method
12396 * is the runtime equivalent of setting the 'editable' config option at config time.
12397 * @param {Boolean} value True to allow the user to directly edit the field text
12399 setEditable : function(value){
12400 if(value == this.editable){
12403 this.editable = value;
12405 this.inputEl().dom.setAttribute('readOnly', true);
12406 this.inputEl().on('mousedown', this.onTriggerClick, this);
12407 this.inputEl().addClass('x-combo-noedit');
12409 this.inputEl().dom.setAttribute('readOnly', false);
12410 this.inputEl().un('mousedown', this.onTriggerClick, this);
12411 this.inputEl().removeClass('x-combo-noedit');
12417 onBeforeLoad : function(combo,opts){
12418 if(!this.hasFocus){
12422 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12424 this.restrictHeight();
12425 this.selectedIndex = -1;
12429 onLoad : function(){
12431 this.hasQuery = false;
12433 if(!this.hasFocus){
12437 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12438 this.loading.hide();
12441 if(this.store.getCount() > 0){
12443 this.restrictHeight();
12444 if(this.lastQuery == this.allQuery){
12445 if(this.editable && !this.tickable){
12446 this.inputEl().dom.select();
12450 !this.selectByValue(this.value, true) &&
12453 !this.store.lastOptions ||
12454 typeof(this.store.lastOptions.add) == 'undefined' ||
12455 this.store.lastOptions.add != true
12458 this.select(0, true);
12461 if(this.autoFocus){
12464 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12465 this.taTask.delay(this.typeAheadDelay);
12469 this.onEmptyResults();
12475 onLoadException : function()
12477 this.hasQuery = false;
12479 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12480 this.loading.hide();
12483 if(this.tickable && this.editable){
12488 // only causes errors at present
12489 //Roo.log(this.store.reader.jsonData);
12490 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12492 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12498 onTypeAhead : function(){
12499 if(this.store.getCount() > 0){
12500 var r = this.store.getAt(0);
12501 var newValue = r.data[this.displayField];
12502 var len = newValue.length;
12503 var selStart = this.getRawValue().length;
12505 if(selStart != len){
12506 this.setRawValue(newValue);
12507 this.selectText(selStart, newValue.length);
12513 onSelect : function(record, index){
12515 if(this.fireEvent('beforeselect', this, record, index) !== false){
12517 this.setFromData(index > -1 ? record.data : false);
12520 this.fireEvent('select', this, record, index);
12525 * Returns the currently selected field value or empty string if no value is set.
12526 * @return {String} value The selected value
12528 getValue : function(){
12531 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12534 if(this.valueField){
12535 return typeof this.value != 'undefined' ? this.value : '';
12537 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12542 * Clears any text/value currently set in the field
12544 clearValue : function(){
12545 if(this.hiddenField){
12546 this.hiddenField.dom.value = '';
12549 this.setRawValue('');
12550 this.lastSelectionText = '';
12551 this.lastData = false;
12553 var close = this.closeTriggerEl();
12562 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12563 * will be displayed in the field. If the value does not match the data value of an existing item,
12564 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12565 * Otherwise the field will be blank (although the value will still be set).
12566 * @param {String} value The value to match
12568 setValue : function(v){
12575 if(this.valueField){
12576 var r = this.findRecord(this.valueField, v);
12578 text = r.data[this.displayField];
12579 }else if(this.valueNotFoundText !== undefined){
12580 text = this.valueNotFoundText;
12583 this.lastSelectionText = text;
12584 if(this.hiddenField){
12585 this.hiddenField.dom.value = v;
12587 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12590 var close = this.closeTriggerEl();
12593 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12597 * @property {Object} the last set data for the element
12602 * Sets the value of the field based on a object which is related to the record format for the store.
12603 * @param {Object} value the value to set as. or false on reset?
12605 setFromData : function(o){
12612 var dv = ''; // display value
12613 var vv = ''; // value value..
12615 if (this.displayField) {
12616 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12618 // this is an error condition!!!
12619 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12622 if(this.valueField){
12623 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12626 var close = this.closeTriggerEl();
12629 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12632 if(this.hiddenField){
12633 this.hiddenField.dom.value = vv;
12635 this.lastSelectionText = dv;
12636 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12640 // no hidden field.. - we store the value in 'value', but still display
12641 // display field!!!!
12642 this.lastSelectionText = dv;
12643 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12650 reset : function(){
12651 // overridden so that last data is reset..
12658 this.setValue(this.originalValue);
12659 this.clearInvalid();
12660 this.lastData = false;
12662 this.view.clearSelections();
12666 findRecord : function(prop, value){
12668 if(this.store.getCount() > 0){
12669 this.store.each(function(r){
12670 if(r.data[prop] == value){
12680 getName: function()
12682 // returns hidden if it's set..
12683 if (!this.rendered) {return ''};
12684 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12688 onViewMove : function(e, t){
12689 this.inKeyMode = false;
12693 onViewOver : function(e, t){
12694 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12697 var item = this.view.findItemFromChild(t);
12700 var index = this.view.indexOf(item);
12701 this.select(index, false);
12706 onViewClick : function(view, doFocus, el, e)
12708 var index = this.view.getSelectedIndexes()[0];
12710 var r = this.store.getAt(index);
12714 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12721 Roo.each(this.tickItems, function(v,k){
12723 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12725 _this.tickItems.splice(k, 1);
12727 if(typeof(e) == 'undefined' && view == false){
12728 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12740 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12741 this.tickItems.push(r.data);
12744 if(typeof(e) == 'undefined' && view == false){
12745 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12752 this.onSelect(r, index);
12754 if(doFocus !== false && !this.blockFocus){
12755 this.inputEl().focus();
12760 restrictHeight : function(){
12761 //this.innerList.dom.style.height = '';
12762 //var inner = this.innerList.dom;
12763 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12764 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12765 //this.list.beginUpdate();
12766 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12767 this.list.alignTo(this.inputEl(), this.listAlign);
12768 this.list.alignTo(this.inputEl(), this.listAlign);
12769 //this.list.endUpdate();
12773 onEmptyResults : function(){
12775 if(this.tickable && this.editable){
12776 this.restrictHeight();
12784 * Returns true if the dropdown list is expanded, else false.
12786 isExpanded : function(){
12787 return this.list.isVisible();
12791 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12792 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12793 * @param {String} value The data value of the item to select
12794 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12795 * selected item if it is not currently in view (defaults to true)
12796 * @return {Boolean} True if the value matched an item in the list, else false
12798 selectByValue : function(v, scrollIntoView){
12799 if(v !== undefined && v !== null){
12800 var r = this.findRecord(this.valueField || this.displayField, v);
12802 this.select(this.store.indexOf(r), scrollIntoView);
12810 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12811 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12812 * @param {Number} index The zero-based index of the list item to select
12813 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12814 * selected item if it is not currently in view (defaults to true)
12816 select : function(index, scrollIntoView){
12817 this.selectedIndex = index;
12818 this.view.select(index);
12819 if(scrollIntoView !== false){
12820 var el = this.view.getNode(index);
12822 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12825 this.list.scrollChildIntoView(el, false);
12831 selectNext : function(){
12832 var ct = this.store.getCount();
12834 if(this.selectedIndex == -1){
12836 }else if(this.selectedIndex < ct-1){
12837 this.select(this.selectedIndex+1);
12843 selectPrev : function(){
12844 var ct = this.store.getCount();
12846 if(this.selectedIndex == -1){
12848 }else if(this.selectedIndex != 0){
12849 this.select(this.selectedIndex-1);
12855 onKeyUp : function(e){
12856 if(this.editable !== false && !e.isSpecialKey()){
12857 this.lastKey = e.getKey();
12858 this.dqTask.delay(this.queryDelay);
12863 validateBlur : function(){
12864 return !this.list || !this.list.isVisible();
12868 initQuery : function(){
12870 var v = this.getRawValue();
12872 if(this.tickable && this.editable){
12873 v = this.tickableInputEl().getValue();
12880 doForce : function(){
12881 if(this.inputEl().dom.value.length > 0){
12882 this.inputEl().dom.value =
12883 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12889 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12890 * query allowing the query action to be canceled if needed.
12891 * @param {String} query The SQL query to execute
12892 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12893 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12894 * saved in the current store (defaults to false)
12896 doQuery : function(q, forceAll){
12898 if(q === undefined || q === null){
12903 forceAll: forceAll,
12907 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12912 forceAll = qe.forceAll;
12913 if(forceAll === true || (q.length >= this.minChars)){
12915 this.hasQuery = true;
12917 if(this.lastQuery != q || this.alwaysQuery){
12918 this.lastQuery = q;
12919 if(this.mode == 'local'){
12920 this.selectedIndex = -1;
12922 this.store.clearFilter();
12925 if(this.specialFilter){
12926 this.fireEvent('specialfilter', this);
12931 this.store.filter(this.displayField, q);
12934 this.store.fireEvent("datachanged", this.store);
12941 this.store.baseParams[this.queryParam] = q;
12943 var options = {params : this.getParams(q)};
12946 options.add = true;
12947 options.params.start = this.page * this.pageSize;
12950 this.store.load(options);
12953 * this code will make the page width larger, at the beginning, the list not align correctly,
12954 * we should expand the list on onLoad
12955 * so command out it
12960 this.selectedIndex = -1;
12965 this.loadNext = false;
12969 getParams : function(q){
12971 //p[this.queryParam] = q;
12975 p.limit = this.pageSize;
12981 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12983 collapse : function(){
12984 if(!this.isExpanded()){
12991 this.hasFocus = false;
12993 this.cancelBtn.hide();
12994 this.trigger.show();
12997 this.tickableInputEl().dom.value = '';
12998 this.tickableInputEl().blur();
13003 Roo.get(document).un('mousedown', this.collapseIf, this);
13004 Roo.get(document).un('mousewheel', this.collapseIf, this);
13005 if (!this.editable) {
13006 Roo.get(document).un('keydown', this.listKeyPress, this);
13008 this.fireEvent('collapse', this);
13012 collapseIf : function(e){
13013 var in_combo = e.within(this.el);
13014 var in_list = e.within(this.list);
13015 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13017 if (in_combo || in_list || is_list) {
13018 //e.stopPropagation();
13023 this.onTickableFooterButtonClick(e, false, false);
13031 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13033 expand : function(){
13035 if(this.isExpanded() || !this.hasFocus){
13039 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13040 this.list.setWidth(lw);
13047 this.restrictHeight();
13051 this.tickItems = Roo.apply([], this.item);
13054 this.cancelBtn.show();
13055 this.trigger.hide();
13058 this.tickableInputEl().focus();
13063 Roo.get(document).on('mousedown', this.collapseIf, this);
13064 Roo.get(document).on('mousewheel', this.collapseIf, this);
13065 if (!this.editable) {
13066 Roo.get(document).on('keydown', this.listKeyPress, this);
13069 this.fireEvent('expand', this);
13073 // Implements the default empty TriggerField.onTriggerClick function
13074 onTriggerClick : function(e)
13076 Roo.log('trigger click');
13078 if(this.disabled || !this.triggerList){
13083 this.loadNext = false;
13085 if(this.isExpanded()){
13087 if (!this.blockFocus) {
13088 this.inputEl().focus();
13092 this.hasFocus = true;
13093 if(this.triggerAction == 'all') {
13094 this.doQuery(this.allQuery, true);
13096 this.doQuery(this.getRawValue());
13098 if (!this.blockFocus) {
13099 this.inputEl().focus();
13104 onTickableTriggerClick : function(e)
13111 this.loadNext = false;
13112 this.hasFocus = true;
13114 if(this.triggerAction == 'all') {
13115 this.doQuery(this.allQuery, true);
13117 this.doQuery(this.getRawValue());
13121 onSearchFieldClick : function(e)
13123 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13124 this.onTickableFooterButtonClick(e, false, false);
13128 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13133 this.loadNext = false;
13134 this.hasFocus = true;
13136 if(this.triggerAction == 'all') {
13137 this.doQuery(this.allQuery, true);
13139 this.doQuery(this.getRawValue());
13143 listKeyPress : function(e)
13145 //Roo.log('listkeypress');
13146 // scroll to first matching element based on key pres..
13147 if (e.isSpecialKey()) {
13150 var k = String.fromCharCode(e.getKey()).toUpperCase();
13153 var csel = this.view.getSelectedNodes();
13154 var cselitem = false;
13156 var ix = this.view.indexOf(csel[0]);
13157 cselitem = this.store.getAt(ix);
13158 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13164 this.store.each(function(v) {
13166 // start at existing selection.
13167 if (cselitem.id == v.id) {
13173 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13174 match = this.store.indexOf(v);
13180 if (match === false) {
13181 return true; // no more action?
13184 this.view.select(match);
13185 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13186 sn.scrollIntoView(sn.dom.parentNode, false);
13189 onViewScroll : function(e, t){
13191 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){
13195 this.hasQuery = true;
13197 this.loading = this.list.select('.loading', true).first();
13199 if(this.loading === null){
13200 this.list.createChild({
13202 cls: 'loading select2-more-results select2-active',
13203 html: 'Loading more results...'
13206 this.loading = this.list.select('.loading', true).first();
13208 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13210 this.loading.hide();
13213 this.loading.show();
13218 this.loadNext = true;
13220 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13225 addItem : function(o)
13227 var dv = ''; // display value
13229 if (this.displayField) {
13230 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13232 // this is an error condition!!!
13233 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13240 var choice = this.choices.createChild({
13242 cls: 'select2-search-choice',
13251 cls: 'select2-search-choice-close',
13256 }, this.searchField);
13258 var close = choice.select('a.select2-search-choice-close', true).first();
13260 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13268 this.inputEl().dom.value = '';
13273 onRemoveItem : function(e, _self, o)
13275 e.preventDefault();
13277 this.lastItem = Roo.apply([], this.item);
13279 var index = this.item.indexOf(o.data) * 1;
13282 Roo.log('not this item?!');
13286 this.item.splice(index, 1);
13291 this.fireEvent('remove', this, e);
13297 syncValue : function()
13299 if(!this.item.length){
13306 Roo.each(this.item, function(i){
13307 if(_this.valueField){
13308 value.push(i[_this.valueField]);
13315 this.value = value.join(',');
13317 if(this.hiddenField){
13318 this.hiddenField.dom.value = this.value;
13321 this.store.fireEvent("datachanged", this.store);
13324 clearItem : function()
13326 if(!this.multiple){
13332 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13340 if(this.tickable && !Roo.isTouch){
13341 this.view.refresh();
13345 inputEl: function ()
13347 if(Roo.isTouch && this.mobileTouchView){
13348 return this.el.select('input.form-control',true).first();
13352 return this.searchField;
13355 return this.el.select('input.form-control',true).first();
13359 onTickableFooterButtonClick : function(e, btn, el)
13361 e.preventDefault();
13363 this.lastItem = Roo.apply([], this.item);
13365 if(btn && btn.name == 'cancel'){
13366 this.tickItems = Roo.apply([], this.item);
13375 Roo.each(this.tickItems, function(o){
13383 validate : function()
13385 var v = this.getRawValue();
13388 v = this.getValue();
13391 if(this.disabled || this.allowBlank || v.length){
13396 this.markInvalid();
13400 tickableInputEl : function()
13402 if(!this.tickable || !this.editable){
13403 return this.inputEl();
13406 return this.inputEl().select('.select2-search-field-input', true).first();
13410 getAutoCreateTouchView : function()
13415 cls: 'form-group' //input-group
13421 type : this.inputType,
13422 cls : 'form-control x-combo-noedit',
13423 autocomplete: 'new-password',
13424 placeholder : this.placeholder || '',
13429 input.name = this.name;
13433 input.cls += ' input-' + this.size;
13436 if (this.disabled) {
13437 input.disabled = true;
13448 inputblock.cls += ' input-group';
13450 inputblock.cn.unshift({
13452 cls : 'input-group-addon',
13457 if(this.removable && !this.multiple){
13458 inputblock.cls += ' roo-removable';
13460 inputblock.cn.push({
13463 cls : 'roo-combo-removable-btn close'
13467 if(this.hasFeedback && !this.allowBlank){
13469 inputblock.cls += ' has-feedback';
13471 inputblock.cn.push({
13473 cls: 'glyphicon form-control-feedback'
13480 inputblock.cls += (this.before) ? '' : ' input-group';
13482 inputblock.cn.push({
13484 cls : 'input-group-addon',
13495 cls: 'form-hidden-field'
13509 cls: 'form-hidden-field'
13513 cls: 'select2-choices',
13517 cls: 'select2-search-field',
13530 cls: 'select2-container input-group',
13537 combobox.cls += ' select2-container-multi';
13540 var align = this.labelAlign || this.parentLabelAlign();
13544 if(this.fieldLabel.length){
13546 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13547 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13552 cls : 'control-label ' + lw,
13553 html : this.fieldLabel
13565 var settings = this;
13567 ['xs','sm','md','lg'].map(function(size){
13568 if (settings[size]) {
13569 cfg.cls += ' col-' + size + '-' + settings[size];
13576 initTouchView : function()
13578 this.renderTouchView();
13580 this.touchViewEl.on('scroll', function(){
13581 this.el.dom.scrollTop = 0;
13584 this.inputEl().on("click", this.showTouchView, this);
13585 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13586 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13588 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13590 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13591 this.store.on('load', this.onTouchViewLoad, this);
13592 this.store.on('loadexception', this.onTouchViewLoadException, this);
13594 if(this.hiddenName){
13596 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13598 this.hiddenField.dom.value =
13599 this.hiddenValue !== undefined ? this.hiddenValue :
13600 this.value !== undefined ? this.value : '';
13602 this.el.dom.removeAttribute('name');
13603 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13607 this.choices = this.el.select('ul.select2-choices', true).first();
13608 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13611 if(this.removable && !this.multiple){
13612 var close = this.closeTriggerEl();
13614 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13615 close.on('click', this.removeBtnClick, this, close);
13624 renderTouchView : function()
13626 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13627 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13629 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13630 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13632 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13633 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13634 this.touchViewBodyEl.setStyle('overflow', 'auto');
13636 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13637 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13640 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13644 showTouchView : function()
13646 this.touchViewHeaderEl.hide();
13648 if(this.fieldLabel.length){
13649 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13650 this.touchViewHeaderEl.show();
13653 this.touchViewEl.show();
13655 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13656 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13658 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13660 if(this.fieldLabel.length){
13661 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13664 this.touchViewBodyEl.setHeight(bodyHeight);
13668 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13670 this.touchViewEl.addClass('in');
13673 this.doTouchViewQuery();
13677 hideTouchView : function()
13679 this.touchViewEl.removeClass('in');
13683 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13685 this.touchViewEl.setStyle('display', 'none');
13690 setTouchViewValue : function()
13697 Roo.each(this.tickItems, function(o){
13702 this.hideTouchView();
13705 doTouchViewQuery : function()
13714 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13718 if(!this.alwaysQuery || this.mode == 'local'){
13719 this.onTouchViewLoad();
13726 onTouchViewBeforeLoad : function(combo,opts)
13732 onTouchViewLoad : function()
13734 if(this.store.getCount() < 1){
13735 this.onTouchViewEmptyResults();
13739 this.clearTouchView();
13741 var rawValue = this.getRawValue();
13743 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13745 this.tickItems = [];
13747 this.store.data.each(function(d, rowIndex){
13748 var row = this.touchViewListGroup.createChild(template);
13750 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13753 html : d.data[this.displayField]
13756 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13757 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13761 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13762 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13765 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13766 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13767 this.tickItems.push(d.data);
13770 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13774 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13776 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13778 if(this.fieldLabel.length){
13779 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13782 var listHeight = this.touchViewListGroup.getHeight();
13786 if(firstChecked && listHeight > bodyHeight){
13787 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13792 onTouchViewLoadException : function()
13794 this.hideTouchView();
13797 onTouchViewEmptyResults : function()
13799 this.clearTouchView();
13801 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13803 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13807 clearTouchView : function()
13809 this.touchViewListGroup.dom.innerHTML = '';
13812 onTouchViewClick : function(e, el, o)
13814 e.preventDefault();
13817 var rowIndex = o.rowIndex;
13819 var r = this.store.getAt(rowIndex);
13821 if(!this.multiple){
13822 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13823 c.dom.removeAttribute('checked');
13826 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13828 this.setFromData(r.data);
13830 var close = this.closeTriggerEl();
13836 this.hideTouchView();
13838 this.fireEvent('select', this, r, rowIndex);
13843 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13844 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13845 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13849 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13850 this.addItem(r.data);
13851 this.tickItems.push(r.data);
13857 * @cfg {Boolean} grow
13861 * @cfg {Number} growMin
13865 * @cfg {Number} growMax
13874 Roo.apply(Roo.bootstrap.ComboBox, {
13878 cls: 'modal-header',
13900 cls: 'list-group-item',
13904 cls: 'roo-combobox-list-group-item-value'
13908 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13922 listItemCheckbox : {
13924 cls: 'list-group-item',
13928 cls: 'roo-combobox-list-group-item-value'
13932 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13948 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13953 cls: 'modal-footer',
13961 cls: 'col-xs-6 text-left',
13964 cls: 'btn btn-danger roo-touch-view-cancel',
13970 cls: 'col-xs-6 text-right',
13973 cls: 'btn btn-success roo-touch-view-ok',
13984 Roo.apply(Roo.bootstrap.ComboBox, {
13986 touchViewTemplate : {
13988 cls: 'modal fade roo-combobox-touch-view',
13992 cls: 'modal-dialog',
13993 style : 'position:fixed', // we have to fix position....
13997 cls: 'modal-content',
13999 Roo.bootstrap.ComboBox.header,
14000 Roo.bootstrap.ComboBox.body,
14001 Roo.bootstrap.ComboBox.footer
14010 * Ext JS Library 1.1.1
14011 * Copyright(c) 2006-2007, Ext JS, LLC.
14013 * Originally Released Under LGPL - original licence link has changed is not relivant.
14016 * <script type="text/javascript">
14021 * @extends Roo.util.Observable
14022 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14023 * This class also supports single and multi selection modes. <br>
14024 * Create a data model bound view:
14026 var store = new Roo.data.Store(...);
14028 var view = new Roo.View({
14030 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14032 singleSelect: true,
14033 selectedClass: "ydataview-selected",
14037 // listen for node click?
14038 view.on("click", function(vw, index, node, e){
14039 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14043 dataModel.load("foobar.xml");
14045 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14047 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14048 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14050 * Note: old style constructor is still suported (container, template, config)
14053 * Create a new View
14054 * @param {Object} config The config object
14057 Roo.View = function(config, depreciated_tpl, depreciated_config){
14059 this.parent = false;
14061 if (typeof(depreciated_tpl) == 'undefined') {
14062 // new way.. - universal constructor.
14063 Roo.apply(this, config);
14064 this.el = Roo.get(this.el);
14067 this.el = Roo.get(config);
14068 this.tpl = depreciated_tpl;
14069 Roo.apply(this, depreciated_config);
14071 this.wrapEl = this.el.wrap().wrap();
14072 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14075 if(typeof(this.tpl) == "string"){
14076 this.tpl = new Roo.Template(this.tpl);
14078 // support xtype ctors..
14079 this.tpl = new Roo.factory(this.tpl, Roo);
14083 this.tpl.compile();
14088 * @event beforeclick
14089 * Fires before a click is processed. Returns false to cancel the default action.
14090 * @param {Roo.View} this
14091 * @param {Number} index The index of the target node
14092 * @param {HTMLElement} node The target node
14093 * @param {Roo.EventObject} e The raw event object
14095 "beforeclick" : true,
14098 * Fires when a template node is clicked.
14099 * @param {Roo.View} this
14100 * @param {Number} index The index of the target node
14101 * @param {HTMLElement} node The target node
14102 * @param {Roo.EventObject} e The raw event object
14107 * Fires when a template node is double clicked.
14108 * @param {Roo.View} this
14109 * @param {Number} index The index of the target node
14110 * @param {HTMLElement} node The target node
14111 * @param {Roo.EventObject} e The raw event object
14115 * @event contextmenu
14116 * Fires when a template node is right clicked.
14117 * @param {Roo.View} this
14118 * @param {Number} index The index of the target node
14119 * @param {HTMLElement} node The target node
14120 * @param {Roo.EventObject} e The raw event object
14122 "contextmenu" : true,
14124 * @event selectionchange
14125 * Fires when the selected nodes change.
14126 * @param {Roo.View} this
14127 * @param {Array} selections Array of the selected nodes
14129 "selectionchange" : true,
14132 * @event beforeselect
14133 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14134 * @param {Roo.View} this
14135 * @param {HTMLElement} node The node to be selected
14136 * @param {Array} selections Array of currently selected nodes
14138 "beforeselect" : true,
14140 * @event preparedata
14141 * Fires on every row to render, to allow you to change the data.
14142 * @param {Roo.View} this
14143 * @param {Object} data to be rendered (change this)
14145 "preparedata" : true
14153 "click": this.onClick,
14154 "dblclick": this.onDblClick,
14155 "contextmenu": this.onContextMenu,
14159 this.selections = [];
14161 this.cmp = new Roo.CompositeElementLite([]);
14163 this.store = Roo.factory(this.store, Roo.data);
14164 this.setStore(this.store, true);
14167 if ( this.footer && this.footer.xtype) {
14169 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14171 this.footer.dataSource = this.store;
14172 this.footer.container = fctr;
14173 this.footer = Roo.factory(this.footer, Roo);
14174 fctr.insertFirst(this.el);
14176 // this is a bit insane - as the paging toolbar seems to detach the el..
14177 // dom.parentNode.parentNode.parentNode
14178 // they get detached?
14182 Roo.View.superclass.constructor.call(this);
14187 Roo.extend(Roo.View, Roo.util.Observable, {
14190 * @cfg {Roo.data.Store} store Data store to load data from.
14195 * @cfg {String|Roo.Element} el The container element.
14200 * @cfg {String|Roo.Template} tpl The template used by this View
14204 * @cfg {String} dataName the named area of the template to use as the data area
14205 * Works with domtemplates roo-name="name"
14209 * @cfg {String} selectedClass The css class to add to selected nodes
14211 selectedClass : "x-view-selected",
14213 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14218 * @cfg {String} text to display on mask (default Loading)
14222 * @cfg {Boolean} multiSelect Allow multiple selection
14224 multiSelect : false,
14226 * @cfg {Boolean} singleSelect Allow single selection
14228 singleSelect: false,
14231 * @cfg {Boolean} toggleSelect - selecting
14233 toggleSelect : false,
14236 * @cfg {Boolean} tickable - selecting
14241 * Returns the element this view is bound to.
14242 * @return {Roo.Element}
14244 getEl : function(){
14245 return this.wrapEl;
14251 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14253 refresh : function(){
14254 //Roo.log('refresh');
14257 // if we are using something like 'domtemplate', then
14258 // the what gets used is:
14259 // t.applySubtemplate(NAME, data, wrapping data..)
14260 // the outer template then get' applied with
14261 // the store 'extra data'
14262 // and the body get's added to the
14263 // roo-name="data" node?
14264 // <span class='roo-tpl-{name}'></span> ?????
14268 this.clearSelections();
14269 this.el.update("");
14271 var records = this.store.getRange();
14272 if(records.length < 1) {
14274 // is this valid?? = should it render a template??
14276 this.el.update(this.emptyText);
14280 if (this.dataName) {
14281 this.el.update(t.apply(this.store.meta)); //????
14282 el = this.el.child('.roo-tpl-' + this.dataName);
14285 for(var i = 0, len = records.length; i < len; i++){
14286 var data = this.prepareData(records[i].data, i, records[i]);
14287 this.fireEvent("preparedata", this, data, i, records[i]);
14289 var d = Roo.apply({}, data);
14292 Roo.apply(d, {'roo-id' : Roo.id()});
14296 Roo.each(this.parent.item, function(item){
14297 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14300 Roo.apply(d, {'roo-data-checked' : 'checked'});
14304 html[html.length] = Roo.util.Format.trim(
14306 t.applySubtemplate(this.dataName, d, this.store.meta) :
14313 el.update(html.join(""));
14314 this.nodes = el.dom.childNodes;
14315 this.updateIndexes(0);
14320 * Function to override to reformat the data that is sent to
14321 * the template for each node.
14322 * DEPRICATED - use the preparedata event handler.
14323 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14324 * a JSON object for an UpdateManager bound view).
14326 prepareData : function(data, index, record)
14328 this.fireEvent("preparedata", this, data, index, record);
14332 onUpdate : function(ds, record){
14333 // Roo.log('on update');
14334 this.clearSelections();
14335 var index = this.store.indexOf(record);
14336 var n = this.nodes[index];
14337 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14338 n.parentNode.removeChild(n);
14339 this.updateIndexes(index, index);
14345 onAdd : function(ds, records, index)
14347 //Roo.log(['on Add', ds, records, index] );
14348 this.clearSelections();
14349 if(this.nodes.length == 0){
14353 var n = this.nodes[index];
14354 for(var i = 0, len = records.length; i < len; i++){
14355 var d = this.prepareData(records[i].data, i, records[i]);
14357 this.tpl.insertBefore(n, d);
14360 this.tpl.append(this.el, d);
14363 this.updateIndexes(index);
14366 onRemove : function(ds, record, index){
14367 // Roo.log('onRemove');
14368 this.clearSelections();
14369 var el = this.dataName ?
14370 this.el.child('.roo-tpl-' + this.dataName) :
14373 el.dom.removeChild(this.nodes[index]);
14374 this.updateIndexes(index);
14378 * Refresh an individual node.
14379 * @param {Number} index
14381 refreshNode : function(index){
14382 this.onUpdate(this.store, this.store.getAt(index));
14385 updateIndexes : function(startIndex, endIndex){
14386 var ns = this.nodes;
14387 startIndex = startIndex || 0;
14388 endIndex = endIndex || ns.length - 1;
14389 for(var i = startIndex; i <= endIndex; i++){
14390 ns[i].nodeIndex = i;
14395 * Changes the data store this view uses and refresh the view.
14396 * @param {Store} store
14398 setStore : function(store, initial){
14399 if(!initial && this.store){
14400 this.store.un("datachanged", this.refresh);
14401 this.store.un("add", this.onAdd);
14402 this.store.un("remove", this.onRemove);
14403 this.store.un("update", this.onUpdate);
14404 this.store.un("clear", this.refresh);
14405 this.store.un("beforeload", this.onBeforeLoad);
14406 this.store.un("load", this.onLoad);
14407 this.store.un("loadexception", this.onLoad);
14411 store.on("datachanged", this.refresh, this);
14412 store.on("add", this.onAdd, this);
14413 store.on("remove", this.onRemove, this);
14414 store.on("update", this.onUpdate, this);
14415 store.on("clear", this.refresh, this);
14416 store.on("beforeload", this.onBeforeLoad, this);
14417 store.on("load", this.onLoad, this);
14418 store.on("loadexception", this.onLoad, this);
14426 * onbeforeLoad - masks the loading area.
14429 onBeforeLoad : function(store,opts)
14431 //Roo.log('onBeforeLoad');
14433 this.el.update("");
14435 this.el.mask(this.mask ? this.mask : "Loading" );
14437 onLoad : function ()
14444 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14445 * @param {HTMLElement} node
14446 * @return {HTMLElement} The template node
14448 findItemFromChild : function(node){
14449 var el = this.dataName ?
14450 this.el.child('.roo-tpl-' + this.dataName,true) :
14453 if(!node || node.parentNode == el){
14456 var p = node.parentNode;
14457 while(p && p != el){
14458 if(p.parentNode == el){
14467 onClick : function(e){
14468 var item = this.findItemFromChild(e.getTarget());
14470 var index = this.indexOf(item);
14471 if(this.onItemClick(item, index, e) !== false){
14472 this.fireEvent("click", this, index, item, e);
14475 this.clearSelections();
14480 onContextMenu : function(e){
14481 var item = this.findItemFromChild(e.getTarget());
14483 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14488 onDblClick : function(e){
14489 var item = this.findItemFromChild(e.getTarget());
14491 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14495 onItemClick : function(item, index, e)
14497 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14500 if (this.toggleSelect) {
14501 var m = this.isSelected(item) ? 'unselect' : 'select';
14504 _t[m](item, true, false);
14507 if(this.multiSelect || this.singleSelect){
14508 if(this.multiSelect && e.shiftKey && this.lastSelection){
14509 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14511 this.select(item, this.multiSelect && e.ctrlKey);
14512 this.lastSelection = item;
14515 if(!this.tickable){
14516 e.preventDefault();
14524 * Get the number of selected nodes.
14527 getSelectionCount : function(){
14528 return this.selections.length;
14532 * Get the currently selected nodes.
14533 * @return {Array} An array of HTMLElements
14535 getSelectedNodes : function(){
14536 return this.selections;
14540 * Get the indexes of the selected nodes.
14543 getSelectedIndexes : function(){
14544 var indexes = [], s = this.selections;
14545 for(var i = 0, len = s.length; i < len; i++){
14546 indexes.push(s[i].nodeIndex);
14552 * Clear all selections
14553 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14555 clearSelections : function(suppressEvent){
14556 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14557 this.cmp.elements = this.selections;
14558 this.cmp.removeClass(this.selectedClass);
14559 this.selections = [];
14560 if(!suppressEvent){
14561 this.fireEvent("selectionchange", this, this.selections);
14567 * Returns true if the passed node is selected
14568 * @param {HTMLElement/Number} node The node or node index
14569 * @return {Boolean}
14571 isSelected : function(node){
14572 var s = this.selections;
14576 node = this.getNode(node);
14577 return s.indexOf(node) !== -1;
14582 * @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
14583 * @param {Boolean} keepExisting (optional) true to keep existing selections
14584 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14586 select : function(nodeInfo, keepExisting, suppressEvent){
14587 if(nodeInfo instanceof Array){
14589 this.clearSelections(true);
14591 for(var i = 0, len = nodeInfo.length; i < len; i++){
14592 this.select(nodeInfo[i], true, true);
14596 var node = this.getNode(nodeInfo);
14597 if(!node || this.isSelected(node)){
14598 return; // already selected.
14601 this.clearSelections(true);
14604 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14605 Roo.fly(node).addClass(this.selectedClass);
14606 this.selections.push(node);
14607 if(!suppressEvent){
14608 this.fireEvent("selectionchange", this, this.selections);
14616 * @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
14617 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14618 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14620 unselect : function(nodeInfo, keepExisting, suppressEvent)
14622 if(nodeInfo instanceof Array){
14623 Roo.each(this.selections, function(s) {
14624 this.unselect(s, nodeInfo);
14628 var node = this.getNode(nodeInfo);
14629 if(!node || !this.isSelected(node)){
14630 //Roo.log("not selected");
14631 return; // not selected.
14635 Roo.each(this.selections, function(s) {
14637 Roo.fly(node).removeClass(this.selectedClass);
14644 this.selections= ns;
14645 this.fireEvent("selectionchange", this, this.selections);
14649 * Gets a template node.
14650 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14651 * @return {HTMLElement} The node or null if it wasn't found
14653 getNode : function(nodeInfo){
14654 if(typeof nodeInfo == "string"){
14655 return document.getElementById(nodeInfo);
14656 }else if(typeof nodeInfo == "number"){
14657 return this.nodes[nodeInfo];
14663 * Gets a range template nodes.
14664 * @param {Number} startIndex
14665 * @param {Number} endIndex
14666 * @return {Array} An array of nodes
14668 getNodes : function(start, end){
14669 var ns = this.nodes;
14670 start = start || 0;
14671 end = typeof end == "undefined" ? ns.length - 1 : end;
14674 for(var i = start; i <= end; i++){
14678 for(var i = start; i >= end; i--){
14686 * Finds the index of the passed node
14687 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14688 * @return {Number} The index of the node or -1
14690 indexOf : function(node){
14691 node = this.getNode(node);
14692 if(typeof node.nodeIndex == "number"){
14693 return node.nodeIndex;
14695 var ns = this.nodes;
14696 for(var i = 0, len = ns.length; i < len; i++){
14707 * based on jquery fullcalendar
14711 Roo.bootstrap = Roo.bootstrap || {};
14713 * @class Roo.bootstrap.Calendar
14714 * @extends Roo.bootstrap.Component
14715 * Bootstrap Calendar class
14716 * @cfg {Boolean} loadMask (true|false) default false
14717 * @cfg {Object} header generate the user specific header of the calendar, default false
14720 * Create a new Container
14721 * @param {Object} config The config object
14726 Roo.bootstrap.Calendar = function(config){
14727 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14731 * Fires when a date is selected
14732 * @param {DatePicker} this
14733 * @param {Date} date The selected date
14737 * @event monthchange
14738 * Fires when the displayed month changes
14739 * @param {DatePicker} this
14740 * @param {Date} date The selected month
14742 'monthchange': true,
14744 * @event evententer
14745 * Fires when mouse over an event
14746 * @param {Calendar} this
14747 * @param {event} Event
14749 'evententer': true,
14751 * @event eventleave
14752 * Fires when the mouse leaves an
14753 * @param {Calendar} this
14756 'eventleave': true,
14758 * @event eventclick
14759 * Fires when the mouse click an
14760 * @param {Calendar} this
14769 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14772 * @cfg {Number} startDay
14773 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14781 getAutoCreate : function(){
14784 var fc_button = function(name, corner, style, content ) {
14785 return Roo.apply({},{
14787 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14789 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14792 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14803 style : 'width:100%',
14810 cls : 'fc-header-left',
14812 fc_button('prev', 'left', 'arrow', '‹' ),
14813 fc_button('next', 'right', 'arrow', '›' ),
14814 { tag: 'span', cls: 'fc-header-space' },
14815 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14823 cls : 'fc-header-center',
14827 cls: 'fc-header-title',
14830 html : 'month / year'
14838 cls : 'fc-header-right',
14840 /* fc_button('month', 'left', '', 'month' ),
14841 fc_button('week', '', '', 'week' ),
14842 fc_button('day', 'right', '', 'day' )
14854 header = this.header;
14857 var cal_heads = function() {
14859 // fixme - handle this.
14861 for (var i =0; i < Date.dayNames.length; i++) {
14862 var d = Date.dayNames[i];
14865 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14866 html : d.substring(0,3)
14870 ret[0].cls += ' fc-first';
14871 ret[6].cls += ' fc-last';
14874 var cal_cell = function(n) {
14877 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14882 cls: 'fc-day-number',
14886 cls: 'fc-day-content',
14890 style: 'position: relative;' // height: 17px;
14902 var cal_rows = function() {
14905 for (var r = 0; r < 6; r++) {
14912 for (var i =0; i < Date.dayNames.length; i++) {
14913 var d = Date.dayNames[i];
14914 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14917 row.cn[0].cls+=' fc-first';
14918 row.cn[0].cn[0].style = 'min-height:90px';
14919 row.cn[6].cls+=' fc-last';
14923 ret[0].cls += ' fc-first';
14924 ret[4].cls += ' fc-prev-last';
14925 ret[5].cls += ' fc-last';
14932 cls: 'fc-border-separate',
14933 style : 'width:100%',
14941 cls : 'fc-first fc-last',
14959 cls : 'fc-content',
14960 style : "position: relative;",
14963 cls : 'fc-view fc-view-month fc-grid',
14964 style : 'position: relative',
14965 unselectable : 'on',
14968 cls : 'fc-event-container',
14969 style : 'position:absolute;z-index:8;top:0;left:0;'
14987 initEvents : function()
14990 throw "can not find store for calendar";
14996 style: "text-align:center",
15000 style: "background-color:white;width:50%;margin:250 auto",
15004 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15015 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15017 var size = this.el.select('.fc-content', true).first().getSize();
15018 this.maskEl.setSize(size.width, size.height);
15019 this.maskEl.enableDisplayMode("block");
15020 if(!this.loadMask){
15021 this.maskEl.hide();
15024 this.store = Roo.factory(this.store, Roo.data);
15025 this.store.on('load', this.onLoad, this);
15026 this.store.on('beforeload', this.onBeforeLoad, this);
15030 this.cells = this.el.select('.fc-day',true);
15031 //Roo.log(this.cells);
15032 this.textNodes = this.el.query('.fc-day-number');
15033 this.cells.addClassOnOver('fc-state-hover');
15035 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15036 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15037 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15038 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15040 this.on('monthchange', this.onMonthChange, this);
15042 this.update(new Date().clearTime());
15045 resize : function() {
15046 var sz = this.el.getSize();
15048 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15049 this.el.select('.fc-day-content div',true).setHeight(34);
15054 showPrevMonth : function(e){
15055 this.update(this.activeDate.add("mo", -1));
15057 showToday : function(e){
15058 this.update(new Date().clearTime());
15061 showNextMonth : function(e){
15062 this.update(this.activeDate.add("mo", 1));
15066 showPrevYear : function(){
15067 this.update(this.activeDate.add("y", -1));
15071 showNextYear : function(){
15072 this.update(this.activeDate.add("y", 1));
15077 update : function(date)
15079 var vd = this.activeDate;
15080 this.activeDate = date;
15081 // if(vd && this.el){
15082 // var t = date.getTime();
15083 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15084 // Roo.log('using add remove');
15086 // this.fireEvent('monthchange', this, date);
15088 // this.cells.removeClass("fc-state-highlight");
15089 // this.cells.each(function(c){
15090 // if(c.dateValue == t){
15091 // c.addClass("fc-state-highlight");
15092 // setTimeout(function(){
15093 // try{c.dom.firstChild.focus();}catch(e){}
15103 var days = date.getDaysInMonth();
15105 var firstOfMonth = date.getFirstDateOfMonth();
15106 var startingPos = firstOfMonth.getDay()-this.startDay;
15108 if(startingPos < this.startDay){
15112 var pm = date.add(Date.MONTH, -1);
15113 var prevStart = pm.getDaysInMonth()-startingPos;
15115 this.cells = this.el.select('.fc-day',true);
15116 this.textNodes = this.el.query('.fc-day-number');
15117 this.cells.addClassOnOver('fc-state-hover');
15119 var cells = this.cells.elements;
15120 var textEls = this.textNodes;
15122 Roo.each(cells, function(cell){
15123 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15126 days += startingPos;
15128 // convert everything to numbers so it's fast
15129 var day = 86400000;
15130 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15133 //Roo.log(prevStart);
15135 var today = new Date().clearTime().getTime();
15136 var sel = date.clearTime().getTime();
15137 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15138 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15139 var ddMatch = this.disabledDatesRE;
15140 var ddText = this.disabledDatesText;
15141 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15142 var ddaysText = this.disabledDaysText;
15143 var format = this.format;
15145 var setCellClass = function(cal, cell){
15149 //Roo.log('set Cell Class');
15151 var t = d.getTime();
15155 cell.dateValue = t;
15157 cell.className += " fc-today";
15158 cell.className += " fc-state-highlight";
15159 cell.title = cal.todayText;
15162 // disable highlight in other month..
15163 //cell.className += " fc-state-highlight";
15168 cell.className = " fc-state-disabled";
15169 cell.title = cal.minText;
15173 cell.className = " fc-state-disabled";
15174 cell.title = cal.maxText;
15178 if(ddays.indexOf(d.getDay()) != -1){
15179 cell.title = ddaysText;
15180 cell.className = " fc-state-disabled";
15183 if(ddMatch && format){
15184 var fvalue = d.dateFormat(format);
15185 if(ddMatch.test(fvalue)){
15186 cell.title = ddText.replace("%0", fvalue);
15187 cell.className = " fc-state-disabled";
15191 if (!cell.initialClassName) {
15192 cell.initialClassName = cell.dom.className;
15195 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15200 for(; i < startingPos; i++) {
15201 textEls[i].innerHTML = (++prevStart);
15202 d.setDate(d.getDate()+1);
15204 cells[i].className = "fc-past fc-other-month";
15205 setCellClass(this, cells[i]);
15210 for(; i < days; i++){
15211 intDay = i - startingPos + 1;
15212 textEls[i].innerHTML = (intDay);
15213 d.setDate(d.getDate()+1);
15215 cells[i].className = ''; // "x-date-active";
15216 setCellClass(this, cells[i]);
15220 for(; i < 42; i++) {
15221 textEls[i].innerHTML = (++extraDays);
15222 d.setDate(d.getDate()+1);
15224 cells[i].className = "fc-future fc-other-month";
15225 setCellClass(this, cells[i]);
15228 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15230 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15232 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15233 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15235 if(totalRows != 6){
15236 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15237 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15240 this.fireEvent('monthchange', this, date);
15244 if(!this.internalRender){
15245 var main = this.el.dom.firstChild;
15246 var w = main.offsetWidth;
15247 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15248 Roo.fly(main).setWidth(w);
15249 this.internalRender = true;
15250 // opera does not respect the auto grow header center column
15251 // then, after it gets a width opera refuses to recalculate
15252 // without a second pass
15253 if(Roo.isOpera && !this.secondPass){
15254 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15255 this.secondPass = true;
15256 this.update.defer(10, this, [date]);
15263 findCell : function(dt) {
15264 dt = dt.clearTime().getTime();
15266 this.cells.each(function(c){
15267 //Roo.log("check " +c.dateValue + '?=' + dt);
15268 if(c.dateValue == dt){
15278 findCells : function(ev) {
15279 var s = ev.start.clone().clearTime().getTime();
15281 var e= ev.end.clone().clearTime().getTime();
15284 this.cells.each(function(c){
15285 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15287 if(c.dateValue > e){
15290 if(c.dateValue < s){
15299 // findBestRow: function(cells)
15303 // for (var i =0 ; i < cells.length;i++) {
15304 // ret = Math.max(cells[i].rows || 0,ret);
15311 addItem : function(ev)
15313 // look for vertical location slot in
15314 var cells = this.findCells(ev);
15316 // ev.row = this.findBestRow(cells);
15318 // work out the location.
15322 for(var i =0; i < cells.length; i++) {
15324 cells[i].row = cells[0].row;
15327 cells[i].row = cells[i].row + 1;
15337 if (crow.start.getY() == cells[i].getY()) {
15339 crow.end = cells[i];
15356 cells[0].events.push(ev);
15358 this.calevents.push(ev);
15361 clearEvents: function() {
15363 if(!this.calevents){
15367 Roo.each(this.cells.elements, function(c){
15373 Roo.each(this.calevents, function(e) {
15374 Roo.each(e.els, function(el) {
15375 el.un('mouseenter' ,this.onEventEnter, this);
15376 el.un('mouseleave' ,this.onEventLeave, this);
15381 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15387 renderEvents: function()
15391 this.cells.each(function(c) {
15400 if(c.row != c.events.length){
15401 r = 4 - (4 - (c.row - c.events.length));
15404 c.events = ev.slice(0, r);
15405 c.more = ev.slice(r);
15407 if(c.more.length && c.more.length == 1){
15408 c.events.push(c.more.pop());
15411 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15415 this.cells.each(function(c) {
15417 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15420 for (var e = 0; e < c.events.length; e++){
15421 var ev = c.events[e];
15422 var rows = ev.rows;
15424 for(var i = 0; i < rows.length; i++) {
15426 // how many rows should it span..
15429 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15430 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15432 unselectable : "on",
15435 cls: 'fc-event-inner',
15439 // cls: 'fc-event-time',
15440 // html : cells.length > 1 ? '' : ev.time
15444 cls: 'fc-event-title',
15445 html : String.format('{0}', ev.title)
15452 cls: 'ui-resizable-handle ui-resizable-e',
15453 html : '  '
15460 cfg.cls += ' fc-event-start';
15462 if ((i+1) == rows.length) {
15463 cfg.cls += ' fc-event-end';
15466 var ctr = _this.el.select('.fc-event-container',true).first();
15467 var cg = ctr.createChild(cfg);
15469 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15470 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15472 var r = (c.more.length) ? 1 : 0;
15473 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15474 cg.setWidth(ebox.right - sbox.x -2);
15476 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15477 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15478 cg.on('click', _this.onEventClick, _this, ev);
15489 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15490 style : 'position: absolute',
15491 unselectable : "on",
15494 cls: 'fc-event-inner',
15498 cls: 'fc-event-title',
15506 cls: 'ui-resizable-handle ui-resizable-e',
15507 html : '  '
15513 var ctr = _this.el.select('.fc-event-container',true).first();
15514 var cg = ctr.createChild(cfg);
15516 var sbox = c.select('.fc-day-content',true).first().getBox();
15517 var ebox = c.select('.fc-day-content',true).first().getBox();
15519 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15520 cg.setWidth(ebox.right - sbox.x -2);
15522 cg.on('click', _this.onMoreEventClick, _this, c.more);
15532 onEventEnter: function (e, el,event,d) {
15533 this.fireEvent('evententer', this, el, event);
15536 onEventLeave: function (e, el,event,d) {
15537 this.fireEvent('eventleave', this, el, event);
15540 onEventClick: function (e, el,event,d) {
15541 this.fireEvent('eventclick', this, el, event);
15544 onMonthChange: function () {
15548 onMoreEventClick: function(e, el, more)
15552 this.calpopover.placement = 'right';
15553 this.calpopover.setTitle('More');
15555 this.calpopover.setContent('');
15557 var ctr = this.calpopover.el.select('.popover-content', true).first();
15559 Roo.each(more, function(m){
15561 cls : 'fc-event-hori fc-event-draggable',
15564 var cg = ctr.createChild(cfg);
15566 cg.on('click', _this.onEventClick, _this, m);
15569 this.calpopover.show(el);
15574 onLoad: function ()
15576 this.calevents = [];
15579 if(this.store.getCount() > 0){
15580 this.store.data.each(function(d){
15583 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15584 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15585 time : d.data.start_time,
15586 title : d.data.title,
15587 description : d.data.description,
15588 venue : d.data.venue
15593 this.renderEvents();
15595 if(this.calevents.length && this.loadMask){
15596 this.maskEl.hide();
15600 onBeforeLoad: function()
15602 this.clearEvents();
15604 this.maskEl.show();
15618 * @class Roo.bootstrap.Popover
15619 * @extends Roo.bootstrap.Component
15620 * Bootstrap Popover class
15621 * @cfg {String} html contents of the popover (or false to use children..)
15622 * @cfg {String} title of popover (or false to hide)
15623 * @cfg {String} placement how it is placed
15624 * @cfg {String} trigger click || hover (or false to trigger manually)
15625 * @cfg {String} over what (parent or false to trigger manually.)
15626 * @cfg {Number} delay - delay before showing
15629 * Create a new Popover
15630 * @param {Object} config The config object
15633 Roo.bootstrap.Popover = function(config){
15634 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15637 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15639 title: 'Fill in a title',
15642 placement : 'right',
15643 trigger : 'hover', // hover
15649 can_build_overlaid : false,
15651 getChildContainer : function()
15653 return this.el.select('.popover-content',true).first();
15656 getAutoCreate : function(){
15657 Roo.log('make popover?');
15659 cls : 'popover roo-dynamic',
15660 style: 'display:block',
15666 cls : 'popover-inner',
15670 cls: 'popover-title',
15674 cls : 'popover-content',
15685 setTitle: function(str)
15688 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15690 setContent: function(str)
15693 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15695 // as it get's added to the bottom of the page.
15696 onRender : function(ct, position)
15698 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15700 var cfg = Roo.apply({}, this.getAutoCreate());
15704 cfg.cls += ' ' + this.cls;
15707 cfg.style = this.style;
15709 //Roo.log("adding to ");
15710 this.el = Roo.get(document.body).createChild(cfg, position);
15716 initEvents : function()
15718 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15719 this.el.enableDisplayMode('block');
15721 if (this.over === false) {
15724 if (this.triggers === false) {
15727 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15728 var triggers = this.trigger ? this.trigger.split(' ') : [];
15729 Roo.each(triggers, function(trigger) {
15731 if (trigger == 'click') {
15732 on_el.on('click', this.toggle, this);
15733 } else if (trigger != 'manual') {
15734 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15735 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15737 on_el.on(eventIn ,this.enter, this);
15738 on_el.on(eventOut, this.leave, this);
15749 toggle : function () {
15750 this.hoverState == 'in' ? this.leave() : this.enter();
15753 enter : function () {
15756 clearTimeout(this.timeout);
15758 this.hoverState = 'in';
15760 if (!this.delay || !this.delay.show) {
15765 this.timeout = setTimeout(function () {
15766 if (_t.hoverState == 'in') {
15769 }, this.delay.show)
15771 leave : function() {
15772 clearTimeout(this.timeout);
15774 this.hoverState = 'out';
15776 if (!this.delay || !this.delay.hide) {
15781 this.timeout = setTimeout(function () {
15782 if (_t.hoverState == 'out') {
15785 }, this.delay.hide)
15788 show : function (on_el)
15791 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15794 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15795 if (this.html !== false) {
15796 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15798 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15799 if (!this.title.length) {
15800 this.el.select('.popover-title',true).hide();
15803 var placement = typeof this.placement == 'function' ?
15804 this.placement.call(this, this.el, on_el) :
15807 var autoToken = /\s?auto?\s?/i;
15808 var autoPlace = autoToken.test(placement);
15810 placement = placement.replace(autoToken, '') || 'top';
15814 //this.el.setXY([0,0]);
15816 this.el.dom.style.display='block';
15817 this.el.addClass(placement);
15819 //this.el.appendTo(on_el);
15821 var p = this.getPosition();
15822 var box = this.el.getBox();
15827 var align = Roo.bootstrap.Popover.alignment[placement];
15828 this.el.alignTo(on_el, align[0],align[1]);
15829 //var arrow = this.el.select('.arrow',true).first();
15830 //arrow.set(align[2],
15832 this.el.addClass('in');
15835 if (this.el.hasClass('fade')) {
15842 this.el.setXY([0,0]);
15843 this.el.removeClass('in');
15845 this.hoverState = null;
15851 Roo.bootstrap.Popover.alignment = {
15852 'left' : ['r-l', [-10,0], 'right'],
15853 'right' : ['l-r', [10,0], 'left'],
15854 'bottom' : ['t-b', [0,10], 'top'],
15855 'top' : [ 'b-t', [0,-10], 'bottom']
15866 * @class Roo.bootstrap.Progress
15867 * @extends Roo.bootstrap.Component
15868 * Bootstrap Progress class
15869 * @cfg {Boolean} striped striped of the progress bar
15870 * @cfg {Boolean} active animated of the progress bar
15874 * Create a new Progress
15875 * @param {Object} config The config object
15878 Roo.bootstrap.Progress = function(config){
15879 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15882 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15887 getAutoCreate : function(){
15895 cfg.cls += ' progress-striped';
15899 cfg.cls += ' active';
15918 * @class Roo.bootstrap.ProgressBar
15919 * @extends Roo.bootstrap.Component
15920 * Bootstrap ProgressBar class
15921 * @cfg {Number} aria_valuenow aria-value now
15922 * @cfg {Number} aria_valuemin aria-value min
15923 * @cfg {Number} aria_valuemax aria-value max
15924 * @cfg {String} label label for the progress bar
15925 * @cfg {String} panel (success | info | warning | danger )
15926 * @cfg {String} role role of the progress bar
15927 * @cfg {String} sr_only text
15931 * Create a new ProgressBar
15932 * @param {Object} config The config object
15935 Roo.bootstrap.ProgressBar = function(config){
15936 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15939 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15943 aria_valuemax : 100,
15949 getAutoCreate : function()
15954 cls: 'progress-bar',
15955 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15967 cfg.role = this.role;
15970 if(this.aria_valuenow){
15971 cfg['aria-valuenow'] = this.aria_valuenow;
15974 if(this.aria_valuemin){
15975 cfg['aria-valuemin'] = this.aria_valuemin;
15978 if(this.aria_valuemax){
15979 cfg['aria-valuemax'] = this.aria_valuemax;
15982 if(this.label && !this.sr_only){
15983 cfg.html = this.label;
15987 cfg.cls += ' progress-bar-' + this.panel;
15993 update : function(aria_valuenow)
15995 this.aria_valuenow = aria_valuenow;
15997 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16012 * @class Roo.bootstrap.TabGroup
16013 * @extends Roo.bootstrap.Column
16014 * Bootstrap Column class
16015 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16016 * @cfg {Boolean} carousel true to make the group behave like a carousel
16017 * @cfg {Boolean} bullets show bullets for the panels
16018 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16019 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16020 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16023 * Create a new TabGroup
16024 * @param {Object} config The config object
16027 Roo.bootstrap.TabGroup = function(config){
16028 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16030 this.navId = Roo.id();
16033 Roo.bootstrap.TabGroup.register(this);
16037 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16040 transition : false,
16045 slideOnTouch : false,
16047 getAutoCreate : function()
16049 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16051 cfg.cls += ' tab-content';
16053 Roo.log('get auto create...............');
16055 if (this.carousel) {
16056 cfg.cls += ' carousel slide';
16059 cls : 'carousel-inner'
16062 if(this.bullets && !Roo.isTouch){
16065 cls : 'carousel-bullets',
16069 if(this.bullets_cls){
16070 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16073 for (var i = 0; i < this.bullets; i++){
16075 cls : 'bullet bullet-' + i
16083 cfg.cn[0].cn = bullets;
16090 initEvents: function()
16092 Roo.log('-------- init events on tab group ---------');
16098 if(Roo.isTouch && this.slideOnTouch){
16099 this.el.on("touchstart", this.onTouchStart, this);
16102 if(this.autoslide){
16105 this.slideFn = window.setInterval(function() {
16106 _this.showPanelNext();
16112 onTouchStart : function(e, el, o)
16114 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16118 this.showPanelNext();
16121 getChildContainer : function()
16123 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16127 * register a Navigation item
16128 * @param {Roo.bootstrap.NavItem} the navitem to add
16130 register : function(item)
16132 this.tabs.push( item);
16133 item.navId = this.navId; // not really needed..
16138 getActivePanel : function()
16141 Roo.each(this.tabs, function(t) {
16151 getPanelByName : function(n)
16154 Roo.each(this.tabs, function(t) {
16155 if (t.tabId == n) {
16163 indexOfPanel : function(p)
16166 Roo.each(this.tabs, function(t,i) {
16167 if (t.tabId == p.tabId) {
16176 * show a specific panel
16177 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16178 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16180 showPanel : function (pan)
16182 if(this.transition){
16183 Roo.log("waiting for the transitionend");
16187 if (typeof(pan) == 'number') {
16188 pan = this.tabs[pan];
16190 if (typeof(pan) == 'string') {
16191 pan = this.getPanelByName(pan);
16193 if (pan.tabId == this.getActivePanel().tabId) {
16196 var cur = this.getActivePanel();
16198 if (false === cur.fireEvent('beforedeactivate')) {
16202 if(this.bullets > 0 && !Roo.isTouch){
16203 this.setActiveBullet(this.indexOfPanel(pan));
16206 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16208 this.transition = true;
16209 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16210 var lr = dir == 'next' ? 'left' : 'right';
16211 pan.el.addClass(dir); // or prev
16212 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16213 cur.el.addClass(lr); // or right
16214 pan.el.addClass(lr);
16217 cur.el.on('transitionend', function() {
16218 Roo.log("trans end?");
16220 pan.el.removeClass([lr,dir]);
16221 pan.setActive(true);
16223 cur.el.removeClass([lr]);
16224 cur.setActive(false);
16226 _this.transition = false;
16228 }, this, { single: true } );
16233 cur.setActive(false);
16234 pan.setActive(true);
16239 showPanelNext : function()
16241 var i = this.indexOfPanel(this.getActivePanel());
16243 if (i >= this.tabs.length - 1 && !this.autoslide) {
16247 if (i >= this.tabs.length - 1 && this.autoslide) {
16251 this.showPanel(this.tabs[i+1]);
16254 showPanelPrev : function()
16256 var i = this.indexOfPanel(this.getActivePanel());
16258 if (i < 1 && !this.autoslide) {
16262 if (i < 1 && this.autoslide) {
16263 i = this.tabs.length;
16266 this.showPanel(this.tabs[i-1]);
16270 addBullet: function()
16272 if(!this.bullets || Roo.isTouch){
16275 var ctr = this.el.select('.carousel-bullets',true).first();
16276 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16277 var bullet = ctr.createChild({
16278 cls : 'bullet bullet-' + i
16279 },ctr.dom.lastChild);
16284 bullet.on('click', (function(e, el, o, ii, t){
16286 e.preventDefault();
16288 this.showPanel(ii);
16290 if(this.autoslide && this.slideFn){
16291 clearInterval(this.slideFn);
16292 this.slideFn = window.setInterval(function() {
16293 _this.showPanelNext();
16297 }).createDelegate(this, [i, bullet], true));
16302 setActiveBullet : function(i)
16308 Roo.each(this.el.select('.bullet', true).elements, function(el){
16309 el.removeClass('selected');
16312 var bullet = this.el.select('.bullet-' + i, true).first();
16318 bullet.addClass('selected');
16329 Roo.apply(Roo.bootstrap.TabGroup, {
16333 * register a Navigation Group
16334 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16336 register : function(navgrp)
16338 this.groups[navgrp.navId] = navgrp;
16342 * fetch a Navigation Group based on the navigation ID
16343 * if one does not exist , it will get created.
16344 * @param {string} the navgroup to add
16345 * @returns {Roo.bootstrap.NavGroup} the navgroup
16347 get: function(navId) {
16348 if (typeof(this.groups[navId]) == 'undefined') {
16349 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16351 return this.groups[navId] ;
16366 * @class Roo.bootstrap.TabPanel
16367 * @extends Roo.bootstrap.Component
16368 * Bootstrap TabPanel class
16369 * @cfg {Boolean} active panel active
16370 * @cfg {String} html panel content
16371 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16372 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16376 * Create a new TabPanel
16377 * @param {Object} config The config object
16380 Roo.bootstrap.TabPanel = function(config){
16381 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16385 * Fires when the active status changes
16386 * @param {Roo.bootstrap.TabPanel} this
16387 * @param {Boolean} state the new state
16392 * @event beforedeactivate
16393 * Fires before a tab is de-activated - can be used to do validation on a form.
16394 * @param {Roo.bootstrap.TabPanel} this
16395 * @return {Boolean} false if there is an error
16398 'beforedeactivate': true
16401 this.tabId = this.tabId || Roo.id();
16405 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16412 getAutoCreate : function(){
16415 // item is needed for carousel - not sure if it has any effect otherwise
16416 cls: 'tab-pane item',
16417 html: this.html || ''
16421 cfg.cls += ' active';
16425 cfg.tabId = this.tabId;
16432 initEvents: function()
16434 Roo.log('-------- init events on tab panel ---------');
16436 var p = this.parent();
16437 this.navId = this.navId || p.navId;
16439 if (typeof(this.navId) != 'undefined') {
16440 // not really needed.. but just in case.. parent should be a NavGroup.
16441 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16442 Roo.log(['register', tg, this]);
16445 var i = tg.tabs.length - 1;
16447 if(this.active && tg.bullets > 0 && i < tg.bullets){
16448 tg.setActiveBullet(i);
16455 onRender : function(ct, position)
16457 // Roo.log("Call onRender: " + this.xtype);
16459 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16467 setActive: function(state)
16469 Roo.log("panel - set active " + this.tabId + "=" + state);
16471 this.active = state;
16473 this.el.removeClass('active');
16475 } else if (!this.el.hasClass('active')) {
16476 this.el.addClass('active');
16479 this.fireEvent('changed', this, state);
16496 * @class Roo.bootstrap.DateField
16497 * @extends Roo.bootstrap.Input
16498 * Bootstrap DateField class
16499 * @cfg {Number} weekStart default 0
16500 * @cfg {String} viewMode default empty, (months|years)
16501 * @cfg {String} minViewMode default empty, (months|years)
16502 * @cfg {Number} startDate default -Infinity
16503 * @cfg {Number} endDate default Infinity
16504 * @cfg {Boolean} todayHighlight default false
16505 * @cfg {Boolean} todayBtn default false
16506 * @cfg {Boolean} calendarWeeks default false
16507 * @cfg {Object} daysOfWeekDisabled default empty
16508 * @cfg {Boolean} singleMode default false (true | false)
16510 * @cfg {Boolean} keyboardNavigation default true
16511 * @cfg {String} language default en
16514 * Create a new DateField
16515 * @param {Object} config The config object
16518 Roo.bootstrap.DateField = function(config){
16519 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16523 * Fires when this field show.
16524 * @param {Roo.bootstrap.DateField} this
16525 * @param {Mixed} date The date value
16530 * Fires when this field hide.
16531 * @param {Roo.bootstrap.DateField} this
16532 * @param {Mixed} date The date value
16537 * Fires when select a date.
16538 * @param {Roo.bootstrap.DateField} this
16539 * @param {Mixed} date The date value
16543 * @event beforeselect
16544 * Fires when before select a date.
16545 * @param {Roo.bootstrap.DateField} this
16546 * @param {Mixed} date The date value
16548 beforeselect : true
16552 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16555 * @cfg {String} format
16556 * The default date format string which can be overriden for localization support. The format must be
16557 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16561 * @cfg {String} altFormats
16562 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16563 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16565 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16573 todayHighlight : false,
16579 keyboardNavigation: true,
16581 calendarWeeks: false,
16583 startDate: -Infinity,
16587 daysOfWeekDisabled: [],
16591 singleMode : false,
16593 UTCDate: function()
16595 return new Date(Date.UTC.apply(Date, arguments));
16598 UTCToday: function()
16600 var today = new Date();
16601 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16604 getDate: function() {
16605 var d = this.getUTCDate();
16606 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16609 getUTCDate: function() {
16613 setDate: function(d) {
16614 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16617 setUTCDate: function(d) {
16619 this.setValue(this.formatDate(this.date));
16622 onRender: function(ct, position)
16625 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16627 this.language = this.language || 'en';
16628 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16629 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16631 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16632 this.format = this.format || 'm/d/y';
16633 this.isInline = false;
16634 this.isInput = true;
16635 this.component = this.el.select('.add-on', true).first() || false;
16636 this.component = (this.component && this.component.length === 0) ? false : this.component;
16637 this.hasInput = this.component && this.inputEL().length;
16639 if (typeof(this.minViewMode === 'string')) {
16640 switch (this.minViewMode) {
16642 this.minViewMode = 1;
16645 this.minViewMode = 2;
16648 this.minViewMode = 0;
16653 if (typeof(this.viewMode === 'string')) {
16654 switch (this.viewMode) {
16667 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16669 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16671 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16673 this.picker().on('mousedown', this.onMousedown, this);
16674 this.picker().on('click', this.onClick, this);
16676 this.picker().addClass('datepicker-dropdown');
16678 this.startViewMode = this.viewMode;
16680 if(this.singleMode){
16681 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16682 v.setVisibilityMode(Roo.Element.DISPLAY);
16686 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16687 v.setStyle('width', '189px');
16691 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16692 if(!this.calendarWeeks){
16697 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16698 v.attr('colspan', function(i, val){
16699 return parseInt(val) + 1;
16704 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16706 this.setStartDate(this.startDate);
16707 this.setEndDate(this.endDate);
16709 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16716 if(this.isInline) {
16721 picker : function()
16723 return this.pickerEl;
16724 // return this.el.select('.datepicker', true).first();
16727 fillDow: function()
16729 var dowCnt = this.weekStart;
16738 if(this.calendarWeeks){
16746 while (dowCnt < this.weekStart + 7) {
16750 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16754 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16757 fillMonths: function()
16760 var months = this.picker().select('>.datepicker-months td', true).first();
16762 months.dom.innerHTML = '';
16768 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16771 months.createChild(month);
16778 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;
16780 if (this.date < this.startDate) {
16781 this.viewDate = new Date(this.startDate);
16782 } else if (this.date > this.endDate) {
16783 this.viewDate = new Date(this.endDate);
16785 this.viewDate = new Date(this.date);
16793 var d = new Date(this.viewDate),
16794 year = d.getUTCFullYear(),
16795 month = d.getUTCMonth(),
16796 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16797 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16798 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16799 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16800 currentDate = this.date && this.date.valueOf(),
16801 today = this.UTCToday();
16803 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16805 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16807 // this.picker.select('>tfoot th.today').
16808 // .text(dates[this.language].today)
16809 // .toggle(this.todayBtn !== false);
16811 this.updateNavArrows();
16814 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16816 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16818 prevMonth.setUTCDate(day);
16820 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16822 var nextMonth = new Date(prevMonth);
16824 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16826 nextMonth = nextMonth.valueOf();
16828 var fillMonths = false;
16830 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16832 while(prevMonth.valueOf() < nextMonth) {
16835 if (prevMonth.getUTCDay() === this.weekStart) {
16837 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16845 if(this.calendarWeeks){
16846 // ISO 8601: First week contains first thursday.
16847 // ISO also states week starts on Monday, but we can be more abstract here.
16849 // Start of current week: based on weekstart/current date
16850 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16851 // Thursday of this week
16852 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16853 // First Thursday of year, year from thursday
16854 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16855 // Calendar week: ms between thursdays, div ms per day, div 7 days
16856 calWeek = (th - yth) / 864e5 / 7 + 1;
16858 fillMonths.cn.push({
16866 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16868 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16871 if (this.todayHighlight &&
16872 prevMonth.getUTCFullYear() == today.getFullYear() &&
16873 prevMonth.getUTCMonth() == today.getMonth() &&
16874 prevMonth.getUTCDate() == today.getDate()) {
16875 clsName += ' today';
16878 if (currentDate && prevMonth.valueOf() === currentDate) {
16879 clsName += ' active';
16882 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16883 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16884 clsName += ' disabled';
16887 fillMonths.cn.push({
16889 cls: 'day ' + clsName,
16890 html: prevMonth.getDate()
16893 prevMonth.setDate(prevMonth.getDate()+1);
16896 var currentYear = this.date && this.date.getUTCFullYear();
16897 var currentMonth = this.date && this.date.getUTCMonth();
16899 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16901 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16902 v.removeClass('active');
16904 if(currentYear === year && k === currentMonth){
16905 v.addClass('active');
16908 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16909 v.addClass('disabled');
16915 year = parseInt(year/10, 10) * 10;
16917 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16919 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16922 for (var i = -1; i < 11; i++) {
16923 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16925 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16933 showMode: function(dir)
16936 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16939 Roo.each(this.picker().select('>div',true).elements, function(v){
16940 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16943 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16948 if(this.isInline) {
16952 this.picker().removeClass(['bottom', 'top']);
16954 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16956 * place to the top of element!
16960 this.picker().addClass('top');
16961 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16966 this.picker().addClass('bottom');
16968 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16971 parseDate : function(value)
16973 if(!value || value instanceof Date){
16976 var v = Date.parseDate(value, this.format);
16977 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16978 v = Date.parseDate(value, 'Y-m-d');
16980 if(!v && this.altFormats){
16981 if(!this.altFormatsArray){
16982 this.altFormatsArray = this.altFormats.split("|");
16984 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16985 v = Date.parseDate(value, this.altFormatsArray[i]);
16991 formatDate : function(date, fmt)
16993 return (!date || !(date instanceof Date)) ?
16994 date : date.dateFormat(fmt || this.format);
16997 onFocus : function()
16999 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17003 onBlur : function()
17005 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17007 var d = this.inputEl().getValue();
17016 this.picker().show();
17020 this.fireEvent('show', this, this.date);
17025 if(this.isInline) {
17028 this.picker().hide();
17029 this.viewMode = this.startViewMode;
17032 this.fireEvent('hide', this, this.date);
17036 onMousedown: function(e)
17038 e.stopPropagation();
17039 e.preventDefault();
17044 Roo.bootstrap.DateField.superclass.keyup.call(this);
17048 setValue: function(v)
17050 if(this.fireEvent('beforeselect', this, v) !== false){
17051 var d = new Date(this.parseDate(v) ).clearTime();
17053 if(isNaN(d.getTime())){
17054 this.date = this.viewDate = '';
17055 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17059 v = this.formatDate(d);
17061 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17063 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17067 this.fireEvent('select', this, this.date);
17071 getValue: function()
17073 return this.formatDate(this.date);
17076 fireKey: function(e)
17078 if (!this.picker().isVisible()){
17079 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17085 var dateChanged = false,
17087 newDate, newViewDate;
17092 e.preventDefault();
17096 if (!this.keyboardNavigation) {
17099 dir = e.keyCode == 37 ? -1 : 1;
17102 newDate = this.moveYear(this.date, dir);
17103 newViewDate = this.moveYear(this.viewDate, dir);
17104 } else if (e.shiftKey){
17105 newDate = this.moveMonth(this.date, dir);
17106 newViewDate = this.moveMonth(this.viewDate, dir);
17108 newDate = new Date(this.date);
17109 newDate.setUTCDate(this.date.getUTCDate() + dir);
17110 newViewDate = new Date(this.viewDate);
17111 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17113 if (this.dateWithinRange(newDate)){
17114 this.date = newDate;
17115 this.viewDate = newViewDate;
17116 this.setValue(this.formatDate(this.date));
17118 e.preventDefault();
17119 dateChanged = true;
17124 if (!this.keyboardNavigation) {
17127 dir = e.keyCode == 38 ? -1 : 1;
17129 newDate = this.moveYear(this.date, dir);
17130 newViewDate = this.moveYear(this.viewDate, dir);
17131 } else if (e.shiftKey){
17132 newDate = this.moveMonth(this.date, dir);
17133 newViewDate = this.moveMonth(this.viewDate, dir);
17135 newDate = new Date(this.date);
17136 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17137 newViewDate = new Date(this.viewDate);
17138 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17140 if (this.dateWithinRange(newDate)){
17141 this.date = newDate;
17142 this.viewDate = newViewDate;
17143 this.setValue(this.formatDate(this.date));
17145 e.preventDefault();
17146 dateChanged = true;
17150 this.setValue(this.formatDate(this.date));
17152 e.preventDefault();
17155 this.setValue(this.formatDate(this.date));
17169 onClick: function(e)
17171 e.stopPropagation();
17172 e.preventDefault();
17174 var target = e.getTarget();
17176 if(target.nodeName.toLowerCase() === 'i'){
17177 target = Roo.get(target).dom.parentNode;
17180 var nodeName = target.nodeName;
17181 var className = target.className;
17182 var html = target.innerHTML;
17183 //Roo.log(nodeName);
17185 switch(nodeName.toLowerCase()) {
17187 switch(className) {
17193 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17194 switch(this.viewMode){
17196 this.viewDate = this.moveMonth(this.viewDate, dir);
17200 this.viewDate = this.moveYear(this.viewDate, dir);
17206 var date = new Date();
17207 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17209 this.setValue(this.formatDate(this.date));
17216 if (className.indexOf('disabled') < 0) {
17217 this.viewDate.setUTCDate(1);
17218 if (className.indexOf('month') > -1) {
17219 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17221 var year = parseInt(html, 10) || 0;
17222 this.viewDate.setUTCFullYear(year);
17226 if(this.singleMode){
17227 this.setValue(this.formatDate(this.viewDate));
17238 //Roo.log(className);
17239 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17240 var day = parseInt(html, 10) || 1;
17241 var year = this.viewDate.getUTCFullYear(),
17242 month = this.viewDate.getUTCMonth();
17244 if (className.indexOf('old') > -1) {
17251 } else if (className.indexOf('new') > -1) {
17259 //Roo.log([year,month,day]);
17260 this.date = this.UTCDate(year, month, day,0,0,0,0);
17261 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17263 //Roo.log(this.formatDate(this.date));
17264 this.setValue(this.formatDate(this.date));
17271 setStartDate: function(startDate)
17273 this.startDate = startDate || -Infinity;
17274 if (this.startDate !== -Infinity) {
17275 this.startDate = this.parseDate(this.startDate);
17278 this.updateNavArrows();
17281 setEndDate: function(endDate)
17283 this.endDate = endDate || Infinity;
17284 if (this.endDate !== Infinity) {
17285 this.endDate = this.parseDate(this.endDate);
17288 this.updateNavArrows();
17291 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17293 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17294 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17295 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17297 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17298 return parseInt(d, 10);
17301 this.updateNavArrows();
17304 updateNavArrows: function()
17306 if(this.singleMode){
17310 var d = new Date(this.viewDate),
17311 year = d.getUTCFullYear(),
17312 month = d.getUTCMonth();
17314 Roo.each(this.picker().select('.prev', true).elements, function(v){
17316 switch (this.viewMode) {
17319 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17325 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17332 Roo.each(this.picker().select('.next', true).elements, function(v){
17334 switch (this.viewMode) {
17337 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17343 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17351 moveMonth: function(date, dir)
17356 var new_date = new Date(date.valueOf()),
17357 day = new_date.getUTCDate(),
17358 month = new_date.getUTCMonth(),
17359 mag = Math.abs(dir),
17361 dir = dir > 0 ? 1 : -1;
17364 // If going back one month, make sure month is not current month
17365 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17367 return new_date.getUTCMonth() == month;
17369 // If going forward one month, make sure month is as expected
17370 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17372 return new_date.getUTCMonth() != new_month;
17374 new_month = month + dir;
17375 new_date.setUTCMonth(new_month);
17376 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17377 if (new_month < 0 || new_month > 11) {
17378 new_month = (new_month + 12) % 12;
17381 // For magnitudes >1, move one month at a time...
17382 for (var i=0; i<mag; i++) {
17383 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17384 new_date = this.moveMonth(new_date, dir);
17386 // ...then reset the day, keeping it in the new month
17387 new_month = new_date.getUTCMonth();
17388 new_date.setUTCDate(day);
17390 return new_month != new_date.getUTCMonth();
17393 // Common date-resetting loop -- if date is beyond end of month, make it
17396 new_date.setUTCDate(--day);
17397 new_date.setUTCMonth(new_month);
17402 moveYear: function(date, dir)
17404 return this.moveMonth(date, dir*12);
17407 dateWithinRange: function(date)
17409 return date >= this.startDate && date <= this.endDate;
17415 this.picker().remove();
17420 Roo.apply(Roo.bootstrap.DateField, {
17431 html: '<i class="fa fa-arrow-left"/>'
17441 html: '<i class="fa fa-arrow-right"/>'
17483 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17484 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17485 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17486 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17487 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17500 navFnc: 'FullYear',
17505 navFnc: 'FullYear',
17510 Roo.apply(Roo.bootstrap.DateField, {
17514 cls: 'datepicker dropdown-menu roo-dynamic',
17518 cls: 'datepicker-days',
17522 cls: 'table-condensed',
17524 Roo.bootstrap.DateField.head,
17528 Roo.bootstrap.DateField.footer
17535 cls: 'datepicker-months',
17539 cls: 'table-condensed',
17541 Roo.bootstrap.DateField.head,
17542 Roo.bootstrap.DateField.content,
17543 Roo.bootstrap.DateField.footer
17550 cls: 'datepicker-years',
17554 cls: 'table-condensed',
17556 Roo.bootstrap.DateField.head,
17557 Roo.bootstrap.DateField.content,
17558 Roo.bootstrap.DateField.footer
17577 * @class Roo.bootstrap.TimeField
17578 * @extends Roo.bootstrap.Input
17579 * Bootstrap DateField class
17583 * Create a new TimeField
17584 * @param {Object} config The config object
17587 Roo.bootstrap.TimeField = function(config){
17588 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17592 * Fires when this field show.
17593 * @param {Roo.bootstrap.DateField} thisthis
17594 * @param {Mixed} date The date value
17599 * Fires when this field hide.
17600 * @param {Roo.bootstrap.DateField} this
17601 * @param {Mixed} date The date value
17606 * Fires when select a date.
17607 * @param {Roo.bootstrap.DateField} this
17608 * @param {Mixed} date The date value
17614 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17617 * @cfg {String} format
17618 * The default time format string which can be overriden for localization support. The format must be
17619 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17623 onRender: function(ct, position)
17626 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17628 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17630 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17632 this.pop = this.picker().select('>.datepicker-time',true).first();
17633 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17635 this.picker().on('mousedown', this.onMousedown, this);
17636 this.picker().on('click', this.onClick, this);
17638 this.picker().addClass('datepicker-dropdown');
17643 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17644 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17645 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17646 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17647 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17648 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17652 fireKey: function(e){
17653 if (!this.picker().isVisible()){
17654 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17660 e.preventDefault();
17668 this.onTogglePeriod();
17671 this.onIncrementMinutes();
17674 this.onDecrementMinutes();
17683 onClick: function(e) {
17684 e.stopPropagation();
17685 e.preventDefault();
17688 picker : function()
17690 return this.el.select('.datepicker', true).first();
17693 fillTime: function()
17695 var time = this.pop.select('tbody', true).first();
17697 time.dom.innerHTML = '';
17712 cls: 'hours-up glyphicon glyphicon-chevron-up'
17732 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17753 cls: 'timepicker-hour',
17768 cls: 'timepicker-minute',
17783 cls: 'btn btn-primary period',
17805 cls: 'hours-down glyphicon glyphicon-chevron-down'
17825 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17843 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17850 var hours = this.time.getHours();
17851 var minutes = this.time.getMinutes();
17864 hours = hours - 12;
17868 hours = '0' + hours;
17872 minutes = '0' + minutes;
17875 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17876 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17877 this.pop.select('button', true).first().dom.innerHTML = period;
17883 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17885 var cls = ['bottom'];
17887 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17894 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17899 this.picker().addClass(cls.join('-'));
17903 Roo.each(cls, function(c){
17905 _this.picker().setTop(_this.inputEl().getHeight());
17909 _this.picker().setTop(0 - _this.picker().getHeight());
17914 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17918 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17925 onFocus : function()
17927 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17931 onBlur : function()
17933 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17939 this.picker().show();
17944 this.fireEvent('show', this, this.date);
17949 this.picker().hide();
17952 this.fireEvent('hide', this, this.date);
17955 setTime : function()
17958 this.setValue(this.time.format(this.format));
17960 this.fireEvent('select', this, this.date);
17965 onMousedown: function(e){
17966 e.stopPropagation();
17967 e.preventDefault();
17970 onIncrementHours: function()
17972 Roo.log('onIncrementHours');
17973 this.time = this.time.add(Date.HOUR, 1);
17978 onDecrementHours: function()
17980 Roo.log('onDecrementHours');
17981 this.time = this.time.add(Date.HOUR, -1);
17985 onIncrementMinutes: function()
17987 Roo.log('onIncrementMinutes');
17988 this.time = this.time.add(Date.MINUTE, 1);
17992 onDecrementMinutes: function()
17994 Roo.log('onDecrementMinutes');
17995 this.time = this.time.add(Date.MINUTE, -1);
17999 onTogglePeriod: function()
18001 Roo.log('onTogglePeriod');
18002 this.time = this.time.add(Date.HOUR, 12);
18009 Roo.apply(Roo.bootstrap.TimeField, {
18039 cls: 'btn btn-info ok',
18051 Roo.apply(Roo.bootstrap.TimeField, {
18055 cls: 'datepicker dropdown-menu',
18059 cls: 'datepicker-time',
18063 cls: 'table-condensed',
18065 Roo.bootstrap.TimeField.content,
18066 Roo.bootstrap.TimeField.footer
18085 * @class Roo.bootstrap.MonthField
18086 * @extends Roo.bootstrap.Input
18087 * Bootstrap MonthField class
18089 * @cfg {String} language default en
18092 * Create a new MonthField
18093 * @param {Object} config The config object
18096 Roo.bootstrap.MonthField = function(config){
18097 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18102 * Fires when this field show.
18103 * @param {Roo.bootstrap.MonthField} this
18104 * @param {Mixed} date The date value
18109 * Fires when this field hide.
18110 * @param {Roo.bootstrap.MonthField} this
18111 * @param {Mixed} date The date value
18116 * Fires when select a date.
18117 * @param {Roo.bootstrap.MonthField} this
18118 * @param {String} oldvalue The old value
18119 * @param {String} newvalue The new value
18125 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18127 onRender: function(ct, position)
18130 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18132 this.language = this.language || 'en';
18133 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18134 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18136 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18137 this.isInline = false;
18138 this.isInput = true;
18139 this.component = this.el.select('.add-on', true).first() || false;
18140 this.component = (this.component && this.component.length === 0) ? false : this.component;
18141 this.hasInput = this.component && this.inputEL().length;
18143 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18145 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18147 this.picker().on('mousedown', this.onMousedown, this);
18148 this.picker().on('click', this.onClick, this);
18150 this.picker().addClass('datepicker-dropdown');
18152 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18153 v.setStyle('width', '189px');
18160 if(this.isInline) {
18166 setValue: function(v, suppressEvent)
18168 var o = this.getValue();
18170 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18174 if(suppressEvent !== true){
18175 this.fireEvent('select', this, o, v);
18180 getValue: function()
18185 onClick: function(e)
18187 e.stopPropagation();
18188 e.preventDefault();
18190 var target = e.getTarget();
18192 if(target.nodeName.toLowerCase() === 'i'){
18193 target = Roo.get(target).dom.parentNode;
18196 var nodeName = target.nodeName;
18197 var className = target.className;
18198 var html = target.innerHTML;
18200 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18204 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18206 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18212 picker : function()
18214 return this.pickerEl;
18217 fillMonths: function()
18220 var months = this.picker().select('>.datepicker-months td', true).first();
18222 months.dom.innerHTML = '';
18228 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18231 months.createChild(month);
18240 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18241 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18244 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18245 e.removeClass('active');
18247 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18248 e.addClass('active');
18255 if(this.isInline) {
18259 this.picker().removeClass(['bottom', 'top']);
18261 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18263 * place to the top of element!
18267 this.picker().addClass('top');
18268 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18273 this.picker().addClass('bottom');
18275 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18278 onFocus : function()
18280 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18284 onBlur : function()
18286 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18288 var d = this.inputEl().getValue();
18297 this.picker().show();
18298 this.picker().select('>.datepicker-months', true).first().show();
18302 this.fireEvent('show', this, this.date);
18307 if(this.isInline) {
18310 this.picker().hide();
18311 this.fireEvent('hide', this, this.date);
18315 onMousedown: function(e)
18317 e.stopPropagation();
18318 e.preventDefault();
18323 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18327 fireKey: function(e)
18329 if (!this.picker().isVisible()){
18330 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18341 e.preventDefault();
18345 dir = e.keyCode == 37 ? -1 : 1;
18347 this.vIndex = this.vIndex + dir;
18349 if(this.vIndex < 0){
18353 if(this.vIndex > 11){
18357 if(isNaN(this.vIndex)){
18361 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18367 dir = e.keyCode == 38 ? -1 : 1;
18369 this.vIndex = this.vIndex + dir * 4;
18371 if(this.vIndex < 0){
18375 if(this.vIndex > 11){
18379 if(isNaN(this.vIndex)){
18383 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18388 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18389 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18393 e.preventDefault();
18396 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18397 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18413 this.picker().remove();
18418 Roo.apply(Roo.bootstrap.MonthField, {
18437 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18438 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18443 Roo.apply(Roo.bootstrap.MonthField, {
18447 cls: 'datepicker dropdown-menu roo-dynamic',
18451 cls: 'datepicker-months',
18455 cls: 'table-condensed',
18457 Roo.bootstrap.DateField.content
18477 * @class Roo.bootstrap.CheckBox
18478 * @extends Roo.bootstrap.Input
18479 * Bootstrap CheckBox class
18481 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18482 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18483 * @cfg {String} boxLabel The text that appears beside the checkbox
18484 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18485 * @cfg {Boolean} checked initnal the element
18486 * @cfg {Boolean} inline inline the element (default false)
18487 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18490 * Create a new CheckBox
18491 * @param {Object} config The config object
18494 Roo.bootstrap.CheckBox = function(config){
18495 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18500 * Fires when the element is checked or unchecked.
18501 * @param {Roo.bootstrap.CheckBox} this This input
18502 * @param {Boolean} checked The new checked value
18509 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18511 inputType: 'checkbox',
18519 getAutoCreate : function()
18521 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18527 cfg.cls = 'form-group ' + this.inputType; //input-group
18530 cfg.cls += ' ' + this.inputType + '-inline';
18536 type : this.inputType,
18537 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18538 cls : 'roo-' + this.inputType, //'form-box',
18539 placeholder : this.placeholder || ''
18543 if (this.weight) { // Validity check?
18544 cfg.cls += " " + this.inputType + "-" + this.weight;
18547 if (this.disabled) {
18548 input.disabled=true;
18552 input.checked = this.checked;
18556 input.name = this.name;
18560 input.cls += ' input-' + this.size;
18565 ['xs','sm','md','lg'].map(function(size){
18566 if (settings[size]) {
18567 cfg.cls += ' col-' + size + '-' + settings[size];
18571 var inputblock = input;
18573 if (this.before || this.after) {
18576 cls : 'input-group',
18581 inputblock.cn.push({
18583 cls : 'input-group-addon',
18588 inputblock.cn.push(input);
18591 inputblock.cn.push({
18593 cls : 'input-group-addon',
18600 if (align ==='left' && this.fieldLabel.length) {
18601 Roo.log("left and has label");
18607 cls : 'control-label col-md-' + this.labelWidth,
18608 html : this.fieldLabel
18612 cls : "col-md-" + (12 - this.labelWidth),
18619 } else if ( this.fieldLabel.length) {
18624 tag: this.boxLabel ? 'span' : 'label',
18626 cls: 'control-label box-input-label',
18627 //cls : 'input-group-addon',
18628 html : this.fieldLabel
18638 Roo.log(" no label && no align");
18639 cfg.cn = [ inputblock ] ;
18644 var boxLabelCfg = {
18646 //'for': id, // box label is handled by onclick - so no for...
18648 html: this.boxLabel
18652 boxLabelCfg.tooltip = this.tooltip;
18655 cfg.cn.push(boxLabelCfg);
18665 * return the real input element.
18667 inputEl: function ()
18669 return this.el.select('input.roo-' + this.inputType,true).first();
18672 labelEl: function()
18674 return this.el.select('label.control-label',true).first();
18676 /* depricated... */
18680 return this.labelEl();
18683 initEvents : function()
18685 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18687 this.inputEl().on('click', this.onClick, this);
18689 if (this.boxLabel) {
18690 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18693 this.startValue = this.getValue();
18696 Roo.bootstrap.CheckBox.register(this);
18700 onClick : function()
18702 this.setChecked(!this.checked);
18705 setChecked : function(state,suppressEvent)
18707 this.startValue = this.getValue();
18709 if(this.inputType == 'radio'){
18711 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18712 e.dom.checked = false;
18715 this.inputEl().dom.checked = true;
18717 this.inputEl().dom.value = this.inputValue;
18719 if(suppressEvent !== true){
18720 this.fireEvent('check', this, true);
18728 this.checked = state;
18730 this.inputEl().dom.checked = state;
18732 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18734 if(suppressEvent !== true){
18735 this.fireEvent('check', this, state);
18741 getValue : function()
18743 if(this.inputType == 'radio'){
18744 return this.getGroupValue();
18747 return this.inputEl().getValue();
18751 getGroupValue : function()
18753 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18757 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18760 setValue : function(v,suppressEvent)
18762 if(this.inputType == 'radio'){
18763 this.setGroupValue(v, suppressEvent);
18767 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18772 setGroupValue : function(v, suppressEvent)
18774 this.startValue = this.getValue();
18776 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18777 e.dom.checked = false;
18779 if(e.dom.value == v){
18780 e.dom.checked = true;
18784 if(suppressEvent !== true){
18785 this.fireEvent('check', this, true);
18793 validate : function()
18797 (this.inputType == 'radio' && this.validateRadio()) ||
18798 (this.inputType == 'checkbox' && this.validateCheckbox())
18804 this.markInvalid();
18808 validateRadio : function()
18812 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18813 if(!e.dom.checked){
18825 validateCheckbox : function()
18828 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18831 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18839 for(var i in group){
18844 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18851 * Mark this field as valid
18853 markValid : function()
18855 if(this.allowBlank){
18861 this.fireEvent('valid', this);
18863 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18866 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18873 if(this.inputType == 'radio'){
18874 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18875 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18876 e.findParent('.form-group', false, true).addClass(_this.validClass);
18883 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18884 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18888 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18894 for(var i in group){
18895 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18896 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18901 * Mark this field as invalid
18902 * @param {String} msg The validation message
18904 markInvalid : function(msg)
18906 if(this.allowBlank){
18912 this.fireEvent('invalid', this, msg);
18914 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18917 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18921 label.markInvalid();
18924 if(this.inputType == 'radio'){
18925 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18926 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18927 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18934 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18935 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18939 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18945 for(var i in group){
18946 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18947 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18954 Roo.apply(Roo.bootstrap.CheckBox, {
18959 * register a CheckBox Group
18960 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18962 register : function(checkbox)
18964 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18965 this.groups[checkbox.groupId] = {};
18968 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18972 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18976 * fetch a CheckBox Group based on the group ID
18977 * @param {string} the group ID
18978 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18980 get: function(groupId) {
18981 if (typeof(this.groups[groupId]) == 'undefined') {
18985 return this.groups[groupId] ;
18997 *<div class="radio">
18999 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19000 Option one is this and that—be sure to include why it's great
19007 *<label class="radio-inline">fieldLabel</label>
19008 *<label class="radio-inline">
19009 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19017 * @class Roo.bootstrap.Radio
19018 * @extends Roo.bootstrap.CheckBox
19019 * Bootstrap Radio class
19022 * Create a new Radio
19023 * @param {Object} config The config object
19026 Roo.bootstrap.Radio = function(config){
19027 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19031 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19033 inputType: 'radio',
19037 getAutoCreate : function()
19039 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19040 align = align || 'left'; // default...
19047 tag : this.inline ? 'span' : 'div',
19052 var inline = this.inline ? ' radio-inline' : '';
19056 // does not need for, as we wrap the input with it..
19058 cls : 'control-label box-label' + inline,
19061 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19065 //cls : 'control-label' + inline,
19066 html : this.fieldLabel,
19067 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19076 type : this.inputType,
19077 //value : (!this.checked) ? this.valueOff : this.inputValue,
19078 value : this.inputValue,
19080 placeholder : this.placeholder || '' // ?? needed????
19083 if (this.weight) { // Validity check?
19084 input.cls += " radio-" + this.weight;
19086 if (this.disabled) {
19087 input.disabled=true;
19091 input.checked = this.checked;
19095 input.name = this.name;
19099 input.cls += ' input-' + this.size;
19102 //?? can span's inline have a width??
19105 ['xs','sm','md','lg'].map(function(size){
19106 if (settings[size]) {
19107 cfg.cls += ' col-' + size + '-' + settings[size];
19111 var inputblock = input;
19113 if (this.before || this.after) {
19116 cls : 'input-group',
19121 inputblock.cn.push({
19123 cls : 'input-group-addon',
19127 inputblock.cn.push(input);
19129 inputblock.cn.push({
19131 cls : 'input-group-addon',
19139 if (this.fieldLabel && this.fieldLabel.length) {
19140 cfg.cn.push(fieldLabel);
19143 // normal bootstrap puts the input inside the label.
19144 // however with our styled version - it has to go after the input.
19146 //lbl.cn.push(inputblock);
19150 cls: 'radio' + inline,
19157 cfg.cn.push( lblwrap);
19162 html: this.boxLabel
19171 initEvents : function()
19173 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19175 this.inputEl().on('click', this.onClick, this);
19176 if (this.boxLabel) {
19177 //Roo.log('find label');
19178 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19183 inputEl: function ()
19185 return this.el.select('input.roo-radio',true).first();
19187 onClick : function()
19190 this.setChecked(true);
19193 setChecked : function(state,suppressEvent)
19196 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19197 v.dom.checked = false;
19200 Roo.log(this.inputEl().dom);
19201 this.checked = state;
19202 this.inputEl().dom.checked = state;
19204 if(suppressEvent !== true){
19205 this.fireEvent('check', this, state);
19208 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19212 getGroupValue : function()
19215 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19216 if(v.dom.checked == true){
19217 value = v.dom.value;
19225 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19226 * @return {Mixed} value The field value
19228 getValue : function(){
19229 return this.getGroupValue();
19235 //<script type="text/javascript">
19238 * Based Ext JS Library 1.1.1
19239 * Copyright(c) 2006-2007, Ext JS, LLC.
19245 * @class Roo.HtmlEditorCore
19246 * @extends Roo.Component
19247 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19249 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19252 Roo.HtmlEditorCore = function(config){
19255 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19260 * @event initialize
19261 * Fires when the editor is fully initialized (including the iframe)
19262 * @param {Roo.HtmlEditorCore} this
19267 * Fires when the editor is first receives the focus. Any insertion must wait
19268 * until after this event.
19269 * @param {Roo.HtmlEditorCore} this
19273 * @event beforesync
19274 * Fires before the textarea is updated with content from the editor iframe. Return false
19275 * to cancel the sync.
19276 * @param {Roo.HtmlEditorCore} this
19277 * @param {String} html
19281 * @event beforepush
19282 * Fires before the iframe editor is updated with content from the textarea. Return false
19283 * to cancel the push.
19284 * @param {Roo.HtmlEditorCore} this
19285 * @param {String} html
19290 * Fires when the textarea is updated with content from the editor iframe.
19291 * @param {Roo.HtmlEditorCore} this
19292 * @param {String} html
19297 * Fires when the iframe editor is updated with content from the textarea.
19298 * @param {Roo.HtmlEditorCore} this
19299 * @param {String} html
19304 * @event editorevent
19305 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19306 * @param {Roo.HtmlEditorCore} this
19312 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19314 // defaults : white / black...
19315 this.applyBlacklists();
19322 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19326 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19332 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19337 * @cfg {Number} height (in pixels)
19341 * @cfg {Number} width (in pixels)
19346 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19349 stylesheets: false,
19354 // private properties
19355 validationEvent : false,
19357 initialized : false,
19359 sourceEditMode : false,
19360 onFocus : Roo.emptyFn,
19362 hideMode:'offsets',
19366 // blacklist + whitelisted elements..
19373 * Protected method that will not generally be called directly. It
19374 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19375 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19377 getDocMarkup : function(){
19381 // inherit styels from page...??
19382 if (this.stylesheets === false) {
19384 Roo.get(document.head).select('style').each(function(node) {
19385 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19388 Roo.get(document.head).select('link').each(function(node) {
19389 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19392 } else if (!this.stylesheets.length) {
19394 st = '<style type="text/css">' +
19395 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19401 st += '<style type="text/css">' +
19402 'IMG { cursor: pointer } ' +
19406 return '<html><head>' + st +
19407 //<style type="text/css">' +
19408 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19410 ' </head><body class="roo-htmleditor-body"></body></html>';
19414 onRender : function(ct, position)
19417 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19418 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19421 this.el.dom.style.border = '0 none';
19422 this.el.dom.setAttribute('tabIndex', -1);
19423 this.el.addClass('x-hidden hide');
19427 if(Roo.isIE){ // fix IE 1px bogus margin
19428 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19432 this.frameId = Roo.id();
19436 var iframe = this.owner.wrap.createChild({
19438 cls: 'form-control', // bootstrap..
19440 name: this.frameId,
19441 frameBorder : 'no',
19442 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19447 this.iframe = iframe.dom;
19449 this.assignDocWin();
19451 this.doc.designMode = 'on';
19454 this.doc.write(this.getDocMarkup());
19458 var task = { // must defer to wait for browser to be ready
19460 //console.log("run task?" + this.doc.readyState);
19461 this.assignDocWin();
19462 if(this.doc.body || this.doc.readyState == 'complete'){
19464 this.doc.designMode="on";
19468 Roo.TaskMgr.stop(task);
19469 this.initEditor.defer(10, this);
19476 Roo.TaskMgr.start(task);
19481 onResize : function(w, h)
19483 Roo.log('resize: ' +w + ',' + h );
19484 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19488 if(typeof w == 'number'){
19490 this.iframe.style.width = w + 'px';
19492 if(typeof h == 'number'){
19494 this.iframe.style.height = h + 'px';
19496 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19503 * Toggles the editor between standard and source edit mode.
19504 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19506 toggleSourceEdit : function(sourceEditMode){
19508 this.sourceEditMode = sourceEditMode === true;
19510 if(this.sourceEditMode){
19512 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19515 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19516 //this.iframe.className = '';
19519 //this.setSize(this.owner.wrap.getSize());
19520 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19527 * Protected method that will not generally be called directly. If you need/want
19528 * custom HTML cleanup, this is the method you should override.
19529 * @param {String} html The HTML to be cleaned
19530 * return {String} The cleaned HTML
19532 cleanHtml : function(html){
19533 html = String(html);
19534 if(html.length > 5){
19535 if(Roo.isSafari){ // strip safari nonsense
19536 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19539 if(html == ' '){
19546 * HTML Editor -> Textarea
19547 * Protected method that will not generally be called directly. Syncs the contents
19548 * of the editor iframe with the textarea.
19550 syncValue : function(){
19551 if(this.initialized){
19552 var bd = (this.doc.body || this.doc.documentElement);
19553 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19554 var html = bd.innerHTML;
19556 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19557 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19559 html = '<div style="'+m[0]+'">' + html + '</div>';
19562 html = this.cleanHtml(html);
19563 // fix up the special chars.. normaly like back quotes in word...
19564 // however we do not want to do this with chinese..
19565 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19566 var cc = b.charCodeAt();
19568 (cc >= 0x4E00 && cc < 0xA000 ) ||
19569 (cc >= 0x3400 && cc < 0x4E00 ) ||
19570 (cc >= 0xf900 && cc < 0xfb00 )
19576 if(this.owner.fireEvent('beforesync', this, html) !== false){
19577 this.el.dom.value = html;
19578 this.owner.fireEvent('sync', this, html);
19584 * Protected method that will not generally be called directly. Pushes the value of the textarea
19585 * into the iframe editor.
19587 pushValue : function(){
19588 if(this.initialized){
19589 var v = this.el.dom.value.trim();
19591 // if(v.length < 1){
19595 if(this.owner.fireEvent('beforepush', this, v) !== false){
19596 var d = (this.doc.body || this.doc.documentElement);
19598 this.cleanUpPaste();
19599 this.el.dom.value = d.innerHTML;
19600 this.owner.fireEvent('push', this, v);
19606 deferFocus : function(){
19607 this.focus.defer(10, this);
19611 focus : function(){
19612 if(this.win && !this.sourceEditMode){
19619 assignDocWin: function()
19621 var iframe = this.iframe;
19624 this.doc = iframe.contentWindow.document;
19625 this.win = iframe.contentWindow;
19627 // if (!Roo.get(this.frameId)) {
19630 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19631 // this.win = Roo.get(this.frameId).dom.contentWindow;
19633 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19637 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19638 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19643 initEditor : function(){
19644 //console.log("INIT EDITOR");
19645 this.assignDocWin();
19649 this.doc.designMode="on";
19651 this.doc.write(this.getDocMarkup());
19654 var dbody = (this.doc.body || this.doc.documentElement);
19655 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19656 // this copies styles from the containing element into thsi one..
19657 // not sure why we need all of this..
19658 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19660 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19661 //ss['background-attachment'] = 'fixed'; // w3c
19662 dbody.bgProperties = 'fixed'; // ie
19663 //Roo.DomHelper.applyStyles(dbody, ss);
19664 Roo.EventManager.on(this.doc, {
19665 //'mousedown': this.onEditorEvent,
19666 'mouseup': this.onEditorEvent,
19667 'dblclick': this.onEditorEvent,
19668 'click': this.onEditorEvent,
19669 'keyup': this.onEditorEvent,
19674 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19676 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19677 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19679 this.initialized = true;
19681 this.owner.fireEvent('initialize', this);
19686 onDestroy : function(){
19692 //for (var i =0; i < this.toolbars.length;i++) {
19693 // // fixme - ask toolbars for heights?
19694 // this.toolbars[i].onDestroy();
19697 //this.wrap.dom.innerHTML = '';
19698 //this.wrap.remove();
19703 onFirstFocus : function(){
19705 this.assignDocWin();
19708 this.activated = true;
19711 if(Roo.isGecko){ // prevent silly gecko errors
19713 var s = this.win.getSelection();
19714 if(!s.focusNode || s.focusNode.nodeType != 3){
19715 var r = s.getRangeAt(0);
19716 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19721 this.execCmd('useCSS', true);
19722 this.execCmd('styleWithCSS', false);
19725 this.owner.fireEvent('activate', this);
19729 adjustFont: function(btn){
19730 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19731 //if(Roo.isSafari){ // safari
19734 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19735 if(Roo.isSafari){ // safari
19736 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19737 v = (v < 10) ? 10 : v;
19738 v = (v > 48) ? 48 : v;
19739 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19744 v = Math.max(1, v+adjust);
19746 this.execCmd('FontSize', v );
19749 onEditorEvent : function(e)
19751 this.owner.fireEvent('editorevent', this, e);
19752 // this.updateToolbar();
19753 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19756 insertTag : function(tg)
19758 // could be a bit smarter... -> wrap the current selected tRoo..
19759 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19761 range = this.createRange(this.getSelection());
19762 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19763 wrappingNode.appendChild(range.extractContents());
19764 range.insertNode(wrappingNode);
19771 this.execCmd("formatblock", tg);
19775 insertText : function(txt)
19779 var range = this.createRange();
19780 range.deleteContents();
19781 //alert(Sender.getAttribute('label'));
19783 range.insertNode(this.doc.createTextNode(txt));
19789 * Executes a Midas editor command on the editor document and performs necessary focus and
19790 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19791 * @param {String} cmd The Midas command
19792 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19794 relayCmd : function(cmd, value){
19796 this.execCmd(cmd, value);
19797 this.owner.fireEvent('editorevent', this);
19798 //this.updateToolbar();
19799 this.owner.deferFocus();
19803 * Executes a Midas editor command directly on the editor document.
19804 * For visual commands, you should use {@link #relayCmd} instead.
19805 * <b>This should only be called after the editor is initialized.</b>
19806 * @param {String} cmd The Midas command
19807 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19809 execCmd : function(cmd, value){
19810 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19817 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19819 * @param {String} text | dom node..
19821 insertAtCursor : function(text)
19826 if(!this.activated){
19832 var r = this.doc.selection.createRange();
19843 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19847 // from jquery ui (MIT licenced)
19849 var win = this.win;
19851 if (win.getSelection && win.getSelection().getRangeAt) {
19852 range = win.getSelection().getRangeAt(0);
19853 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19854 range.insertNode(node);
19855 } else if (win.document.selection && win.document.selection.createRange) {
19856 // no firefox support
19857 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19858 win.document.selection.createRange().pasteHTML(txt);
19860 // no firefox support
19861 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19862 this.execCmd('InsertHTML', txt);
19871 mozKeyPress : function(e){
19873 var c = e.getCharCode(), cmd;
19876 c = String.fromCharCode(c).toLowerCase();
19890 this.cleanUpPaste.defer(100, this);
19898 e.preventDefault();
19906 fixKeys : function(){ // load time branching for fastest keydown performance
19908 return function(e){
19909 var k = e.getKey(), r;
19912 r = this.doc.selection.createRange();
19915 r.pasteHTML('    ');
19922 r = this.doc.selection.createRange();
19924 var target = r.parentElement();
19925 if(!target || target.tagName.toLowerCase() != 'li'){
19927 r.pasteHTML('<br />');
19933 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19934 this.cleanUpPaste.defer(100, this);
19940 }else if(Roo.isOpera){
19941 return function(e){
19942 var k = e.getKey();
19946 this.execCmd('InsertHTML','    ');
19949 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19950 this.cleanUpPaste.defer(100, this);
19955 }else if(Roo.isSafari){
19956 return function(e){
19957 var k = e.getKey();
19961 this.execCmd('InsertText','\t');
19965 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19966 this.cleanUpPaste.defer(100, this);
19974 getAllAncestors: function()
19976 var p = this.getSelectedNode();
19979 a.push(p); // push blank onto stack..
19980 p = this.getParentElement();
19984 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19988 a.push(this.doc.body);
19992 lastSelNode : false,
19995 getSelection : function()
19997 this.assignDocWin();
19998 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20001 getSelectedNode: function()
20003 // this may only work on Gecko!!!
20005 // should we cache this!!!!
20010 var range = this.createRange(this.getSelection()).cloneRange();
20013 var parent = range.parentElement();
20015 var testRange = range.duplicate();
20016 testRange.moveToElementText(parent);
20017 if (testRange.inRange(range)) {
20020 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20023 parent = parent.parentElement;
20028 // is ancestor a text element.
20029 var ac = range.commonAncestorContainer;
20030 if (ac.nodeType == 3) {
20031 ac = ac.parentNode;
20034 var ar = ac.childNodes;
20037 var other_nodes = [];
20038 var has_other_nodes = false;
20039 for (var i=0;i<ar.length;i++) {
20040 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20043 // fullly contained node.
20045 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20050 // probably selected..
20051 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20052 other_nodes.push(ar[i]);
20056 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20061 has_other_nodes = true;
20063 if (!nodes.length && other_nodes.length) {
20064 nodes= other_nodes;
20066 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20072 createRange: function(sel)
20074 // this has strange effects when using with
20075 // top toolbar - not sure if it's a great idea.
20076 //this.editor.contentWindow.focus();
20077 if (typeof sel != "undefined") {
20079 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20081 return this.doc.createRange();
20084 return this.doc.createRange();
20087 getParentElement: function()
20090 this.assignDocWin();
20091 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20093 var range = this.createRange(sel);
20096 var p = range.commonAncestorContainer;
20097 while (p.nodeType == 3) { // text node
20108 * Range intersection.. the hard stuff...
20112 * [ -- selected range --- ]
20116 * if end is before start or hits it. fail.
20117 * if start is after end or hits it fail.
20119 * if either hits (but other is outside. - then it's not
20125 // @see http://www.thismuchiknow.co.uk/?p=64.
20126 rangeIntersectsNode : function(range, node)
20128 var nodeRange = node.ownerDocument.createRange();
20130 nodeRange.selectNode(node);
20132 nodeRange.selectNodeContents(node);
20135 var rangeStartRange = range.cloneRange();
20136 rangeStartRange.collapse(true);
20138 var rangeEndRange = range.cloneRange();
20139 rangeEndRange.collapse(false);
20141 var nodeStartRange = nodeRange.cloneRange();
20142 nodeStartRange.collapse(true);
20144 var nodeEndRange = nodeRange.cloneRange();
20145 nodeEndRange.collapse(false);
20147 return rangeStartRange.compareBoundaryPoints(
20148 Range.START_TO_START, nodeEndRange) == -1 &&
20149 rangeEndRange.compareBoundaryPoints(
20150 Range.START_TO_START, nodeStartRange) == 1;
20154 rangeCompareNode : function(range, node)
20156 var nodeRange = node.ownerDocument.createRange();
20158 nodeRange.selectNode(node);
20160 nodeRange.selectNodeContents(node);
20164 range.collapse(true);
20166 nodeRange.collapse(true);
20168 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20169 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20171 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20173 var nodeIsBefore = ss == 1;
20174 var nodeIsAfter = ee == -1;
20176 if (nodeIsBefore && nodeIsAfter) {
20179 if (!nodeIsBefore && nodeIsAfter) {
20180 return 1; //right trailed.
20183 if (nodeIsBefore && !nodeIsAfter) {
20184 return 2; // left trailed.
20190 // private? - in a new class?
20191 cleanUpPaste : function()
20193 // cleans up the whole document..
20194 Roo.log('cleanuppaste');
20196 this.cleanUpChildren(this.doc.body);
20197 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20198 if (clean != this.doc.body.innerHTML) {
20199 this.doc.body.innerHTML = clean;
20204 cleanWordChars : function(input) {// change the chars to hex code
20205 var he = Roo.HtmlEditorCore;
20207 var output = input;
20208 Roo.each(he.swapCodes, function(sw) {
20209 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20211 output = output.replace(swapper, sw[1]);
20218 cleanUpChildren : function (n)
20220 if (!n.childNodes.length) {
20223 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20224 this.cleanUpChild(n.childNodes[i]);
20231 cleanUpChild : function (node)
20234 //console.log(node);
20235 if (node.nodeName == "#text") {
20236 // clean up silly Windows -- stuff?
20239 if (node.nodeName == "#comment") {
20240 node.parentNode.removeChild(node);
20241 // clean up silly Windows -- stuff?
20244 var lcname = node.tagName.toLowerCase();
20245 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20246 // whitelist of tags..
20248 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20250 node.parentNode.removeChild(node);
20255 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20257 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20258 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20260 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20261 // remove_keep_children = true;
20264 if (remove_keep_children) {
20265 this.cleanUpChildren(node);
20266 // inserts everything just before this node...
20267 while (node.childNodes.length) {
20268 var cn = node.childNodes[0];
20269 node.removeChild(cn);
20270 node.parentNode.insertBefore(cn, node);
20272 node.parentNode.removeChild(node);
20276 if (!node.attributes || !node.attributes.length) {
20277 this.cleanUpChildren(node);
20281 function cleanAttr(n,v)
20284 if (v.match(/^\./) || v.match(/^\//)) {
20287 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20290 if (v.match(/^#/)) {
20293 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20294 node.removeAttribute(n);
20298 var cwhite = this.cwhite;
20299 var cblack = this.cblack;
20301 function cleanStyle(n,v)
20303 if (v.match(/expression/)) { //XSS?? should we even bother..
20304 node.removeAttribute(n);
20308 var parts = v.split(/;/);
20311 Roo.each(parts, function(p) {
20312 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20316 var l = p.split(':').shift().replace(/\s+/g,'');
20317 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20319 if ( cwhite.length && cblack.indexOf(l) > -1) {
20320 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20321 //node.removeAttribute(n);
20325 // only allow 'c whitelisted system attributes'
20326 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20327 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20328 //node.removeAttribute(n);
20338 if (clean.length) {
20339 node.setAttribute(n, clean.join(';'));
20341 node.removeAttribute(n);
20347 for (var i = node.attributes.length-1; i > -1 ; i--) {
20348 var a = node.attributes[i];
20351 if (a.name.toLowerCase().substr(0,2)=='on') {
20352 node.removeAttribute(a.name);
20355 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20356 node.removeAttribute(a.name);
20359 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20360 cleanAttr(a.name,a.value); // fixme..
20363 if (a.name == 'style') {
20364 cleanStyle(a.name,a.value);
20367 /// clean up MS crap..
20368 // tecnically this should be a list of valid class'es..
20371 if (a.name == 'class') {
20372 if (a.value.match(/^Mso/)) {
20373 node.className = '';
20376 if (a.value.match(/body/)) {
20377 node.className = '';
20388 this.cleanUpChildren(node);
20394 * Clean up MS wordisms...
20396 cleanWord : function(node)
20401 this.cleanWord(this.doc.body);
20404 if (node.nodeName == "#text") {
20405 // clean up silly Windows -- stuff?
20408 if (node.nodeName == "#comment") {
20409 node.parentNode.removeChild(node);
20410 // clean up silly Windows -- stuff?
20414 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20415 node.parentNode.removeChild(node);
20419 // remove - but keep children..
20420 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20421 while (node.childNodes.length) {
20422 var cn = node.childNodes[0];
20423 node.removeChild(cn);
20424 node.parentNode.insertBefore(cn, node);
20426 node.parentNode.removeChild(node);
20427 this.iterateChildren(node, this.cleanWord);
20431 if (node.className.length) {
20433 var cn = node.className.split(/\W+/);
20435 Roo.each(cn, function(cls) {
20436 if (cls.match(/Mso[a-zA-Z]+/)) {
20441 node.className = cna.length ? cna.join(' ') : '';
20443 node.removeAttribute("class");
20447 if (node.hasAttribute("lang")) {
20448 node.removeAttribute("lang");
20451 if (node.hasAttribute("style")) {
20453 var styles = node.getAttribute("style").split(";");
20455 Roo.each(styles, function(s) {
20456 if (!s.match(/:/)) {
20459 var kv = s.split(":");
20460 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20463 // what ever is left... we allow.
20466 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20467 if (!nstyle.length) {
20468 node.removeAttribute('style');
20471 this.iterateChildren(node, this.cleanWord);
20477 * iterateChildren of a Node, calling fn each time, using this as the scole..
20478 * @param {DomNode} node node to iterate children of.
20479 * @param {Function} fn method of this class to call on each item.
20481 iterateChildren : function(node, fn)
20483 if (!node.childNodes.length) {
20486 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20487 fn.call(this, node.childNodes[i])
20493 * cleanTableWidths.
20495 * Quite often pasting from word etc.. results in tables with column and widths.
20496 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20499 cleanTableWidths : function(node)
20504 this.cleanTableWidths(this.doc.body);
20509 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20512 Roo.log(node.tagName);
20513 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20514 this.iterateChildren(node, this.cleanTableWidths);
20517 if (node.hasAttribute('width')) {
20518 node.removeAttribute('width');
20522 if (node.hasAttribute("style")) {
20525 var styles = node.getAttribute("style").split(";");
20527 Roo.each(styles, function(s) {
20528 if (!s.match(/:/)) {
20531 var kv = s.split(":");
20532 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20535 // what ever is left... we allow.
20538 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20539 if (!nstyle.length) {
20540 node.removeAttribute('style');
20544 this.iterateChildren(node, this.cleanTableWidths);
20552 domToHTML : function(currentElement, depth, nopadtext) {
20554 depth = depth || 0;
20555 nopadtext = nopadtext || false;
20557 if (!currentElement) {
20558 return this.domToHTML(this.doc.body);
20561 //Roo.log(currentElement);
20563 var allText = false;
20564 var nodeName = currentElement.nodeName;
20565 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20567 if (nodeName == '#text') {
20569 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20574 if (nodeName != 'BODY') {
20577 // Prints the node tagName, such as <A>, <IMG>, etc
20580 for(i = 0; i < currentElement.attributes.length;i++) {
20582 var aname = currentElement.attributes.item(i).name;
20583 if (!currentElement.attributes.item(i).value.length) {
20586 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20589 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20598 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20601 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20606 // Traverse the tree
20608 var currentElementChild = currentElement.childNodes.item(i);
20609 var allText = true;
20610 var innerHTML = '';
20612 while (currentElementChild) {
20613 // Formatting code (indent the tree so it looks nice on the screen)
20614 var nopad = nopadtext;
20615 if (lastnode == 'SPAN') {
20619 if (currentElementChild.nodeName == '#text') {
20620 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20621 toadd = nopadtext ? toadd : toadd.trim();
20622 if (!nopad && toadd.length > 80) {
20623 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20625 innerHTML += toadd;
20628 currentElementChild = currentElement.childNodes.item(i);
20634 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20636 // Recursively traverse the tree structure of the child node
20637 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20638 lastnode = currentElementChild.nodeName;
20640 currentElementChild=currentElement.childNodes.item(i);
20646 // The remaining code is mostly for formatting the tree
20647 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20652 ret+= "</"+tagName+">";
20658 applyBlacklists : function()
20660 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20661 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20665 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20666 if (b.indexOf(tag) > -1) {
20669 this.white.push(tag);
20673 Roo.each(w, function(tag) {
20674 if (b.indexOf(tag) > -1) {
20677 if (this.white.indexOf(tag) > -1) {
20680 this.white.push(tag);
20685 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20686 if (w.indexOf(tag) > -1) {
20689 this.black.push(tag);
20693 Roo.each(b, function(tag) {
20694 if (w.indexOf(tag) > -1) {
20697 if (this.black.indexOf(tag) > -1) {
20700 this.black.push(tag);
20705 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20706 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20710 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20711 if (b.indexOf(tag) > -1) {
20714 this.cwhite.push(tag);
20718 Roo.each(w, function(tag) {
20719 if (b.indexOf(tag) > -1) {
20722 if (this.cwhite.indexOf(tag) > -1) {
20725 this.cwhite.push(tag);
20730 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20731 if (w.indexOf(tag) > -1) {
20734 this.cblack.push(tag);
20738 Roo.each(b, function(tag) {
20739 if (w.indexOf(tag) > -1) {
20742 if (this.cblack.indexOf(tag) > -1) {
20745 this.cblack.push(tag);
20750 setStylesheets : function(stylesheets)
20752 if(typeof(stylesheets) == 'string'){
20753 Roo.get(this.iframe.contentDocument.head).createChild({
20755 rel : 'stylesheet',
20764 Roo.each(stylesheets, function(s) {
20769 Roo.get(_this.iframe.contentDocument.head).createChild({
20771 rel : 'stylesheet',
20780 removeStylesheets : function()
20784 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20789 // hide stuff that is not compatible
20803 * @event specialkey
20807 * @cfg {String} fieldClass @hide
20810 * @cfg {String} focusClass @hide
20813 * @cfg {String} autoCreate @hide
20816 * @cfg {String} inputType @hide
20819 * @cfg {String} invalidClass @hide
20822 * @cfg {String} invalidText @hide
20825 * @cfg {String} msgFx @hide
20828 * @cfg {String} validateOnBlur @hide
20832 Roo.HtmlEditorCore.white = [
20833 'area', 'br', 'img', 'input', 'hr', 'wbr',
20835 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20836 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20837 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20838 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20839 'table', 'ul', 'xmp',
20841 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20844 'dir', 'menu', 'ol', 'ul', 'dl',
20850 Roo.HtmlEditorCore.black = [
20851 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20853 'base', 'basefont', 'bgsound', 'blink', 'body',
20854 'frame', 'frameset', 'head', 'html', 'ilayer',
20855 'iframe', 'layer', 'link', 'meta', 'object',
20856 'script', 'style' ,'title', 'xml' // clean later..
20858 Roo.HtmlEditorCore.clean = [
20859 'script', 'style', 'title', 'xml'
20861 Roo.HtmlEditorCore.remove = [
20866 Roo.HtmlEditorCore.ablack = [
20870 Roo.HtmlEditorCore.aclean = [
20871 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20875 Roo.HtmlEditorCore.pwhite= [
20876 'http', 'https', 'mailto'
20879 // white listed style attributes.
20880 Roo.HtmlEditorCore.cwhite= [
20881 // 'text-align', /// default is to allow most things..
20887 // black listed style attributes.
20888 Roo.HtmlEditorCore.cblack= [
20889 // 'font-size' -- this can be set by the project
20893 Roo.HtmlEditorCore.swapCodes =[
20912 * @class Roo.bootstrap.HtmlEditor
20913 * @extends Roo.bootstrap.TextArea
20914 * Bootstrap HtmlEditor class
20917 * Create a new HtmlEditor
20918 * @param {Object} config The config object
20921 Roo.bootstrap.HtmlEditor = function(config){
20922 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20923 if (!this.toolbars) {
20924 this.toolbars = [];
20926 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20929 * @event initialize
20930 * Fires when the editor is fully initialized (including the iframe)
20931 * @param {HtmlEditor} this
20936 * Fires when the editor is first receives the focus. Any insertion must wait
20937 * until after this event.
20938 * @param {HtmlEditor} this
20942 * @event beforesync
20943 * Fires before the textarea is updated with content from the editor iframe. Return false
20944 * to cancel the sync.
20945 * @param {HtmlEditor} this
20946 * @param {String} html
20950 * @event beforepush
20951 * Fires before the iframe editor is updated with content from the textarea. Return false
20952 * to cancel the push.
20953 * @param {HtmlEditor} this
20954 * @param {String} html
20959 * Fires when the textarea is updated with content from the editor iframe.
20960 * @param {HtmlEditor} this
20961 * @param {String} html
20966 * Fires when the iframe editor is updated with content from the textarea.
20967 * @param {HtmlEditor} this
20968 * @param {String} html
20972 * @event editmodechange
20973 * Fires when the editor switches edit modes
20974 * @param {HtmlEditor} this
20975 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20977 editmodechange: true,
20979 * @event editorevent
20980 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20981 * @param {HtmlEditor} this
20985 * @event firstfocus
20986 * Fires when on first focus - needed by toolbars..
20987 * @param {HtmlEditor} this
20992 * Auto save the htmlEditor value as a file into Events
20993 * @param {HtmlEditor} this
20997 * @event savedpreview
20998 * preview the saved version of htmlEditor
20999 * @param {HtmlEditor} this
21006 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21010 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21015 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21020 * @cfg {Number} height (in pixels)
21024 * @cfg {Number} width (in pixels)
21029 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21032 stylesheets: false,
21037 // private properties
21038 validationEvent : false,
21040 initialized : false,
21043 onFocus : Roo.emptyFn,
21045 hideMode:'offsets',
21048 tbContainer : false,
21050 toolbarContainer :function() {
21051 return this.wrap.select('.x-html-editor-tb',true).first();
21055 * Protected method that will not generally be called directly. It
21056 * is called when the editor creates its toolbar. Override this method if you need to
21057 * add custom toolbar buttons.
21058 * @param {HtmlEditor} editor
21060 createToolbar : function(){
21062 Roo.log("create toolbars");
21064 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21065 this.toolbars[0].render(this.toolbarContainer());
21069 // if (!editor.toolbars || !editor.toolbars.length) {
21070 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21073 // for (var i =0 ; i < editor.toolbars.length;i++) {
21074 // editor.toolbars[i] = Roo.factory(
21075 // typeof(editor.toolbars[i]) == 'string' ?
21076 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21077 // Roo.bootstrap.HtmlEditor);
21078 // editor.toolbars[i].init(editor);
21084 onRender : function(ct, position)
21086 // Roo.log("Call onRender: " + this.xtype);
21088 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21090 this.wrap = this.inputEl().wrap({
21091 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21094 this.editorcore.onRender(ct, position);
21096 if (this.resizable) {
21097 this.resizeEl = new Roo.Resizable(this.wrap, {
21101 minHeight : this.height,
21102 height: this.height,
21103 handles : this.resizable,
21106 resize : function(r, w, h) {
21107 _t.onResize(w,h); // -something
21113 this.createToolbar(this);
21116 if(!this.width && this.resizable){
21117 this.setSize(this.wrap.getSize());
21119 if (this.resizeEl) {
21120 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21121 // should trigger onReize..
21127 onResize : function(w, h)
21129 Roo.log('resize: ' +w + ',' + h );
21130 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21134 if(this.inputEl() ){
21135 if(typeof w == 'number'){
21136 var aw = w - this.wrap.getFrameWidth('lr');
21137 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21140 if(typeof h == 'number'){
21141 var tbh = -11; // fixme it needs to tool bar size!
21142 for (var i =0; i < this.toolbars.length;i++) {
21143 // fixme - ask toolbars for heights?
21144 tbh += this.toolbars[i].el.getHeight();
21145 //if (this.toolbars[i].footer) {
21146 // tbh += this.toolbars[i].footer.el.getHeight();
21154 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21155 ah -= 5; // knock a few pixes off for look..
21156 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21160 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21161 this.editorcore.onResize(ew,eh);
21166 * Toggles the editor between standard and source edit mode.
21167 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21169 toggleSourceEdit : function(sourceEditMode)
21171 this.editorcore.toggleSourceEdit(sourceEditMode);
21173 if(this.editorcore.sourceEditMode){
21174 Roo.log('editor - showing textarea');
21177 // Roo.log(this.syncValue());
21179 this.inputEl().removeClass(['hide', 'x-hidden']);
21180 this.inputEl().dom.removeAttribute('tabIndex');
21181 this.inputEl().focus();
21183 Roo.log('editor - hiding textarea');
21185 // Roo.log(this.pushValue());
21188 this.inputEl().addClass(['hide', 'x-hidden']);
21189 this.inputEl().dom.setAttribute('tabIndex', -1);
21190 //this.deferFocus();
21193 if(this.resizable){
21194 this.setSize(this.wrap.getSize());
21197 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21200 // private (for BoxComponent)
21201 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21203 // private (for BoxComponent)
21204 getResizeEl : function(){
21208 // private (for BoxComponent)
21209 getPositionEl : function(){
21214 initEvents : function(){
21215 this.originalValue = this.getValue();
21219 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21222 // markInvalid : Roo.emptyFn,
21224 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21227 // clearInvalid : Roo.emptyFn,
21229 setValue : function(v){
21230 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21231 this.editorcore.pushValue();
21236 deferFocus : function(){
21237 this.focus.defer(10, this);
21241 focus : function(){
21242 this.editorcore.focus();
21248 onDestroy : function(){
21254 for (var i =0; i < this.toolbars.length;i++) {
21255 // fixme - ask toolbars for heights?
21256 this.toolbars[i].onDestroy();
21259 this.wrap.dom.innerHTML = '';
21260 this.wrap.remove();
21265 onFirstFocus : function(){
21266 //Roo.log("onFirstFocus");
21267 this.editorcore.onFirstFocus();
21268 for (var i =0; i < this.toolbars.length;i++) {
21269 this.toolbars[i].onFirstFocus();
21275 syncValue : function()
21277 this.editorcore.syncValue();
21280 pushValue : function()
21282 this.editorcore.pushValue();
21286 // hide stuff that is not compatible
21300 * @event specialkey
21304 * @cfg {String} fieldClass @hide
21307 * @cfg {String} focusClass @hide
21310 * @cfg {String} autoCreate @hide
21313 * @cfg {String} inputType @hide
21316 * @cfg {String} invalidClass @hide
21319 * @cfg {String} invalidText @hide
21322 * @cfg {String} msgFx @hide
21325 * @cfg {String} validateOnBlur @hide
21334 Roo.namespace('Roo.bootstrap.htmleditor');
21336 * @class Roo.bootstrap.HtmlEditorToolbar1
21341 new Roo.bootstrap.HtmlEditor({
21344 new Roo.bootstrap.HtmlEditorToolbar1({
21345 disable : { fonts: 1 , format: 1, ..., ... , ...],
21351 * @cfg {Object} disable List of elements to disable..
21352 * @cfg {Array} btns List of additional buttons.
21356 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21359 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21362 Roo.apply(this, config);
21364 // default disabled, based on 'good practice'..
21365 this.disable = this.disable || {};
21366 Roo.applyIf(this.disable, {
21369 specialElements : true
21371 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21373 this.editor = config.editor;
21374 this.editorcore = config.editor.editorcore;
21376 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21378 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21379 // dont call parent... till later.
21381 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21386 editorcore : false,
21391 "h1","h2","h3","h4","h5","h6",
21393 "abbr", "acronym", "address", "cite", "samp", "var",
21397 onRender : function(ct, position)
21399 // Roo.log("Call onRender: " + this.xtype);
21401 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21403 this.el.dom.style.marginBottom = '0';
21405 var editorcore = this.editorcore;
21406 var editor= this.editor;
21409 var btn = function(id,cmd , toggle, handler){
21411 var event = toggle ? 'toggle' : 'click';
21416 xns: Roo.bootstrap,
21419 enableToggle:toggle !== false,
21421 pressed : toggle ? false : null,
21424 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21425 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21434 xns: Roo.bootstrap,
21435 glyphicon : 'font',
21439 xns: Roo.bootstrap,
21443 Roo.each(this.formats, function(f) {
21444 style.menu.items.push({
21446 xns: Roo.bootstrap,
21447 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21452 editorcore.insertTag(this.tagname);
21459 children.push(style);
21462 btn('bold',false,true);
21463 btn('italic',false,true);
21464 btn('align-left', 'justifyleft',true);
21465 btn('align-center', 'justifycenter',true);
21466 btn('align-right' , 'justifyright',true);
21467 btn('link', false, false, function(btn) {
21468 //Roo.log("create link?");
21469 var url = prompt(this.createLinkText, this.defaultLinkValue);
21470 if(url && url != 'http:/'+'/'){
21471 this.editorcore.relayCmd('createlink', url);
21474 btn('list','insertunorderedlist',true);
21475 btn('pencil', false,true, function(btn){
21478 this.toggleSourceEdit(btn.pressed);
21484 xns: Roo.bootstrap,
21489 xns: Roo.bootstrap,
21494 cog.menu.items.push({
21496 xns: Roo.bootstrap,
21497 html : Clean styles,
21502 editorcore.insertTag(this.tagname);
21511 this.xtype = 'NavSimplebar';
21513 for(var i=0;i< children.length;i++) {
21515 this.buttons.add(this.addxtypeChild(children[i]));
21519 editor.on('editorevent', this.updateToolbar, this);
21521 onBtnClick : function(id)
21523 this.editorcore.relayCmd(id);
21524 this.editorcore.focus();
21528 * Protected method that will not generally be called directly. It triggers
21529 * a toolbar update by reading the markup state of the current selection in the editor.
21531 updateToolbar: function(){
21533 if(!this.editorcore.activated){
21534 this.editor.onFirstFocus(); // is this neeed?
21538 var btns = this.buttons;
21539 var doc = this.editorcore.doc;
21540 btns.get('bold').setActive(doc.queryCommandState('bold'));
21541 btns.get('italic').setActive(doc.queryCommandState('italic'));
21542 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21544 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21545 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21546 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21548 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21549 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21552 var ans = this.editorcore.getAllAncestors();
21553 if (this.formatCombo) {
21556 var store = this.formatCombo.store;
21557 this.formatCombo.setValue("");
21558 for (var i =0; i < ans.length;i++) {
21559 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21561 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21569 // hides menus... - so this cant be on a menu...
21570 Roo.bootstrap.MenuMgr.hideAll();
21572 Roo.bootstrap.MenuMgr.hideAll();
21573 //this.editorsyncValue();
21575 onFirstFocus: function() {
21576 this.buttons.each(function(item){
21580 toggleSourceEdit : function(sourceEditMode){
21583 if(sourceEditMode){
21584 Roo.log("disabling buttons");
21585 this.buttons.each( function(item){
21586 if(item.cmd != 'pencil'){
21592 Roo.log("enabling buttons");
21593 if(this.editorcore.initialized){
21594 this.buttons.each( function(item){
21600 Roo.log("calling toggole on editor");
21601 // tell the editor that it's been pressed..
21602 this.editor.toggleSourceEdit(sourceEditMode);
21612 * @class Roo.bootstrap.Table.AbstractSelectionModel
21613 * @extends Roo.util.Observable
21614 * Abstract base class for grid SelectionModels. It provides the interface that should be
21615 * implemented by descendant classes. This class should not be directly instantiated.
21618 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21619 this.locked = false;
21620 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21624 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21625 /** @ignore Called by the grid automatically. Do not call directly. */
21626 init : function(grid){
21632 * Locks the selections.
21635 this.locked = true;
21639 * Unlocks the selections.
21641 unlock : function(){
21642 this.locked = false;
21646 * Returns true if the selections are locked.
21647 * @return {Boolean}
21649 isLocked : function(){
21650 return this.locked;
21654 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21655 * @class Roo.bootstrap.Table.RowSelectionModel
21656 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21657 * It supports multiple selections and keyboard selection/navigation.
21659 * @param {Object} config
21662 Roo.bootstrap.Table.RowSelectionModel = function(config){
21663 Roo.apply(this, config);
21664 this.selections = new Roo.util.MixedCollection(false, function(o){
21669 this.lastActive = false;
21673 * @event selectionchange
21674 * Fires when the selection changes
21675 * @param {SelectionModel} this
21677 "selectionchange" : true,
21679 * @event afterselectionchange
21680 * Fires after the selection changes (eg. by key press or clicking)
21681 * @param {SelectionModel} this
21683 "afterselectionchange" : true,
21685 * @event beforerowselect
21686 * Fires when a row is selected being selected, return false to cancel.
21687 * @param {SelectionModel} this
21688 * @param {Number} rowIndex The selected index
21689 * @param {Boolean} keepExisting False if other selections will be cleared
21691 "beforerowselect" : true,
21694 * Fires when a row is selected.
21695 * @param {SelectionModel} this
21696 * @param {Number} rowIndex The selected index
21697 * @param {Roo.data.Record} r The record
21699 "rowselect" : true,
21701 * @event rowdeselect
21702 * Fires when a row is deselected.
21703 * @param {SelectionModel} this
21704 * @param {Number} rowIndex The selected index
21706 "rowdeselect" : true
21708 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21709 this.locked = false;
21712 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21714 * @cfg {Boolean} singleSelect
21715 * True to allow selection of only one row at a time (defaults to false)
21717 singleSelect : false,
21720 initEvents : function(){
21722 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21723 this.grid.on("mousedown", this.handleMouseDown, this);
21724 }else{ // allow click to work like normal
21725 this.grid.on("rowclick", this.handleDragableRowClick, this);
21728 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21729 "up" : function(e){
21731 this.selectPrevious(e.shiftKey);
21732 }else if(this.last !== false && this.lastActive !== false){
21733 var last = this.last;
21734 this.selectRange(this.last, this.lastActive-1);
21735 this.grid.getView().focusRow(this.lastActive);
21736 if(last !== false){
21740 this.selectFirstRow();
21742 this.fireEvent("afterselectionchange", this);
21744 "down" : function(e){
21746 this.selectNext(e.shiftKey);
21747 }else if(this.last !== false && this.lastActive !== false){
21748 var last = this.last;
21749 this.selectRange(this.last, this.lastActive+1);
21750 this.grid.getView().focusRow(this.lastActive);
21751 if(last !== false){
21755 this.selectFirstRow();
21757 this.fireEvent("afterselectionchange", this);
21762 var view = this.grid.view;
21763 view.on("refresh", this.onRefresh, this);
21764 view.on("rowupdated", this.onRowUpdated, this);
21765 view.on("rowremoved", this.onRemove, this);
21769 onRefresh : function(){
21770 var ds = this.grid.dataSource, i, v = this.grid.view;
21771 var s = this.selections;
21772 s.each(function(r){
21773 if((i = ds.indexOfId(r.id)) != -1){
21782 onRemove : function(v, index, r){
21783 this.selections.remove(r);
21787 onRowUpdated : function(v, index, r){
21788 if(this.isSelected(r)){
21789 v.onRowSelect(index);
21795 * @param {Array} records The records to select
21796 * @param {Boolean} keepExisting (optional) True to keep existing selections
21798 selectRecords : function(records, keepExisting){
21800 this.clearSelections();
21802 var ds = this.grid.dataSource;
21803 for(var i = 0, len = records.length; i < len; i++){
21804 this.selectRow(ds.indexOf(records[i]), true);
21809 * Gets the number of selected rows.
21812 getCount : function(){
21813 return this.selections.length;
21817 * Selects the first row in the grid.
21819 selectFirstRow : function(){
21824 * Select the last row.
21825 * @param {Boolean} keepExisting (optional) True to keep existing selections
21827 selectLastRow : function(keepExisting){
21828 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21832 * Selects the row immediately following the last selected row.
21833 * @param {Boolean} keepExisting (optional) True to keep existing selections
21835 selectNext : function(keepExisting){
21836 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21837 this.selectRow(this.last+1, keepExisting);
21838 this.grid.getView().focusRow(this.last);
21843 * Selects the row that precedes the last selected row.
21844 * @param {Boolean} keepExisting (optional) True to keep existing selections
21846 selectPrevious : function(keepExisting){
21848 this.selectRow(this.last-1, keepExisting);
21849 this.grid.getView().focusRow(this.last);
21854 * Returns the selected records
21855 * @return {Array} Array of selected records
21857 getSelections : function(){
21858 return [].concat(this.selections.items);
21862 * Returns the first selected record.
21865 getSelected : function(){
21866 return this.selections.itemAt(0);
21871 * Clears all selections.
21873 clearSelections : function(fast){
21878 var ds = this.grid.dataSource;
21879 var s = this.selections;
21880 s.each(function(r){
21881 this.deselectRow(ds.indexOfId(r.id));
21885 this.selections.clear();
21892 * Selects all rows.
21894 selectAll : function(){
21898 this.selections.clear();
21899 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21900 this.selectRow(i, true);
21905 * Returns True if there is a selection.
21906 * @return {Boolean}
21908 hasSelection : function(){
21909 return this.selections.length > 0;
21913 * Returns True if the specified row is selected.
21914 * @param {Number/Record} record The record or index of the record to check
21915 * @return {Boolean}
21917 isSelected : function(index){
21918 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21919 return (r && this.selections.key(r.id) ? true : false);
21923 * Returns True if the specified record id is selected.
21924 * @param {String} id The id of record to check
21925 * @return {Boolean}
21927 isIdSelected : function(id){
21928 return (this.selections.key(id) ? true : false);
21932 handleMouseDown : function(e, t){
21933 var view = this.grid.getView(), rowIndex;
21934 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21937 if(e.shiftKey && this.last !== false){
21938 var last = this.last;
21939 this.selectRange(last, rowIndex, e.ctrlKey);
21940 this.last = last; // reset the last
21941 view.focusRow(rowIndex);
21943 var isSelected = this.isSelected(rowIndex);
21944 if(e.button !== 0 && isSelected){
21945 view.focusRow(rowIndex);
21946 }else if(e.ctrlKey && isSelected){
21947 this.deselectRow(rowIndex);
21948 }else if(!isSelected){
21949 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21950 view.focusRow(rowIndex);
21953 this.fireEvent("afterselectionchange", this);
21956 handleDragableRowClick : function(grid, rowIndex, e)
21958 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21959 this.selectRow(rowIndex, false);
21960 grid.view.focusRow(rowIndex);
21961 this.fireEvent("afterselectionchange", this);
21966 * Selects multiple rows.
21967 * @param {Array} rows Array of the indexes of the row to select
21968 * @param {Boolean} keepExisting (optional) True to keep existing selections
21970 selectRows : function(rows, keepExisting){
21972 this.clearSelections();
21974 for(var i = 0, len = rows.length; i < len; i++){
21975 this.selectRow(rows[i], true);
21980 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21981 * @param {Number} startRow The index of the first row in the range
21982 * @param {Number} endRow The index of the last row in the range
21983 * @param {Boolean} keepExisting (optional) True to retain existing selections
21985 selectRange : function(startRow, endRow, keepExisting){
21990 this.clearSelections();
21992 if(startRow <= endRow){
21993 for(var i = startRow; i <= endRow; i++){
21994 this.selectRow(i, true);
21997 for(var i = startRow; i >= endRow; i--){
21998 this.selectRow(i, true);
22004 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22005 * @param {Number} startRow The index of the first row in the range
22006 * @param {Number} endRow The index of the last row in the range
22008 deselectRange : function(startRow, endRow, preventViewNotify){
22012 for(var i = startRow; i <= endRow; i++){
22013 this.deselectRow(i, preventViewNotify);
22019 * @param {Number} row The index of the row to select
22020 * @param {Boolean} keepExisting (optional) True to keep existing selections
22022 selectRow : function(index, keepExisting, preventViewNotify){
22023 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22026 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22027 if(!keepExisting || this.singleSelect){
22028 this.clearSelections();
22030 var r = this.grid.dataSource.getAt(index);
22031 this.selections.add(r);
22032 this.last = this.lastActive = index;
22033 if(!preventViewNotify){
22034 this.grid.getView().onRowSelect(index);
22036 this.fireEvent("rowselect", this, index, r);
22037 this.fireEvent("selectionchange", this);
22043 * @param {Number} row The index of the row to deselect
22045 deselectRow : function(index, preventViewNotify){
22049 if(this.last == index){
22052 if(this.lastActive == index){
22053 this.lastActive = false;
22055 var r = this.grid.dataSource.getAt(index);
22056 this.selections.remove(r);
22057 if(!preventViewNotify){
22058 this.grid.getView().onRowDeselect(index);
22060 this.fireEvent("rowdeselect", this, index);
22061 this.fireEvent("selectionchange", this);
22065 restoreLast : function(){
22067 this.last = this._last;
22072 acceptsNav : function(row, col, cm){
22073 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22077 onEditorKey : function(field, e){
22078 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22083 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22085 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22087 }else if(k == e.ENTER && !e.ctrlKey){
22091 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22093 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22095 }else if(k == e.ESC){
22099 g.startEditing(newCell[0], newCell[1]);
22104 * Ext JS Library 1.1.1
22105 * Copyright(c) 2006-2007, Ext JS, LLC.
22107 * Originally Released Under LGPL - original licence link has changed is not relivant.
22110 * <script type="text/javascript">
22114 * @class Roo.bootstrap.PagingToolbar
22115 * @extends Roo.bootstrap.NavSimplebar
22116 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22118 * Create a new PagingToolbar
22119 * @param {Object} config The config object
22120 * @param {Roo.data.Store} store
22122 Roo.bootstrap.PagingToolbar = function(config)
22124 // old args format still supported... - xtype is prefered..
22125 // created from xtype...
22127 this.ds = config.dataSource;
22129 if (config.store && !this.ds) {
22130 this.store= Roo.factory(config.store, Roo.data);
22131 this.ds = this.store;
22132 this.ds.xmodule = this.xmodule || false;
22135 this.toolbarItems = [];
22136 if (config.items) {
22137 this.toolbarItems = config.items;
22140 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22145 this.bind(this.ds);
22148 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22152 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22154 * @cfg {Roo.data.Store} dataSource
22155 * The underlying data store providing the paged data
22158 * @cfg {String/HTMLElement/Element} container
22159 * container The id or element that will contain the toolbar
22162 * @cfg {Boolean} displayInfo
22163 * True to display the displayMsg (defaults to false)
22166 * @cfg {Number} pageSize
22167 * The number of records to display per page (defaults to 20)
22171 * @cfg {String} displayMsg
22172 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22174 displayMsg : 'Displaying {0} - {1} of {2}',
22176 * @cfg {String} emptyMsg
22177 * The message to display when no records are found (defaults to "No data to display")
22179 emptyMsg : 'No data to display',
22181 * Customizable piece of the default paging text (defaults to "Page")
22184 beforePageText : "Page",
22186 * Customizable piece of the default paging text (defaults to "of %0")
22189 afterPageText : "of {0}",
22191 * Customizable piece of the default paging text (defaults to "First Page")
22194 firstText : "First Page",
22196 * Customizable piece of the default paging text (defaults to "Previous Page")
22199 prevText : "Previous Page",
22201 * Customizable piece of the default paging text (defaults to "Next Page")
22204 nextText : "Next Page",
22206 * Customizable piece of the default paging text (defaults to "Last Page")
22209 lastText : "Last Page",
22211 * Customizable piece of the default paging text (defaults to "Refresh")
22214 refreshText : "Refresh",
22218 onRender : function(ct, position)
22220 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22221 this.navgroup.parentId = this.id;
22222 this.navgroup.onRender(this.el, null);
22223 // add the buttons to the navgroup
22225 if(this.displayInfo){
22226 Roo.log(this.el.select('ul.navbar-nav',true).first());
22227 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22228 this.displayEl = this.el.select('.x-paging-info', true).first();
22229 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22230 // this.displayEl = navel.el.select('span',true).first();
22236 Roo.each(_this.buttons, function(e){ // this might need to use render????
22237 Roo.factory(e).onRender(_this.el, null);
22241 Roo.each(_this.toolbarItems, function(e) {
22242 _this.navgroup.addItem(e);
22246 this.first = this.navgroup.addItem({
22247 tooltip: this.firstText,
22249 icon : 'fa fa-backward',
22251 preventDefault: true,
22252 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22255 this.prev = this.navgroup.addItem({
22256 tooltip: this.prevText,
22258 icon : 'fa fa-step-backward',
22260 preventDefault: true,
22261 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22263 //this.addSeparator();
22266 var field = this.navgroup.addItem( {
22268 cls : 'x-paging-position',
22270 html : this.beforePageText +
22271 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22272 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22275 this.field = field.el.select('input', true).first();
22276 this.field.on("keydown", this.onPagingKeydown, this);
22277 this.field.on("focus", function(){this.dom.select();});
22280 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22281 //this.field.setHeight(18);
22282 //this.addSeparator();
22283 this.next = this.navgroup.addItem({
22284 tooltip: this.nextText,
22286 html : ' <i class="fa fa-step-forward">',
22288 preventDefault: true,
22289 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22291 this.last = this.navgroup.addItem({
22292 tooltip: this.lastText,
22293 icon : 'fa fa-forward',
22296 preventDefault: true,
22297 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22299 //this.addSeparator();
22300 this.loading = this.navgroup.addItem({
22301 tooltip: this.refreshText,
22302 icon: 'fa fa-refresh',
22303 preventDefault: true,
22304 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22310 updateInfo : function(){
22311 if(this.displayEl){
22312 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22313 var msg = count == 0 ?
22317 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22319 this.displayEl.update(msg);
22324 onLoad : function(ds, r, o){
22325 this.cursor = o.params ? o.params.start : 0;
22326 var d = this.getPageData(),
22330 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22331 this.field.dom.value = ap;
22332 this.first.setDisabled(ap == 1);
22333 this.prev.setDisabled(ap == 1);
22334 this.next.setDisabled(ap == ps);
22335 this.last.setDisabled(ap == ps);
22336 this.loading.enable();
22341 getPageData : function(){
22342 var total = this.ds.getTotalCount();
22345 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22346 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22351 onLoadError : function(){
22352 this.loading.enable();
22356 onPagingKeydown : function(e){
22357 var k = e.getKey();
22358 var d = this.getPageData();
22360 var v = this.field.dom.value, pageNum;
22361 if(!v || isNaN(pageNum = parseInt(v, 10))){
22362 this.field.dom.value = d.activePage;
22365 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22366 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22369 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))
22371 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22372 this.field.dom.value = pageNum;
22373 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22376 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22378 var v = this.field.dom.value, pageNum;
22379 var increment = (e.shiftKey) ? 10 : 1;
22380 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22383 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22384 this.field.dom.value = d.activePage;
22387 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22389 this.field.dom.value = parseInt(v, 10) + increment;
22390 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22391 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22398 beforeLoad : function(){
22400 this.loading.disable();
22405 onClick : function(which){
22414 ds.load({params:{start: 0, limit: this.pageSize}});
22417 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22420 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22423 var total = ds.getTotalCount();
22424 var extra = total % this.pageSize;
22425 var lastStart = extra ? (total - extra) : total-this.pageSize;
22426 ds.load({params:{start: lastStart, limit: this.pageSize}});
22429 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22435 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22436 * @param {Roo.data.Store} store The data store to unbind
22438 unbind : function(ds){
22439 ds.un("beforeload", this.beforeLoad, this);
22440 ds.un("load", this.onLoad, this);
22441 ds.un("loadexception", this.onLoadError, this);
22442 ds.un("remove", this.updateInfo, this);
22443 ds.un("add", this.updateInfo, this);
22444 this.ds = undefined;
22448 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22449 * @param {Roo.data.Store} store The data store to bind
22451 bind : function(ds){
22452 ds.on("beforeload", this.beforeLoad, this);
22453 ds.on("load", this.onLoad, this);
22454 ds.on("loadexception", this.onLoadError, this);
22455 ds.on("remove", this.updateInfo, this);
22456 ds.on("add", this.updateInfo, this);
22467 * @class Roo.bootstrap.MessageBar
22468 * @extends Roo.bootstrap.Component
22469 * Bootstrap MessageBar class
22470 * @cfg {String} html contents of the MessageBar
22471 * @cfg {String} weight (info | success | warning | danger) default info
22472 * @cfg {String} beforeClass insert the bar before the given class
22473 * @cfg {Boolean} closable (true | false) default false
22474 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22477 * Create a new Element
22478 * @param {Object} config The config object
22481 Roo.bootstrap.MessageBar = function(config){
22482 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22485 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22491 beforeClass: 'bootstrap-sticky-wrap',
22493 getAutoCreate : function(){
22497 cls: 'alert alert-dismissable alert-' + this.weight,
22502 html: this.html || ''
22508 cfg.cls += ' alert-messages-fixed';
22522 onRender : function(ct, position)
22524 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22527 var cfg = Roo.apply({}, this.getAutoCreate());
22531 cfg.cls += ' ' + this.cls;
22534 cfg.style = this.style;
22536 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22538 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22541 this.el.select('>button.close').on('click', this.hide, this);
22547 if (!this.rendered) {
22553 this.fireEvent('show', this);
22559 if (!this.rendered) {
22565 this.fireEvent('hide', this);
22568 update : function()
22570 // var e = this.el.dom.firstChild;
22572 // if(this.closable){
22573 // e = e.nextSibling;
22576 // e.data = this.html || '';
22578 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22594 * @class Roo.bootstrap.Graph
22595 * @extends Roo.bootstrap.Component
22596 * Bootstrap Graph class
22600 @cfg {String} graphtype bar | vbar | pie
22601 @cfg {number} g_x coodinator | centre x (pie)
22602 @cfg {number} g_y coodinator | centre y (pie)
22603 @cfg {number} g_r radius (pie)
22604 @cfg {number} g_height height of the chart (respected by all elements in the set)
22605 @cfg {number} g_width width of the chart (respected by all elements in the set)
22606 @cfg {Object} title The title of the chart
22609 -opts (object) options for the chart
22611 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22612 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22614 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.
22615 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22617 o stretch (boolean)
22619 -opts (object) options for the pie
22622 o startAngle (number)
22623 o endAngle (number)
22627 * Create a new Input
22628 * @param {Object} config The config object
22631 Roo.bootstrap.Graph = function(config){
22632 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22638 * The img click event for the img.
22639 * @param {Roo.EventObject} e
22645 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22656 //g_colors: this.colors,
22663 getAutoCreate : function(){
22674 onRender : function(ct,position){
22675 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22676 this.raphael = Raphael(this.el.dom);
22678 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22679 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22680 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22681 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22683 r.text(160, 10, "Single Series Chart").attr(txtattr);
22684 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22685 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22686 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22688 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22689 r.barchart(330, 10, 300, 220, data1);
22690 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22691 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22694 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22695 // r.barchart(30, 30, 560, 250, xdata, {
22696 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22697 // axis : "0 0 1 1",
22698 // axisxlabels : xdata
22699 // //yvalues : cols,
22702 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22704 // this.load(null,xdata,{
22705 // axis : "0 0 1 1",
22706 // axisxlabels : xdata
22711 load : function(graphtype,xdata,opts){
22712 this.raphael.clear();
22714 graphtype = this.graphtype;
22719 var r = this.raphael,
22720 fin = function () {
22721 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22723 fout = function () {
22724 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22726 pfin = function() {
22727 this.sector.stop();
22728 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22731 this.label[0].stop();
22732 this.label[0].attr({ r: 7.5 });
22733 this.label[1].attr({ "font-weight": 800 });
22736 pfout = function() {
22737 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22740 this.label[0].animate({ r: 5 }, 500, "bounce");
22741 this.label[1].attr({ "font-weight": 400 });
22747 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22750 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22753 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22754 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22756 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22763 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22768 setTitle: function(o)
22773 initEvents: function() {
22776 this.el.on('click', this.onClick, this);
22780 onClick : function(e)
22782 Roo.log('img onclick');
22783 this.fireEvent('click', this, e);
22795 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22798 * @class Roo.bootstrap.dash.NumberBox
22799 * @extends Roo.bootstrap.Component
22800 * Bootstrap NumberBox class
22801 * @cfg {String} headline Box headline
22802 * @cfg {String} content Box content
22803 * @cfg {String} icon Box icon
22804 * @cfg {String} footer Footer text
22805 * @cfg {String} fhref Footer href
22808 * Create a new NumberBox
22809 * @param {Object} config The config object
22813 Roo.bootstrap.dash.NumberBox = function(config){
22814 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22818 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22827 getAutoCreate : function(){
22831 cls : 'small-box ',
22839 cls : 'roo-headline',
22840 html : this.headline
22844 cls : 'roo-content',
22845 html : this.content
22859 cls : 'ion ' + this.icon
22868 cls : 'small-box-footer',
22869 href : this.fhref || '#',
22873 cfg.cn.push(footer);
22880 onRender : function(ct,position){
22881 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22888 setHeadline: function (value)
22890 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22893 setFooter: function (value, href)
22895 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22898 this.el.select('a.small-box-footer',true).first().attr('href', href);
22903 setContent: function (value)
22905 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22908 initEvents: function()
22922 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22925 * @class Roo.bootstrap.dash.TabBox
22926 * @extends Roo.bootstrap.Component
22927 * Bootstrap TabBox class
22928 * @cfg {String} title Title of the TabBox
22929 * @cfg {String} icon Icon of the TabBox
22930 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22931 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22934 * Create a new TabBox
22935 * @param {Object} config The config object
22939 Roo.bootstrap.dash.TabBox = function(config){
22940 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22945 * When a pane is added
22946 * @param {Roo.bootstrap.dash.TabPane} pane
22950 * @event activatepane
22951 * When a pane is activated
22952 * @param {Roo.bootstrap.dash.TabPane} pane
22954 "activatepane" : true
22962 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22967 tabScrollable : false,
22969 getChildContainer : function()
22971 return this.el.select('.tab-content', true).first();
22974 getAutoCreate : function(){
22978 cls: 'pull-left header',
22986 cls: 'fa ' + this.icon
22992 cls: 'nav nav-tabs pull-right',
22998 if(this.tabScrollable){
23005 cls: 'nav nav-tabs pull-right',
23016 cls: 'nav-tabs-custom',
23021 cls: 'tab-content no-padding',
23029 initEvents : function()
23031 //Roo.log('add add pane handler');
23032 this.on('addpane', this.onAddPane, this);
23035 * Updates the box title
23036 * @param {String} html to set the title to.
23038 setTitle : function(value)
23040 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23042 onAddPane : function(pane)
23044 this.panes.push(pane);
23045 //Roo.log('addpane');
23047 // tabs are rendere left to right..
23048 if(!this.showtabs){
23052 var ctr = this.el.select('.nav-tabs', true).first();
23055 var existing = ctr.select('.nav-tab',true);
23056 var qty = existing.getCount();;
23059 var tab = ctr.createChild({
23061 cls : 'nav-tab' + (qty ? '' : ' active'),
23069 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23072 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23074 pane.el.addClass('active');
23079 onTabClick : function(ev,un,ob,pane)
23081 //Roo.log('tab - prev default');
23082 ev.preventDefault();
23085 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23086 pane.tab.addClass('active');
23087 //Roo.log(pane.title);
23088 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23089 // technically we should have a deactivate event.. but maybe add later.
23090 // and it should not de-activate the selected tab...
23091 this.fireEvent('activatepane', pane);
23092 pane.el.addClass('active');
23093 pane.fireEvent('activate');
23098 getActivePane : function()
23101 Roo.each(this.panes, function(p) {
23102 if(p.el.hasClass('active')){
23123 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23125 * @class Roo.bootstrap.TabPane
23126 * @extends Roo.bootstrap.Component
23127 * Bootstrap TabPane class
23128 * @cfg {Boolean} active (false | true) Default false
23129 * @cfg {String} title title of panel
23133 * Create a new TabPane
23134 * @param {Object} config The config object
23137 Roo.bootstrap.dash.TabPane = function(config){
23138 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23144 * When a pane is activated
23145 * @param {Roo.bootstrap.dash.TabPane} pane
23152 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23157 // the tabBox that this is attached to.
23160 getAutoCreate : function()
23168 cfg.cls += ' active';
23173 initEvents : function()
23175 //Roo.log('trigger add pane handler');
23176 this.parent().fireEvent('addpane', this)
23180 * Updates the tab title
23181 * @param {String} html to set the title to.
23183 setTitle: function(str)
23189 this.tab.select('a', true).first().dom.innerHTML = str;
23206 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23209 * @class Roo.bootstrap.menu.Menu
23210 * @extends Roo.bootstrap.Component
23211 * Bootstrap Menu class - container for Menu
23212 * @cfg {String} html Text of the menu
23213 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23214 * @cfg {String} icon Font awesome icon
23215 * @cfg {String} pos Menu align to (top | bottom) default bottom
23219 * Create a new Menu
23220 * @param {Object} config The config object
23224 Roo.bootstrap.menu.Menu = function(config){
23225 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23229 * @event beforeshow
23230 * Fires before this menu is displayed
23231 * @param {Roo.bootstrap.menu.Menu} this
23235 * @event beforehide
23236 * Fires before this menu is hidden
23237 * @param {Roo.bootstrap.menu.Menu} this
23242 * Fires after this menu is displayed
23243 * @param {Roo.bootstrap.menu.Menu} this
23248 * Fires after this menu is hidden
23249 * @param {Roo.bootstrap.menu.Menu} this
23254 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23255 * @param {Roo.bootstrap.menu.Menu} this
23256 * @param {Roo.EventObject} e
23263 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23267 weight : 'default',
23272 getChildContainer : function() {
23273 if(this.isSubMenu){
23277 return this.el.select('ul.dropdown-menu', true).first();
23280 getAutoCreate : function()
23285 cls : 'roo-menu-text',
23293 cls : 'fa ' + this.icon
23304 cls : 'dropdown-button btn btn-' + this.weight,
23309 cls : 'dropdown-toggle btn btn-' + this.weight,
23319 cls : 'dropdown-menu'
23325 if(this.pos == 'top'){
23326 cfg.cls += ' dropup';
23329 if(this.isSubMenu){
23332 cls : 'dropdown-menu'
23339 onRender : function(ct, position)
23341 this.isSubMenu = ct.hasClass('dropdown-submenu');
23343 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23346 initEvents : function()
23348 if(this.isSubMenu){
23352 this.hidden = true;
23354 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23355 this.triggerEl.on('click', this.onTriggerPress, this);
23357 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23358 this.buttonEl.on('click', this.onClick, this);
23364 if(this.isSubMenu){
23368 return this.el.select('ul.dropdown-menu', true).first();
23371 onClick : function(e)
23373 this.fireEvent("click", this, e);
23376 onTriggerPress : function(e)
23378 if (this.isVisible()) {
23385 isVisible : function(){
23386 return !this.hidden;
23391 this.fireEvent("beforeshow", this);
23393 this.hidden = false;
23394 this.el.addClass('open');
23396 Roo.get(document).on("mouseup", this.onMouseUp, this);
23398 this.fireEvent("show", this);
23405 this.fireEvent("beforehide", this);
23407 this.hidden = true;
23408 this.el.removeClass('open');
23410 Roo.get(document).un("mouseup", this.onMouseUp);
23412 this.fireEvent("hide", this);
23415 onMouseUp : function()
23429 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23432 * @class Roo.bootstrap.menu.Item
23433 * @extends Roo.bootstrap.Component
23434 * Bootstrap MenuItem class
23435 * @cfg {Boolean} submenu (true | false) default false
23436 * @cfg {String} html text of the item
23437 * @cfg {String} href the link
23438 * @cfg {Boolean} disable (true | false) default false
23439 * @cfg {Boolean} preventDefault (true | false) default true
23440 * @cfg {String} icon Font awesome icon
23441 * @cfg {String} pos Submenu align to (left | right) default right
23445 * Create a new Item
23446 * @param {Object} config The config object
23450 Roo.bootstrap.menu.Item = function(config){
23451 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23455 * Fires when the mouse is hovering over this menu
23456 * @param {Roo.bootstrap.menu.Item} this
23457 * @param {Roo.EventObject} e
23462 * Fires when the mouse exits this menu
23463 * @param {Roo.bootstrap.menu.Item} this
23464 * @param {Roo.EventObject} e
23470 * The raw click event for the entire grid.
23471 * @param {Roo.EventObject} e
23477 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23482 preventDefault: true,
23487 getAutoCreate : function()
23492 cls : 'roo-menu-item-text',
23500 cls : 'fa ' + this.icon
23509 href : this.href || '#',
23516 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23520 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23522 if(this.pos == 'left'){
23523 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23530 initEvents : function()
23532 this.el.on('mouseover', this.onMouseOver, this);
23533 this.el.on('mouseout', this.onMouseOut, this);
23535 this.el.select('a', true).first().on('click', this.onClick, this);
23539 onClick : function(e)
23541 if(this.preventDefault){
23542 e.preventDefault();
23545 this.fireEvent("click", this, e);
23548 onMouseOver : function(e)
23550 if(this.submenu && this.pos == 'left'){
23551 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23554 this.fireEvent("mouseover", this, e);
23557 onMouseOut : function(e)
23559 this.fireEvent("mouseout", this, e);
23571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23574 * @class Roo.bootstrap.menu.Separator
23575 * @extends Roo.bootstrap.Component
23576 * Bootstrap Separator class
23579 * Create a new Separator
23580 * @param {Object} config The config object
23584 Roo.bootstrap.menu.Separator = function(config){
23585 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23588 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23590 getAutoCreate : function(){
23611 * @class Roo.bootstrap.Tooltip
23612 * Bootstrap Tooltip class
23613 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23614 * to determine which dom element triggers the tooltip.
23616 * It needs to add support for additional attributes like tooltip-position
23619 * Create a new Toolti
23620 * @param {Object} config The config object
23623 Roo.bootstrap.Tooltip = function(config){
23624 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23627 Roo.apply(Roo.bootstrap.Tooltip, {
23629 * @function init initialize tooltip monitoring.
23633 currentTip : false,
23634 currentRegion : false,
23640 Roo.get(document).on('mouseover', this.enter ,this);
23641 Roo.get(document).on('mouseout', this.leave, this);
23644 this.currentTip = new Roo.bootstrap.Tooltip();
23647 enter : function(ev)
23649 var dom = ev.getTarget();
23651 //Roo.log(['enter',dom]);
23652 var el = Roo.fly(dom);
23653 if (this.currentEl) {
23655 //Roo.log(this.currentEl);
23656 //Roo.log(this.currentEl.contains(dom));
23657 if (this.currentEl == el) {
23660 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23666 if (this.currentTip.el) {
23667 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23672 // you can not look for children, as if el is the body.. then everythign is the child..
23673 if (!el.attr('tooltip')) { //
23674 if (!el.select("[tooltip]").elements.length) {
23677 // is the mouse over this child...?
23678 bindEl = el.select("[tooltip]").first();
23679 var xy = ev.getXY();
23680 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23681 //Roo.log("not in region.");
23684 //Roo.log("child element over..");
23687 this.currentEl = bindEl;
23688 this.currentTip.bind(bindEl);
23689 this.currentRegion = Roo.lib.Region.getRegion(dom);
23690 this.currentTip.enter();
23693 leave : function(ev)
23695 var dom = ev.getTarget();
23696 //Roo.log(['leave',dom]);
23697 if (!this.currentEl) {
23702 if (dom != this.currentEl.dom) {
23705 var xy = ev.getXY();
23706 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23709 // only activate leave if mouse cursor is outside... bounding box..
23714 if (this.currentTip) {
23715 this.currentTip.leave();
23717 //Roo.log('clear currentEl');
23718 this.currentEl = false;
23723 'left' : ['r-l', [-2,0], 'right'],
23724 'right' : ['l-r', [2,0], 'left'],
23725 'bottom' : ['t-b', [0,2], 'top'],
23726 'top' : [ 'b-t', [0,-2], 'bottom']
23732 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23737 delay : null, // can be { show : 300 , hide: 500}
23741 hoverState : null, //???
23743 placement : 'bottom',
23745 getAutoCreate : function(){
23752 cls : 'tooltip-arrow'
23755 cls : 'tooltip-inner'
23762 bind : function(el)
23768 enter : function () {
23770 if (this.timeout != null) {
23771 clearTimeout(this.timeout);
23774 this.hoverState = 'in';
23775 //Roo.log("enter - show");
23776 if (!this.delay || !this.delay.show) {
23781 this.timeout = setTimeout(function () {
23782 if (_t.hoverState == 'in') {
23785 }, this.delay.show);
23789 clearTimeout(this.timeout);
23791 this.hoverState = 'out';
23792 if (!this.delay || !this.delay.hide) {
23798 this.timeout = setTimeout(function () {
23799 //Roo.log("leave - timeout");
23801 if (_t.hoverState == 'out') {
23803 Roo.bootstrap.Tooltip.currentEl = false;
23811 this.render(document.body);
23814 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23816 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23818 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23820 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23822 var placement = typeof this.placement == 'function' ?
23823 this.placement.call(this, this.el, on_el) :
23826 var autoToken = /\s?auto?\s?/i;
23827 var autoPlace = autoToken.test(placement);
23829 placement = placement.replace(autoToken, '') || 'top';
23833 //this.el.setXY([0,0]);
23835 //this.el.dom.style.display='block';
23837 //this.el.appendTo(on_el);
23839 var p = this.getPosition();
23840 var box = this.el.getBox();
23846 var align = Roo.bootstrap.Tooltip.alignment[placement];
23848 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23850 if(placement == 'top' || placement == 'bottom'){
23852 placement = 'right';
23855 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23856 placement = 'left';
23860 align = Roo.bootstrap.Tooltip.alignment[placement];
23862 this.el.alignTo(this.bindEl, align[0],align[1]);
23863 //var arrow = this.el.select('.arrow',true).first();
23864 //arrow.set(align[2],
23866 this.el.addClass(placement);
23868 this.el.addClass('in fade');
23870 this.hoverState = null;
23872 if (this.el.hasClass('fade')) {
23883 //this.el.setXY([0,0]);
23884 this.el.removeClass('in');
23900 * @class Roo.bootstrap.LocationPicker
23901 * @extends Roo.bootstrap.Component
23902 * Bootstrap LocationPicker class
23903 * @cfg {Number} latitude Position when init default 0
23904 * @cfg {Number} longitude Position when init default 0
23905 * @cfg {Number} zoom default 15
23906 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23907 * @cfg {Boolean} mapTypeControl default false
23908 * @cfg {Boolean} disableDoubleClickZoom default false
23909 * @cfg {Boolean} scrollwheel default true
23910 * @cfg {Boolean} streetViewControl default false
23911 * @cfg {Number} radius default 0
23912 * @cfg {String} locationName
23913 * @cfg {Boolean} draggable default true
23914 * @cfg {Boolean} enableAutocomplete default false
23915 * @cfg {Boolean} enableReverseGeocode default true
23916 * @cfg {String} markerTitle
23919 * Create a new LocationPicker
23920 * @param {Object} config The config object
23924 Roo.bootstrap.LocationPicker = function(config){
23926 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23931 * Fires when the picker initialized.
23932 * @param {Roo.bootstrap.LocationPicker} this
23933 * @param {Google Location} location
23937 * @event positionchanged
23938 * Fires when the picker position changed.
23939 * @param {Roo.bootstrap.LocationPicker} this
23940 * @param {Google Location} location
23942 positionchanged : true,
23945 * Fires when the map resize.
23946 * @param {Roo.bootstrap.LocationPicker} this
23951 * Fires when the map show.
23952 * @param {Roo.bootstrap.LocationPicker} this
23957 * Fires when the map hide.
23958 * @param {Roo.bootstrap.LocationPicker} this
23963 * Fires when click the map.
23964 * @param {Roo.bootstrap.LocationPicker} this
23965 * @param {Map event} e
23969 * @event mapRightClick
23970 * Fires when right click the map.
23971 * @param {Roo.bootstrap.LocationPicker} this
23972 * @param {Map event} e
23974 mapRightClick : true,
23976 * @event markerClick
23977 * Fires when click the marker.
23978 * @param {Roo.bootstrap.LocationPicker} this
23979 * @param {Map event} e
23981 markerClick : true,
23983 * @event markerRightClick
23984 * Fires when right click the marker.
23985 * @param {Roo.bootstrap.LocationPicker} this
23986 * @param {Map event} e
23988 markerRightClick : true,
23990 * @event OverlayViewDraw
23991 * Fires when OverlayView Draw
23992 * @param {Roo.bootstrap.LocationPicker} this
23994 OverlayViewDraw : true,
23996 * @event OverlayViewOnAdd
23997 * Fires when OverlayView Draw
23998 * @param {Roo.bootstrap.LocationPicker} this
24000 OverlayViewOnAdd : true,
24002 * @event OverlayViewOnRemove
24003 * Fires when OverlayView Draw
24004 * @param {Roo.bootstrap.LocationPicker} this
24006 OverlayViewOnRemove : true,
24008 * @event OverlayViewShow
24009 * Fires when OverlayView Draw
24010 * @param {Roo.bootstrap.LocationPicker} this
24011 * @param {Pixel} cpx
24013 OverlayViewShow : true,
24015 * @event OverlayViewHide
24016 * Fires when OverlayView Draw
24017 * @param {Roo.bootstrap.LocationPicker} this
24019 OverlayViewHide : true,
24021 * @event loadexception
24022 * Fires when load google lib failed.
24023 * @param {Roo.bootstrap.LocationPicker} this
24025 loadexception : true
24030 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24032 gMapContext: false,
24038 mapTypeControl: false,
24039 disableDoubleClickZoom: false,
24041 streetViewControl: false,
24045 enableAutocomplete: false,
24046 enableReverseGeocode: true,
24049 getAutoCreate: function()
24054 cls: 'roo-location-picker'
24060 initEvents: function(ct, position)
24062 if(!this.el.getWidth() || this.isApplied()){
24066 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24071 initial: function()
24073 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24074 this.fireEvent('loadexception', this);
24078 if(!this.mapTypeId){
24079 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24082 this.gMapContext = this.GMapContext();
24084 this.initOverlayView();
24086 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24090 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24091 _this.setPosition(_this.gMapContext.marker.position);
24094 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24095 _this.fireEvent('mapClick', this, event);
24099 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24100 _this.fireEvent('mapRightClick', this, event);
24104 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24105 _this.fireEvent('markerClick', this, event);
24109 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24110 _this.fireEvent('markerRightClick', this, event);
24114 this.setPosition(this.gMapContext.location);
24116 this.fireEvent('initial', this, this.gMapContext.location);
24119 initOverlayView: function()
24123 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24127 _this.fireEvent('OverlayViewDraw', _this);
24132 _this.fireEvent('OverlayViewOnAdd', _this);
24135 onRemove: function()
24137 _this.fireEvent('OverlayViewOnRemove', _this);
24140 show: function(cpx)
24142 _this.fireEvent('OverlayViewShow', _this, cpx);
24147 _this.fireEvent('OverlayViewHide', _this);
24153 fromLatLngToContainerPixel: function(event)
24155 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24158 isApplied: function()
24160 return this.getGmapContext() == false ? false : true;
24163 getGmapContext: function()
24165 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24168 GMapContext: function()
24170 var position = new google.maps.LatLng(this.latitude, this.longitude);
24172 var _map = new google.maps.Map(this.el.dom, {
24175 mapTypeId: this.mapTypeId,
24176 mapTypeControl: this.mapTypeControl,
24177 disableDoubleClickZoom: this.disableDoubleClickZoom,
24178 scrollwheel: this.scrollwheel,
24179 streetViewControl: this.streetViewControl,
24180 locationName: this.locationName,
24181 draggable: this.draggable,
24182 enableAutocomplete: this.enableAutocomplete,
24183 enableReverseGeocode: this.enableReverseGeocode
24186 var _marker = new google.maps.Marker({
24187 position: position,
24189 title: this.markerTitle,
24190 draggable: this.draggable
24197 location: position,
24198 radius: this.radius,
24199 locationName: this.locationName,
24200 addressComponents: {
24201 formatted_address: null,
24202 addressLine1: null,
24203 addressLine2: null,
24205 streetNumber: null,
24209 stateOrProvince: null
24212 domContainer: this.el.dom,
24213 geodecoder: new google.maps.Geocoder()
24217 drawCircle: function(center, radius, options)
24219 if (this.gMapContext.circle != null) {
24220 this.gMapContext.circle.setMap(null);
24224 options = Roo.apply({}, options, {
24225 strokeColor: "#0000FF",
24226 strokeOpacity: .35,
24228 fillColor: "#0000FF",
24232 options.map = this.gMapContext.map;
24233 options.radius = radius;
24234 options.center = center;
24235 this.gMapContext.circle = new google.maps.Circle(options);
24236 return this.gMapContext.circle;
24242 setPosition: function(location)
24244 this.gMapContext.location = location;
24245 this.gMapContext.marker.setPosition(location);
24246 this.gMapContext.map.panTo(location);
24247 this.drawCircle(location, this.gMapContext.radius, {});
24251 if (this.gMapContext.settings.enableReverseGeocode) {
24252 this.gMapContext.geodecoder.geocode({
24253 latLng: this.gMapContext.location
24254 }, function(results, status) {
24256 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24257 _this.gMapContext.locationName = results[0].formatted_address;
24258 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24260 _this.fireEvent('positionchanged', this, location);
24267 this.fireEvent('positionchanged', this, location);
24272 google.maps.event.trigger(this.gMapContext.map, "resize");
24274 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24276 this.fireEvent('resize', this);
24279 setPositionByLatLng: function(latitude, longitude)
24281 this.setPosition(new google.maps.LatLng(latitude, longitude));
24284 getCurrentPosition: function()
24287 latitude: this.gMapContext.location.lat(),
24288 longitude: this.gMapContext.location.lng()
24292 getAddressName: function()
24294 return this.gMapContext.locationName;
24297 getAddressComponents: function()
24299 return this.gMapContext.addressComponents;
24302 address_component_from_google_geocode: function(address_components)
24306 for (var i = 0; i < address_components.length; i++) {
24307 var component = address_components[i];
24308 if (component.types.indexOf("postal_code") >= 0) {
24309 result.postalCode = component.short_name;
24310 } else if (component.types.indexOf("street_number") >= 0) {
24311 result.streetNumber = component.short_name;
24312 } else if (component.types.indexOf("route") >= 0) {
24313 result.streetName = component.short_name;
24314 } else if (component.types.indexOf("neighborhood") >= 0) {
24315 result.city = component.short_name;
24316 } else if (component.types.indexOf("locality") >= 0) {
24317 result.city = component.short_name;
24318 } else if (component.types.indexOf("sublocality") >= 0) {
24319 result.district = component.short_name;
24320 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24321 result.stateOrProvince = component.short_name;
24322 } else if (component.types.indexOf("country") >= 0) {
24323 result.country = component.short_name;
24327 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24328 result.addressLine2 = "";
24332 setZoomLevel: function(zoom)
24334 this.gMapContext.map.setZoom(zoom);
24347 this.fireEvent('show', this);
24358 this.fireEvent('hide', this);
24363 Roo.apply(Roo.bootstrap.LocationPicker, {
24365 OverlayView : function(map, options)
24367 options = options || {};
24381 * @class Roo.bootstrap.Alert
24382 * @extends Roo.bootstrap.Component
24383 * Bootstrap Alert class
24384 * @cfg {String} title The title of alert
24385 * @cfg {String} html The content of alert
24386 * @cfg {String} weight ( success | info | warning | danger )
24387 * @cfg {String} faicon font-awesomeicon
24390 * Create a new alert
24391 * @param {Object} config The config object
24395 Roo.bootstrap.Alert = function(config){
24396 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24400 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24407 getAutoCreate : function()
24416 cls : 'roo-alert-icon'
24421 cls : 'roo-alert-title',
24426 cls : 'roo-alert-text',
24433 cfg.cn[0].cls += ' fa ' + this.faicon;
24437 cfg.cls += ' alert-' + this.weight;
24443 initEvents: function()
24445 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24448 setTitle : function(str)
24450 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24453 setText : function(str)
24455 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24458 setWeight : function(weight)
24461 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24464 this.weight = weight;
24466 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24469 setIcon : function(icon)
24472 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24475 this.faicon = icon;
24477 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24498 * @class Roo.bootstrap.UploadCropbox
24499 * @extends Roo.bootstrap.Component
24500 * Bootstrap UploadCropbox class
24501 * @cfg {String} emptyText show when image has been loaded
24502 * @cfg {String} rotateNotify show when image too small to rotate
24503 * @cfg {Number} errorTimeout default 3000
24504 * @cfg {Number} minWidth default 300
24505 * @cfg {Number} minHeight default 300
24506 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24507 * @cfg {Boolean} isDocument (true|false) default false
24508 * @cfg {String} url action url
24509 * @cfg {String} paramName default 'imageUpload'
24510 * @cfg {String} method default POST
24511 * @cfg {Boolean} loadMask (true|false) default true
24512 * @cfg {Boolean} loadingText default 'Loading...'
24515 * Create a new UploadCropbox
24516 * @param {Object} config The config object
24519 Roo.bootstrap.UploadCropbox = function(config){
24520 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24524 * @event beforeselectfile
24525 * Fire before select file
24526 * @param {Roo.bootstrap.UploadCropbox} this
24528 "beforeselectfile" : true,
24531 * Fire after initEvent
24532 * @param {Roo.bootstrap.UploadCropbox} this
24537 * Fire after initEvent
24538 * @param {Roo.bootstrap.UploadCropbox} this
24539 * @param {String} data
24544 * Fire when preparing the file data
24545 * @param {Roo.bootstrap.UploadCropbox} this
24546 * @param {Object} file
24551 * Fire when get exception
24552 * @param {Roo.bootstrap.UploadCropbox} this
24553 * @param {XMLHttpRequest} xhr
24555 "exception" : true,
24557 * @event beforeloadcanvas
24558 * Fire before load the canvas
24559 * @param {Roo.bootstrap.UploadCropbox} this
24560 * @param {String} src
24562 "beforeloadcanvas" : true,
24565 * Fire when trash image
24566 * @param {Roo.bootstrap.UploadCropbox} this
24571 * Fire when download the image
24572 * @param {Roo.bootstrap.UploadCropbox} this
24576 * @event footerbuttonclick
24577 * Fire when footerbuttonclick
24578 * @param {Roo.bootstrap.UploadCropbox} this
24579 * @param {String} type
24581 "footerbuttonclick" : true,
24585 * @param {Roo.bootstrap.UploadCropbox} this
24590 * Fire when rotate the image
24591 * @param {Roo.bootstrap.UploadCropbox} this
24592 * @param {String} pos
24597 * Fire when inspect the file
24598 * @param {Roo.bootstrap.UploadCropbox} this
24599 * @param {Object} file
24604 * Fire when xhr upload the file
24605 * @param {Roo.bootstrap.UploadCropbox} this
24606 * @param {Object} data
24611 * Fire when arrange the file data
24612 * @param {Roo.bootstrap.UploadCropbox} this
24613 * @param {Object} formData
24618 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24621 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24623 emptyText : 'Click to upload image',
24624 rotateNotify : 'Image is too small to rotate',
24625 errorTimeout : 3000,
24639 cropType : 'image/jpeg',
24641 canvasLoaded : false,
24642 isDocument : false,
24644 paramName : 'imageUpload',
24646 loadingText : 'Loading...',
24649 getAutoCreate : function()
24653 cls : 'roo-upload-cropbox',
24657 cls : 'roo-upload-cropbox-selector',
24662 cls : 'roo-upload-cropbox-body',
24663 style : 'cursor:pointer',
24667 cls : 'roo-upload-cropbox-preview'
24671 cls : 'roo-upload-cropbox-thumb'
24675 cls : 'roo-upload-cropbox-empty-notify',
24676 html : this.emptyText
24680 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24681 html : this.rotateNotify
24687 cls : 'roo-upload-cropbox-footer',
24690 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24700 onRender : function(ct, position)
24702 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24704 if (this.buttons.length) {
24706 Roo.each(this.buttons, function(bb) {
24708 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24710 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24716 this.maskEl = this.el;
24720 initEvents : function()
24722 this.urlAPI = (window.createObjectURL && window) ||
24723 (window.URL && URL.revokeObjectURL && URL) ||
24724 (window.webkitURL && webkitURL);
24726 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24727 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24729 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24730 this.selectorEl.hide();
24732 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24733 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24735 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24736 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24737 this.thumbEl.hide();
24739 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24740 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24742 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24743 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24744 this.errorEl.hide();
24746 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24747 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24748 this.footerEl.hide();
24750 this.setThumbBoxSize();
24756 this.fireEvent('initial', this);
24763 window.addEventListener("resize", function() { _this.resize(); } );
24765 this.bodyEl.on('click', this.beforeSelectFile, this);
24768 this.bodyEl.on('touchstart', this.onTouchStart, this);
24769 this.bodyEl.on('touchmove', this.onTouchMove, this);
24770 this.bodyEl.on('touchend', this.onTouchEnd, this);
24774 this.bodyEl.on('mousedown', this.onMouseDown, this);
24775 this.bodyEl.on('mousemove', this.onMouseMove, this);
24776 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24777 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24778 Roo.get(document).on('mouseup', this.onMouseUp, this);
24781 this.selectorEl.on('change', this.onFileSelected, this);
24787 this.baseScale = 1;
24789 this.baseRotate = 1;
24790 this.dragable = false;
24791 this.pinching = false;
24794 this.cropData = false;
24795 this.notifyEl.dom.innerHTML = this.emptyText;
24797 this.selectorEl.dom.value = '';
24801 resize : function()
24803 if(this.fireEvent('resize', this) != false){
24804 this.setThumbBoxPosition();
24805 this.setCanvasPosition();
24809 onFooterButtonClick : function(e, el, o, type)
24812 case 'rotate-left' :
24813 this.onRotateLeft(e);
24815 case 'rotate-right' :
24816 this.onRotateRight(e);
24819 this.beforeSelectFile(e);
24834 this.fireEvent('footerbuttonclick', this, type);
24837 beforeSelectFile : function(e)
24839 e.preventDefault();
24841 if(this.fireEvent('beforeselectfile', this) != false){
24842 this.selectorEl.dom.click();
24846 onFileSelected : function(e)
24848 e.preventDefault();
24850 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24854 var file = this.selectorEl.dom.files[0];
24856 if(this.fireEvent('inspect', this, file) != false){
24857 this.prepare(file);
24862 trash : function(e)
24864 this.fireEvent('trash', this);
24867 download : function(e)
24869 this.fireEvent('download', this);
24872 loadCanvas : function(src)
24874 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24878 this.imageEl = document.createElement('img');
24882 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24884 this.imageEl.src = src;
24888 onLoadCanvas : function()
24890 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24891 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24893 this.bodyEl.un('click', this.beforeSelectFile, this);
24895 this.notifyEl.hide();
24896 this.thumbEl.show();
24897 this.footerEl.show();
24899 this.baseRotateLevel();
24901 if(this.isDocument){
24902 this.setThumbBoxSize();
24905 this.setThumbBoxPosition();
24907 this.baseScaleLevel();
24913 this.canvasLoaded = true;
24916 this.maskEl.unmask();
24921 setCanvasPosition : function()
24923 if(!this.canvasEl){
24927 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24928 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24930 this.previewEl.setLeft(pw);
24931 this.previewEl.setTop(ph);
24935 onMouseDown : function(e)
24939 this.dragable = true;
24940 this.pinching = false;
24942 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24943 this.dragable = false;
24947 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24948 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24952 onMouseMove : function(e)
24956 if(!this.canvasLoaded){
24960 if (!this.dragable){
24964 var minX = Math.ceil(this.thumbEl.getLeft(true));
24965 var minY = Math.ceil(this.thumbEl.getTop(true));
24967 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24968 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24970 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24971 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24973 x = x - this.mouseX;
24974 y = y - this.mouseY;
24976 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24977 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24979 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24980 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24982 this.previewEl.setLeft(bgX);
24983 this.previewEl.setTop(bgY);
24985 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24986 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24989 onMouseUp : function(e)
24993 this.dragable = false;
24996 onMouseWheel : function(e)
25000 this.startScale = this.scale;
25002 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25004 if(!this.zoomable()){
25005 this.scale = this.startScale;
25014 zoomable : function()
25016 var minScale = this.thumbEl.getWidth() / this.minWidth;
25018 if(this.minWidth < this.minHeight){
25019 minScale = this.thumbEl.getHeight() / this.minHeight;
25022 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25023 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25027 (this.rotate == 0 || this.rotate == 180) &&
25029 width > this.imageEl.OriginWidth ||
25030 height > this.imageEl.OriginHeight ||
25031 (width < this.minWidth && height < this.minHeight)
25039 (this.rotate == 90 || this.rotate == 270) &&
25041 width > this.imageEl.OriginWidth ||
25042 height > this.imageEl.OriginHeight ||
25043 (width < this.minHeight && height < this.minWidth)
25050 !this.isDocument &&
25051 (this.rotate == 0 || this.rotate == 180) &&
25053 width < this.minWidth ||
25054 width > this.imageEl.OriginWidth ||
25055 height < this.minHeight ||
25056 height > this.imageEl.OriginHeight
25063 !this.isDocument &&
25064 (this.rotate == 90 || this.rotate == 270) &&
25066 width < this.minHeight ||
25067 width > this.imageEl.OriginWidth ||
25068 height < this.minWidth ||
25069 height > this.imageEl.OriginHeight
25079 onRotateLeft : function(e)
25081 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25083 var minScale = this.thumbEl.getWidth() / this.minWidth;
25085 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25086 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25088 this.startScale = this.scale;
25090 while (this.getScaleLevel() < minScale){
25092 this.scale = this.scale + 1;
25094 if(!this.zoomable()){
25099 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25100 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25105 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25112 this.scale = this.startScale;
25114 this.onRotateFail();
25119 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25121 if(this.isDocument){
25122 this.setThumbBoxSize();
25123 this.setThumbBoxPosition();
25124 this.setCanvasPosition();
25129 this.fireEvent('rotate', this, 'left');
25133 onRotateRight : function(e)
25135 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25137 var minScale = this.thumbEl.getWidth() / this.minWidth;
25139 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25140 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25142 this.startScale = this.scale;
25144 while (this.getScaleLevel() < minScale){
25146 this.scale = this.scale + 1;
25148 if(!this.zoomable()){
25153 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25154 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25159 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25166 this.scale = this.startScale;
25168 this.onRotateFail();
25173 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25175 if(this.isDocument){
25176 this.setThumbBoxSize();
25177 this.setThumbBoxPosition();
25178 this.setCanvasPosition();
25183 this.fireEvent('rotate', this, 'right');
25186 onRotateFail : function()
25188 this.errorEl.show(true);
25192 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25197 this.previewEl.dom.innerHTML = '';
25199 var canvasEl = document.createElement("canvas");
25201 var contextEl = canvasEl.getContext("2d");
25203 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25204 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25205 var center = this.imageEl.OriginWidth / 2;
25207 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25208 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25209 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25210 center = this.imageEl.OriginHeight / 2;
25213 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25215 contextEl.translate(center, center);
25216 contextEl.rotate(this.rotate * Math.PI / 180);
25218 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25220 this.canvasEl = document.createElement("canvas");
25222 this.contextEl = this.canvasEl.getContext("2d");
25224 switch (this.rotate) {
25227 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25228 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25230 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25235 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25236 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25238 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25239 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);
25243 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25248 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25249 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25251 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25252 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);
25256 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);
25261 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25262 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25264 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25265 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25269 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);
25276 this.previewEl.appendChild(this.canvasEl);
25278 this.setCanvasPosition();
25283 if(!this.canvasLoaded){
25287 var imageCanvas = document.createElement("canvas");
25289 var imageContext = imageCanvas.getContext("2d");
25291 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25292 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25294 var center = imageCanvas.width / 2;
25296 imageContext.translate(center, center);
25298 imageContext.rotate(this.rotate * Math.PI / 180);
25300 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25302 var canvas = document.createElement("canvas");
25304 var context = canvas.getContext("2d");
25306 canvas.width = this.minWidth;
25307 canvas.height = this.minHeight;
25309 switch (this.rotate) {
25312 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25313 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25315 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25316 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25318 var targetWidth = this.minWidth - 2 * x;
25319 var targetHeight = this.minHeight - 2 * y;
25323 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25324 scale = targetWidth / width;
25327 if(x > 0 && y == 0){
25328 scale = targetHeight / height;
25331 if(x > 0 && y > 0){
25332 scale = targetWidth / width;
25334 if(width < height){
25335 scale = targetHeight / height;
25339 context.scale(scale, scale);
25341 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25342 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25344 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25345 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25347 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25352 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25353 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25355 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25356 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25358 var targetWidth = this.minWidth - 2 * x;
25359 var targetHeight = this.minHeight - 2 * y;
25363 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25364 scale = targetWidth / width;
25367 if(x > 0 && y == 0){
25368 scale = targetHeight / height;
25371 if(x > 0 && y > 0){
25372 scale = targetWidth / width;
25374 if(width < height){
25375 scale = targetHeight / height;
25379 context.scale(scale, scale);
25381 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25382 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25384 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25385 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25387 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25389 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25394 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25395 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25397 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25398 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25400 var targetWidth = this.minWidth - 2 * x;
25401 var targetHeight = this.minHeight - 2 * y;
25405 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25406 scale = targetWidth / width;
25409 if(x > 0 && y == 0){
25410 scale = targetHeight / height;
25413 if(x > 0 && y > 0){
25414 scale = targetWidth / width;
25416 if(width < height){
25417 scale = targetHeight / height;
25421 context.scale(scale, scale);
25423 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25424 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25426 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25427 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25429 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25430 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25432 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25437 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25438 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25440 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25441 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25443 var targetWidth = this.minWidth - 2 * x;
25444 var targetHeight = this.minHeight - 2 * y;
25448 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25449 scale = targetWidth / width;
25452 if(x > 0 && y == 0){
25453 scale = targetHeight / height;
25456 if(x > 0 && y > 0){
25457 scale = targetWidth / width;
25459 if(width < height){
25460 scale = targetHeight / height;
25464 context.scale(scale, scale);
25466 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25467 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25469 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25470 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25472 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25474 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25481 this.cropData = canvas.toDataURL(this.cropType);
25483 if(this.fireEvent('crop', this, this.cropData) !== false){
25484 this.process(this.file, this.cropData);
25491 setThumbBoxSize : function()
25495 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25496 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25497 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25499 this.minWidth = width;
25500 this.minHeight = height;
25502 if(this.rotate == 90 || this.rotate == 270){
25503 this.minWidth = height;
25504 this.minHeight = width;
25509 width = Math.ceil(this.minWidth * height / this.minHeight);
25511 if(this.minWidth > this.minHeight){
25513 height = Math.ceil(this.minHeight * width / this.minWidth);
25516 this.thumbEl.setStyle({
25517 width : width + 'px',
25518 height : height + 'px'
25525 setThumbBoxPosition : function()
25527 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25528 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25530 this.thumbEl.setLeft(x);
25531 this.thumbEl.setTop(y);
25535 baseRotateLevel : function()
25537 this.baseRotate = 1;
25540 typeof(this.exif) != 'undefined' &&
25541 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25542 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25544 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25547 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25551 baseScaleLevel : function()
25555 if(this.isDocument){
25557 if(this.baseRotate == 6 || this.baseRotate == 8){
25559 height = this.thumbEl.getHeight();
25560 this.baseScale = height / this.imageEl.OriginWidth;
25562 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25563 width = this.thumbEl.getWidth();
25564 this.baseScale = width / this.imageEl.OriginHeight;
25570 height = this.thumbEl.getHeight();
25571 this.baseScale = height / this.imageEl.OriginHeight;
25573 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25574 width = this.thumbEl.getWidth();
25575 this.baseScale = width / this.imageEl.OriginWidth;
25581 if(this.baseRotate == 6 || this.baseRotate == 8){
25583 width = this.thumbEl.getHeight();
25584 this.baseScale = width / this.imageEl.OriginHeight;
25586 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25587 height = this.thumbEl.getWidth();
25588 this.baseScale = height / this.imageEl.OriginHeight;
25591 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25592 height = this.thumbEl.getWidth();
25593 this.baseScale = height / this.imageEl.OriginHeight;
25595 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25596 width = this.thumbEl.getHeight();
25597 this.baseScale = width / this.imageEl.OriginWidth;
25604 width = this.thumbEl.getWidth();
25605 this.baseScale = width / this.imageEl.OriginWidth;
25607 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25608 height = this.thumbEl.getHeight();
25609 this.baseScale = height / this.imageEl.OriginHeight;
25612 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25614 height = this.thumbEl.getHeight();
25615 this.baseScale = height / this.imageEl.OriginHeight;
25617 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25618 width = this.thumbEl.getWidth();
25619 this.baseScale = width / this.imageEl.OriginWidth;
25627 getScaleLevel : function()
25629 return this.baseScale * Math.pow(1.1, this.scale);
25632 onTouchStart : function(e)
25634 if(!this.canvasLoaded){
25635 this.beforeSelectFile(e);
25639 var touches = e.browserEvent.touches;
25645 if(touches.length == 1){
25646 this.onMouseDown(e);
25650 if(touches.length != 2){
25656 for(var i = 0, finger; finger = touches[i]; i++){
25657 coords.push(finger.pageX, finger.pageY);
25660 var x = Math.pow(coords[0] - coords[2], 2);
25661 var y = Math.pow(coords[1] - coords[3], 2);
25663 this.startDistance = Math.sqrt(x + y);
25665 this.startScale = this.scale;
25667 this.pinching = true;
25668 this.dragable = false;
25672 onTouchMove : function(e)
25674 if(!this.pinching && !this.dragable){
25678 var touches = e.browserEvent.touches;
25685 this.onMouseMove(e);
25691 for(var i = 0, finger; finger = touches[i]; i++){
25692 coords.push(finger.pageX, finger.pageY);
25695 var x = Math.pow(coords[0] - coords[2], 2);
25696 var y = Math.pow(coords[1] - coords[3], 2);
25698 this.endDistance = Math.sqrt(x + y);
25700 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25702 if(!this.zoomable()){
25703 this.scale = this.startScale;
25711 onTouchEnd : function(e)
25713 this.pinching = false;
25714 this.dragable = false;
25718 process : function(file, crop)
25721 this.maskEl.mask(this.loadingText);
25724 this.xhr = new XMLHttpRequest();
25726 file.xhr = this.xhr;
25728 this.xhr.open(this.method, this.url, true);
25731 "Accept": "application/json",
25732 "Cache-Control": "no-cache",
25733 "X-Requested-With": "XMLHttpRequest"
25736 for (var headerName in headers) {
25737 var headerValue = headers[headerName];
25739 this.xhr.setRequestHeader(headerName, headerValue);
25745 this.xhr.onload = function()
25747 _this.xhrOnLoad(_this.xhr);
25750 this.xhr.onerror = function()
25752 _this.xhrOnError(_this.xhr);
25755 var formData = new FormData();
25757 formData.append('returnHTML', 'NO');
25760 formData.append('crop', crop);
25763 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25764 formData.append(this.paramName, file, file.name);
25767 if(typeof(file.filename) != 'undefined'){
25768 formData.append('filename', file.filename);
25771 if(typeof(file.mimetype) != 'undefined'){
25772 formData.append('mimetype', file.mimetype);
25775 if(this.fireEvent('arrange', this, formData) != false){
25776 this.xhr.send(formData);
25780 xhrOnLoad : function(xhr)
25783 this.maskEl.unmask();
25786 if (xhr.readyState !== 4) {
25787 this.fireEvent('exception', this, xhr);
25791 var response = Roo.decode(xhr.responseText);
25793 if(!response.success){
25794 this.fireEvent('exception', this, xhr);
25798 var response = Roo.decode(xhr.responseText);
25800 this.fireEvent('upload', this, response);
25804 xhrOnError : function()
25807 this.maskEl.unmask();
25810 Roo.log('xhr on error');
25812 var response = Roo.decode(xhr.responseText);
25818 prepare : function(file)
25821 this.maskEl.mask(this.loadingText);
25827 if(typeof(file) === 'string'){
25828 this.loadCanvas(file);
25832 if(!file || !this.urlAPI){
25837 this.cropType = file.type;
25841 if(this.fireEvent('prepare', this, this.file) != false){
25843 var reader = new FileReader();
25845 reader.onload = function (e) {
25846 if (e.target.error) {
25847 Roo.log(e.target.error);
25851 var buffer = e.target.result,
25852 dataView = new DataView(buffer),
25854 maxOffset = dataView.byteLength - 4,
25858 if (dataView.getUint16(0) === 0xffd8) {
25859 while (offset < maxOffset) {
25860 markerBytes = dataView.getUint16(offset);
25862 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25863 markerLength = dataView.getUint16(offset + 2) + 2;
25864 if (offset + markerLength > dataView.byteLength) {
25865 Roo.log('Invalid meta data: Invalid segment size.');
25869 if(markerBytes == 0xffe1){
25870 _this.parseExifData(
25877 offset += markerLength;
25887 var url = _this.urlAPI.createObjectURL(_this.file);
25889 _this.loadCanvas(url);
25894 reader.readAsArrayBuffer(this.file);
25900 parseExifData : function(dataView, offset, length)
25902 var tiffOffset = offset + 10,
25906 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25907 // No Exif data, might be XMP data instead
25911 // Check for the ASCII code for "Exif" (0x45786966):
25912 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25913 // No Exif data, might be XMP data instead
25916 if (tiffOffset + 8 > dataView.byteLength) {
25917 Roo.log('Invalid Exif data: Invalid segment size.');
25920 // Check for the two null bytes:
25921 if (dataView.getUint16(offset + 8) !== 0x0000) {
25922 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25925 // Check the byte alignment:
25926 switch (dataView.getUint16(tiffOffset)) {
25928 littleEndian = true;
25931 littleEndian = false;
25934 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25937 // Check for the TIFF tag marker (0x002A):
25938 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25939 Roo.log('Invalid Exif data: Missing TIFF marker.');
25942 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25943 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25945 this.parseExifTags(
25948 tiffOffset + dirOffset,
25953 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25958 if (dirOffset + 6 > dataView.byteLength) {
25959 Roo.log('Invalid Exif data: Invalid directory offset.');
25962 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25963 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25964 if (dirEndOffset + 4 > dataView.byteLength) {
25965 Roo.log('Invalid Exif data: Invalid directory size.');
25968 for (i = 0; i < tagsNumber; i += 1) {
25972 dirOffset + 2 + 12 * i, // tag offset
25976 // Return the offset to the next directory:
25977 return dataView.getUint32(dirEndOffset, littleEndian);
25980 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25982 var tag = dataView.getUint16(offset, littleEndian);
25984 this.exif[tag] = this.getExifValue(
25988 dataView.getUint16(offset + 2, littleEndian), // tag type
25989 dataView.getUint32(offset + 4, littleEndian), // tag length
25994 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25996 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26005 Roo.log('Invalid Exif data: Invalid tag type.');
26009 tagSize = tagType.size * length;
26010 // Determine if the value is contained in the dataOffset bytes,
26011 // or if the value at the dataOffset is a pointer to the actual data:
26012 dataOffset = tagSize > 4 ?
26013 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26014 if (dataOffset + tagSize > dataView.byteLength) {
26015 Roo.log('Invalid Exif data: Invalid data offset.');
26018 if (length === 1) {
26019 return tagType.getValue(dataView, dataOffset, littleEndian);
26022 for (i = 0; i < length; i += 1) {
26023 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26026 if (tagType.ascii) {
26028 // Concatenate the chars:
26029 for (i = 0; i < values.length; i += 1) {
26031 // Ignore the terminating NULL byte(s):
26032 if (c === '\u0000') {
26044 Roo.apply(Roo.bootstrap.UploadCropbox, {
26046 'Orientation': 0x0112
26050 1: 0, //'top-left',
26052 3: 180, //'bottom-right',
26053 // 4: 'bottom-left',
26055 6: 90, //'right-top',
26056 // 7: 'right-bottom',
26057 8: 270 //'left-bottom'
26061 // byte, 8-bit unsigned int:
26063 getValue: function (dataView, dataOffset) {
26064 return dataView.getUint8(dataOffset);
26068 // ascii, 8-bit byte:
26070 getValue: function (dataView, dataOffset) {
26071 return String.fromCharCode(dataView.getUint8(dataOffset));
26076 // short, 16 bit int:
26078 getValue: function (dataView, dataOffset, littleEndian) {
26079 return dataView.getUint16(dataOffset, littleEndian);
26083 // long, 32 bit int:
26085 getValue: function (dataView, dataOffset, littleEndian) {
26086 return dataView.getUint32(dataOffset, littleEndian);
26090 // rational = two long values, first is numerator, second is denominator:
26092 getValue: function (dataView, dataOffset, littleEndian) {
26093 return dataView.getUint32(dataOffset, littleEndian) /
26094 dataView.getUint32(dataOffset + 4, littleEndian);
26098 // slong, 32 bit signed int:
26100 getValue: function (dataView, dataOffset, littleEndian) {
26101 return dataView.getInt32(dataOffset, littleEndian);
26105 // srational, two slongs, first is numerator, second is denominator:
26107 getValue: function (dataView, dataOffset, littleEndian) {
26108 return dataView.getInt32(dataOffset, littleEndian) /
26109 dataView.getInt32(dataOffset + 4, littleEndian);
26119 cls : 'btn-group roo-upload-cropbox-rotate-left',
26120 action : 'rotate-left',
26124 cls : 'btn btn-default',
26125 html : '<i class="fa fa-undo"></i>'
26131 cls : 'btn-group roo-upload-cropbox-picture',
26132 action : 'picture',
26136 cls : 'btn btn-default',
26137 html : '<i class="fa fa-picture-o"></i>'
26143 cls : 'btn-group roo-upload-cropbox-rotate-right',
26144 action : 'rotate-right',
26148 cls : 'btn btn-default',
26149 html : '<i class="fa fa-repeat"></i>'
26157 cls : 'btn-group roo-upload-cropbox-rotate-left',
26158 action : 'rotate-left',
26162 cls : 'btn btn-default',
26163 html : '<i class="fa fa-undo"></i>'
26169 cls : 'btn-group roo-upload-cropbox-download',
26170 action : 'download',
26174 cls : 'btn btn-default',
26175 html : '<i class="fa fa-download"></i>'
26181 cls : 'btn-group roo-upload-cropbox-crop',
26186 cls : 'btn btn-default',
26187 html : '<i class="fa fa-crop"></i>'
26193 cls : 'btn-group roo-upload-cropbox-trash',
26198 cls : 'btn btn-default',
26199 html : '<i class="fa fa-trash"></i>'
26205 cls : 'btn-group roo-upload-cropbox-rotate-right',
26206 action : 'rotate-right',
26210 cls : 'btn btn-default',
26211 html : '<i class="fa fa-repeat"></i>'
26219 cls : 'btn-group roo-upload-cropbox-rotate-left',
26220 action : 'rotate-left',
26224 cls : 'btn btn-default',
26225 html : '<i class="fa fa-undo"></i>'
26231 cls : 'btn-group roo-upload-cropbox-rotate-right',
26232 action : 'rotate-right',
26236 cls : 'btn btn-default',
26237 html : '<i class="fa fa-repeat"></i>'
26250 * @class Roo.bootstrap.DocumentManager
26251 * @extends Roo.bootstrap.Component
26252 * Bootstrap DocumentManager class
26253 * @cfg {String} paramName default 'imageUpload'
26254 * @cfg {String} method default POST
26255 * @cfg {String} url action url
26256 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26257 * @cfg {Boolean} multiple multiple upload default true
26258 * @cfg {Number} thumbSize default 300
26259 * @cfg {String} fieldLabel
26260 * @cfg {Number} labelWidth default 4
26261 * @cfg {String} labelAlign (left|top) default left
26262 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26265 * Create a new DocumentManager
26266 * @param {Object} config The config object
26269 Roo.bootstrap.DocumentManager = function(config){
26270 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26275 * Fire when initial the DocumentManager
26276 * @param {Roo.bootstrap.DocumentManager} this
26281 * inspect selected file
26282 * @param {Roo.bootstrap.DocumentManager} this
26283 * @param {File} file
26288 * Fire when xhr load exception
26289 * @param {Roo.bootstrap.DocumentManager} this
26290 * @param {XMLHttpRequest} xhr
26292 "exception" : true,
26295 * prepare the form data
26296 * @param {Roo.bootstrap.DocumentManager} this
26297 * @param {Object} formData
26302 * Fire when remove the file
26303 * @param {Roo.bootstrap.DocumentManager} this
26304 * @param {Object} file
26309 * Fire after refresh the file
26310 * @param {Roo.bootstrap.DocumentManager} this
26315 * Fire after click the image
26316 * @param {Roo.bootstrap.DocumentManager} this
26317 * @param {Object} file
26322 * Fire when upload a image and editable set to true
26323 * @param {Roo.bootstrap.DocumentManager} this
26324 * @param {Object} file
26328 * @event beforeselectfile
26329 * Fire before select file
26330 * @param {Roo.bootstrap.DocumentManager} this
26332 "beforeselectfile" : true,
26335 * Fire before process file
26336 * @param {Roo.bootstrap.DocumentManager} this
26337 * @param {Object} file
26344 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26353 paramName : 'imageUpload',
26356 labelAlign : 'left',
26363 getAutoCreate : function()
26365 var managerWidget = {
26367 cls : 'roo-document-manager',
26371 cls : 'roo-document-manager-selector',
26376 cls : 'roo-document-manager-uploader',
26380 cls : 'roo-document-manager-upload-btn',
26381 html : '<i class="fa fa-plus"></i>'
26392 cls : 'column col-md-12',
26397 if(this.fieldLabel.length){
26402 cls : 'column col-md-12',
26403 html : this.fieldLabel
26407 cls : 'column col-md-12',
26412 if(this.labelAlign == 'left'){
26416 cls : 'column col-md-' + this.labelWidth,
26417 html : this.fieldLabel
26421 cls : 'column col-md-' + (12 - this.labelWidth),
26431 cls : 'row clearfix',
26439 initEvents : function()
26441 this.managerEl = this.el.select('.roo-document-manager', true).first();
26442 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26444 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26445 this.selectorEl.hide();
26448 this.selectorEl.attr('multiple', 'multiple');
26451 this.selectorEl.on('change', this.onFileSelected, this);
26453 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26454 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26456 this.uploader.on('click', this.onUploaderClick, this);
26458 this.renderProgressDialog();
26462 window.addEventListener("resize", function() { _this.refresh(); } );
26464 this.fireEvent('initial', this);
26467 renderProgressDialog : function()
26471 this.progressDialog = new Roo.bootstrap.Modal({
26472 cls : 'roo-document-manager-progress-dialog',
26473 allow_close : false,
26483 btnclick : function() {
26484 _this.uploadCancel();
26490 this.progressDialog.render(Roo.get(document.body));
26492 this.progress = new Roo.bootstrap.Progress({
26493 cls : 'roo-document-manager-progress',
26498 this.progress.render(this.progressDialog.getChildContainer());
26500 this.progressBar = new Roo.bootstrap.ProgressBar({
26501 cls : 'roo-document-manager-progress-bar',
26504 aria_valuemax : 12,
26508 this.progressBar.render(this.progress.getChildContainer());
26511 onUploaderClick : function(e)
26513 e.preventDefault();
26515 if(this.fireEvent('beforeselectfile', this) != false){
26516 this.selectorEl.dom.click();
26521 onFileSelected : function(e)
26523 e.preventDefault();
26525 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26529 Roo.each(this.selectorEl.dom.files, function(file){
26530 if(this.fireEvent('inspect', this, file) != false){
26531 this.files.push(file);
26541 this.selectorEl.dom.value = '';
26543 if(!this.files.length){
26547 if(this.boxes > 0 && this.files.length > this.boxes){
26548 this.files = this.files.slice(0, this.boxes);
26551 this.uploader.show();
26553 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26554 this.uploader.hide();
26563 Roo.each(this.files, function(file){
26565 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26566 var f = this.renderPreview(file);
26571 if(file.type.indexOf('image') != -1){
26572 this.delegates.push(
26574 _this.process(file);
26575 }).createDelegate(this)
26583 _this.process(file);
26584 }).createDelegate(this)
26589 this.files = files;
26591 this.delegates = this.delegates.concat(docs);
26593 if(!this.delegates.length){
26598 this.progressBar.aria_valuemax = this.delegates.length;
26605 arrange : function()
26607 if(!this.delegates.length){
26608 this.progressDialog.hide();
26613 var delegate = this.delegates.shift();
26615 this.progressDialog.show();
26617 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26619 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26624 refresh : function()
26626 this.uploader.show();
26628 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26629 this.uploader.hide();
26632 Roo.isTouch ? this.closable(false) : this.closable(true);
26634 this.fireEvent('refresh', this);
26637 onRemove : function(e, el, o)
26639 e.preventDefault();
26641 this.fireEvent('remove', this, o);
26645 remove : function(o)
26649 Roo.each(this.files, function(file){
26650 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26659 this.files = files;
26666 Roo.each(this.files, function(file){
26671 file.target.remove();
26680 onClick : function(e, el, o)
26682 e.preventDefault();
26684 this.fireEvent('click', this, o);
26688 closable : function(closable)
26690 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26692 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26704 xhrOnLoad : function(xhr)
26706 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26710 if (xhr.readyState !== 4) {
26712 this.fireEvent('exception', this, xhr);
26716 var response = Roo.decode(xhr.responseText);
26718 if(!response.success){
26720 this.fireEvent('exception', this, xhr);
26724 var file = this.renderPreview(response.data);
26726 this.files.push(file);
26732 xhrOnError : function()
26734 Roo.log('xhr on error');
26736 var response = Roo.decode(xhr.responseText);
26743 process : function(file)
26745 if(this.fireEvent('process', this, file) !== false){
26746 if(this.editable && file.type.indexOf('image') != -1){
26747 this.fireEvent('edit', this, file);
26751 this.uploadStart(file, false);
26758 uploadStart : function(file, crop)
26760 this.xhr = new XMLHttpRequest();
26762 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26767 file.xhr = this.xhr;
26769 this.managerEl.createChild({
26771 cls : 'roo-document-manager-loading',
26775 tooltip : file.name,
26776 cls : 'roo-document-manager-thumb',
26777 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26783 this.xhr.open(this.method, this.url, true);
26786 "Accept": "application/json",
26787 "Cache-Control": "no-cache",
26788 "X-Requested-With": "XMLHttpRequest"
26791 for (var headerName in headers) {
26792 var headerValue = headers[headerName];
26794 this.xhr.setRequestHeader(headerName, headerValue);
26800 this.xhr.onload = function()
26802 _this.xhrOnLoad(_this.xhr);
26805 this.xhr.onerror = function()
26807 _this.xhrOnError(_this.xhr);
26810 var formData = new FormData();
26812 formData.append('returnHTML', 'NO');
26815 formData.append('crop', crop);
26818 formData.append(this.paramName, file, file.name);
26820 if(this.fireEvent('prepare', this, formData) != false){
26821 this.xhr.send(formData);
26825 uploadCancel : function()
26832 this.delegates = [];
26834 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26841 renderPreview : function(file)
26843 if(typeof(file.target) != 'undefined' && file.target){
26847 var previewEl = this.managerEl.createChild({
26849 cls : 'roo-document-manager-preview',
26853 tooltip : file.filename,
26854 cls : 'roo-document-manager-thumb',
26855 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26860 html : '<i class="fa fa-times-circle"></i>'
26865 var close = previewEl.select('button.close', true).first();
26867 close.on('click', this.onRemove, this, file);
26869 file.target = previewEl;
26871 var image = previewEl.select('img', true).first();
26875 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26877 image.on('click', this.onClick, this, file);
26883 onPreviewLoad : function(file, image)
26885 if(typeof(file.target) == 'undefined' || !file.target){
26889 var width = image.dom.naturalWidth || image.dom.width;
26890 var height = image.dom.naturalHeight || image.dom.height;
26892 if(width > height){
26893 file.target.addClass('wide');
26897 file.target.addClass('tall');
26902 uploadFromSource : function(file, crop)
26904 this.xhr = new XMLHttpRequest();
26906 this.managerEl.createChild({
26908 cls : 'roo-document-manager-loading',
26912 tooltip : file.name,
26913 cls : 'roo-document-manager-thumb',
26914 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26920 this.xhr.open(this.method, this.url, true);
26923 "Accept": "application/json",
26924 "Cache-Control": "no-cache",
26925 "X-Requested-With": "XMLHttpRequest"
26928 for (var headerName in headers) {
26929 var headerValue = headers[headerName];
26931 this.xhr.setRequestHeader(headerName, headerValue);
26937 this.xhr.onload = function()
26939 _this.xhrOnLoad(_this.xhr);
26942 this.xhr.onerror = function()
26944 _this.xhrOnError(_this.xhr);
26947 var formData = new FormData();
26949 formData.append('returnHTML', 'NO');
26951 formData.append('crop', crop);
26953 if(typeof(file.filename) != 'undefined'){
26954 formData.append('filename', file.filename);
26957 if(typeof(file.mimetype) != 'undefined'){
26958 formData.append('mimetype', file.mimetype);
26961 if(this.fireEvent('prepare', this, formData) != false){
26962 this.xhr.send(formData);
26972 * @class Roo.bootstrap.DocumentViewer
26973 * @extends Roo.bootstrap.Component
26974 * Bootstrap DocumentViewer class
26977 * Create a new DocumentViewer
26978 * @param {Object} config The config object
26981 Roo.bootstrap.DocumentViewer = function(config){
26982 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26987 * Fire after initEvent
26988 * @param {Roo.bootstrap.DocumentViewer} this
26994 * @param {Roo.bootstrap.DocumentViewer} this
26999 * Fire after trash button
27000 * @param {Roo.bootstrap.DocumentViewer} this
27007 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27009 getAutoCreate : function()
27013 cls : 'roo-document-viewer',
27017 cls : 'roo-document-viewer-body',
27021 cls : 'roo-document-viewer-thumb',
27025 cls : 'roo-document-viewer-image'
27033 cls : 'roo-document-viewer-footer',
27036 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27044 cls : 'btn btn-default roo-document-viewer-trash',
27045 html : '<i class="fa fa-trash"></i>'
27058 initEvents : function()
27061 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27062 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27064 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27065 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27067 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27068 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27071 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27074 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076 this.bodyEl.on('click', this.onClick, this);
27078 this.trashBtn.on('click', this.onTrash, this);
27082 initial : function()
27084 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27087 this.fireEvent('initial', this);
27091 onClick : function(e)
27093 e.preventDefault();
27095 this.fireEvent('click', this);
27098 onTrash : function(e)
27100 e.preventDefault();
27102 this.fireEvent('trash', this);
27114 * @class Roo.bootstrap.NavProgressBar
27115 * @extends Roo.bootstrap.Component
27116 * Bootstrap NavProgressBar class
27119 * Create a new nav progress bar
27120 * @param {Object} config The config object
27123 Roo.bootstrap.NavProgressBar = function(config){
27124 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27126 this.bullets = this.bullets || [];
27128 // Roo.bootstrap.NavProgressBar.register(this);
27132 * Fires when the active item changes
27133 * @param {Roo.bootstrap.NavProgressBar} this
27134 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27135 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27142 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27147 getAutoCreate : function()
27149 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27153 cls : 'roo-navigation-bar-group',
27157 cls : 'roo-navigation-top-bar'
27161 cls : 'roo-navigation-bullets-bar',
27165 cls : 'roo-navigation-bar'
27172 cls : 'roo-navigation-bottom-bar'
27182 initEvents: function()
27187 onRender : function(ct, position)
27189 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27191 if(this.bullets.length){
27192 Roo.each(this.bullets, function(b){
27201 addItem : function(cfg)
27203 var item = new Roo.bootstrap.NavProgressItem(cfg);
27205 item.parentId = this.id;
27206 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27209 var top = new Roo.bootstrap.Element({
27211 cls : 'roo-navigation-bar-text'
27214 var bottom = new Roo.bootstrap.Element({
27216 cls : 'roo-navigation-bar-text'
27219 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27220 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27222 var topText = new Roo.bootstrap.Element({
27224 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27227 var bottomText = new Roo.bootstrap.Element({
27229 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27232 topText.onRender(top.el, null);
27233 bottomText.onRender(bottom.el, null);
27236 item.bottomEl = bottom;
27239 this.barItems.push(item);
27244 getActive : function()
27246 var active = false;
27248 Roo.each(this.barItems, function(v){
27250 if (!v.isActive()) {
27262 setActiveItem : function(item)
27266 Roo.each(this.barItems, function(v){
27267 if (v.rid == item.rid) {
27271 if (v.isActive()) {
27272 v.setActive(false);
27277 item.setActive(true);
27279 this.fireEvent('changed', this, item, prev);
27282 getBarItem: function(rid)
27286 Roo.each(this.barItems, function(e) {
27287 if (e.rid != rid) {
27298 indexOfItem : function(item)
27302 Roo.each(this.barItems, function(v, i){
27304 if (v.rid != item.rid) {
27315 setActiveNext : function()
27317 var i = this.indexOfItem(this.getActive());
27319 if (i > this.barItems.length) {
27323 this.setActiveItem(this.barItems[i+1]);
27326 setActivePrev : function()
27328 var i = this.indexOfItem(this.getActive());
27334 this.setActiveItem(this.barItems[i-1]);
27337 format : function()
27339 if(!this.barItems.length){
27343 var width = 100 / this.barItems.length;
27345 Roo.each(this.barItems, function(i){
27346 i.el.setStyle('width', width + '%');
27347 i.topEl.el.setStyle('width', width + '%');
27348 i.bottomEl.el.setStyle('width', width + '%');
27357 * Nav Progress Item
27362 * @class Roo.bootstrap.NavProgressItem
27363 * @extends Roo.bootstrap.Component
27364 * Bootstrap NavProgressItem class
27365 * @cfg {String} rid the reference id
27366 * @cfg {Boolean} active (true|false) Is item active default false
27367 * @cfg {Boolean} disabled (true|false) Is item active default false
27368 * @cfg {String} html
27369 * @cfg {String} position (top|bottom) text position default bottom
27370 * @cfg {String} icon show icon instead of number
27373 * Create a new NavProgressItem
27374 * @param {Object} config The config object
27376 Roo.bootstrap.NavProgressItem = function(config){
27377 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27382 * The raw click event for the entire grid.
27383 * @param {Roo.bootstrap.NavProgressItem} this
27384 * @param {Roo.EventObject} e
27391 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27397 position : 'bottom',
27400 getAutoCreate : function()
27402 var iconCls = 'roo-navigation-bar-item-icon';
27404 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27408 cls: 'roo-navigation-bar-item',
27418 cfg.cls += ' active';
27421 cfg.cls += ' disabled';
27427 disable : function()
27429 this.setDisabled(true);
27432 enable : function()
27434 this.setDisabled(false);
27437 initEvents: function()
27439 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27441 this.iconEl.on('click', this.onClick, this);
27444 onClick : function(e)
27446 e.preventDefault();
27452 if(this.fireEvent('click', this, e) === false){
27456 this.parent().setActiveItem(this);
27459 isActive: function ()
27461 return this.active;
27464 setActive : function(state)
27466 if(this.active == state){
27470 this.active = state;
27473 this.el.addClass('active');
27477 this.el.removeClass('active');
27482 setDisabled : function(state)
27484 if(this.disabled == state){
27488 this.disabled = state;
27491 this.el.addClass('disabled');
27495 this.el.removeClass('disabled');
27498 tooltipEl : function()
27500 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27513 * @class Roo.bootstrap.FieldLabel
27514 * @extends Roo.bootstrap.Component
27515 * Bootstrap FieldLabel class
27516 * @cfg {String} html contents of the element
27517 * @cfg {String} tag tag of the element default label
27518 * @cfg {String} cls class of the element
27519 * @cfg {String} target label target
27520 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27521 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27522 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27523 * @cfg {String} iconTooltip default "This field is required"
27526 * Create a new FieldLabel
27527 * @param {Object} config The config object
27530 Roo.bootstrap.FieldLabel = function(config){
27531 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27536 * Fires after the field has been marked as invalid.
27537 * @param {Roo.form.FieldLabel} this
27538 * @param {String} msg The validation message
27543 * Fires after the field has been validated with no errors.
27544 * @param {Roo.form.FieldLabel} this
27550 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27557 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27558 validClass : 'text-success fa fa-lg fa-check',
27559 iconTooltip : 'This field is required',
27561 getAutoCreate : function(){
27565 cls : 'roo-bootstrap-field-label ' + this.cls,
27571 tooltip : this.iconTooltip
27583 initEvents: function()
27585 Roo.bootstrap.Element.superclass.initEvents.call(this);
27587 this.iconEl = this.el.select('i', true).first();
27589 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27591 Roo.bootstrap.FieldLabel.register(this);
27595 * Mark this field as valid
27597 markValid : function()
27599 this.iconEl.show();
27601 this.iconEl.removeClass(this.invalidClass);
27603 this.iconEl.addClass(this.validClass);
27605 this.fireEvent('valid', this);
27609 * Mark this field as invalid
27610 * @param {String} msg The validation message
27612 markInvalid : function(msg)
27614 this.iconEl.show();
27616 this.iconEl.removeClass(this.validClass);
27618 this.iconEl.addClass(this.invalidClass);
27620 this.fireEvent('invalid', this, msg);
27626 Roo.apply(Roo.bootstrap.FieldLabel, {
27631 * register a FieldLabel Group
27632 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27634 register : function(label)
27636 if(this.groups.hasOwnProperty(label.target)){
27640 this.groups[label.target] = label;
27644 * fetch a FieldLabel Group based on the target
27645 * @param {string} target
27646 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27648 get: function(target) {
27649 if (typeof(this.groups[target]) == 'undefined') {
27653 return this.groups[target] ;
27662 * page DateSplitField.
27668 * @class Roo.bootstrap.DateSplitField
27669 * @extends Roo.bootstrap.Component
27670 * Bootstrap DateSplitField class
27671 * @cfg {string} fieldLabel - the label associated
27672 * @cfg {Number} labelWidth set the width of label (0-12)
27673 * @cfg {String} labelAlign (top|left)
27674 * @cfg {Boolean} dayAllowBlank (true|false) default false
27675 * @cfg {Boolean} monthAllowBlank (true|false) default false
27676 * @cfg {Boolean} yearAllowBlank (true|false) default false
27677 * @cfg {string} dayPlaceholder
27678 * @cfg {string} monthPlaceholder
27679 * @cfg {string} yearPlaceholder
27680 * @cfg {string} dayFormat default 'd'
27681 * @cfg {string} monthFormat default 'm'
27682 * @cfg {string} yearFormat default 'Y'
27686 * Create a new DateSplitField
27687 * @param {Object} config The config object
27690 Roo.bootstrap.DateSplitField = function(config){
27691 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27697 * getting the data of years
27698 * @param {Roo.bootstrap.DateSplitField} this
27699 * @param {Object} years
27704 * getting the data of days
27705 * @param {Roo.bootstrap.DateSplitField} this
27706 * @param {Object} days
27711 * Fires after the field has been marked as invalid.
27712 * @param {Roo.form.Field} this
27713 * @param {String} msg The validation message
27718 * Fires after the field has been validated with no errors.
27719 * @param {Roo.form.Field} this
27725 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27728 labelAlign : 'top',
27730 dayAllowBlank : false,
27731 monthAllowBlank : false,
27732 yearAllowBlank : false,
27733 dayPlaceholder : '',
27734 monthPlaceholder : '',
27735 yearPlaceholder : '',
27739 isFormField : true,
27741 getAutoCreate : function()
27745 cls : 'row roo-date-split-field-group',
27750 cls : 'form-hidden-field roo-date-split-field-group-value',
27756 if(this.fieldLabel){
27759 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27763 html : this.fieldLabel
27769 Roo.each(['day', 'month', 'year'], function(t){
27772 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27779 inputEl: function ()
27781 return this.el.select('.roo-date-split-field-group-value', true).first();
27784 onRender : function(ct, position)
27788 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27790 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27792 this.dayField = new Roo.bootstrap.ComboBox({
27793 allowBlank : this.dayAllowBlank,
27794 alwaysQuery : true,
27795 displayField : 'value',
27798 forceSelection : true,
27800 placeholder : this.dayPlaceholder,
27801 selectOnFocus : true,
27802 tpl : '<div class="select2-result"><b>{value}</b></div>',
27803 triggerAction : 'all',
27805 valueField : 'value',
27806 store : new Roo.data.SimpleStore({
27807 data : (function() {
27809 _this.fireEvent('days', _this, days);
27812 fields : [ 'value' ]
27815 select : function (_self, record, index)
27817 _this.setValue(_this.getValue());
27822 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27824 this.monthField = new Roo.bootstrap.MonthField({
27825 after : '<i class=\"fa fa-calendar\"></i>',
27826 allowBlank : this.monthAllowBlank,
27827 placeholder : this.monthPlaceholder,
27830 render : function (_self)
27832 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27833 e.preventDefault();
27837 select : function (_self, oldvalue, newvalue)
27839 _this.setValue(_this.getValue());
27844 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27846 this.yearField = new Roo.bootstrap.ComboBox({
27847 allowBlank : this.yearAllowBlank,
27848 alwaysQuery : true,
27849 displayField : 'value',
27852 forceSelection : true,
27854 placeholder : this.yearPlaceholder,
27855 selectOnFocus : true,
27856 tpl : '<div class="select2-result"><b>{value}</b></div>',
27857 triggerAction : 'all',
27859 valueField : 'value',
27860 store : new Roo.data.SimpleStore({
27861 data : (function() {
27863 _this.fireEvent('years', _this, years);
27866 fields : [ 'value' ]
27869 select : function (_self, record, index)
27871 _this.setValue(_this.getValue());
27876 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27879 setValue : function(v, format)
27881 this.inputEl.dom.value = v;
27883 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27885 var d = Date.parseDate(v, f);
27892 this.setDay(d.format(this.dayFormat));
27893 this.setMonth(d.format(this.monthFormat));
27894 this.setYear(d.format(this.yearFormat));
27901 setDay : function(v)
27903 this.dayField.setValue(v);
27904 this.inputEl.dom.value = this.getValue();
27909 setMonth : function(v)
27911 this.monthField.setValue(v, true);
27912 this.inputEl.dom.value = this.getValue();
27917 setYear : function(v)
27919 this.yearField.setValue(v);
27920 this.inputEl.dom.value = this.getValue();
27925 getDay : function()
27927 return this.dayField.getValue();
27930 getMonth : function()
27932 return this.monthField.getValue();
27935 getYear : function()
27937 return this.yearField.getValue();
27940 getValue : function()
27942 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27944 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27954 this.inputEl.dom.value = '';
27959 validate : function()
27961 var d = this.dayField.validate();
27962 var m = this.monthField.validate();
27963 var y = this.yearField.validate();
27968 (!this.dayAllowBlank && !d) ||
27969 (!this.monthAllowBlank && !m) ||
27970 (!this.yearAllowBlank && !y)
27975 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27984 this.markInvalid();
27989 markValid : function()
27992 var label = this.el.select('label', true).first();
27993 var icon = this.el.select('i.fa-star', true).first();
27999 this.fireEvent('valid', this);
28003 * Mark this field as invalid
28004 * @param {String} msg The validation message
28006 markInvalid : function(msg)
28009 var label = this.el.select('label', true).first();
28010 var icon = this.el.select('i.fa-star', true).first();
28012 if(label && !icon){
28013 this.el.select('.roo-date-split-field-label', true).createChild({
28015 cls : 'text-danger fa fa-lg fa-star',
28016 tooltip : 'This field is required',
28017 style : 'margin-right:5px;'
28021 this.fireEvent('invalid', this, msg);
28024 clearInvalid : function()
28026 var label = this.el.select('label', true).first();
28027 var icon = this.el.select('i.fa-star', true).first();
28033 this.fireEvent('valid', this);
28036 getName: function()