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) {
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;
2439 if(this.tabIndex !== undefined){
2440 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2444 this.bodyEl = this.el.select('.modal-body',true).first();
2445 this.closeEl = this.el.select('.modal-header .close', true).first();
2446 this.footerEl = this.el.select('.modal-footer',true).first();
2447 this.titleEl = this.el.select('.modal-title',true).first();
2451 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2452 this.maskEl.enableDisplayMode("block");
2454 //this.el.addClass("x-dlg-modal");
2456 if (this.buttons.length) {
2457 Roo.each(this.buttons, function(bb) {
2458 var b = Roo.apply({}, bb);
2459 b.xns = b.xns || Roo.bootstrap;
2460 b.xtype = b.xtype || 'Button';
2461 if (typeof(b.listeners) == 'undefined') {
2462 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2465 var btn = Roo.factory(b);
2467 btn.onRender(this.el.select('.modal-footer div').first());
2471 // render the children.
2474 if(typeof(this.items) != 'undefined'){
2475 var items = this.items;
2478 for(var i =0;i < items.length;i++) {
2479 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2483 this.items = nitems;
2485 // where are these used - they used to be body/close/footer
2489 //this.el.addClass([this.fieldClass, this.cls]);
2493 getAutoCreate : function(){
2498 html : this.html || ''
2503 cls : 'modal-title',
2507 if(this.specificTitle){
2513 if (this.allow_close) {
2524 style : 'display: none',
2527 cls: "modal-dialog",
2530 cls : "modal-content",
2533 cls : 'modal-header',
2538 cls : 'modal-footer',
2542 cls: 'btn-' + this.buttonPosition
2559 modal.cls += ' fade';
2565 getChildContainer : function() {
2570 getButtonContainer : function() {
2571 return this.el.select('.modal-footer div',true).first();
2574 initEvents : function()
2576 if (this.allow_close) {
2577 this.closeEl.on('click', this.hide, this);
2582 window.addEventListener("resize", function() { _this.resize(); } );
2588 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2593 if (!this.rendered) {
2597 this.el.setStyle('display', 'block');
2599 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2602 this.el.addClass('in');
2605 this.el.addClass('in');
2609 // not sure how we can show data in here..
2611 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2614 Roo.get(document.body).addClass("x-body-masked");
2615 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2617 this.el.setStyle('zIndex', '10001');
2619 this.fireEvent('show', this);
2627 Roo.get(document.body).removeClass("x-body-masked");
2628 this.el.removeClass('in');
2629 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2631 if(this.animate){ // why
2633 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2635 this.el.setStyle('display', 'none');
2638 this.fireEvent('hide', this);
2641 addButton : function(str, cb)
2645 var b = Roo.apply({}, { html : str } );
2646 b.xns = b.xns || Roo.bootstrap;
2647 b.xtype = b.xtype || 'Button';
2648 if (typeof(b.listeners) == 'undefined') {
2649 b.listeners = { click : cb.createDelegate(this) };
2652 var btn = Roo.factory(b);
2654 btn.onRender(this.el.select('.modal-footer div').first());
2660 setDefaultButton : function(btn)
2662 //this.el.select('.modal-footer').()
2664 resizeTo: function(w,h)
2668 setContentSize : function(w, h)
2672 onButtonClick: function(btn,e)
2675 this.fireEvent('btnclick', btn.name, e);
2678 * Set the title of the Dialog
2679 * @param {String} str new Title
2681 setTitle: function(str) {
2682 this.titleEl.dom.innerHTML = str;
2685 * Set the body of the Dialog
2686 * @param {String} str new Title
2688 setBody: function(str) {
2689 this.bodyEl.dom.innerHTML = str;
2692 * Set the body of the Dialog using the template
2693 * @param {Obj} data - apply this data to the template and replace the body contents.
2695 applyBody: function(obj)
2698 Roo.log("Error - using apply Body without a template");
2701 this.tmpl.overwrite(this.bodyEl, obj);
2707 Roo.apply(Roo.bootstrap.Modal, {
2709 * Button config that displays a single OK button
2718 * Button config that displays Yes and No buttons
2734 * Button config that displays OK and Cancel buttons
2749 * Button config that displays Yes, No and Cancel buttons
2772 * messagebox - can be used as a replace
2776 * @class Roo.MessageBox
2777 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2781 Roo.Msg.alert('Status', 'Changes saved successfully.');
2783 // Prompt for user data:
2784 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2786 // process text value...
2790 // Show a dialog using config options:
2792 title:'Save Changes?',
2793 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2794 buttons: Roo.Msg.YESNOCANCEL,
2801 Roo.bootstrap.MessageBox = function(){
2802 var dlg, opt, mask, waitTimer;
2803 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2804 var buttons, activeTextEl, bwidth;
2808 var handleButton = function(button){
2810 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2814 var handleHide = function(){
2816 dlg.el.removeClass(opt.cls);
2819 // Roo.TaskMgr.stop(waitTimer);
2820 // waitTimer = null;
2825 var updateButtons = function(b){
2828 buttons["ok"].hide();
2829 buttons["cancel"].hide();
2830 buttons["yes"].hide();
2831 buttons["no"].hide();
2832 //dlg.footer.dom.style.display = 'none';
2835 dlg.footerEl.dom.style.display = '';
2836 for(var k in buttons){
2837 if(typeof buttons[k] != "function"){
2840 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2841 width += buttons[k].el.getWidth()+15;
2851 var handleEsc = function(d, k, e){
2852 if(opt && opt.closable !== false){
2862 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2863 * @return {Roo.BasicDialog} The BasicDialog element
2865 getDialog : function(){
2867 dlg = new Roo.bootstrap.Modal( {
2870 //constraintoviewport:false,
2872 //collapsible : false,
2877 //buttonAlign:"center",
2878 closeClick : function(){
2879 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2882 handleButton("cancel");
2887 dlg.on("hide", handleHide);
2889 //dlg.addKeyListener(27, handleEsc);
2891 this.buttons = buttons;
2892 var bt = this.buttonText;
2893 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2894 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2895 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2896 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2898 bodyEl = dlg.bodyEl.createChild({
2900 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2901 '<textarea class="roo-mb-textarea"></textarea>' +
2902 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2904 msgEl = bodyEl.dom.firstChild;
2905 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2906 textboxEl.enableDisplayMode();
2907 textboxEl.addKeyListener([10,13], function(){
2908 if(dlg.isVisible() && opt && opt.buttons){
2911 }else if(opt.buttons.yes){
2912 handleButton("yes");
2916 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2917 textareaEl.enableDisplayMode();
2918 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2919 progressEl.enableDisplayMode();
2920 var pf = progressEl.dom.firstChild;
2922 pp = Roo.get(pf.firstChild);
2923 pp.setHeight(pf.offsetHeight);
2931 * Updates the message box body text
2932 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2933 * the XHTML-compliant non-breaking space character '&#160;')
2934 * @return {Roo.MessageBox} This message box
2936 updateText : function(text){
2937 if(!dlg.isVisible() && !opt.width){
2938 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2940 msgEl.innerHTML = text || ' ';
2942 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2943 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2945 Math.min(opt.width || cw , this.maxWidth),
2946 Math.max(opt.minWidth || this.minWidth, bwidth)
2949 activeTextEl.setWidth(w);
2951 if(dlg.isVisible()){
2952 dlg.fixedcenter = false;
2954 // to big, make it scroll. = But as usual stupid IE does not support
2957 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2958 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2959 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2961 bodyEl.dom.style.height = '';
2962 bodyEl.dom.style.overflowY = '';
2965 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2967 bodyEl.dom.style.overflowX = '';
2970 dlg.setContentSize(w, bodyEl.getHeight());
2971 if(dlg.isVisible()){
2972 dlg.fixedcenter = true;
2978 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2979 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2980 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2981 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2982 * @return {Roo.MessageBox} This message box
2984 updateProgress : function(value, text){
2986 this.updateText(text);
2988 if (pp) { // weird bug on my firefox - for some reason this is not defined
2989 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2995 * Returns true if the message box is currently displayed
2996 * @return {Boolean} True if the message box is visible, else false
2998 isVisible : function(){
2999 return dlg && dlg.isVisible();
3003 * Hides the message box if it is displayed
3006 if(this.isVisible()){
3012 * Displays a new message box, or reinitializes an existing message box, based on the config options
3013 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3014 * The following config object properties are supported:
3016 Property Type Description
3017 ---------- --------------- ------------------------------------------------------------------------------------
3018 animEl String/Element An id or Element from which the message box should animate as it opens and
3019 closes (defaults to undefined)
3020 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3021 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3022 closable Boolean False to hide the top-right close button (defaults to true). Note that
3023 progress and wait dialogs will ignore this property and always hide the
3024 close button as they can only be closed programmatically.
3025 cls String A custom CSS class to apply to the message box element
3026 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3027 displayed (defaults to 75)
3028 fn Function A callback function to execute after closing the dialog. The arguments to the
3029 function will be btn (the name of the button that was clicked, if applicable,
3030 e.g. "ok"), and text (the value of the active text field, if applicable).
3031 Progress and wait dialogs will ignore this option since they do not respond to
3032 user actions and can only be closed programmatically, so any required function
3033 should be called by the same code after it closes the dialog.
3034 icon String A CSS class that provides a background image to be used as an icon for
3035 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3036 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3037 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3038 modal Boolean False to allow user interaction with the page while the message box is
3039 displayed (defaults to true)
3040 msg String A string that will replace the existing message box body text (defaults
3041 to the XHTML-compliant non-breaking space character ' ')
3042 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3043 progress Boolean True to display a progress bar (defaults to false)
3044 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3045 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3046 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3047 title String The title text
3048 value String The string value to set into the active textbox element if displayed
3049 wait Boolean True to display a progress bar (defaults to false)
3050 width Number The width of the dialog in pixels
3057 msg: 'Please enter your address:',
3059 buttons: Roo.MessageBox.OKCANCEL,
3062 animEl: 'addAddressBtn'
3065 * @param {Object} config Configuration options
3066 * @return {Roo.MessageBox} This message box
3068 show : function(options)
3071 // this causes nightmares if you show one dialog after another
3072 // especially on callbacks..
3074 if(this.isVisible()){
3077 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3078 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3079 Roo.log("New Dialog Message:" + options.msg )
3080 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3081 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3084 var d = this.getDialog();
3086 d.setTitle(opt.title || " ");
3087 d.closeEl.setDisplayed(opt.closable !== false);
3088 activeTextEl = textboxEl;
3089 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3094 textareaEl.setHeight(typeof opt.multiline == "number" ?
3095 opt.multiline : this.defaultTextHeight);
3096 activeTextEl = textareaEl;
3105 progressEl.setDisplayed(opt.progress === true);
3106 this.updateProgress(0);
3107 activeTextEl.dom.value = opt.value || "";
3109 dlg.setDefaultButton(activeTextEl);
3111 var bs = opt.buttons;
3115 }else if(bs && bs.yes){
3116 db = buttons["yes"];
3118 dlg.setDefaultButton(db);
3120 bwidth = updateButtons(opt.buttons);
3121 this.updateText(opt.msg);
3123 d.el.addClass(opt.cls);
3125 d.proxyDrag = opt.proxyDrag === true;
3126 d.modal = opt.modal !== false;
3127 d.mask = opt.modal !== false ? mask : false;
3129 // force it to the end of the z-index stack so it gets a cursor in FF
3130 document.body.appendChild(dlg.el.dom);
3131 d.animateTarget = null;
3132 d.show(options.animEl);
3138 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3139 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3140 * and closing the message box when the process is complete.
3141 * @param {String} title The title bar text
3142 * @param {String} msg The message box body text
3143 * @return {Roo.MessageBox} This message box
3145 progress : function(title, msg){
3152 minWidth: this.minProgressWidth,
3159 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3160 * If a callback function is passed it will be called after the user clicks the button, and the
3161 * id of the button that was clicked will be passed as the only parameter to the callback
3162 * (could also be the top-right close button).
3163 * @param {String} title The title bar text
3164 * @param {String} msg The message box body text
3165 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3166 * @param {Object} scope (optional) The scope of the callback function
3167 * @return {Roo.MessageBox} This message box
3169 alert : function(title, msg, fn, scope){
3182 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3183 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3184 * You are responsible for closing the message box when the process is complete.
3185 * @param {String} msg The message box body text
3186 * @param {String} title (optional) The title bar text
3187 * @return {Roo.MessageBox} This message box
3189 wait : function(msg, title){
3200 waitTimer = Roo.TaskMgr.start({
3202 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3210 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3211 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3212 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3213 * @param {String} title The title bar text
3214 * @param {String} msg The message box body text
3215 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3216 * @param {Object} scope (optional) The scope of the callback function
3217 * @return {Roo.MessageBox} This message box
3219 confirm : function(title, msg, fn, scope){
3223 buttons: this.YESNO,
3232 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3233 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3234 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3235 * (could also be the top-right close button) and the text that was entered will be passed as the two
3236 * parameters to the callback.
3237 * @param {String} title The title bar text
3238 * @param {String} msg The message box body text
3239 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3240 * @param {Object} scope (optional) The scope of the callback function
3241 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3242 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3243 * @return {Roo.MessageBox} This message box
3245 prompt : function(title, msg, fn, scope, multiline){
3249 buttons: this.OKCANCEL,
3254 multiline: multiline,
3261 * Button config that displays a single OK button
3266 * Button config that displays Yes and No buttons
3269 YESNO : {yes:true, no:true},
3271 * Button config that displays OK and Cancel buttons
3274 OKCANCEL : {ok:true, cancel:true},
3276 * Button config that displays Yes, No and Cancel buttons
3279 YESNOCANCEL : {yes:true, no:true, cancel:true},
3282 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3285 defaultTextHeight : 75,
3287 * The maximum width in pixels of the message box (defaults to 600)
3292 * The minimum width in pixels of the message box (defaults to 100)
3297 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3298 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3301 minProgressWidth : 250,
3303 * An object containing the default button text strings that can be overriden for localized language support.
3304 * Supported properties are: ok, cancel, yes and no.
3305 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3318 * Shorthand for {@link Roo.MessageBox}
3320 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3321 Roo.Msg = Roo.Msg || Roo.MessageBox;
3330 * @class Roo.bootstrap.Navbar
3331 * @extends Roo.bootstrap.Component
3332 * Bootstrap Navbar class
3335 * Create a new Navbar
3336 * @param {Object} config The config object
3340 Roo.bootstrap.Navbar = function(config){
3341 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3354 getAutoCreate : function(){
3357 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3361 initEvents :function ()
3363 //Roo.log(this.el.select('.navbar-toggle',true));
3364 this.el.select('.navbar-toggle',true).on('click', function() {
3365 // Roo.log('click');
3366 this.el.select('.navbar-collapse',true).toggleClass('in');
3374 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3376 var size = this.el.getSize();
3377 this.maskEl.setSize(size.width, size.height);
3378 this.maskEl.enableDisplayMode("block");
3387 getChildContainer : function()
3389 if (this.el.select('.collapse').getCount()) {
3390 return this.el.select('.collapse',true).first();
3423 * @class Roo.bootstrap.NavSimplebar
3424 * @extends Roo.bootstrap.Navbar
3425 * Bootstrap Sidebar class
3427 * @cfg {Boolean} inverse is inverted color
3429 * @cfg {String} type (nav | pills | tabs)
3430 * @cfg {Boolean} arrangement stacked | justified
3431 * @cfg {String} align (left | right) alignment
3433 * @cfg {Boolean} main (true|false) main nav bar? default false
3434 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3436 * @cfg {String} tag (header|footer|nav|div) default is nav
3442 * Create a new Sidebar
3443 * @param {Object} config The config object
3447 Roo.bootstrap.NavSimplebar = function(config){
3448 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3451 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3467 getAutoCreate : function(){
3471 tag : this.tag || 'div',
3484 this.type = this.type || 'nav';
3485 if (['tabs','pills'].indexOf(this.type)!==-1) {
3486 cfg.cn[0].cls += ' nav-' + this.type
3490 if (this.type!=='nav') {
3491 Roo.log('nav type must be nav/tabs/pills')
3493 cfg.cn[0].cls += ' navbar-nav'
3499 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3500 cfg.cn[0].cls += ' nav-' + this.arrangement;
3504 if (this.align === 'right') {
3505 cfg.cn[0].cls += ' navbar-right';
3509 cfg.cls += ' navbar-inverse';
3536 * @class Roo.bootstrap.NavHeaderbar
3537 * @extends Roo.bootstrap.NavSimplebar
3538 * Bootstrap Sidebar class
3540 * @cfg {String} brand what is brand
3541 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3542 * @cfg {String} brand_href href of the brand
3543 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3544 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3545 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3546 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3549 * Create a new Sidebar
3550 * @param {Object} config The config object
3554 Roo.bootstrap.NavHeaderbar = function(config){
3555 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3559 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3566 desktopCenter : false,
3569 getAutoCreate : function(){
3572 tag: this.nav || 'nav',
3579 if (this.desktopCenter) {
3580 cn.push({cls : 'container', cn : []});
3587 cls: 'navbar-header',
3592 cls: 'navbar-toggle',
3593 'data-toggle': 'collapse',
3598 html: 'Toggle navigation'
3620 cls: 'collapse navbar-collapse',
3624 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3626 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3627 cfg.cls += ' navbar-' + this.position;
3629 // tag can override this..
3631 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3634 if (this.brand !== '') {
3637 href: this.brand_href ? this.brand_href : '#',
3638 cls: 'navbar-brand',
3646 cfg.cls += ' main-nav';
3654 getHeaderChildContainer : function()
3656 if (this.el.select('.navbar-header').getCount()) {
3657 return this.el.select('.navbar-header',true).first();
3660 return this.getChildContainer();
3664 initEvents : function()
3666 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3668 if (this.autohide) {
3673 Roo.get(document).on('scroll',function(e) {
3674 var ns = Roo.get(document).getScroll().top;
3675 var os = prevScroll;
3679 ft.removeClass('slideDown');
3680 ft.addClass('slideUp');
3683 ft.removeClass('slideUp');
3684 ft.addClass('slideDown');
3705 * @class Roo.bootstrap.NavSidebar
3706 * @extends Roo.bootstrap.Navbar
3707 * Bootstrap Sidebar class
3710 * Create a new Sidebar
3711 * @param {Object} config The config object
3715 Roo.bootstrap.NavSidebar = function(config){
3716 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3719 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3721 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3723 getAutoCreate : function(){
3728 cls: 'sidebar sidebar-nav'
3750 * @class Roo.bootstrap.NavGroup
3751 * @extends Roo.bootstrap.Component
3752 * Bootstrap NavGroup class
3753 * @cfg {String} align (left|right)
3754 * @cfg {Boolean} inverse
3755 * @cfg {String} type (nav|pills|tab) default nav
3756 * @cfg {String} navId - reference Id for navbar.
3760 * Create a new nav group
3761 * @param {Object} config The config object
3764 Roo.bootstrap.NavGroup = function(config){
3765 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3768 Roo.bootstrap.NavGroup.register(this);
3772 * Fires when the active item changes
3773 * @param {Roo.bootstrap.NavGroup} this
3774 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3775 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3782 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3793 getAutoCreate : function()
3795 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3802 if (['tabs','pills'].indexOf(this.type)!==-1) {
3803 cfg.cls += ' nav-' + this.type
3805 if (this.type!=='nav') {
3806 Roo.log('nav type must be nav/tabs/pills')
3808 cfg.cls += ' navbar-nav'
3811 if (this.parent().sidebar) {
3814 cls: 'dashboard-menu sidebar-menu'
3820 if (this.form === true) {
3826 if (this.align === 'right') {
3827 cfg.cls += ' navbar-right';
3829 cfg.cls += ' navbar-left';
3833 if (this.align === 'right') {
3834 cfg.cls += ' navbar-right';
3838 cfg.cls += ' navbar-inverse';
3846 * sets the active Navigation item
3847 * @param {Roo.bootstrap.NavItem} the new current navitem
3849 setActiveItem : function(item)
3852 Roo.each(this.navItems, function(v){
3857 v.setActive(false, true);
3864 item.setActive(true, true);
3865 this.fireEvent('changed', this, item, prev);
3870 * gets the active Navigation item
3871 * @return {Roo.bootstrap.NavItem} the current navitem
3873 getActive : function()
3877 Roo.each(this.navItems, function(v){
3888 indexOfNav : function()
3892 Roo.each(this.navItems, function(v,i){
3903 * adds a Navigation item
3904 * @param {Roo.bootstrap.NavItem} the navitem to add
3906 addItem : function(cfg)
3908 var cn = new Roo.bootstrap.NavItem(cfg);
3910 cn.parentId = this.id;
3911 cn.onRender(this.el, null);
3915 * register a Navigation item
3916 * @param {Roo.bootstrap.NavItem} the navitem to add
3918 register : function(item)
3920 this.navItems.push( item);
3921 item.navId = this.navId;
3926 * clear all the Navigation item
3929 clearAll : function()
3932 this.el.dom.innerHTML = '';
3935 getNavItem: function(tabId)
3938 Roo.each(this.navItems, function(e) {
3939 if (e.tabId == tabId) {
3949 setActiveNext : function()
3951 var i = this.indexOfNav(this.getActive());
3952 if (i > this.navItems.length) {
3955 this.setActiveItem(this.navItems[i+1]);
3957 setActivePrev : function()
3959 var i = this.indexOfNav(this.getActive());
3963 this.setActiveItem(this.navItems[i-1]);
3965 clearWasActive : function(except) {
3966 Roo.each(this.navItems, function(e) {
3967 if (e.tabId != except.tabId && e.was_active) {
3968 e.was_active = false;
3975 getWasActive : function ()
3978 Roo.each(this.navItems, function(e) {
3993 Roo.apply(Roo.bootstrap.NavGroup, {
3997 * register a Navigation Group
3998 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4000 register : function(navgrp)
4002 this.groups[navgrp.navId] = navgrp;
4006 * fetch a Navigation Group based on the navigation ID
4007 * @param {string} the navgroup to add
4008 * @returns {Roo.bootstrap.NavGroup} the navgroup
4010 get: function(navId) {
4011 if (typeof(this.groups[navId]) == 'undefined') {
4013 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4015 return this.groups[navId] ;
4030 * @class Roo.bootstrap.NavItem
4031 * @extends Roo.bootstrap.Component
4032 * Bootstrap Navbar.NavItem class
4033 * @cfg {String} href link to
4034 * @cfg {String} html content of button
4035 * @cfg {String} badge text inside badge
4036 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4037 * @cfg {String} glyphicon name of glyphicon
4038 * @cfg {String} icon name of font awesome icon
4039 * @cfg {Boolean} active Is item active
4040 * @cfg {Boolean} disabled Is item disabled
4042 * @cfg {Boolean} preventDefault (true | false) default false
4043 * @cfg {String} tabId the tab that this item activates.
4044 * @cfg {String} tagtype (a|span) render as a href or span?
4045 * @cfg {Boolean} animateRef (true|false) link to element default false
4048 * Create a new Navbar Item
4049 * @param {Object} config The config object
4051 Roo.bootstrap.NavItem = function(config){
4052 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4057 * The raw click event for the entire grid.
4058 * @param {Roo.EventObject} e
4063 * Fires when the active item active state changes
4064 * @param {Roo.bootstrap.NavItem} this
4065 * @param {boolean} state the new state
4071 * Fires when scroll to element
4072 * @param {Roo.bootstrap.NavItem} this
4073 * @param {Object} options
4074 * @param {Roo.EventObject} e
4082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4090 preventDefault : false,
4097 getAutoCreate : function(){
4106 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4108 if (this.disabled) {
4109 cfg.cls += ' disabled';
4112 if (this.href || this.html || this.glyphicon || this.icon) {
4116 href : this.href || "#",
4117 html: this.html || ''
4122 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4125 if(this.glyphicon) {
4126 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4131 cfg.cn[0].html += " <span class='caret'></span>";
4135 if (this.badge !== '') {
4137 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4145 initEvents: function()
4147 if (typeof (this.menu) != 'undefined') {
4148 this.menu.parentType = this.xtype;
4149 this.menu.triggerEl = this.el;
4150 this.menu = this.addxtype(Roo.apply({}, this.menu));
4153 this.el.select('a',true).on('click', this.onClick, this);
4155 if(this.tagtype == 'span'){
4156 this.el.select('span',true).on('click', this.onClick, this);
4159 // at this point parent should be available..
4160 this.parent().register(this);
4163 onClick : function(e)
4166 this.preventDefault ||
4173 if (this.disabled) {
4177 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4178 if (tg && tg.transition) {
4179 Roo.log("waiting for the transitionend");
4185 //Roo.log("fire event clicked");
4186 if(this.fireEvent('click', this, e) === false){
4190 if(this.tagtype == 'span'){
4194 //Roo.log(this.href);
4195 var ael = this.el.select('a',true).first();
4198 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4199 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4200 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4201 return; // ignore... - it's a 'hash' to another page.
4205 this.scrollToElement(e);
4209 var p = this.parent();
4211 if (['tabs','pills'].indexOf(p.type)!==-1) {
4212 if (typeof(p.setActiveItem) !== 'undefined') {
4213 p.setActiveItem(this);
4217 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4218 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4219 // remove the collapsed menu expand...
4220 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4224 isActive: function () {
4227 setActive : function(state, fire, is_was_active)
4229 if (this.active && !state && this.navId) {
4230 this.was_active = true;
4231 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4233 nv.clearWasActive(this);
4237 this.active = state;
4240 this.el.removeClass('active');
4241 } else if (!this.el.hasClass('active')) {
4242 this.el.addClass('active');
4245 this.fireEvent('changed', this, state);
4248 // show a panel if it's registered and related..
4250 if (!this.navId || !this.tabId || !state || is_was_active) {
4254 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4258 var pan = tg.getPanelByName(this.tabId);
4262 // if we can not flip to new panel - go back to old nav highlight..
4263 if (false == tg.showPanel(pan)) {
4264 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4266 var onav = nv.getWasActive();
4268 onav.setActive(true, false, true);
4277 // this should not be here...
4278 setDisabled : function(state)
4280 this.disabled = state;
4282 this.el.removeClass('disabled');
4283 } else if (!this.el.hasClass('disabled')) {
4284 this.el.addClass('disabled');
4290 * Fetch the element to display the tooltip on.
4291 * @return {Roo.Element} defaults to this.el
4293 tooltipEl : function()
4295 return this.el.select('' + this.tagtype + '', true).first();
4298 scrollToElement : function(e)
4300 var c = document.body;
4303 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4305 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4306 c = document.documentElement;
4309 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4315 var o = target.calcOffsetsTo(c);
4322 this.fireEvent('scrollto', this, options, e);
4324 Roo.get(c).scrollTo('top', options.value, true);
4337 * <span> icon </span>
4338 * <span> text </span>
4339 * <span>badge </span>
4343 * @class Roo.bootstrap.NavSidebarItem
4344 * @extends Roo.bootstrap.NavItem
4345 * Bootstrap Navbar.NavSidebarItem class
4346 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4348 * Create a new Navbar Button
4349 * @param {Object} config The config object
4351 Roo.bootstrap.NavSidebarItem = function(config){
4352 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4357 * The raw click event for the entire grid.
4358 * @param {Roo.EventObject} e
4363 * Fires when the active item active state changes
4364 * @param {Roo.bootstrap.NavSidebarItem} this
4365 * @param {boolean} state the new state
4373 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4375 badgeWeight : 'default',
4377 getAutoCreate : function(){
4382 href : this.href || '#',
4394 html : this.html || ''
4399 cfg.cls += ' active';
4402 if (this.disabled) {
4403 cfg.cls += ' disabled';
4407 if (this.glyphicon || this.icon) {
4408 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4409 a.cn.push({ tag : 'i', cls : c }) ;
4414 if (this.badge !== '') {
4416 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4420 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4421 a.cls += 'dropdown-toggle treeview' ;
4432 initEvents : function()
4434 this.el.on('click', this.onClick, this);
4437 if(this.badge !== ''){
4439 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4444 onClick : function(e)
4451 if(this.preventDefault){
4455 this.fireEvent('click', this);
4458 disable : function()
4460 this.setDisabled(true);
4465 this.setDisabled(false);
4468 setDisabled : function(state)
4470 if(this.disabled == state){
4474 this.disabled = state;
4477 this.el.addClass('disabled');
4481 this.el.removeClass('disabled');
4486 setActive : function(state)
4488 if(this.active == state){
4492 this.active = state;
4495 this.el.addClass('active');
4499 this.el.removeClass('active');
4504 isActive: function ()
4509 setBadge : function(str)
4515 this.badgeEl.dom.innerHTML = str;
4532 * @class Roo.bootstrap.Row
4533 * @extends Roo.bootstrap.Component
4534 * Bootstrap Row class (contains columns...)
4538 * @param {Object} config The config object
4541 Roo.bootstrap.Row = function(config){
4542 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4545 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4547 getAutoCreate : function(){
4566 * @class Roo.bootstrap.Element
4567 * @extends Roo.bootstrap.Component
4568 * Bootstrap Element class
4569 * @cfg {String} html contents of the element
4570 * @cfg {String} tag tag of the element
4571 * @cfg {String} cls class of the element
4572 * @cfg {Boolean} preventDefault (true|false) default false
4573 * @cfg {Boolean} clickable (true|false) default false
4576 * Create a new Element
4577 * @param {Object} config The config object
4580 Roo.bootstrap.Element = function(config){
4581 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4587 * When a element is chick
4588 * @param {Roo.bootstrap.Element} this
4589 * @param {Roo.EventObject} e
4595 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4600 preventDefault: false,
4603 getAutoCreate : function(){
4614 initEvents: function()
4616 Roo.bootstrap.Element.superclass.initEvents.call(this);
4619 this.el.on('click', this.onClick, this);
4624 onClick : function(e)
4626 if(this.preventDefault){
4630 this.fireEvent('click', this, e);
4633 getValue : function()
4635 return this.el.dom.innerHTML;
4638 setValue : function(value)
4640 this.el.dom.innerHTML = value;
4655 * @class Roo.bootstrap.Pagination
4656 * @extends Roo.bootstrap.Component
4657 * Bootstrap Pagination class
4658 * @cfg {String} size xs | sm | md | lg
4659 * @cfg {Boolean} inverse false | true
4662 * Create a new Pagination
4663 * @param {Object} config The config object
4666 Roo.bootstrap.Pagination = function(config){
4667 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4670 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4676 getAutoCreate : function(){
4682 cfg.cls += ' inverse';
4688 cfg.cls += " " + this.cls;
4706 * @class Roo.bootstrap.PaginationItem
4707 * @extends Roo.bootstrap.Component
4708 * Bootstrap PaginationItem class
4709 * @cfg {String} html text
4710 * @cfg {String} href the link
4711 * @cfg {Boolean} preventDefault (true | false) default true
4712 * @cfg {Boolean} active (true | false) default false
4713 * @cfg {Boolean} disabled default false
4717 * Create a new PaginationItem
4718 * @param {Object} config The config object
4722 Roo.bootstrap.PaginationItem = function(config){
4723 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4728 * The raw click event for the entire grid.
4729 * @param {Roo.EventObject} e
4735 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4739 preventDefault: true,
4744 getAutoCreate : function(){
4750 href : this.href ? this.href : '#',
4751 html : this.html ? this.html : ''
4761 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4765 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4771 initEvents: function() {
4773 this.el.on('click', this.onClick, this);
4776 onClick : function(e)
4778 Roo.log('PaginationItem on click ');
4779 if(this.preventDefault){
4787 this.fireEvent('click', this, e);
4803 * @class Roo.bootstrap.Slider
4804 * @extends Roo.bootstrap.Component
4805 * Bootstrap Slider class
4808 * Create a new Slider
4809 * @param {Object} config The config object
4812 Roo.bootstrap.Slider = function(config){
4813 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4816 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4818 getAutoCreate : function(){
4822 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4826 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4838 * Ext JS Library 1.1.1
4839 * Copyright(c) 2006-2007, Ext JS, LLC.
4841 * Originally Released Under LGPL - original licence link has changed is not relivant.
4844 * <script type="text/javascript">
4849 * @class Roo.grid.ColumnModel
4850 * @extends Roo.util.Observable
4851 * This is the default implementation of a ColumnModel used by the Grid. It defines
4852 * the columns in the grid.
4855 var colModel = new Roo.grid.ColumnModel([
4856 {header: "Ticker", width: 60, sortable: true, locked: true},
4857 {header: "Company Name", width: 150, sortable: true},
4858 {header: "Market Cap.", width: 100, sortable: true},
4859 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4860 {header: "Employees", width: 100, sortable: true, resizable: false}
4865 * The config options listed for this class are options which may appear in each
4866 * individual column definition.
4867 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4869 * @param {Object} config An Array of column config objects. See this class's
4870 * config objects for details.
4872 Roo.grid.ColumnModel = function(config){
4874 * The config passed into the constructor
4876 this.config = config;
4879 // if no id, create one
4880 // if the column does not have a dataIndex mapping,
4881 // map it to the order it is in the config
4882 for(var i = 0, len = config.length; i < len; i++){
4884 if(typeof c.dataIndex == "undefined"){
4887 if(typeof c.renderer == "string"){
4888 c.renderer = Roo.util.Format[c.renderer];
4890 if(typeof c.id == "undefined"){
4893 if(c.editor && c.editor.xtype){
4894 c.editor = Roo.factory(c.editor, Roo.grid);
4896 if(c.editor && c.editor.isFormField){
4897 c.editor = new Roo.grid.GridEditor(c.editor);
4899 this.lookup[c.id] = c;
4903 * The width of columns which have no width specified (defaults to 100)
4906 this.defaultWidth = 100;
4909 * Default sortable of columns which have no sortable specified (defaults to false)
4912 this.defaultSortable = false;
4916 * @event widthchange
4917 * Fires when the width of a column changes.
4918 * @param {ColumnModel} this
4919 * @param {Number} columnIndex The column index
4920 * @param {Number} newWidth The new width
4922 "widthchange": true,
4924 * @event headerchange
4925 * Fires when the text of a header changes.
4926 * @param {ColumnModel} this
4927 * @param {Number} columnIndex The column index
4928 * @param {Number} newText The new header text
4930 "headerchange": true,
4932 * @event hiddenchange
4933 * Fires when a column is hidden or "unhidden".
4934 * @param {ColumnModel} this
4935 * @param {Number} columnIndex The column index
4936 * @param {Boolean} hidden true if hidden, false otherwise
4938 "hiddenchange": true,
4940 * @event columnmoved
4941 * Fires when a column is moved.
4942 * @param {ColumnModel} this
4943 * @param {Number} oldIndex
4944 * @param {Number} newIndex
4946 "columnmoved" : true,
4948 * @event columlockchange
4949 * Fires when a column's locked state is changed
4950 * @param {ColumnModel} this
4951 * @param {Number} colIndex
4952 * @param {Boolean} locked true if locked
4954 "columnlockchange" : true
4956 Roo.grid.ColumnModel.superclass.constructor.call(this);
4958 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4960 * @cfg {String} header The header text to display in the Grid view.
4963 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4964 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4965 * specified, the column's index is used as an index into the Record's data Array.
4968 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4969 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4972 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4973 * Defaults to the value of the {@link #defaultSortable} property.
4974 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4977 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4980 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4983 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4986 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4989 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4990 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4991 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4992 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4995 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4998 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5001 * @cfg {String} cursor (Optional)
5004 * @cfg {String} tooltip (Optional)
5007 * @cfg {Number} xs (Optional)
5010 * @cfg {Number} sm (Optional)
5013 * @cfg {Number} md (Optional)
5016 * @cfg {Number} lg (Optional)
5019 * Returns the id of the column at the specified index.
5020 * @param {Number} index The column index
5021 * @return {String} the id
5023 getColumnId : function(index){
5024 return this.config[index].id;
5028 * Returns the column for a specified id.
5029 * @param {String} id The column id
5030 * @return {Object} the column
5032 getColumnById : function(id){
5033 return this.lookup[id];
5038 * Returns the column for a specified dataIndex.
5039 * @param {String} dataIndex The column dataIndex
5040 * @return {Object|Boolean} the column or false if not found
5042 getColumnByDataIndex: function(dataIndex){
5043 var index = this.findColumnIndex(dataIndex);
5044 return index > -1 ? this.config[index] : false;
5048 * Returns the index for a specified column id.
5049 * @param {String} id The column id
5050 * @return {Number} the index, or -1 if not found
5052 getIndexById : function(id){
5053 for(var i = 0, len = this.config.length; i < len; i++){
5054 if(this.config[i].id == id){
5062 * Returns the index for a specified column dataIndex.
5063 * @param {String} dataIndex The column dataIndex
5064 * @return {Number} the index, or -1 if not found
5067 findColumnIndex : function(dataIndex){
5068 for(var i = 0, len = this.config.length; i < len; i++){
5069 if(this.config[i].dataIndex == dataIndex){
5077 moveColumn : function(oldIndex, newIndex){
5078 var c = this.config[oldIndex];
5079 this.config.splice(oldIndex, 1);
5080 this.config.splice(newIndex, 0, c);
5081 this.dataMap = null;
5082 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5085 isLocked : function(colIndex){
5086 return this.config[colIndex].locked === true;
5089 setLocked : function(colIndex, value, suppressEvent){
5090 if(this.isLocked(colIndex) == value){
5093 this.config[colIndex].locked = value;
5095 this.fireEvent("columnlockchange", this, colIndex, value);
5099 getTotalLockedWidth : function(){
5101 for(var i = 0; i < this.config.length; i++){
5102 if(this.isLocked(i) && !this.isHidden(i)){
5103 this.totalWidth += this.getColumnWidth(i);
5109 getLockedCount : function(){
5110 for(var i = 0, len = this.config.length; i < len; i++){
5111 if(!this.isLocked(i)){
5118 * Returns the number of columns.
5121 getColumnCount : function(visibleOnly){
5122 if(visibleOnly === true){
5124 for(var i = 0, len = this.config.length; i < len; i++){
5125 if(!this.isHidden(i)){
5131 return this.config.length;
5135 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5136 * @param {Function} fn
5137 * @param {Object} scope (optional)
5138 * @return {Array} result
5140 getColumnsBy : function(fn, scope){
5142 for(var i = 0, len = this.config.length; i < len; i++){
5143 var c = this.config[i];
5144 if(fn.call(scope||this, c, i) === true){
5152 * Returns true if the specified column is sortable.
5153 * @param {Number} col The column index
5156 isSortable : function(col){
5157 if(typeof this.config[col].sortable == "undefined"){
5158 return this.defaultSortable;
5160 return this.config[col].sortable;
5164 * Returns the rendering (formatting) function defined for the column.
5165 * @param {Number} col The column index.
5166 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5168 getRenderer : function(col){
5169 if(!this.config[col].renderer){
5170 return Roo.grid.ColumnModel.defaultRenderer;
5172 return this.config[col].renderer;
5176 * Sets the rendering (formatting) function for a column.
5177 * @param {Number} col The column index
5178 * @param {Function} fn The function to use to process the cell's raw data
5179 * to return HTML markup for the grid view. The render function is called with
5180 * the following parameters:<ul>
5181 * <li>Data value.</li>
5182 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5183 * <li>css A CSS style string to apply to the table cell.</li>
5184 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5185 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5186 * <li>Row index</li>
5187 * <li>Column index</li>
5188 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5190 setRenderer : function(col, fn){
5191 this.config[col].renderer = fn;
5195 * Returns the width for the specified column.
5196 * @param {Number} col The column index
5199 getColumnWidth : function(col){
5200 return this.config[col].width * 1 || this.defaultWidth;
5204 * Sets the width for a column.
5205 * @param {Number} col The column index
5206 * @param {Number} width The new width
5208 setColumnWidth : function(col, width, suppressEvent){
5209 this.config[col].width = width;
5210 this.totalWidth = null;
5212 this.fireEvent("widthchange", this, col, width);
5217 * Returns the total width of all columns.
5218 * @param {Boolean} includeHidden True to include hidden column widths
5221 getTotalWidth : function(includeHidden){
5222 if(!this.totalWidth){
5223 this.totalWidth = 0;
5224 for(var i = 0, len = this.config.length; i < len; i++){
5225 if(includeHidden || !this.isHidden(i)){
5226 this.totalWidth += this.getColumnWidth(i);
5230 return this.totalWidth;
5234 * Returns the header for the specified column.
5235 * @param {Number} col The column index
5238 getColumnHeader : function(col){
5239 return this.config[col].header;
5243 * Sets the header for a column.
5244 * @param {Number} col The column index
5245 * @param {String} header The new header
5247 setColumnHeader : function(col, header){
5248 this.config[col].header = header;
5249 this.fireEvent("headerchange", this, col, header);
5253 * Returns the tooltip for the specified column.
5254 * @param {Number} col The column index
5257 getColumnTooltip : function(col){
5258 return this.config[col].tooltip;
5261 * Sets the tooltip for a column.
5262 * @param {Number} col The column index
5263 * @param {String} tooltip The new tooltip
5265 setColumnTooltip : function(col, tooltip){
5266 this.config[col].tooltip = tooltip;
5270 * Returns the dataIndex for the specified column.
5271 * @param {Number} col The column index
5274 getDataIndex : function(col){
5275 return this.config[col].dataIndex;
5279 * Sets the dataIndex for a column.
5280 * @param {Number} col The column index
5281 * @param {Number} dataIndex The new dataIndex
5283 setDataIndex : function(col, dataIndex){
5284 this.config[col].dataIndex = dataIndex;
5290 * Returns true if the cell is editable.
5291 * @param {Number} colIndex The column index
5292 * @param {Number} rowIndex The row index - this is nto actually used..?
5295 isCellEditable : function(colIndex, rowIndex){
5296 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5300 * Returns the editor defined for the cell/column.
5301 * return false or null to disable editing.
5302 * @param {Number} colIndex The column index
5303 * @param {Number} rowIndex The row index
5306 getCellEditor : function(colIndex, rowIndex){
5307 return this.config[colIndex].editor;
5311 * Sets if a column is editable.
5312 * @param {Number} col The column index
5313 * @param {Boolean} editable True if the column is editable
5315 setEditable : function(col, editable){
5316 this.config[col].editable = editable;
5321 * Returns true if the column is hidden.
5322 * @param {Number} colIndex The column index
5325 isHidden : function(colIndex){
5326 return this.config[colIndex].hidden;
5331 * Returns true if the column width cannot be changed
5333 isFixed : function(colIndex){
5334 return this.config[colIndex].fixed;
5338 * Returns true if the column can be resized
5341 isResizable : function(colIndex){
5342 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5345 * Sets if a column is hidden.
5346 * @param {Number} colIndex The column index
5347 * @param {Boolean} hidden True if the column is hidden
5349 setHidden : function(colIndex, hidden){
5350 this.config[colIndex].hidden = hidden;
5351 this.totalWidth = null;
5352 this.fireEvent("hiddenchange", this, colIndex, hidden);
5356 * Sets the editor for a column.
5357 * @param {Number} col The column index
5358 * @param {Object} editor The editor object
5360 setEditor : function(col, editor){
5361 this.config[col].editor = editor;
5365 Roo.grid.ColumnModel.defaultRenderer = function(value){
5366 if(typeof value == "string" && value.length < 1){
5372 // Alias for backwards compatibility
5373 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5376 * Ext JS Library 1.1.1
5377 * Copyright(c) 2006-2007, Ext JS, LLC.
5379 * Originally Released Under LGPL - original licence link has changed is not relivant.
5382 * <script type="text/javascript">
5386 * @class Roo.LoadMask
5387 * A simple utility class for generically masking elements while loading data. If the element being masked has
5388 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5389 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5390 * element's UpdateManager load indicator and will be destroyed after the initial load.
5392 * Create a new LoadMask
5393 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5394 * @param {Object} config The config object
5396 Roo.LoadMask = function(el, config){
5397 this.el = Roo.get(el);
5398 Roo.apply(this, config);
5400 this.store.on('beforeload', this.onBeforeLoad, this);
5401 this.store.on('load', this.onLoad, this);
5402 this.store.on('loadexception', this.onLoadException, this);
5403 this.removeMask = false;
5405 var um = this.el.getUpdateManager();
5406 um.showLoadIndicator = false; // disable the default indicator
5407 um.on('beforeupdate', this.onBeforeLoad, this);
5408 um.on('update', this.onLoad, this);
5409 um.on('failure', this.onLoad, this);
5410 this.removeMask = true;
5414 Roo.LoadMask.prototype = {
5416 * @cfg {Boolean} removeMask
5417 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5418 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5422 * The text to display in a centered loading message box (defaults to 'Loading...')
5426 * @cfg {String} msgCls
5427 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5429 msgCls : 'x-mask-loading',
5432 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5438 * Disables the mask to prevent it from being displayed
5440 disable : function(){
5441 this.disabled = true;
5445 * Enables the mask so that it can be displayed
5447 enable : function(){
5448 this.disabled = false;
5451 onLoadException : function()
5455 if (typeof(arguments[3]) != 'undefined') {
5456 Roo.MessageBox.alert("Error loading",arguments[3]);
5460 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5461 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5470 this.el.unmask(this.removeMask);
5475 this.el.unmask(this.removeMask);
5479 onBeforeLoad : function(){
5481 this.el.mask(this.msg, this.msgCls);
5486 destroy : function(){
5488 this.store.un('beforeload', this.onBeforeLoad, this);
5489 this.store.un('load', this.onLoad, this);
5490 this.store.un('loadexception', this.onLoadException, this);
5492 var um = this.el.getUpdateManager();
5493 um.un('beforeupdate', this.onBeforeLoad, this);
5494 um.un('update', this.onLoad, this);
5495 um.un('failure', this.onLoad, this);
5506 * @class Roo.bootstrap.Table
5507 * @extends Roo.bootstrap.Component
5508 * Bootstrap Table class
5509 * @cfg {String} cls table class
5510 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5511 * @cfg {String} bgcolor Specifies the background color for a table
5512 * @cfg {Number} border Specifies whether the table cells should have borders or not
5513 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5514 * @cfg {Number} cellspacing Specifies the space between cells
5515 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5516 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5517 * @cfg {String} sortable Specifies that the table should be sortable
5518 * @cfg {String} summary Specifies a summary of the content of a table
5519 * @cfg {Number} width Specifies the width of a table
5520 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5522 * @cfg {boolean} striped Should the rows be alternative striped
5523 * @cfg {boolean} bordered Add borders to the table
5524 * @cfg {boolean} hover Add hover highlighting
5525 * @cfg {boolean} condensed Format condensed
5526 * @cfg {boolean} responsive Format condensed
5527 * @cfg {Boolean} loadMask (true|false) default false
5528 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5529 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5530 * @cfg {Boolean} rowSelection (true|false) default false
5531 * @cfg {Boolean} cellSelection (true|false) default false
5532 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5536 * Create a new Table
5537 * @param {Object} config The config object
5540 Roo.bootstrap.Table = function(config){
5541 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5544 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5545 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5546 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5547 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5551 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5552 this.sm = this.selModel;
5553 this.sm.xmodule = this.xmodule || false;
5555 if (this.cm && typeof(this.cm.config) == 'undefined') {
5556 this.colModel = new Roo.grid.ColumnModel(this.cm);
5557 this.cm = this.colModel;
5558 this.cm.xmodule = this.xmodule || false;
5561 this.store= Roo.factory(this.store, Roo.data);
5562 this.ds = this.store;
5563 this.ds.xmodule = this.xmodule || false;
5566 if (this.footer && this.store) {
5567 this.footer.dataSource = this.ds;
5568 this.footer = Roo.factory(this.footer);
5575 * Fires when a cell is clicked
5576 * @param {Roo.bootstrap.Table} this
5577 * @param {Roo.Element} el
5578 * @param {Number} rowIndex
5579 * @param {Number} columnIndex
5580 * @param {Roo.EventObject} e
5584 * @event celldblclick
5585 * Fires when a cell is double clicked
5586 * @param {Roo.bootstrap.Table} this
5587 * @param {Roo.Element} el
5588 * @param {Number} rowIndex
5589 * @param {Number} columnIndex
5590 * @param {Roo.EventObject} e
5592 "celldblclick" : true,
5595 * Fires when a row is clicked
5596 * @param {Roo.bootstrap.Table} this
5597 * @param {Roo.Element} el
5598 * @param {Number} rowIndex
5599 * @param {Roo.EventObject} e
5603 * @event rowdblclick
5604 * Fires when a row is double clicked
5605 * @param {Roo.bootstrap.Table} this
5606 * @param {Roo.Element} el
5607 * @param {Number} rowIndex
5608 * @param {Roo.EventObject} e
5610 "rowdblclick" : true,
5613 * Fires when a mouseover occur
5614 * @param {Roo.bootstrap.Table} this
5615 * @param {Roo.Element} el
5616 * @param {Number} rowIndex
5617 * @param {Number} columnIndex
5618 * @param {Roo.EventObject} e
5623 * Fires when a mouseout occur
5624 * @param {Roo.bootstrap.Table} this
5625 * @param {Roo.Element} el
5626 * @param {Number} rowIndex
5627 * @param {Number} columnIndex
5628 * @param {Roo.EventObject} e
5633 * Fires when a row is rendered, so you can change add a style to it.
5634 * @param {Roo.bootstrap.Table} this
5635 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5639 * @event rowsrendered
5640 * Fires when all the rows have been rendered
5641 * @param {Roo.bootstrap.Table} this
5643 'rowsrendered' : true
5648 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5673 rowSelection : false,
5674 cellSelection : false,
5677 // Roo.Element - the tbody
5680 getAutoCreate : function(){
5681 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5690 cfg.cls += ' table-striped';
5694 cfg.cls += ' table-hover';
5696 if (this.bordered) {
5697 cfg.cls += ' table-bordered';
5699 if (this.condensed) {
5700 cfg.cls += ' table-condensed';
5702 if (this.responsive) {
5703 cfg.cls += ' table-responsive';
5707 cfg.cls+= ' ' +this.cls;
5710 // this lot should be simplifed...
5713 cfg.align=this.align;
5716 cfg.bgcolor=this.bgcolor;
5719 cfg.border=this.border;
5721 if (this.cellpadding) {
5722 cfg.cellpadding=this.cellpadding;
5724 if (this.cellspacing) {
5725 cfg.cellspacing=this.cellspacing;
5728 cfg.frame=this.frame;
5731 cfg.rules=this.rules;
5733 if (this.sortable) {
5734 cfg.sortable=this.sortable;
5737 cfg.summary=this.summary;
5740 cfg.width=this.width;
5743 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5746 if(this.store || this.cm){
5747 if(this.headerShow){
5748 cfg.cn.push(this.renderHeader());
5751 cfg.cn.push(this.renderBody());
5753 if(this.footerShow){
5754 cfg.cn.push(this.renderFooter());
5757 cfg.cls+= ' TableGrid';
5760 return { cn : [ cfg ] };
5763 initEvents : function()
5765 if(!this.store || !this.cm){
5769 //Roo.log('initEvents with ds!!!!');
5771 this.mainBody = this.el.select('tbody', true).first();
5776 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5777 e.on('click', _this.sort, _this);
5780 this.el.on("click", this.onClick, this);
5781 this.el.on("dblclick", this.onDblClick, this);
5783 // why is this done????? = it breaks dialogs??
5784 //this.parent().el.setStyle('position', 'relative');
5788 this.footer.parentId = this.id;
5789 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5792 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5794 this.store.on('load', this.onLoad, this);
5795 this.store.on('beforeload', this.onBeforeLoad, this);
5796 this.store.on('update', this.onUpdate, this);
5797 this.store.on('add', this.onAdd, this);
5801 onMouseover : function(e, el)
5803 var cell = Roo.get(el);
5809 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810 cell = cell.findParent('td', false, true);
5813 var row = cell.findParent('tr', false, true);
5814 var cellIndex = cell.dom.cellIndex;
5815 var rowIndex = row.dom.rowIndex - 1; // start from 0
5817 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5821 onMouseout : function(e, el)
5823 var cell = Roo.get(el);
5829 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830 cell = cell.findParent('td', false, true);
5833 var row = cell.findParent('tr', false, true);
5834 var cellIndex = cell.dom.cellIndex;
5835 var rowIndex = row.dom.rowIndex - 1; // start from 0
5837 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5841 onClick : function(e, el)
5843 var cell = Roo.get(el);
5845 if(!cell || (!this.cellSelection && !this.rowSelection)){
5849 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5850 cell = cell.findParent('td', false, true);
5853 if(!cell || typeof(cell) == 'undefined'){
5857 var row = cell.findParent('tr', false, true);
5859 if(!row || typeof(row) == 'undefined'){
5863 var cellIndex = cell.dom.cellIndex;
5864 var rowIndex = this.getRowIndex(row);
5866 // why??? - should these not be based on SelectionModel?
5867 if(this.cellSelection){
5868 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5871 if(this.rowSelection){
5872 this.fireEvent('rowclick', this, row, rowIndex, e);
5878 onDblClick : function(e,el)
5880 var cell = Roo.get(el);
5882 if(!cell || (!this.CellSelection && !this.RowSelection)){
5886 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5887 cell = cell.findParent('td', false, true);
5890 if(!cell || typeof(cell) == 'undefined'){
5894 var row = cell.findParent('tr', false, true);
5896 if(!row || typeof(row) == 'undefined'){
5900 var cellIndex = cell.dom.cellIndex;
5901 var rowIndex = this.getRowIndex(row);
5903 if(this.CellSelection){
5904 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5907 if(this.RowSelection){
5908 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5912 sort : function(e,el)
5914 var col = Roo.get(el);
5916 if(!col.hasClass('sortable')){
5920 var sort = col.attr('sort');
5923 if(col.hasClass('glyphicon-arrow-up')){
5927 this.store.sortInfo = {field : sort, direction : dir};
5930 Roo.log("calling footer first");
5931 this.footer.onClick('first');
5934 this.store.load({ params : { start : 0 } });
5938 renderHeader : function()
5947 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5949 var config = cm.config[i];
5954 html: cm.getColumnHeader(i)
5959 if(typeof(config.lgHeader) != 'undefined'){
5960 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5963 if(typeof(config.mdHeader) != 'undefined'){
5964 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5967 if(typeof(config.smHeader) != 'undefined'){
5968 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5971 if(typeof(config.xsHeader) != 'undefined'){
5972 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5979 if(typeof(config.tooltip) != 'undefined'){
5980 c.tooltip = config.tooltip;
5983 if(typeof(config.colspan) != 'undefined'){
5984 c.colspan = config.colspan;
5987 if(typeof(config.hidden) != 'undefined' && config.hidden){
5988 c.style += ' display:none;';
5991 if(typeof(config.dataIndex) != 'undefined'){
5992 c.sort = config.dataIndex;
5995 if(typeof(config.sortable) != 'undefined' && config.sortable){
5999 if(typeof(config.align) != 'undefined' && config.align.length){
6000 c.style += ' text-align:' + config.align + ';';
6003 if(typeof(config.width) != 'undefined'){
6004 c.style += ' width:' + config.width + 'px;';
6007 if(typeof(config.cls) != 'undefined'){
6008 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6011 ['xs','sm','md','lg'].map(function(size){
6013 if(typeof(config[size]) == 'undefined'){
6017 if (!config[size]) { // 0 = hidden
6018 c.cls += ' hidden-' + size;
6022 c.cls += ' col-' + size + '-' + config[size];
6032 renderBody : function()
6042 colspan : this.cm.getColumnCount()
6052 renderFooter : function()
6062 colspan : this.cm.getColumnCount()
6076 Roo.log('ds onload');
6081 var ds = this.store;
6083 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6084 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6086 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6087 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6090 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6091 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6095 var tbody = this.mainBody;
6097 if(ds.getCount() > 0){
6098 ds.data.each(function(d,rowIndex){
6099 var row = this.renderRow(cm, ds, rowIndex);
6101 tbody.createChild(row);
6105 if(row.cellObjects.length){
6106 Roo.each(row.cellObjects, function(r){
6107 _this.renderCellObject(r);
6114 Roo.each(this.el.select('tbody td', true).elements, function(e){
6115 e.on('mouseover', _this.onMouseover, _this);
6118 Roo.each(this.el.select('tbody td', true).elements, function(e){
6119 e.on('mouseout', _this.onMouseout, _this);
6121 this.fireEvent('rowsrendered', this);
6122 //if(this.loadMask){
6123 // this.maskEl.hide();
6128 onUpdate : function(ds,record)
6130 this.refreshRow(record);
6133 onRemove : function(ds, record, index, isUpdate){
6134 if(isUpdate !== true){
6135 this.fireEvent("beforerowremoved", this, index, record);
6137 var bt = this.mainBody.dom;
6139 var rows = this.el.select('tbody > tr', true).elements;
6141 if(typeof(rows[index]) != 'undefined'){
6142 bt.removeChild(rows[index].dom);
6145 // if(bt.rows[index]){
6146 // bt.removeChild(bt.rows[index]);
6149 if(isUpdate !== true){
6150 //this.stripeRows(index);
6151 //this.syncRowHeights(index, index);
6153 this.fireEvent("rowremoved", this, index, record);
6157 onAdd : function(ds, records, rowIndex)
6159 //Roo.log('on Add called');
6160 // - note this does not handle multiple adding very well..
6161 var bt = this.mainBody.dom;
6162 for (var i =0 ; i < records.length;i++) {
6163 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6164 //Roo.log(records[i]);
6165 //Roo.log(this.store.getAt(rowIndex+i));
6166 this.insertRow(this.store, rowIndex + i, false);
6173 refreshRow : function(record){
6174 var ds = this.store, index;
6175 if(typeof record == 'number'){
6177 record = ds.getAt(index);
6179 index = ds.indexOf(record);
6181 this.insertRow(ds, index, true);
6182 this.onRemove(ds, record, index+1, true);
6183 //this.syncRowHeights(index, index);
6185 this.fireEvent("rowupdated", this, index, record);
6188 insertRow : function(dm, rowIndex, isUpdate){
6191 this.fireEvent("beforerowsinserted", this, rowIndex);
6193 //var s = this.getScrollState();
6194 var row = this.renderRow(this.cm, this.store, rowIndex);
6195 // insert before rowIndex..
6196 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6200 if(row.cellObjects.length){
6201 Roo.each(row.cellObjects, function(r){
6202 _this.renderCellObject(r);
6207 this.fireEvent("rowsinserted", this, rowIndex);
6208 //this.syncRowHeights(firstRow, lastRow);
6209 //this.stripeRows(firstRow);
6216 getRowDom : function(rowIndex)
6218 var rows = this.el.select('tbody > tr', true).elements;
6220 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6223 // returns the object tree for a tr..
6226 renderRow : function(cm, ds, rowIndex)
6229 var d = ds.getAt(rowIndex);
6236 var cellObjects = [];
6238 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6239 var config = cm.config[i];
6241 var renderer = cm.getRenderer(i);
6245 if(typeof(renderer) !== 'undefined'){
6246 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6248 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6249 // and are rendered into the cells after the row is rendered - using the id for the element.
6251 if(typeof(value) === 'object'){
6261 rowIndex : rowIndex,
6266 this.fireEvent('rowclass', this, rowcfg);
6270 cls : rowcfg.rowClass,
6272 html: (typeof(value) === 'object') ? '' : value
6279 if(typeof(config.colspan) != 'undefined'){
6280 td.colspan = config.colspan;
6283 if(typeof(config.hidden) != 'undefined' && config.hidden){
6284 td.style += ' display:none;';
6287 if(typeof(config.align) != 'undefined' && config.align.length){
6288 td.style += ' text-align:' + config.align + ';';
6291 if(typeof(config.width) != 'undefined'){
6292 td.style += ' width:' + config.width + 'px;';
6295 if(typeof(config.cursor) != 'undefined'){
6296 td.style += ' cursor:' + config.cursor + ';';
6299 if(typeof(config.cls) != 'undefined'){
6300 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6303 ['xs','sm','md','lg'].map(function(size){
6305 if(typeof(config[size]) == 'undefined'){
6309 if (!config[size]) { // 0 = hidden
6310 td.cls += ' hidden-' + size;
6314 td.cls += ' col-' + size + '-' + config[size];
6322 row.cellObjects = cellObjects;
6330 onBeforeLoad : function()
6332 //Roo.log('ds onBeforeLoad');
6336 //if(this.loadMask){
6337 // this.maskEl.show();
6345 this.el.select('tbody', true).first().dom.innerHTML = '';
6348 * Show or hide a row.
6349 * @param {Number} rowIndex to show or hide
6350 * @param {Boolean} state hide
6352 setRowVisibility : function(rowIndex, state)
6354 var bt = this.mainBody.dom;
6356 var rows = this.el.select('tbody > tr', true).elements;
6358 if(typeof(rows[rowIndex]) == 'undefined'){
6361 rows[rowIndex].dom.style.display = state ? '' : 'none';
6365 getSelectionModel : function(){
6367 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6369 return this.selModel;
6372 * Render the Roo.bootstrap object from renderder
6374 renderCellObject : function(r)
6378 var t = r.cfg.render(r.container);
6381 Roo.each(r.cfg.cn, function(c){
6383 container: t.getChildContainer(),
6386 _this.renderCellObject(child);
6391 getRowIndex : function(row)
6395 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6418 * @class Roo.bootstrap.TableCell
6419 * @extends Roo.bootstrap.Component
6420 * Bootstrap TableCell class
6421 * @cfg {String} html cell contain text
6422 * @cfg {String} cls cell class
6423 * @cfg {String} tag cell tag (td|th) default td
6424 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6425 * @cfg {String} align Aligns the content in a cell
6426 * @cfg {String} axis Categorizes cells
6427 * @cfg {String} bgcolor Specifies the background color of a cell
6428 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6429 * @cfg {Number} colspan Specifies the number of columns a cell should span
6430 * @cfg {String} headers Specifies one or more header cells a cell is related to
6431 * @cfg {Number} height Sets the height of a cell
6432 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6433 * @cfg {Number} rowspan Sets the number of rows a cell should span
6434 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6435 * @cfg {String} valign Vertical aligns the content in a cell
6436 * @cfg {Number} width Specifies the width of a cell
6439 * Create a new TableCell
6440 * @param {Object} config The config object
6443 Roo.bootstrap.TableCell = function(config){
6444 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6447 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6467 getAutoCreate : function(){
6468 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6488 cfg.align=this.align
6494 cfg.bgcolor=this.bgcolor
6497 cfg.charoff=this.charoff
6500 cfg.colspan=this.colspan
6503 cfg.headers=this.headers
6506 cfg.height=this.height
6509 cfg.nowrap=this.nowrap
6512 cfg.rowspan=this.rowspan
6515 cfg.scope=this.scope
6518 cfg.valign=this.valign
6521 cfg.width=this.width
6540 * @class Roo.bootstrap.TableRow
6541 * @extends Roo.bootstrap.Component
6542 * Bootstrap TableRow class
6543 * @cfg {String} cls row class
6544 * @cfg {String} align Aligns the content in a table row
6545 * @cfg {String} bgcolor Specifies a background color for a table row
6546 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6547 * @cfg {String} valign Vertical aligns the content in a table row
6550 * Create a new TableRow
6551 * @param {Object} config The config object
6554 Roo.bootstrap.TableRow = function(config){
6555 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6558 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6566 getAutoCreate : function(){
6567 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6577 cfg.align = this.align;
6580 cfg.bgcolor = this.bgcolor;
6583 cfg.charoff = this.charoff;
6586 cfg.valign = this.valign;
6604 * @class Roo.bootstrap.TableBody
6605 * @extends Roo.bootstrap.Component
6606 * Bootstrap TableBody class
6607 * @cfg {String} cls element class
6608 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6609 * @cfg {String} align Aligns the content inside the element
6610 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6611 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6614 * Create a new TableBody
6615 * @param {Object} config The config object
6618 Roo.bootstrap.TableBody = function(config){
6619 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6622 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6630 getAutoCreate : function(){
6631 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6645 cfg.align = this.align;
6648 cfg.charoff = this.charoff;
6651 cfg.valign = this.valign;
6658 // initEvents : function()
6665 // this.store = Roo.factory(this.store, Roo.data);
6666 // this.store.on('load', this.onLoad, this);
6668 // this.store.load();
6672 // onLoad: function ()
6674 // this.fireEvent('load', this);
6684 * Ext JS Library 1.1.1
6685 * Copyright(c) 2006-2007, Ext JS, LLC.
6687 * Originally Released Under LGPL - original licence link has changed is not relivant.
6690 * <script type="text/javascript">
6693 // as we use this in bootstrap.
6694 Roo.namespace('Roo.form');
6696 * @class Roo.form.Action
6697 * Internal Class used to handle form actions
6699 * @param {Roo.form.BasicForm} el The form element or its id
6700 * @param {Object} config Configuration options
6705 // define the action interface
6706 Roo.form.Action = function(form, options){
6708 this.options = options || {};
6711 * Client Validation Failed
6714 Roo.form.Action.CLIENT_INVALID = 'client';
6716 * Server Validation Failed
6719 Roo.form.Action.SERVER_INVALID = 'server';
6721 * Connect to Server Failed
6724 Roo.form.Action.CONNECT_FAILURE = 'connect';
6726 * Reading Data from Server Failed
6729 Roo.form.Action.LOAD_FAILURE = 'load';
6731 Roo.form.Action.prototype = {
6733 failureType : undefined,
6734 response : undefined,
6738 run : function(options){
6743 success : function(response){
6748 handleResponse : function(response){
6752 // default connection failure
6753 failure : function(response){
6755 this.response = response;
6756 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6757 this.form.afterAction(this, false);
6760 processResponse : function(response){
6761 this.response = response;
6762 if(!response.responseText){
6765 this.result = this.handleResponse(response);
6769 // utility functions used internally
6770 getUrl : function(appendParams){
6771 var url = this.options.url || this.form.url || this.form.el.dom.action;
6773 var p = this.getParams();
6775 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6781 getMethod : function(){
6782 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6785 getParams : function(){
6786 var bp = this.form.baseParams;
6787 var p = this.options.params;
6789 if(typeof p == "object"){
6790 p = Roo.urlEncode(Roo.applyIf(p, bp));
6791 }else if(typeof p == 'string' && bp){
6792 p += '&' + Roo.urlEncode(bp);
6795 p = Roo.urlEncode(bp);
6800 createCallback : function(){
6802 success: this.success,
6803 failure: this.failure,
6805 timeout: (this.form.timeout*1000),
6806 upload: this.form.fileUpload ? this.success : undefined
6811 Roo.form.Action.Submit = function(form, options){
6812 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6815 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6818 haveProgress : false,
6819 uploadComplete : false,
6821 // uploadProgress indicator.
6822 uploadProgress : function()
6824 if (!this.form.progressUrl) {
6828 if (!this.haveProgress) {
6829 Roo.MessageBox.progress("Uploading", "Uploading");
6831 if (this.uploadComplete) {
6832 Roo.MessageBox.hide();
6836 this.haveProgress = true;
6838 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6840 var c = new Roo.data.Connection();
6842 url : this.form.progressUrl,
6847 success : function(req){
6848 //console.log(data);
6852 rdata = Roo.decode(req.responseText)
6854 Roo.log("Invalid data from server..");
6858 if (!rdata || !rdata.success) {
6860 Roo.MessageBox.alert(Roo.encode(rdata));
6863 var data = rdata.data;
6865 if (this.uploadComplete) {
6866 Roo.MessageBox.hide();
6871 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6872 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6875 this.uploadProgress.defer(2000,this);
6878 failure: function(data) {
6879 Roo.log('progress url failed ');
6890 // run get Values on the form, so it syncs any secondary forms.
6891 this.form.getValues();
6893 var o = this.options;
6894 var method = this.getMethod();
6895 var isPost = method == 'POST';
6896 if(o.clientValidation === false || this.form.isValid()){
6898 if (this.form.progressUrl) {
6899 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6900 (new Date() * 1) + '' + Math.random());
6905 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6906 form:this.form.el.dom,
6907 url:this.getUrl(!isPost),
6909 params:isPost ? this.getParams() : null,
6910 isUpload: this.form.fileUpload
6913 this.uploadProgress();
6915 }else if (o.clientValidation !== false){ // client validation failed
6916 this.failureType = Roo.form.Action.CLIENT_INVALID;
6917 this.form.afterAction(this, false);
6921 success : function(response)
6923 this.uploadComplete= true;
6924 if (this.haveProgress) {
6925 Roo.MessageBox.hide();
6929 var result = this.processResponse(response);
6930 if(result === true || result.success){
6931 this.form.afterAction(this, true);
6935 this.form.markInvalid(result.errors);
6936 this.failureType = Roo.form.Action.SERVER_INVALID;
6938 this.form.afterAction(this, false);
6940 failure : function(response)
6942 this.uploadComplete= true;
6943 if (this.haveProgress) {
6944 Roo.MessageBox.hide();
6947 this.response = response;
6948 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6949 this.form.afterAction(this, false);
6952 handleResponse : function(response){
6953 if(this.form.errorReader){
6954 var rs = this.form.errorReader.read(response);
6957 for(var i = 0, len = rs.records.length; i < len; i++) {
6958 var r = rs.records[i];
6962 if(errors.length < 1){
6966 success : rs.success,
6972 ret = Roo.decode(response.responseText);
6976 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6986 Roo.form.Action.Load = function(form, options){
6987 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6988 this.reader = this.form.reader;
6991 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6996 Roo.Ajax.request(Roo.apply(
6997 this.createCallback(), {
6998 method:this.getMethod(),
6999 url:this.getUrl(false),
7000 params:this.getParams()
7004 success : function(response){
7006 var result = this.processResponse(response);
7007 if(result === true || !result.success || !result.data){
7008 this.failureType = Roo.form.Action.LOAD_FAILURE;
7009 this.form.afterAction(this, false);
7012 this.form.clearInvalid();
7013 this.form.setValues(result.data);
7014 this.form.afterAction(this, true);
7017 handleResponse : function(response){
7018 if(this.form.reader){
7019 var rs = this.form.reader.read(response);
7020 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7022 success : rs.success,
7026 return Roo.decode(response.responseText);
7030 Roo.form.Action.ACTION_TYPES = {
7031 'load' : Roo.form.Action.Load,
7032 'submit' : Roo.form.Action.Submit
7041 * @class Roo.bootstrap.Form
7042 * @extends Roo.bootstrap.Component
7043 * Bootstrap Form class
7044 * @cfg {String} method GET | POST (default POST)
7045 * @cfg {String} labelAlign top | left (default top)
7046 * @cfg {String} align left | right - for navbars
7047 * @cfg {Boolean} loadMask load mask when submit (default true)
7052 * @param {Object} config The config object
7056 Roo.bootstrap.Form = function(config){
7057 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7060 * @event clientvalidation
7061 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7062 * @param {Form} this
7063 * @param {Boolean} valid true if the form has passed client-side validation
7065 clientvalidation: true,
7067 * @event beforeaction
7068 * Fires before any action is performed. Return false to cancel the action.
7069 * @param {Form} this
7070 * @param {Action} action The action to be performed
7074 * @event actionfailed
7075 * Fires when an action fails.
7076 * @param {Form} this
7077 * @param {Action} action The action that failed
7079 actionfailed : true,
7081 * @event actioncomplete
7082 * Fires when an action is completed.
7083 * @param {Form} this
7084 * @param {Action} action The action that completed
7086 actioncomplete : true
7091 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7094 * @cfg {String} method
7095 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7100 * The URL to use for form actions if one isn't supplied in the action options.
7103 * @cfg {Boolean} fileUpload
7104 * Set to true if this form is a file upload.
7108 * @cfg {Object} baseParams
7109 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7113 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7117 * @cfg {Sting} align (left|right) for navbar forms
7122 activeAction : null,
7125 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7126 * element by passing it or its id or mask the form itself by passing in true.
7129 waitMsgTarget : false,
7133 getAutoCreate : function(){
7137 method : this.method || 'POST',
7138 id : this.id || Roo.id(),
7141 if (this.parent().xtype.match(/^Nav/)) {
7142 cfg.cls = 'navbar-form navbar-' + this.align;
7146 if (this.labelAlign == 'left' ) {
7147 cfg.cls += ' form-horizontal';
7153 initEvents : function()
7155 this.el.on('submit', this.onSubmit, this);
7156 // this was added as random key presses on the form where triggering form submit.
7157 this.el.on('keypress', function(e) {
7158 if (e.getCharCode() != 13) {
7161 // we might need to allow it for textareas.. and some other items.
7162 // check e.getTarget().
7164 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7168 Roo.log("keypress blocked");
7176 onSubmit : function(e){
7181 * Returns true if client-side validation on the form is successful.
7184 isValid : function(){
7185 var items = this.getItems();
7187 items.each(function(f){
7196 * Returns true if any fields in this form have changed since their original load.
7199 isDirty : function(){
7201 var items = this.getItems();
7202 items.each(function(f){
7212 * Performs a predefined action (submit or load) or custom actions you define on this form.
7213 * @param {String} actionName The name of the action type
7214 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7215 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7216 * accept other config options):
7218 Property Type Description
7219 ---------------- --------------- ----------------------------------------------------------------------------------
7220 url String The url for the action (defaults to the form's url)
7221 method String The form method to use (defaults to the form's method, or POST if not defined)
7222 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7223 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7224 validate the form on the client (defaults to false)
7226 * @return {BasicForm} this
7228 doAction : function(action, options){
7229 if(typeof action == 'string'){
7230 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7232 if(this.fireEvent('beforeaction', this, action) !== false){
7233 this.beforeAction(action);
7234 action.run.defer(100, action);
7240 beforeAction : function(action){
7241 var o = action.options;
7244 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7246 // not really supported yet.. ??
7248 //if(this.waitMsgTarget === true){
7249 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7250 //}else if(this.waitMsgTarget){
7251 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7252 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7254 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7260 afterAction : function(action, success){
7261 this.activeAction = null;
7262 var o = action.options;
7264 //if(this.waitMsgTarget === true){
7266 //}else if(this.waitMsgTarget){
7267 // this.waitMsgTarget.unmask();
7269 // Roo.MessageBox.updateProgress(1);
7270 // Roo.MessageBox.hide();
7277 Roo.callback(o.success, o.scope, [this, action]);
7278 this.fireEvent('actioncomplete', this, action);
7282 // failure condition..
7283 // we have a scenario where updates need confirming.
7284 // eg. if a locking scenario exists..
7285 // we look for { errors : { needs_confirm : true }} in the response.
7287 (typeof(action.result) != 'undefined') &&
7288 (typeof(action.result.errors) != 'undefined') &&
7289 (typeof(action.result.errors.needs_confirm) != 'undefined')
7292 Roo.log("not supported yet");
7295 Roo.MessageBox.confirm(
7296 "Change requires confirmation",
7297 action.result.errorMsg,
7302 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7312 Roo.callback(o.failure, o.scope, [this, action]);
7313 // show an error message if no failed handler is set..
7314 if (!this.hasListener('actionfailed')) {
7315 Roo.log("need to add dialog support");
7317 Roo.MessageBox.alert("Error",
7318 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7319 action.result.errorMsg :
7320 "Saving Failed, please check your entries or try again"
7325 this.fireEvent('actionfailed', this, action);
7330 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7331 * @param {String} id The value to search for
7334 findField : function(id){
7335 var items = this.getItems();
7336 var field = items.get(id);
7338 items.each(function(f){
7339 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7346 return field || null;
7349 * Mark fields in this form invalid in bulk.
7350 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7351 * @return {BasicForm} this
7353 markInvalid : function(errors){
7354 if(errors instanceof Array){
7355 for(var i = 0, len = errors.length; i < len; i++){
7356 var fieldError = errors[i];
7357 var f = this.findField(fieldError.id);
7359 f.markInvalid(fieldError.msg);
7365 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7366 field.markInvalid(errors[id]);
7370 //Roo.each(this.childForms || [], function (f) {
7371 // f.markInvalid(errors);
7378 * Set values for fields in this form in bulk.
7379 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7380 * @return {BasicForm} this
7382 setValues : function(values){
7383 if(values instanceof Array){ // array of objects
7384 for(var i = 0, len = values.length; i < len; i++){
7386 var f = this.findField(v.id);
7388 f.setValue(v.value);
7389 if(this.trackResetOnLoad){
7390 f.originalValue = f.getValue();
7394 }else{ // object hash
7397 if(typeof values[id] != 'function' && (field = this.findField(id))){
7399 if (field.setFromData &&
7401 field.displayField &&
7402 // combos' with local stores can
7403 // be queried via setValue()
7404 // to set their value..
7405 (field.store && !field.store.isLocal)
7409 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7410 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7411 field.setFromData(sd);
7414 field.setValue(values[id]);
7418 if(this.trackResetOnLoad){
7419 field.originalValue = field.getValue();
7425 //Roo.each(this.childForms || [], function (f) {
7426 // f.setValues(values);
7433 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7434 * they are returned as an array.
7435 * @param {Boolean} asString
7438 getValues : function(asString){
7439 //if (this.childForms) {
7440 // copy values from the child forms
7441 // Roo.each(this.childForms, function (f) {
7442 // this.setValues(f.getValues());
7448 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7449 if(asString === true){
7452 return Roo.urlDecode(fs);
7456 * Returns the fields in this form as an object with key/value pairs.
7457 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7460 getFieldValues : function(with_hidden)
7462 var items = this.getItems();
7464 items.each(function(f){
7468 var v = f.getValue();
7469 if (f.inputType =='radio') {
7470 if (typeof(ret[f.getName()]) == 'undefined') {
7471 ret[f.getName()] = ''; // empty..
7474 if (!f.el.dom.checked) {
7482 // not sure if this supported any more..
7483 if ((typeof(v) == 'object') && f.getRawValue) {
7484 v = f.getRawValue() ; // dates..
7486 // combo boxes where name != hiddenName...
7487 if (f.name != f.getName()) {
7488 ret[f.name] = f.getRawValue();
7490 ret[f.getName()] = v;
7497 * Clears all invalid messages in this form.
7498 * @return {BasicForm} this
7500 clearInvalid : function(){
7501 var items = this.getItems();
7503 items.each(function(f){
7514 * @return {BasicForm} this
7517 var items = this.getItems();
7518 items.each(function(f){
7522 Roo.each(this.childForms || [], function (f) {
7529 getItems : function()
7531 var r=new Roo.util.MixedCollection(false, function(o){
7532 return o.id || (o.id = Roo.id());
7534 var iter = function(el) {
7541 Roo.each(el.items,function(e) {
7561 * Ext JS Library 1.1.1
7562 * Copyright(c) 2006-2007, Ext JS, LLC.
7564 * Originally Released Under LGPL - original licence link has changed is not relivant.
7567 * <script type="text/javascript">
7570 * @class Roo.form.VTypes
7571 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7574 Roo.form.VTypes = function(){
7575 // closure these in so they are only created once.
7576 var alpha = /^[a-zA-Z_]+$/;
7577 var alphanum = /^[a-zA-Z0-9_]+$/;
7578 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7579 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7581 // All these messages and functions are configurable
7584 * The function used to validate email addresses
7585 * @param {String} value The email address
7587 'email' : function(v){
7588 return email.test(v);
7591 * The error text to display when the email validation function returns false
7594 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7596 * The keystroke filter mask to be applied on email input
7599 'emailMask' : /[a-z0-9_\.\-@]/i,
7602 * The function used to validate URLs
7603 * @param {String} value The URL
7605 'url' : function(v){
7609 * The error text to display when the url validation function returns false
7612 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7615 * The function used to validate alpha values
7616 * @param {String} value The value
7618 'alpha' : function(v){
7619 return alpha.test(v);
7622 * The error text to display when the alpha validation function returns false
7625 'alphaText' : 'This field should only contain letters and _',
7627 * The keystroke filter mask to be applied on alpha input
7630 'alphaMask' : /[a-z_]/i,
7633 * The function used to validate alphanumeric values
7634 * @param {String} value The value
7636 'alphanum' : function(v){
7637 return alphanum.test(v);
7640 * The error text to display when the alphanumeric validation function returns false
7643 'alphanumText' : 'This field should only contain letters, numbers and _',
7645 * The keystroke filter mask to be applied on alphanumeric input
7648 'alphanumMask' : /[a-z0-9_]/i
7658 * @class Roo.bootstrap.Input
7659 * @extends Roo.bootstrap.Component
7660 * Bootstrap Input class
7661 * @cfg {Boolean} disabled is it disabled
7662 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7663 * @cfg {String} name name of the input
7664 * @cfg {string} fieldLabel - the label associated
7665 * @cfg {string} placeholder - placeholder to put in text.
7666 * @cfg {string} before - input group add on before
7667 * @cfg {string} after - input group add on after
7668 * @cfg {string} size - (lg|sm) or leave empty..
7669 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7670 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7671 * @cfg {Number} md colspan out of 12 for computer-sized screens
7672 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7673 * @cfg {string} value default value of the input
7674 * @cfg {Number} labelWidth set the width of label (0-12)
7675 * @cfg {String} labelAlign (top|left)
7676 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7677 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7679 * @cfg {String} align (left|center|right) Default left
7680 * @cfg {Boolean} forceFeedback (true|false) Default false
7686 * Create a new Input
7687 * @param {Object} config The config object
7690 Roo.bootstrap.Input = function(config){
7691 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7696 * Fires when this field receives input focus.
7697 * @param {Roo.form.Field} this
7702 * Fires when this field loses input focus.
7703 * @param {Roo.form.Field} this
7708 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7709 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7710 * @param {Roo.form.Field} this
7711 * @param {Roo.EventObject} e The event object
7716 * Fires just before the field blurs if the field value has changed.
7717 * @param {Roo.form.Field} this
7718 * @param {Mixed} newValue The new value
7719 * @param {Mixed} oldValue The original value
7724 * Fires after the field has been marked as invalid.
7725 * @param {Roo.form.Field} this
7726 * @param {String} msg The validation message
7731 * Fires after the field has been validated with no errors.
7732 * @param {Roo.form.Field} this
7737 * Fires after the key up
7738 * @param {Roo.form.Field} this
7739 * @param {Roo.EventObject} e The event Object
7745 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7747 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7748 automatic validation (defaults to "keyup").
7750 validationEvent : "keyup",
7752 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7754 validateOnBlur : true,
7756 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7758 validationDelay : 250,
7760 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7762 focusClass : "x-form-focus", // not needed???
7766 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7768 invalidClass : "has-warning",
7771 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7773 validClass : "has-success",
7776 * @cfg {Boolean} hasFeedback (true|false) default true
7781 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7783 invalidFeedbackClass : "glyphicon-warning-sign",
7786 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7788 validFeedbackClass : "glyphicon-ok",
7791 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7793 selectOnFocus : false,
7796 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7800 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7805 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7807 disableKeyFilter : false,
7810 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7814 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7818 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7820 blankText : "This field is required",
7823 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7827 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7829 maxLength : Number.MAX_VALUE,
7831 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7833 minLengthText : "The minimum length for this field is {0}",
7835 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7837 maxLengthText : "The maximum length for this field is {0}",
7841 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7842 * If available, this function will be called only after the basic validators all return true, and will be passed the
7843 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7847 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7848 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7849 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7853 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7857 autocomplete: false,
7876 formatedValue : false,
7877 forceFeedback : false,
7879 parentLabelAlign : function()
7882 while (parent.parent()) {
7883 parent = parent.parent();
7884 if (typeof(parent.labelAlign) !='undefined') {
7885 return parent.labelAlign;
7892 getAutoCreate : function(){
7894 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7900 if(this.inputType != 'hidden'){
7901 cfg.cls = 'form-group' //input-group
7907 type : this.inputType,
7909 cls : 'form-control',
7910 placeholder : this.placeholder || '',
7911 autocomplete : this.autocomplete || 'new-password'
7916 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7919 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7920 input.maxLength = this.maxLength;
7923 if (this.disabled) {
7924 input.disabled=true;
7927 if (this.readOnly) {
7928 input.readonly=true;
7932 input.name = this.name;
7935 input.cls += ' input-' + this.size;
7938 ['xs','sm','md','lg'].map(function(size){
7939 if (settings[size]) {
7940 cfg.cls += ' col-' + size + '-' + settings[size];
7944 var inputblock = input;
7948 cls: 'glyphicon form-control-feedback'
7951 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7954 cls : 'has-feedback',
7962 if (this.before || this.after) {
7965 cls : 'input-group',
7969 if (this.before && typeof(this.before) == 'string') {
7971 inputblock.cn.push({
7973 cls : 'roo-input-before input-group-addon',
7977 if (this.before && typeof(this.before) == 'object') {
7978 this.before = Roo.factory(this.before);
7979 Roo.log(this.before);
7980 inputblock.cn.push({
7982 cls : 'roo-input-before input-group-' +
7983 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7987 inputblock.cn.push(input);
7989 if (this.after && typeof(this.after) == 'string') {
7990 inputblock.cn.push({
7992 cls : 'roo-input-after input-group-addon',
7996 if (this.after && typeof(this.after) == 'object') {
7997 this.after = Roo.factory(this.after);
7998 Roo.log(this.after);
7999 inputblock.cn.push({
8001 cls : 'roo-input-after input-group-' +
8002 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8006 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8007 inputblock.cls += ' has-feedback';
8008 inputblock.cn.push(feedback);
8012 if (align ==='left' && this.fieldLabel.length) {
8013 Roo.log("left and has label");
8019 cls : 'control-label col-sm-' + this.labelWidth,
8020 html : this.fieldLabel
8024 cls : "col-sm-" + (12 - this.labelWidth),
8031 } else if ( this.fieldLabel.length) {
8037 //cls : 'input-group-addon',
8038 html : this.fieldLabel
8048 Roo.log(" no label && no align");
8057 Roo.log('input-parentType: ' + this.parentType);
8059 if (this.parentType === 'Navbar' && this.parent().bar) {
8060 cfg.cls += ' navbar-form';
8068 * return the real input element.
8070 inputEl: function ()
8072 return this.el.select('input.form-control',true).first();
8075 tooltipEl : function()
8077 return this.inputEl();
8080 setDisabled : function(v)
8082 var i = this.inputEl().dom;
8084 i.removeAttribute('disabled');
8088 i.setAttribute('disabled','true');
8090 initEvents : function()
8093 this.inputEl().on("keydown" , this.fireKey, this);
8094 this.inputEl().on("focus", this.onFocus, this);
8095 this.inputEl().on("blur", this.onBlur, this);
8097 this.inputEl().relayEvent('keyup', this);
8099 // reference to original value for reset
8100 this.originalValue = this.getValue();
8101 //Roo.form.TextField.superclass.initEvents.call(this);
8102 if(this.validationEvent == 'keyup'){
8103 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8104 this.inputEl().on('keyup', this.filterValidation, this);
8106 else if(this.validationEvent !== false){
8107 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8110 if(this.selectOnFocus){
8111 this.on("focus", this.preFocus, this);
8114 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8115 this.inputEl().on("keypress", this.filterKeys, this);
8118 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8119 this.el.on("click", this.autoSize, this);
8122 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8123 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8126 if (typeof(this.before) == 'object') {
8127 this.before.render(this.el.select('.roo-input-before',true).first());
8129 if (typeof(this.after) == 'object') {
8130 this.after.render(this.el.select('.roo-input-after',true).first());
8135 filterValidation : function(e){
8136 if(!e.isNavKeyPress()){
8137 this.validationTask.delay(this.validationDelay);
8141 * Validates the field value
8142 * @return {Boolean} True if the value is valid, else false
8144 validate : function(){
8145 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8146 if(this.disabled || this.validateValue(this.getRawValue())){
8157 * Validates a value according to the field's validation rules and marks the field as invalid
8158 * if the validation fails
8159 * @param {Mixed} value The value to validate
8160 * @return {Boolean} True if the value is valid, else false
8162 validateValue : function(value){
8163 if(value.length < 1) { // if it's blank
8164 if(this.allowBlank){
8170 if(value.length < this.minLength){
8173 if(value.length > this.maxLength){
8177 var vt = Roo.form.VTypes;
8178 if(!vt[this.vtype](value, this)){
8182 if(typeof this.validator == "function"){
8183 var msg = this.validator(value);
8189 if(this.regex && !this.regex.test(value)){
8199 fireKey : function(e){
8200 //Roo.log('field ' + e.getKey());
8201 if(e.isNavKeyPress()){
8202 this.fireEvent("specialkey", this, e);
8205 focus : function (selectText){
8207 this.inputEl().focus();
8208 if(selectText === true){
8209 this.inputEl().dom.select();
8215 onFocus : function(){
8216 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8217 // this.el.addClass(this.focusClass);
8220 this.hasFocus = true;
8221 this.startValue = this.getValue();
8222 this.fireEvent("focus", this);
8226 beforeBlur : Roo.emptyFn,
8230 onBlur : function(){
8232 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8233 //this.el.removeClass(this.focusClass);
8235 this.hasFocus = false;
8236 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8239 var v = this.getValue();
8240 if(String(v) !== String(this.startValue)){
8241 this.fireEvent('change', this, v, this.startValue);
8243 this.fireEvent("blur", this);
8247 * Resets the current field value to the originally loaded value and clears any validation messages
8250 this.setValue(this.originalValue);
8254 * Returns the name of the field
8255 * @return {Mixed} name The name field
8257 getName: function(){
8261 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8262 * @return {Mixed} value The field value
8264 getValue : function(){
8266 var v = this.inputEl().getValue();
8271 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8272 * @return {Mixed} value The field value
8274 getRawValue : function(){
8275 var v = this.inputEl().getValue();
8281 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8282 * @param {Mixed} value The value to set
8284 setRawValue : function(v){
8285 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8288 selectText : function(start, end){
8289 var v = this.getRawValue();
8291 start = start === undefined ? 0 : start;
8292 end = end === undefined ? v.length : end;
8293 var d = this.inputEl().dom;
8294 if(d.setSelectionRange){
8295 d.setSelectionRange(start, end);
8296 }else if(d.createTextRange){
8297 var range = d.createTextRange();
8298 range.moveStart("character", start);
8299 range.moveEnd("character", v.length-end);
8306 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8307 * @param {Mixed} value The value to set
8309 setValue : function(v){
8312 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8318 processValue : function(value){
8319 if(this.stripCharsRe){
8320 var newValue = value.replace(this.stripCharsRe, '');
8321 if(newValue !== value){
8322 this.setRawValue(newValue);
8329 preFocus : function(){
8331 if(this.selectOnFocus){
8332 this.inputEl().dom.select();
8335 filterKeys : function(e){
8337 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8340 var c = e.getCharCode(), cc = String.fromCharCode(c);
8341 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8344 if(!this.maskRe.test(cc)){
8349 * Clear any invalid styles/messages for this field
8351 clearInvalid : function(){
8353 if(!this.el || this.preventMark){ // not rendered
8356 this.el.removeClass(this.invalidClass);
8358 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8360 var feedback = this.el.select('.form-control-feedback', true).first();
8363 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8368 this.fireEvent('valid', this);
8372 * Mark this field as valid
8374 markValid : function()
8376 if(!this.el || this.preventMark){ // not rendered
8380 this.el.removeClass([this.invalidClass, this.validClass]);
8382 var feedback = this.el.select('.form-control-feedback', true).first();
8385 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8388 if(this.disabled || this.allowBlank){
8392 var formGroup = this.el.findParent('.form-group', false, true);
8396 var label = formGroup.select('label', true).first();
8397 var icon = formGroup.select('i.fa-star', true).first();
8404 this.el.addClass(this.validClass);
8406 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8408 var feedback = this.el.select('.form-control-feedback', true).first();
8411 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8412 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8417 this.fireEvent('valid', this);
8421 * Mark this field as invalid
8422 * @param {String} msg The validation message
8424 markInvalid : function(msg)
8426 if(!this.el || this.preventMark){ // not rendered
8430 this.el.removeClass([this.invalidClass, this.validClass]);
8432 var feedback = this.el.select('.form-control-feedback', true).first();
8435 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8438 if(this.disabled || this.allowBlank){
8442 var formGroup = this.el.findParent('.form-group', false, true);
8445 var label = formGroup.select('label', true).first();
8446 var icon = formGroup.select('i.fa-star', true).first();
8448 if(!this.getValue().length && label && !icon){
8449 this.el.findParent('.form-group', false, true).createChild({
8451 cls : 'text-danger fa fa-lg fa-star',
8452 tooltip : 'This field is required',
8453 style : 'margin-right:5px;'
8459 this.el.addClass(this.invalidClass);
8461 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8463 var feedback = this.el.select('.form-control-feedback', true).first();
8466 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8468 if(this.getValue().length || this.forceFeedback){
8469 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8476 this.fireEvent('invalid', this, msg);
8479 SafariOnKeyDown : function(event)
8481 // this is a workaround for a password hang bug on chrome/ webkit.
8483 var isSelectAll = false;
8485 if(this.inputEl().dom.selectionEnd > 0){
8486 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8488 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8489 event.preventDefault();
8494 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8496 event.preventDefault();
8497 // this is very hacky as keydown always get's upper case.
8499 var cc = String.fromCharCode(event.getCharCode());
8500 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8504 adjustWidth : function(tag, w){
8505 tag = tag.toLowerCase();
8506 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8507 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8511 if(tag == 'textarea'){
8514 }else if(Roo.isOpera){
8518 if(tag == 'textarea'){
8537 * @class Roo.bootstrap.TextArea
8538 * @extends Roo.bootstrap.Input
8539 * Bootstrap TextArea class
8540 * @cfg {Number} cols Specifies the visible width of a text area
8541 * @cfg {Number} rows Specifies the visible number of lines in a text area
8542 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8543 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8544 * @cfg {string} html text
8547 * Create a new TextArea
8548 * @param {Object} config The config object
8551 Roo.bootstrap.TextArea = function(config){
8552 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8556 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8566 getAutoCreate : function(){
8568 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8579 value : this.value || '',
8580 html: this.html || '',
8581 cls : 'form-control',
8582 placeholder : this.placeholder || ''
8586 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8587 input.maxLength = this.maxLength;
8591 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8595 input.cols = this.cols;
8598 if (this.readOnly) {
8599 input.readonly = true;
8603 input.name = this.name;
8607 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8611 ['xs','sm','md','lg'].map(function(size){
8612 if (settings[size]) {
8613 cfg.cls += ' col-' + size + '-' + settings[size];
8617 var inputblock = input;
8619 if(this.hasFeedback && !this.allowBlank){
8623 cls: 'glyphicon form-control-feedback'
8627 cls : 'has-feedback',
8636 if (this.before || this.after) {
8639 cls : 'input-group',
8643 inputblock.cn.push({
8645 cls : 'input-group-addon',
8650 inputblock.cn.push(input);
8652 if(this.hasFeedback && !this.allowBlank){
8653 inputblock.cls += ' has-feedback';
8654 inputblock.cn.push(feedback);
8658 inputblock.cn.push({
8660 cls : 'input-group-addon',
8667 if (align ==='left' && this.fieldLabel.length) {
8668 Roo.log("left and has label");
8674 cls : 'control-label col-sm-' + this.labelWidth,
8675 html : this.fieldLabel
8679 cls : "col-sm-" + (12 - this.labelWidth),
8686 } else if ( this.fieldLabel.length) {
8692 //cls : 'input-group-addon',
8693 html : this.fieldLabel
8703 Roo.log(" no label && no align");
8713 if (this.disabled) {
8714 input.disabled=true;
8721 * return the real textarea element.
8723 inputEl: function ()
8725 return this.el.select('textarea.form-control',true).first();
8729 * Clear any invalid styles/messages for this field
8731 clearInvalid : function()
8734 if(!this.el || this.preventMark){ // not rendered
8738 var label = this.el.select('label', true).first();
8739 var icon = this.el.select('i.fa-star', true).first();
8745 this.el.removeClass(this.invalidClass);
8747 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8749 var feedback = this.el.select('.form-control-feedback', true).first();
8752 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8757 this.fireEvent('valid', this);
8761 * Mark this field as valid
8763 markValid : function()
8765 if(!this.el || this.preventMark){ // not rendered
8769 this.el.removeClass([this.invalidClass, this.validClass]);
8771 var feedback = this.el.select('.form-control-feedback', true).first();
8774 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8777 if(this.disabled || this.allowBlank){
8781 var label = this.el.select('label', true).first();
8782 var icon = this.el.select('i.fa-star', true).first();
8788 this.el.addClass(this.validClass);
8790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8792 var feedback = this.el.select('.form-control-feedback', true).first();
8795 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8796 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8801 this.fireEvent('valid', this);
8805 * Mark this field as invalid
8806 * @param {String} msg The validation message
8808 markInvalid : function(msg)
8810 if(!this.el || this.preventMark){ // not rendered
8814 this.el.removeClass([this.invalidClass, this.validClass]);
8816 var feedback = this.el.select('.form-control-feedback', true).first();
8819 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8822 if(this.disabled || this.allowBlank){
8826 var label = this.el.select('label', true).first();
8827 var icon = this.el.select('i.fa-star', true).first();
8829 if(!this.getValue().length && label && !icon){
8830 this.el.createChild({
8832 cls : 'text-danger fa fa-lg fa-star',
8833 tooltip : 'This field is required',
8834 style : 'margin-right:5px;'
8838 this.el.addClass(this.invalidClass);
8840 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8842 var feedback = this.el.select('.form-control-feedback', true).first();
8845 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8847 if(this.getValue().length || this.forceFeedback){
8848 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8855 this.fireEvent('invalid', this, msg);
8863 * trigger field - base class for combo..
8868 * @class Roo.bootstrap.TriggerField
8869 * @extends Roo.bootstrap.Input
8870 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8871 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8872 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8873 * for which you can provide a custom implementation. For example:
8875 var trigger = new Roo.bootstrap.TriggerField();
8876 trigger.onTriggerClick = myTriggerFn;
8877 trigger.applyTo('my-field');
8880 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8881 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8882 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8883 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8884 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8887 * Create a new TriggerField.
8888 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8889 * to the base TextField)
8891 Roo.bootstrap.TriggerField = function(config){
8892 this.mimicing = false;
8893 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8896 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8898 * @cfg {String} triggerClass A CSS class to apply to the trigger
8901 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8906 * @cfg {Boolean} removable (true|false) special filter default false
8910 /** @cfg {Boolean} grow @hide */
8911 /** @cfg {Number} growMin @hide */
8912 /** @cfg {Number} growMax @hide */
8918 autoSize: Roo.emptyFn,
8925 actionMode : 'wrap',
8930 getAutoCreate : function(){
8932 var align = this.labelAlign || this.parentLabelAlign();
8937 cls: 'form-group' //input-group
8944 type : this.inputType,
8945 cls : 'form-control',
8946 autocomplete: 'new-password',
8947 placeholder : this.placeholder || ''
8951 input.name = this.name;
8954 input.cls += ' input-' + this.size;
8957 if (this.disabled) {
8958 input.disabled=true;
8961 var inputblock = input;
8963 if(this.hasFeedback && !this.allowBlank){
8967 cls: 'glyphicon form-control-feedback'
8970 if(this.removable && !this.editable && !this.tickable){
8972 cls : 'has-feedback',
8978 cls : 'roo-combo-removable-btn close'
8985 cls : 'has-feedback',
8994 if(this.removable && !this.editable && !this.tickable){
8996 cls : 'roo-removable',
9002 cls : 'roo-combo-removable-btn close'
9009 if (this.before || this.after) {
9012 cls : 'input-group',
9016 inputblock.cn.push({
9018 cls : 'input-group-addon',
9023 inputblock.cn.push(input);
9025 if(this.hasFeedback && !this.allowBlank){
9026 inputblock.cls += ' has-feedback';
9027 inputblock.cn.push(feedback);
9031 inputblock.cn.push({
9033 cls : 'input-group-addon',
9046 cls: 'form-hidden-field'
9054 Roo.log('multiple');
9062 cls: 'form-hidden-field'
9066 cls: 'select2-choices',
9070 cls: 'select2-search-field',
9083 cls: 'select2-container input-group',
9088 // cls: 'typeahead typeahead-long dropdown-menu',
9089 // style: 'display:none'
9094 if(!this.multiple && this.showToggleBtn){
9100 if (this.caret != false) {
9103 cls: 'fa fa-' + this.caret
9110 cls : 'input-group-addon btn dropdown-toggle',
9115 cls: 'combobox-clear',
9129 combobox.cls += ' select2-container-multi';
9132 if (align ==='left' && this.fieldLabel.length) {
9134 Roo.log("left and has label");
9140 cls : 'control-label col-sm-' + this.labelWidth,
9141 html : this.fieldLabel
9145 cls : "col-sm-" + (12 - this.labelWidth),
9152 } else if ( this.fieldLabel.length) {
9158 //cls : 'input-group-addon',
9159 html : this.fieldLabel
9169 Roo.log(" no label && no align");
9176 ['xs','sm','md','lg'].map(function(size){
9177 if (settings[size]) {
9178 cfg.cls += ' col-' + size + '-' + settings[size];
9189 onResize : function(w, h){
9190 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9191 // if(typeof w == 'number'){
9192 // var x = w - this.trigger.getWidth();
9193 // this.inputEl().setWidth(this.adjustWidth('input', x));
9194 // this.trigger.setStyle('left', x+'px');
9199 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9202 getResizeEl : function(){
9203 return this.inputEl();
9207 getPositionEl : function(){
9208 return this.inputEl();
9212 alignErrorIcon : function(){
9213 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9217 initEvents : function(){
9221 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9222 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9223 if(!this.multiple && this.showToggleBtn){
9224 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9225 if(this.hideTrigger){
9226 this.trigger.setDisplayed(false);
9228 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9232 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9235 if(this.removable && !this.editable && !this.tickable){
9236 var close = this.closeTriggerEl();
9239 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9240 close.on('click', this.removeBtnClick, this, close);
9244 //this.trigger.addClassOnOver('x-form-trigger-over');
9245 //this.trigger.addClassOnClick('x-form-trigger-click');
9248 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9252 closeTriggerEl : function()
9254 var close = this.el.select('.roo-combo-removable-btn', true).first();
9255 return close ? close : false;
9258 removeBtnClick : function(e, h, el)
9262 if(this.fireEvent("remove", this) !== false){
9267 createList : function()
9269 this.list = Roo.get(document.body).createChild({
9271 cls: 'typeahead typeahead-long dropdown-menu',
9272 style: 'display:none'
9275 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9280 initTrigger : function(){
9285 onDestroy : function(){
9287 this.trigger.removeAllListeners();
9288 // this.trigger.remove();
9291 // this.wrap.remove();
9293 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9297 onFocus : function(){
9298 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9301 this.wrap.addClass('x-trigger-wrap-focus');
9302 this.mimicing = true;
9303 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9304 if(this.monitorTab){
9305 this.el.on("keydown", this.checkTab, this);
9312 checkTab : function(e){
9313 if(e.getKey() == e.TAB){
9319 onBlur : function(){
9324 mimicBlur : function(e, t){
9326 if(!this.wrap.contains(t) && this.validateBlur()){
9333 triggerBlur : function(){
9334 this.mimicing = false;
9335 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9336 if(this.monitorTab){
9337 this.el.un("keydown", this.checkTab, this);
9339 //this.wrap.removeClass('x-trigger-wrap-focus');
9340 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9344 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9345 validateBlur : function(e, t){
9350 onDisable : function(){
9351 this.inputEl().dom.disabled = true;
9352 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9354 // this.wrap.addClass('x-item-disabled');
9359 onEnable : function(){
9360 this.inputEl().dom.disabled = false;
9361 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9363 // this.el.removeClass('x-item-disabled');
9368 onShow : function(){
9369 var ae = this.getActionEl();
9372 ae.dom.style.display = '';
9373 ae.dom.style.visibility = 'visible';
9379 onHide : function(){
9380 var ae = this.getActionEl();
9381 ae.dom.style.display = 'none';
9385 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9386 * by an implementing function.
9388 * @param {EventObject} e
9390 onTriggerClick : Roo.emptyFn
9394 * Ext JS Library 1.1.1
9395 * Copyright(c) 2006-2007, Ext JS, LLC.
9397 * Originally Released Under LGPL - original licence link has changed is not relivant.
9400 * <script type="text/javascript">
9405 * @class Roo.data.SortTypes
9407 * Defines the default sorting (casting?) comparison functions used when sorting data.
9409 Roo.data.SortTypes = {
9411 * Default sort that does nothing
9412 * @param {Mixed} s The value being converted
9413 * @return {Mixed} The comparison value
9420 * The regular expression used to strip tags
9424 stripTagsRE : /<\/?[^>]+>/gi,
9427 * Strips all HTML tags to sort on text only
9428 * @param {Mixed} s The value being converted
9429 * @return {String} The comparison value
9431 asText : function(s){
9432 return String(s).replace(this.stripTagsRE, "");
9436 * Strips all HTML tags to sort on text only - Case insensitive
9437 * @param {Mixed} s The value being converted
9438 * @return {String} The comparison value
9440 asUCText : function(s){
9441 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9445 * Case insensitive string
9446 * @param {Mixed} s The value being converted
9447 * @return {String} The comparison value
9449 asUCString : function(s) {
9450 return String(s).toUpperCase();
9455 * @param {Mixed} s The value being converted
9456 * @return {Number} The comparison value
9458 asDate : function(s) {
9462 if(s instanceof Date){
9465 return Date.parse(String(s));
9470 * @param {Mixed} s The value being converted
9471 * @return {Float} The comparison value
9473 asFloat : function(s) {
9474 var val = parseFloat(String(s).replace(/,/g, ""));
9483 * @param {Mixed} s The value being converted
9484 * @return {Number} The comparison value
9486 asInt : function(s) {
9487 var val = parseInt(String(s).replace(/,/g, ""));
9495 * Ext JS Library 1.1.1
9496 * Copyright(c) 2006-2007, Ext JS, LLC.
9498 * Originally Released Under LGPL - original licence link has changed is not relivant.
9501 * <script type="text/javascript">
9505 * @class Roo.data.Record
9506 * Instances of this class encapsulate both record <em>definition</em> information, and record
9507 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9508 * to access Records cached in an {@link Roo.data.Store} object.<br>
9510 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9511 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9514 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9516 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9517 * {@link #create}. The parameters are the same.
9518 * @param {Array} data An associative Array of data values keyed by the field name.
9519 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9520 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9521 * not specified an integer id is generated.
9523 Roo.data.Record = function(data, id){
9524 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9529 * Generate a constructor for a specific record layout.
9530 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9531 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9532 * Each field definition object may contain the following properties: <ul>
9533 * <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,
9534 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9535 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9536 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9537 * is being used, then this is a string containing the javascript expression to reference the data relative to
9538 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9539 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9540 * this may be omitted.</p></li>
9541 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9542 * <ul><li>auto (Default, implies no conversion)</li>
9547 * <li>date</li></ul></p></li>
9548 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9549 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9550 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9551 * by the Reader into an object that will be stored in the Record. It is passed the
9552 * following parameters:<ul>
9553 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9555 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9557 * <br>usage:<br><pre><code>
9558 var TopicRecord = Roo.data.Record.create(
9559 {name: 'title', mapping: 'topic_title'},
9560 {name: 'author', mapping: 'username'},
9561 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9562 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9563 {name: 'lastPoster', mapping: 'user2'},
9564 {name: 'excerpt', mapping: 'post_text'}
9567 var myNewRecord = new TopicRecord({
9568 title: 'Do my job please',
9571 lastPost: new Date(),
9572 lastPoster: 'Animal',
9573 excerpt: 'No way dude!'
9575 myStore.add(myNewRecord);
9580 Roo.data.Record.create = function(o){
9582 f.superclass.constructor.apply(this, arguments);
9584 Roo.extend(f, Roo.data.Record);
9585 var p = f.prototype;
9586 p.fields = new Roo.util.MixedCollection(false, function(field){
9589 for(var i = 0, len = o.length; i < len; i++){
9590 p.fields.add(new Roo.data.Field(o[i]));
9592 f.getField = function(name){
9593 return p.fields.get(name);
9598 Roo.data.Record.AUTO_ID = 1000;
9599 Roo.data.Record.EDIT = 'edit';
9600 Roo.data.Record.REJECT = 'reject';
9601 Roo.data.Record.COMMIT = 'commit';
9603 Roo.data.Record.prototype = {
9605 * Readonly flag - true if this record has been modified.
9614 join : function(store){
9619 * Set the named field to the specified value.
9620 * @param {String} name The name of the field to set.
9621 * @param {Object} value The value to set the field to.
9623 set : function(name, value){
9624 if(this.data[name] == value){
9631 if(typeof this.modified[name] == 'undefined'){
9632 this.modified[name] = this.data[name];
9634 this.data[name] = value;
9635 if(!this.editing && this.store){
9636 this.store.afterEdit(this);
9641 * Get the value of the named field.
9642 * @param {String} name The name of the field to get the value of.
9643 * @return {Object} The value of the field.
9645 get : function(name){
9646 return this.data[name];
9650 beginEdit : function(){
9651 this.editing = true;
9656 cancelEdit : function(){
9657 this.editing = false;
9658 delete this.modified;
9662 endEdit : function(){
9663 this.editing = false;
9664 if(this.dirty && this.store){
9665 this.store.afterEdit(this);
9670 * Usually called by the {@link Roo.data.Store} which owns the Record.
9671 * Rejects all changes made to the Record since either creation, or the last commit operation.
9672 * Modified fields are reverted to their original values.
9674 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9675 * of reject operations.
9677 reject : function(){
9678 var m = this.modified;
9680 if(typeof m[n] != "function"){
9681 this.data[n] = m[n];
9685 delete this.modified;
9686 this.editing = false;
9688 this.store.afterReject(this);
9693 * Usually called by the {@link Roo.data.Store} which owns the Record.
9694 * Commits all changes made to the Record since either creation, or the last commit operation.
9696 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9697 * of commit operations.
9699 commit : function(){
9701 delete this.modified;
9702 this.editing = false;
9704 this.store.afterCommit(this);
9709 hasError : function(){
9710 return this.error != null;
9714 clearError : function(){
9719 * Creates a copy of this record.
9720 * @param {String} id (optional) A new record id if you don't want to use this record's id
9723 copy : function(newId) {
9724 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9728 * Ext JS Library 1.1.1
9729 * Copyright(c) 2006-2007, Ext JS, LLC.
9731 * Originally Released Under LGPL - original licence link has changed is not relivant.
9734 * <script type="text/javascript">
9740 * @class Roo.data.Store
9741 * @extends Roo.util.Observable
9742 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9743 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9745 * 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
9746 * has no knowledge of the format of the data returned by the Proxy.<br>
9748 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9749 * instances from the data object. These records are cached and made available through accessor functions.
9751 * Creates a new Store.
9752 * @param {Object} config A config object containing the objects needed for the Store to access data,
9753 * and read the data into Records.
9755 Roo.data.Store = function(config){
9756 this.data = new Roo.util.MixedCollection(false);
9757 this.data.getKey = function(o){
9760 this.baseParams = {};
9767 "multisort" : "_multisort"
9770 if(config && config.data){
9771 this.inlineData = config.data;
9775 Roo.apply(this, config);
9777 if(this.reader){ // reader passed
9778 this.reader = Roo.factory(this.reader, Roo.data);
9779 this.reader.xmodule = this.xmodule || false;
9780 if(!this.recordType){
9781 this.recordType = this.reader.recordType;
9783 if(this.reader.onMetaChange){
9784 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9788 if(this.recordType){
9789 this.fields = this.recordType.prototype.fields;
9795 * @event datachanged
9796 * Fires when the data cache has changed, and a widget which is using this Store
9797 * as a Record cache should refresh its view.
9798 * @param {Store} this
9803 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9804 * @param {Store} this
9805 * @param {Object} meta The JSON metadata
9810 * Fires when Records have been added to the Store
9811 * @param {Store} this
9812 * @param {Roo.data.Record[]} records The array of Records added
9813 * @param {Number} index The index at which the record(s) were added
9818 * Fires when a Record has been removed from the Store
9819 * @param {Store} this
9820 * @param {Roo.data.Record} record The Record that was removed
9821 * @param {Number} index The index at which the record was removed
9826 * Fires when a Record has been updated
9827 * @param {Store} this
9828 * @param {Roo.data.Record} record The Record that was updated
9829 * @param {String} operation The update operation being performed. Value may be one of:
9831 Roo.data.Record.EDIT
9832 Roo.data.Record.REJECT
9833 Roo.data.Record.COMMIT
9839 * Fires when the data cache has been cleared.
9840 * @param {Store} this
9845 * Fires before a request is made for a new data object. If the beforeload handler returns false
9846 * the load action will be canceled.
9847 * @param {Store} this
9848 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9852 * @event beforeloadadd
9853 * Fires after a new set of Records has been loaded.
9854 * @param {Store} this
9855 * @param {Roo.data.Record[]} records The Records that were loaded
9856 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9858 beforeloadadd : true,
9861 * Fires after a new set of Records has been loaded, before they are added to the store.
9862 * @param {Store} this
9863 * @param {Roo.data.Record[]} records The Records that were loaded
9864 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9865 * @params {Object} return from reader
9869 * @event loadexception
9870 * Fires if an exception occurs in the Proxy during loading.
9871 * Called with the signature of the Proxy's "loadexception" event.
9872 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9875 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9876 * @param {Object} load options
9877 * @param {Object} jsonData from your request (normally this contains the Exception)
9879 loadexception : true
9883 this.proxy = Roo.factory(this.proxy, Roo.data);
9884 this.proxy.xmodule = this.xmodule || false;
9885 this.relayEvents(this.proxy, ["loadexception"]);
9887 this.sortToggle = {};
9888 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9890 Roo.data.Store.superclass.constructor.call(this);
9892 if(this.inlineData){
9893 this.loadData(this.inlineData);
9894 delete this.inlineData;
9898 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9900 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9901 * without a remote query - used by combo/forms at present.
9905 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9908 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9911 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9912 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9915 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9916 * on any HTTP request
9919 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9922 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9926 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9927 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9932 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9933 * loaded or when a record is removed. (defaults to false).
9935 pruneModifiedRecords : false,
9941 * Add Records to the Store and fires the add event.
9942 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9944 add : function(records){
9945 records = [].concat(records);
9946 for(var i = 0, len = records.length; i < len; i++){
9947 records[i].join(this);
9949 var index = this.data.length;
9950 this.data.addAll(records);
9951 this.fireEvent("add", this, records, index);
9955 * Remove a Record from the Store and fires the remove event.
9956 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9958 remove : function(record){
9959 var index = this.data.indexOf(record);
9960 this.data.removeAt(index);
9961 if(this.pruneModifiedRecords){
9962 this.modified.remove(record);
9964 this.fireEvent("remove", this, record, index);
9968 * Remove all Records from the Store and fires the clear event.
9970 removeAll : function(){
9972 if(this.pruneModifiedRecords){
9975 this.fireEvent("clear", this);
9979 * Inserts Records to the Store at the given index and fires the add event.
9980 * @param {Number} index The start index at which to insert the passed Records.
9981 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9983 insert : function(index, records){
9984 records = [].concat(records);
9985 for(var i = 0, len = records.length; i < len; i++){
9986 this.data.insert(index, records[i]);
9987 records[i].join(this);
9989 this.fireEvent("add", this, records, index);
9993 * Get the index within the cache of the passed Record.
9994 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9995 * @return {Number} The index of the passed Record. Returns -1 if not found.
9997 indexOf : function(record){
9998 return this.data.indexOf(record);
10002 * Get the index within the cache of the Record with the passed id.
10003 * @param {String} id The id of the Record to find.
10004 * @return {Number} The index of the Record. Returns -1 if not found.
10006 indexOfId : function(id){
10007 return this.data.indexOfKey(id);
10011 * Get the Record with the specified id.
10012 * @param {String} id The id of the Record to find.
10013 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10015 getById : function(id){
10016 return this.data.key(id);
10020 * Get the Record at the specified index.
10021 * @param {Number} index The index of the Record to find.
10022 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10024 getAt : function(index){
10025 return this.data.itemAt(index);
10029 * Returns a range of Records between specified indices.
10030 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10031 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10032 * @return {Roo.data.Record[]} An array of Records
10034 getRange : function(start, end){
10035 return this.data.getRange(start, end);
10039 storeOptions : function(o){
10040 o = Roo.apply({}, o);
10043 this.lastOptions = o;
10047 * Loads the Record cache from the configured Proxy using the configured Reader.
10049 * If using remote paging, then the first load call must specify the <em>start</em>
10050 * and <em>limit</em> properties in the options.params property to establish the initial
10051 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10053 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10054 * and this call will return before the new data has been loaded. Perform any post-processing
10055 * in a callback function, or in a "load" event handler.</strong>
10057 * @param {Object} options An object containing properties which control loading options:<ul>
10058 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10059 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10060 * passed the following arguments:<ul>
10061 * <li>r : Roo.data.Record[]</li>
10062 * <li>options: Options object from the load call</li>
10063 * <li>success: Boolean success indicator</li></ul></li>
10064 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10065 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10068 load : function(options){
10069 options = options || {};
10070 if(this.fireEvent("beforeload", this, options) !== false){
10071 this.storeOptions(options);
10072 var p = Roo.apply(options.params || {}, this.baseParams);
10073 // if meta was not loaded from remote source.. try requesting it.
10074 if (!this.reader.metaFromRemote) {
10075 p._requestMeta = 1;
10077 if(this.sortInfo && this.remoteSort){
10078 var pn = this.paramNames;
10079 p[pn["sort"]] = this.sortInfo.field;
10080 p[pn["dir"]] = this.sortInfo.direction;
10082 if (this.multiSort) {
10083 var pn = this.paramNames;
10084 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10087 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10092 * Reloads the Record cache from the configured Proxy using the configured Reader and
10093 * the options from the last load operation performed.
10094 * @param {Object} options (optional) An object containing properties which may override the options
10095 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10096 * the most recently used options are reused).
10098 reload : function(options){
10099 this.load(Roo.applyIf(options||{}, this.lastOptions));
10103 // Called as a callback by the Reader during a load operation.
10104 loadRecords : function(o, options, success){
10105 if(!o || success === false){
10106 if(success !== false){
10107 this.fireEvent("load", this, [], options, o);
10109 if(options.callback){
10110 options.callback.call(options.scope || this, [], options, false);
10114 // if data returned failure - throw an exception.
10115 if (o.success === false) {
10116 // show a message if no listener is registered.
10117 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10118 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10120 // loadmask wil be hooked into this..
10121 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10124 var r = o.records, t = o.totalRecords || r.length;
10126 this.fireEvent("beforeloadadd", this, r, options, o);
10128 if(!options || options.add !== true){
10129 if(this.pruneModifiedRecords){
10130 this.modified = [];
10132 for(var i = 0, len = r.length; i < len; i++){
10136 this.data = this.snapshot;
10137 delete this.snapshot;
10140 this.data.addAll(r);
10141 this.totalLength = t;
10143 this.fireEvent("datachanged", this);
10145 this.totalLength = Math.max(t, this.data.length+r.length);
10148 this.fireEvent("load", this, r, options, o);
10149 if(options.callback){
10150 options.callback.call(options.scope || this, r, options, true);
10156 * Loads data from a passed data block. A Reader which understands the format of the data
10157 * must have been configured in the constructor.
10158 * @param {Object} data The data block from which to read the Records. The format of the data expected
10159 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10160 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10162 loadData : function(o, append){
10163 var r = this.reader.readRecords(o);
10164 this.loadRecords(r, {add: append}, true);
10168 * Gets the number of cached records.
10170 * <em>If using paging, this may not be the total size of the dataset. If the data object
10171 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10172 * the data set size</em>
10174 getCount : function(){
10175 return this.data.length || 0;
10179 * Gets the total number of records in the dataset as returned by the server.
10181 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10182 * the dataset size</em>
10184 getTotalCount : function(){
10185 return this.totalLength || 0;
10189 * Returns the sort state of the Store as an object with two properties:
10191 field {String} The name of the field by which the Records are sorted
10192 direction {String} The sort order, "ASC" or "DESC"
10195 getSortState : function(){
10196 return this.sortInfo;
10200 applySort : function(){
10201 if(this.sortInfo && !this.remoteSort){
10202 var s = this.sortInfo, f = s.field;
10203 var st = this.fields.get(f).sortType;
10204 var fn = function(r1, r2){
10205 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10206 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10208 this.data.sort(s.direction, fn);
10209 if(this.snapshot && this.snapshot != this.data){
10210 this.snapshot.sort(s.direction, fn);
10216 * Sets the default sort column and order to be used by the next load operation.
10217 * @param {String} fieldName The name of the field to sort by.
10218 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10220 setDefaultSort : function(field, dir){
10221 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10225 * Sort the Records.
10226 * If remote sorting is used, the sort is performed on the server, and the cache is
10227 * reloaded. If local sorting is used, the cache is sorted internally.
10228 * @param {String} fieldName The name of the field to sort by.
10229 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10231 sort : function(fieldName, dir){
10232 var f = this.fields.get(fieldName);
10234 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10236 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10237 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10242 this.sortToggle[f.name] = dir;
10243 this.sortInfo = {field: f.name, direction: dir};
10244 if(!this.remoteSort){
10246 this.fireEvent("datachanged", this);
10248 this.load(this.lastOptions);
10253 * Calls the specified function for each of the Records in the cache.
10254 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10255 * Returning <em>false</em> aborts and exits the iteration.
10256 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10258 each : function(fn, scope){
10259 this.data.each(fn, scope);
10263 * Gets all records modified since the last commit. Modified records are persisted across load operations
10264 * (e.g., during paging).
10265 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10267 getModifiedRecords : function(){
10268 return this.modified;
10272 createFilterFn : function(property, value, anyMatch){
10273 if(!value.exec){ // not a regex
10274 value = String(value);
10275 if(value.length == 0){
10278 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10280 return function(r){
10281 return value.test(r.data[property]);
10286 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10287 * @param {String} property A field on your records
10288 * @param {Number} start The record index to start at (defaults to 0)
10289 * @param {Number} end The last record index to include (defaults to length - 1)
10290 * @return {Number} The sum
10292 sum : function(property, start, end){
10293 var rs = this.data.items, v = 0;
10294 start = start || 0;
10295 end = (end || end === 0) ? end : rs.length-1;
10297 for(var i = start; i <= end; i++){
10298 v += (rs[i].data[property] || 0);
10304 * Filter the records by a specified property.
10305 * @param {String} field A field on your records
10306 * @param {String/RegExp} value Either a string that the field
10307 * should start with or a RegExp to test against the field
10308 * @param {Boolean} anyMatch True to match any part not just the beginning
10310 filter : function(property, value, anyMatch){
10311 var fn = this.createFilterFn(property, value, anyMatch);
10312 return fn ? this.filterBy(fn) : this.clearFilter();
10316 * Filter by a function. The specified function will be called with each
10317 * record in this data source. If the function returns true the record is included,
10318 * otherwise it is filtered.
10319 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10320 * @param {Object} scope (optional) The scope of the function (defaults to this)
10322 filterBy : function(fn, scope){
10323 this.snapshot = this.snapshot || this.data;
10324 this.data = this.queryBy(fn, scope||this);
10325 this.fireEvent("datachanged", this);
10329 * Query the records by a specified property.
10330 * @param {String} field A field on your records
10331 * @param {String/RegExp} value Either a string that the field
10332 * should start with or a RegExp to test against the field
10333 * @param {Boolean} anyMatch True to match any part not just the beginning
10334 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10336 query : function(property, value, anyMatch){
10337 var fn = this.createFilterFn(property, value, anyMatch);
10338 return fn ? this.queryBy(fn) : this.data.clone();
10342 * Query by a function. The specified function will be called with each
10343 * record in this data source. If the function returns true the record is included
10345 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10346 * @param {Object} scope (optional) The scope of the function (defaults to this)
10347 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10349 queryBy : function(fn, scope){
10350 var data = this.snapshot || this.data;
10351 return data.filterBy(fn, scope||this);
10355 * Collects unique values for a particular dataIndex from this store.
10356 * @param {String} dataIndex The property to collect
10357 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10358 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10359 * @return {Array} An array of the unique values
10361 collect : function(dataIndex, allowNull, bypassFilter){
10362 var d = (bypassFilter === true && this.snapshot) ?
10363 this.snapshot.items : this.data.items;
10364 var v, sv, r = [], l = {};
10365 for(var i = 0, len = d.length; i < len; i++){
10366 v = d[i].data[dataIndex];
10368 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10377 * Revert to a view of the Record cache with no filtering applied.
10378 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10380 clearFilter : function(suppressEvent){
10381 if(this.snapshot && this.snapshot != this.data){
10382 this.data = this.snapshot;
10383 delete this.snapshot;
10384 if(suppressEvent !== true){
10385 this.fireEvent("datachanged", this);
10391 afterEdit : function(record){
10392 if(this.modified.indexOf(record) == -1){
10393 this.modified.push(record);
10395 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10399 afterReject : function(record){
10400 this.modified.remove(record);
10401 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10405 afterCommit : function(record){
10406 this.modified.remove(record);
10407 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10411 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10412 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10414 commitChanges : function(){
10415 var m = this.modified.slice(0);
10416 this.modified = [];
10417 for(var i = 0, len = m.length; i < len; i++){
10423 * Cancel outstanding changes on all changed records.
10425 rejectChanges : function(){
10426 var m = this.modified.slice(0);
10427 this.modified = [];
10428 for(var i = 0, len = m.length; i < len; i++){
10433 onMetaChange : function(meta, rtype, o){
10434 this.recordType = rtype;
10435 this.fields = rtype.prototype.fields;
10436 delete this.snapshot;
10437 this.sortInfo = meta.sortInfo || this.sortInfo;
10438 this.modified = [];
10439 this.fireEvent('metachange', this, this.reader.meta);
10442 moveIndex : function(data, type)
10444 var index = this.indexOf(data);
10446 var newIndex = index + type;
10450 this.insert(newIndex, data);
10455 * Ext JS Library 1.1.1
10456 * Copyright(c) 2006-2007, Ext JS, LLC.
10458 * Originally Released Under LGPL - original licence link has changed is not relivant.
10461 * <script type="text/javascript">
10465 * @class Roo.data.SimpleStore
10466 * @extends Roo.data.Store
10467 * Small helper class to make creating Stores from Array data easier.
10468 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10469 * @cfg {Array} fields An array of field definition objects, or field name strings.
10470 * @cfg {Array} data The multi-dimensional array of data
10472 * @param {Object} config
10474 Roo.data.SimpleStore = function(config){
10475 Roo.data.SimpleStore.superclass.constructor.call(this, {
10477 reader: new Roo.data.ArrayReader({
10480 Roo.data.Record.create(config.fields)
10482 proxy : new Roo.data.MemoryProxy(config.data)
10486 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10488 * Ext JS Library 1.1.1
10489 * Copyright(c) 2006-2007, Ext JS, LLC.
10491 * Originally Released Under LGPL - original licence link has changed is not relivant.
10494 * <script type="text/javascript">
10499 * @extends Roo.data.Store
10500 * @class Roo.data.JsonStore
10501 * Small helper class to make creating Stores for JSON data easier. <br/>
10503 var store = new Roo.data.JsonStore({
10504 url: 'get-images.php',
10506 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10509 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10510 * JsonReader and HttpProxy (unless inline data is provided).</b>
10511 * @cfg {Array} fields An array of field definition objects, or field name strings.
10513 * @param {Object} config
10515 Roo.data.JsonStore = function(c){
10516 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10517 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10518 reader: new Roo.data.JsonReader(c, c.fields)
10521 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10523 * Ext JS Library 1.1.1
10524 * Copyright(c) 2006-2007, Ext JS, LLC.
10526 * Originally Released Under LGPL - original licence link has changed is not relivant.
10529 * <script type="text/javascript">
10533 Roo.data.Field = function(config){
10534 if(typeof config == "string"){
10535 config = {name: config};
10537 Roo.apply(this, config);
10540 this.type = "auto";
10543 var st = Roo.data.SortTypes;
10544 // named sortTypes are supported, here we look them up
10545 if(typeof this.sortType == "string"){
10546 this.sortType = st[this.sortType];
10549 // set default sortType for strings and dates
10550 if(!this.sortType){
10553 this.sortType = st.asUCString;
10556 this.sortType = st.asDate;
10559 this.sortType = st.none;
10564 var stripRe = /[\$,%]/g;
10566 // prebuilt conversion function for this field, instead of
10567 // switching every time we're reading a value
10569 var cv, dateFormat = this.dateFormat;
10574 cv = function(v){ return v; };
10577 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10581 return v !== undefined && v !== null && v !== '' ?
10582 parseInt(String(v).replace(stripRe, ""), 10) : '';
10587 return v !== undefined && v !== null && v !== '' ?
10588 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10593 cv = function(v){ return v === true || v === "true" || v == 1; };
10600 if(v instanceof Date){
10604 if(dateFormat == "timestamp"){
10605 return new Date(v*1000);
10607 return Date.parseDate(v, dateFormat);
10609 var parsed = Date.parse(v);
10610 return parsed ? new Date(parsed) : null;
10619 Roo.data.Field.prototype = {
10627 * Ext JS Library 1.1.1
10628 * Copyright(c) 2006-2007, Ext JS, LLC.
10630 * Originally Released Under LGPL - original licence link has changed is not relivant.
10633 * <script type="text/javascript">
10636 // Base class for reading structured data from a data source. This class is intended to be
10637 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10640 * @class Roo.data.DataReader
10641 * Base class for reading structured data from a data source. This class is intended to be
10642 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10645 Roo.data.DataReader = function(meta, recordType){
10649 this.recordType = recordType instanceof Array ?
10650 Roo.data.Record.create(recordType) : recordType;
10653 Roo.data.DataReader.prototype = {
10655 * Create an empty record
10656 * @param {Object} data (optional) - overlay some values
10657 * @return {Roo.data.Record} record created.
10659 newRow : function(d) {
10661 this.recordType.prototype.fields.each(function(c) {
10663 case 'int' : da[c.name] = 0; break;
10664 case 'date' : da[c.name] = new Date(); break;
10665 case 'float' : da[c.name] = 0.0; break;
10666 case 'boolean' : da[c.name] = false; break;
10667 default : da[c.name] = ""; break;
10671 return new this.recordType(Roo.apply(da, d));
10676 * Ext JS Library 1.1.1
10677 * Copyright(c) 2006-2007, Ext JS, LLC.
10679 * Originally Released Under LGPL - original licence link has changed is not relivant.
10682 * <script type="text/javascript">
10686 * @class Roo.data.DataProxy
10687 * @extends Roo.data.Observable
10688 * This class is an abstract base class for implementations which provide retrieval of
10689 * unformatted data objects.<br>
10691 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10692 * (of the appropriate type which knows how to parse the data object) to provide a block of
10693 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10695 * Custom implementations must implement the load method as described in
10696 * {@link Roo.data.HttpProxy#load}.
10698 Roo.data.DataProxy = function(){
10701 * @event beforeload
10702 * Fires before a network request is made to retrieve a data object.
10703 * @param {Object} This DataProxy object.
10704 * @param {Object} params The params parameter to the load function.
10709 * Fires before the load method's callback is called.
10710 * @param {Object} This DataProxy object.
10711 * @param {Object} o The data object.
10712 * @param {Object} arg The callback argument object passed to the load function.
10716 * @event loadexception
10717 * Fires if an Exception occurs during data retrieval.
10718 * @param {Object} This DataProxy object.
10719 * @param {Object} o The data object.
10720 * @param {Object} arg The callback argument object passed to the load function.
10721 * @param {Object} e The Exception.
10723 loadexception : true
10725 Roo.data.DataProxy.superclass.constructor.call(this);
10728 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10731 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10735 * Ext JS Library 1.1.1
10736 * Copyright(c) 2006-2007, Ext JS, LLC.
10738 * Originally Released Under LGPL - original licence link has changed is not relivant.
10741 * <script type="text/javascript">
10744 * @class Roo.data.MemoryProxy
10745 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10746 * to the Reader when its load method is called.
10748 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10750 Roo.data.MemoryProxy = function(data){
10754 Roo.data.MemoryProxy.superclass.constructor.call(this);
10758 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10760 * Load data from the requested source (in this case an in-memory
10761 * data object passed to the constructor), read the data object into
10762 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10763 * process that block using the passed callback.
10764 * @param {Object} params This parameter is not used by the MemoryProxy class.
10765 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10766 * object into a block of Roo.data.Records.
10767 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10768 * The function must be passed <ul>
10769 * <li>The Record block object</li>
10770 * <li>The "arg" argument from the load function</li>
10771 * <li>A boolean success indicator</li>
10773 * @param {Object} scope The scope in which to call the callback
10774 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10776 load : function(params, reader, callback, scope, arg){
10777 params = params || {};
10780 result = reader.readRecords(this.data);
10782 this.fireEvent("loadexception", this, arg, null, e);
10783 callback.call(scope, null, arg, false);
10786 callback.call(scope, result, arg, true);
10790 update : function(params, records){
10795 * Ext JS Library 1.1.1
10796 * Copyright(c) 2006-2007, Ext JS, LLC.
10798 * Originally Released Under LGPL - original licence link has changed is not relivant.
10801 * <script type="text/javascript">
10804 * @class Roo.data.HttpProxy
10805 * @extends Roo.data.DataProxy
10806 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10807 * configured to reference a certain URL.<br><br>
10809 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10810 * from which the running page was served.<br><br>
10812 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10814 * Be aware that to enable the browser to parse an XML document, the server must set
10815 * the Content-Type header in the HTTP response to "text/xml".
10817 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10818 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10819 * will be used to make the request.
10821 Roo.data.HttpProxy = function(conn){
10822 Roo.data.HttpProxy.superclass.constructor.call(this);
10823 // is conn a conn config or a real conn?
10825 this.useAjax = !conn || !conn.events;
10829 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10830 // thse are take from connection...
10833 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10836 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10837 * extra parameters to each request made by this object. (defaults to undefined)
10840 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10841 * to each request made by this object. (defaults to undefined)
10844 * @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)
10847 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10850 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10856 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10860 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10861 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10862 * a finer-grained basis than the DataProxy events.
10864 getConnection : function(){
10865 return this.useAjax ? Roo.Ajax : this.conn;
10869 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10870 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10871 * process that block using the passed callback.
10872 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10873 * for the request to the remote server.
10874 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10875 * object into a block of Roo.data.Records.
10876 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10877 * The function must be passed <ul>
10878 * <li>The Record block object</li>
10879 * <li>The "arg" argument from the load function</li>
10880 * <li>A boolean success indicator</li>
10882 * @param {Object} scope The scope in which to call the callback
10883 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10885 load : function(params, reader, callback, scope, arg){
10886 if(this.fireEvent("beforeload", this, params) !== false){
10888 params : params || {},
10890 callback : callback,
10895 callback : this.loadResponse,
10899 Roo.applyIf(o, this.conn);
10900 if(this.activeRequest){
10901 Roo.Ajax.abort(this.activeRequest);
10903 this.activeRequest = Roo.Ajax.request(o);
10905 this.conn.request(o);
10908 callback.call(scope||this, null, arg, false);
10913 loadResponse : function(o, success, response){
10914 delete this.activeRequest;
10916 this.fireEvent("loadexception", this, o, response);
10917 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10922 result = o.reader.read(response);
10924 this.fireEvent("loadexception", this, o, response, e);
10925 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10929 this.fireEvent("load", this, o, o.request.arg);
10930 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10934 update : function(dataSet){
10939 updateResponse : function(dataSet){
10944 * Ext JS Library 1.1.1
10945 * Copyright(c) 2006-2007, Ext JS, LLC.
10947 * Originally Released Under LGPL - original licence link has changed is not relivant.
10950 * <script type="text/javascript">
10954 * @class Roo.data.ScriptTagProxy
10955 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10956 * other than the originating domain of the running page.<br><br>
10958 * <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
10959 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10961 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10962 * source code that is used as the source inside a <script> tag.<br><br>
10964 * In order for the browser to process the returned data, the server must wrap the data object
10965 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10966 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10967 * depending on whether the callback name was passed:
10970 boolean scriptTag = false;
10971 String cb = request.getParameter("callback");
10974 response.setContentType("text/javascript");
10976 response.setContentType("application/x-json");
10978 Writer out = response.getWriter();
10980 out.write(cb + "(");
10982 out.print(dataBlock.toJsonString());
10989 * @param {Object} config A configuration object.
10991 Roo.data.ScriptTagProxy = function(config){
10992 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10993 Roo.apply(this, config);
10994 this.head = document.getElementsByTagName("head")[0];
10997 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10999 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11001 * @cfg {String} url The URL from which to request the data object.
11004 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11008 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11009 * the server the name of the callback function set up by the load call to process the returned data object.
11010 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11011 * javascript output which calls this named function passing the data object as its only parameter.
11013 callbackParam : "callback",
11015 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11016 * name to the request.
11021 * Load data from the configured URL, read the data object into
11022 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11023 * process that block using the passed callback.
11024 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11025 * for the request to the remote server.
11026 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11027 * object into a block of Roo.data.Records.
11028 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11029 * The function must be passed <ul>
11030 * <li>The Record block object</li>
11031 * <li>The "arg" argument from the load function</li>
11032 * <li>A boolean success indicator</li>
11034 * @param {Object} scope The scope in which to call the callback
11035 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11037 load : function(params, reader, callback, scope, arg){
11038 if(this.fireEvent("beforeload", this, params) !== false){
11040 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11042 var url = this.url;
11043 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11045 url += "&_dc=" + (new Date().getTime());
11047 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11050 cb : "stcCallback"+transId,
11051 scriptId : "stcScript"+transId,
11055 callback : callback,
11061 window[trans.cb] = function(o){
11062 conn.handleResponse(o, trans);
11065 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11067 if(this.autoAbort !== false){
11071 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11073 var script = document.createElement("script");
11074 script.setAttribute("src", url);
11075 script.setAttribute("type", "text/javascript");
11076 script.setAttribute("id", trans.scriptId);
11077 this.head.appendChild(script);
11079 this.trans = trans;
11081 callback.call(scope||this, null, arg, false);
11086 isLoading : function(){
11087 return this.trans ? true : false;
11091 * Abort the current server request.
11093 abort : function(){
11094 if(this.isLoading()){
11095 this.destroyTrans(this.trans);
11100 destroyTrans : function(trans, isLoaded){
11101 this.head.removeChild(document.getElementById(trans.scriptId));
11102 clearTimeout(trans.timeoutId);
11104 window[trans.cb] = undefined;
11106 delete window[trans.cb];
11109 // if hasn't been loaded, wait for load to remove it to prevent script error
11110 window[trans.cb] = function(){
11111 window[trans.cb] = undefined;
11113 delete window[trans.cb];
11120 handleResponse : function(o, trans){
11121 this.trans = false;
11122 this.destroyTrans(trans, true);
11125 result = trans.reader.readRecords(o);
11127 this.fireEvent("loadexception", this, o, trans.arg, e);
11128 trans.callback.call(trans.scope||window, null, trans.arg, false);
11131 this.fireEvent("load", this, o, trans.arg);
11132 trans.callback.call(trans.scope||window, result, trans.arg, true);
11136 handleFailure : function(trans){
11137 this.trans = false;
11138 this.destroyTrans(trans, false);
11139 this.fireEvent("loadexception", this, null, trans.arg);
11140 trans.callback.call(trans.scope||window, null, trans.arg, false);
11144 * Ext JS Library 1.1.1
11145 * Copyright(c) 2006-2007, Ext JS, LLC.
11147 * Originally Released Under LGPL - original licence link has changed is not relivant.
11150 * <script type="text/javascript">
11154 * @class Roo.data.JsonReader
11155 * @extends Roo.data.DataReader
11156 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11157 * based on mappings in a provided Roo.data.Record constructor.
11159 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11160 * in the reply previously.
11165 var RecordDef = Roo.data.Record.create([
11166 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11167 {name: 'occupation'} // This field will use "occupation" as the mapping.
11169 var myReader = new Roo.data.JsonReader({
11170 totalProperty: "results", // The property which contains the total dataset size (optional)
11171 root: "rows", // The property which contains an Array of row objects
11172 id: "id" // The property within each row object that provides an ID for the record (optional)
11176 * This would consume a JSON file like this:
11178 { 'results': 2, 'rows': [
11179 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11180 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11183 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11184 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11185 * paged from the remote server.
11186 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11187 * @cfg {String} root name of the property which contains the Array of row objects.
11188 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11189 * @cfg {Array} fields Array of field definition objects
11191 * Create a new JsonReader
11192 * @param {Object} meta Metadata configuration options
11193 * @param {Object} recordType Either an Array of field definition objects,
11194 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11196 Roo.data.JsonReader = function(meta, recordType){
11199 // set some defaults:
11200 Roo.applyIf(meta, {
11201 totalProperty: 'total',
11202 successProperty : 'success',
11207 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11209 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11212 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11213 * Used by Store query builder to append _requestMeta to params.
11216 metaFromRemote : false,
11218 * This method is only used by a DataProxy which has retrieved data from a remote server.
11219 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11220 * @return {Object} data A data block which is used by an Roo.data.Store object as
11221 * a cache of Roo.data.Records.
11223 read : function(response){
11224 var json = response.responseText;
11226 var o = /* eval:var:o */ eval("("+json+")");
11228 throw {message: "JsonReader.read: Json object not found"};
11234 this.metaFromRemote = true;
11235 this.meta = o.metaData;
11236 this.recordType = Roo.data.Record.create(o.metaData.fields);
11237 this.onMetaChange(this.meta, this.recordType, o);
11239 return this.readRecords(o);
11242 // private function a store will implement
11243 onMetaChange : function(meta, recordType, o){
11250 simpleAccess: function(obj, subsc) {
11257 getJsonAccessor: function(){
11259 return function(expr) {
11261 return(re.test(expr))
11262 ? new Function("obj", "return obj." + expr)
11267 return Roo.emptyFn;
11272 * Create a data block containing Roo.data.Records from an XML document.
11273 * @param {Object} o An object which contains an Array of row objects in the property specified
11274 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11275 * which contains the total size of the dataset.
11276 * @return {Object} data A data block which is used by an Roo.data.Store object as
11277 * a cache of Roo.data.Records.
11279 readRecords : function(o){
11281 * After any data loads, the raw JSON data is available for further custom processing.
11285 var s = this.meta, Record = this.recordType,
11286 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11288 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11290 if(s.totalProperty) {
11291 this.getTotal = this.getJsonAccessor(s.totalProperty);
11293 if(s.successProperty) {
11294 this.getSuccess = this.getJsonAccessor(s.successProperty);
11296 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11298 var g = this.getJsonAccessor(s.id);
11299 this.getId = function(rec) {
11301 return (r === undefined || r === "") ? null : r;
11304 this.getId = function(){return null;};
11307 for(var jj = 0; jj < fl; jj++){
11309 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11310 this.ef[jj] = this.getJsonAccessor(map);
11314 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11315 if(s.totalProperty){
11316 var vt = parseInt(this.getTotal(o), 10);
11321 if(s.successProperty){
11322 var vs = this.getSuccess(o);
11323 if(vs === false || vs === 'false'){
11328 for(var i = 0; i < c; i++){
11331 var id = this.getId(n);
11332 for(var j = 0; j < fl; j++){
11334 var v = this.ef[j](n);
11336 Roo.log('missing convert for ' + f.name);
11340 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11342 var record = new Record(values, id);
11344 records[i] = record;
11350 totalRecords : totalRecords
11355 * Ext JS Library 1.1.1
11356 * Copyright(c) 2006-2007, Ext JS, LLC.
11358 * Originally Released Under LGPL - original licence link has changed is not relivant.
11361 * <script type="text/javascript">
11365 * @class Roo.data.ArrayReader
11366 * @extends Roo.data.DataReader
11367 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11368 * Each element of that Array represents a row of data fields. The
11369 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11370 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11374 var RecordDef = Roo.data.Record.create([
11375 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11376 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11378 var myReader = new Roo.data.ArrayReader({
11379 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11383 * This would consume an Array like this:
11385 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11387 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11389 * Create a new JsonReader
11390 * @param {Object} meta Metadata configuration options.
11391 * @param {Object} recordType Either an Array of field definition objects
11392 * as specified to {@link Roo.data.Record#create},
11393 * or an {@link Roo.data.Record} object
11394 * created using {@link Roo.data.Record#create}.
11396 Roo.data.ArrayReader = function(meta, recordType){
11397 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11400 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11402 * Create a data block containing Roo.data.Records from an XML document.
11403 * @param {Object} o An Array of row objects which represents the dataset.
11404 * @return {Object} data A data block which is used by an Roo.data.Store object as
11405 * a cache of Roo.data.Records.
11407 readRecords : function(o){
11408 var sid = this.meta ? this.meta.id : null;
11409 var recordType = this.recordType, fields = recordType.prototype.fields;
11412 for(var i = 0; i < root.length; i++){
11415 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11416 for(var j = 0, jlen = fields.length; j < jlen; j++){
11417 var f = fields.items[j];
11418 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11419 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11421 values[f.name] = v;
11423 var record = new recordType(values, id);
11425 records[records.length] = record;
11429 totalRecords : records.length
11438 * @class Roo.bootstrap.ComboBox
11439 * @extends Roo.bootstrap.TriggerField
11440 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11441 * @cfg {Boolean} append (true|false) default false
11442 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11443 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11444 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11445 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11446 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11447 * @cfg {Boolean} animate default true
11448 * @cfg {Boolean} emptyResultText only for touch device
11449 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11451 * Create a new ComboBox.
11452 * @param {Object} config Configuration options
11454 Roo.bootstrap.ComboBox = function(config){
11455 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11459 * Fires when the dropdown list is expanded
11460 * @param {Roo.bootstrap.ComboBox} combo This combo box
11465 * Fires when the dropdown list is collapsed
11466 * @param {Roo.bootstrap.ComboBox} combo This combo box
11470 * @event beforeselect
11471 * Fires before a list item is selected. Return false to cancel the selection.
11472 * @param {Roo.bootstrap.ComboBox} combo This combo box
11473 * @param {Roo.data.Record} record The data record returned from the underlying store
11474 * @param {Number} index The index of the selected item in the dropdown list
11476 'beforeselect' : true,
11479 * Fires when a list item is selected
11480 * @param {Roo.bootstrap.ComboBox} combo This combo box
11481 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11482 * @param {Number} index The index of the selected item in the dropdown list
11486 * @event beforequery
11487 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11488 * The event object passed has these properties:
11489 * @param {Roo.bootstrap.ComboBox} combo This combo box
11490 * @param {String} query The query
11491 * @param {Boolean} forceAll true to force "all" query
11492 * @param {Boolean} cancel true to cancel the query
11493 * @param {Object} e The query event object
11495 'beforequery': true,
11498 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11499 * @param {Roo.bootstrap.ComboBox} combo This combo box
11504 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11505 * @param {Roo.bootstrap.ComboBox} combo This combo box
11506 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11511 * Fires when the remove value from the combobox array
11512 * @param {Roo.bootstrap.ComboBox} combo This combo box
11516 * @event specialfilter
11517 * Fires when specialfilter
11518 * @param {Roo.bootstrap.ComboBox} combo This combo box
11520 'specialfilter' : true,
11523 * Fires when tick the element
11524 * @param {Roo.bootstrap.ComboBox} combo This combo box
11528 * @event touchviewdisplay
11529 * Fires when touch view require special display (default is using displayField)
11530 * @param {Roo.bootstrap.ComboBox} combo This combo box
11531 * @param {Object} cfg set html .
11533 'touchviewdisplay' : true
11538 this.tickItems = [];
11540 this.selectedIndex = -1;
11541 if(this.mode == 'local'){
11542 if(config.queryDelay === undefined){
11543 this.queryDelay = 10;
11545 if(config.minChars === undefined){
11551 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11554 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11555 * rendering into an Roo.Editor, defaults to false)
11558 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11559 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11562 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11565 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11566 * the dropdown list (defaults to undefined, with no header element)
11570 * @cfg {String/Roo.Template} tpl The template to use to render the output
11574 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11576 listWidth: undefined,
11578 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11579 * mode = 'remote' or 'text' if mode = 'local')
11581 displayField: undefined,
11584 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11585 * mode = 'remote' or 'value' if mode = 'local').
11586 * Note: use of a valueField requires the user make a selection
11587 * in order for a value to be mapped.
11589 valueField: undefined,
11593 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11594 * field's data value (defaults to the underlying DOM element's name)
11596 hiddenName: undefined,
11598 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11602 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11604 selectedClass: 'active',
11607 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11611 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11612 * anchor positions (defaults to 'tl-bl')
11614 listAlign: 'tl-bl?',
11616 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11620 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11621 * query specified by the allQuery config option (defaults to 'query')
11623 triggerAction: 'query',
11625 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11626 * (defaults to 4, does not apply if editable = false)
11630 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11631 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11635 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11636 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11640 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11641 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11645 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11646 * when editable = true (defaults to false)
11648 selectOnFocus:false,
11650 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11652 queryParam: 'query',
11654 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11655 * when mode = 'remote' (defaults to 'Loading...')
11657 loadingText: 'Loading...',
11659 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11663 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11667 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11668 * traditional select (defaults to true)
11672 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11676 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11680 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11681 * listWidth has a higher value)
11685 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11686 * allow the user to set arbitrary text into the field (defaults to false)
11688 forceSelection:false,
11690 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11691 * if typeAhead = true (defaults to 250)
11693 typeAheadDelay : 250,
11695 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11696 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11698 valueNotFoundText : undefined,
11700 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11702 blockFocus : false,
11705 * @cfg {Boolean} disableClear Disable showing of clear button.
11707 disableClear : false,
11709 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11711 alwaysQuery : false,
11714 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11719 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11721 invalidClass : "has-warning",
11724 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11726 validClass : "has-success",
11729 * @cfg {Boolean} specialFilter (true|false) special filter default false
11731 specialFilter : false,
11734 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11736 mobileTouchView : true,
11748 btnPosition : 'right',
11749 triggerList : true,
11750 showToggleBtn : true,
11752 emptyResultText: 'Empty',
11753 triggerText : 'Select',
11755 // element that contains real text value.. (when hidden is used..)
11757 getAutoCreate : function()
11765 if(Roo.isTouch && this.mobileTouchView){
11766 cfg = this.getAutoCreateTouchView();
11773 if(!this.tickable){
11774 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11779 * ComboBox with tickable selections
11782 var align = this.labelAlign || this.parentLabelAlign();
11785 cls : 'form-group roo-combobox-tickable' //input-group
11790 cls : 'tickable-buttons',
11795 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11796 html : this.triggerText
11802 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11809 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11816 buttons.cn.unshift({
11818 cls: 'select2-search-field-input'
11824 Roo.each(buttons.cn, function(c){
11826 c.cls += ' btn-' + _this.size;
11829 if (_this.disabled) {
11840 cls: 'form-hidden-field'
11844 cls: 'select2-choices',
11848 cls: 'select2-search-field',
11860 cls: 'select2-container input-group select2-container-multi',
11865 // cls: 'typeahead typeahead-long dropdown-menu',
11866 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11871 if(this.hasFeedback && !this.allowBlank){
11875 cls: 'glyphicon form-control-feedback'
11878 combobox.cn.push(feedback);
11881 if (align ==='left' && this.fieldLabel.length) {
11883 Roo.log("left and has label");
11889 cls : 'control-label col-sm-' + this.labelWidth,
11890 html : this.fieldLabel
11894 cls : "col-sm-" + (12 - this.labelWidth),
11901 } else if ( this.fieldLabel.length) {
11907 //cls : 'input-group-addon',
11908 html : this.fieldLabel
11918 Roo.log(" no label && no align");
11925 ['xs','sm','md','lg'].map(function(size){
11926 if (settings[size]) {
11927 cfg.cls += ' col-' + size + '-' + settings[size];
11935 _initEventsCalled : false,
11938 initEvents: function()
11941 if (this._initEventsCalled) { // as we call render... prevent looping...
11944 this._initEventsCalled = true;
11947 throw "can not find store for combo";
11950 this.store = Roo.factory(this.store, Roo.data);
11952 // if we are building from html. then this element is so complex, that we can not really
11953 // use the rendered HTML.
11954 // so we have to trash and replace the previous code.
11955 if (Roo.XComponent.build_from_html) {
11957 // remove this element....
11958 var e = this.el.dom, k=0;
11959 while (e ) { e = e.previousSibling; ++k;}
11964 this.rendered = false;
11966 this.render(this.parent().getChildContainer(true), k);
11977 if(Roo.isTouch && this.mobileTouchView){
11978 this.initTouchView();
11983 this.initTickableEvents();
11987 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11989 if(this.hiddenName){
11991 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11993 this.hiddenField.dom.value =
11994 this.hiddenValue !== undefined ? this.hiddenValue :
11995 this.value !== undefined ? this.value : '';
11997 // prevent input submission
11998 this.el.dom.removeAttribute('name');
11999 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12004 // this.el.dom.setAttribute('autocomplete', 'off');
12007 var cls = 'x-combo-list';
12009 //this.list = new Roo.Layer({
12010 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12016 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12017 _this.list.setWidth(lw);
12020 this.list.on('mouseover', this.onViewOver, this);
12021 this.list.on('mousemove', this.onViewMove, this);
12023 this.list.on('scroll', this.onViewScroll, this);
12026 this.list.swallowEvent('mousewheel');
12027 this.assetHeight = 0;
12030 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12031 this.assetHeight += this.header.getHeight();
12034 this.innerList = this.list.createChild({cls:cls+'-inner'});
12035 this.innerList.on('mouseover', this.onViewOver, this);
12036 this.innerList.on('mousemove', this.onViewMove, this);
12037 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12039 if(this.allowBlank && !this.pageSize && !this.disableClear){
12040 this.footer = this.list.createChild({cls:cls+'-ft'});
12041 this.pageTb = new Roo.Toolbar(this.footer);
12045 this.footer = this.list.createChild({cls:cls+'-ft'});
12046 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12047 {pageSize: this.pageSize});
12051 if (this.pageTb && this.allowBlank && !this.disableClear) {
12053 this.pageTb.add(new Roo.Toolbar.Fill(), {
12054 cls: 'x-btn-icon x-btn-clear',
12056 handler: function()
12059 _this.clearValue();
12060 _this.onSelect(false, -1);
12065 this.assetHeight += this.footer.getHeight();
12070 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12073 this.view = new Roo.View(this.list, this.tpl, {
12074 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12076 //this.view.wrapEl.setDisplayed(false);
12077 this.view.on('click', this.onViewClick, this);
12081 this.store.on('beforeload', this.onBeforeLoad, this);
12082 this.store.on('load', this.onLoad, this);
12083 this.store.on('loadexception', this.onLoadException, this);
12085 if(this.resizable){
12086 this.resizer = new Roo.Resizable(this.list, {
12087 pinned:true, handles:'se'
12089 this.resizer.on('resize', function(r, w, h){
12090 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12091 this.listWidth = w;
12092 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12093 this.restrictHeight();
12095 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12098 if(!this.editable){
12099 this.editable = true;
12100 this.setEditable(false);
12105 if (typeof(this.events.add.listeners) != 'undefined') {
12107 this.addicon = this.wrap.createChild(
12108 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12110 this.addicon.on('click', function(e) {
12111 this.fireEvent('add', this);
12114 if (typeof(this.events.edit.listeners) != 'undefined') {
12116 this.editicon = this.wrap.createChild(
12117 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12118 if (this.addicon) {
12119 this.editicon.setStyle('margin-left', '40px');
12121 this.editicon.on('click', function(e) {
12123 // we fire even if inothing is selected..
12124 this.fireEvent('edit', this, this.lastData );
12130 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12131 "up" : function(e){
12132 this.inKeyMode = true;
12136 "down" : function(e){
12137 if(!this.isExpanded()){
12138 this.onTriggerClick();
12140 this.inKeyMode = true;
12145 "enter" : function(e){
12146 // this.onViewClick();
12150 if(this.fireEvent("specialkey", this, e)){
12151 this.onViewClick(false);
12157 "esc" : function(e){
12161 "tab" : function(e){
12164 if(this.fireEvent("specialkey", this, e)){
12165 this.onViewClick(false);
12173 doRelay : function(foo, bar, hname){
12174 if(hname == 'down' || this.scope.isExpanded()){
12175 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12184 this.queryDelay = Math.max(this.queryDelay || 10,
12185 this.mode == 'local' ? 10 : 250);
12188 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12190 if(this.typeAhead){
12191 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12193 if(this.editable !== false){
12194 this.inputEl().on("keyup", this.onKeyUp, this);
12196 if(this.forceSelection){
12197 this.inputEl().on('blur', this.doForce, this);
12201 this.choices = this.el.select('ul.select2-choices', true).first();
12202 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12206 initTickableEvents: function()
12210 if(this.hiddenName){
12212 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12214 this.hiddenField.dom.value =
12215 this.hiddenValue !== undefined ? this.hiddenValue :
12216 this.value !== undefined ? this.value : '';
12218 // prevent input submission
12219 this.el.dom.removeAttribute('name');
12220 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12225 // this.list = this.el.select('ul.dropdown-menu',true).first();
12227 this.choices = this.el.select('ul.select2-choices', true).first();
12228 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12229 if(this.triggerList){
12230 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12233 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12234 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12236 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12237 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12239 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12240 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12242 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12243 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12244 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12247 this.cancelBtn.hide();
12252 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12253 _this.list.setWidth(lw);
12256 this.list.on('mouseover', this.onViewOver, this);
12257 this.list.on('mousemove', this.onViewMove, this);
12259 this.list.on('scroll', this.onViewScroll, this);
12262 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>';
12265 this.view = new Roo.View(this.list, this.tpl, {
12266 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12269 //this.view.wrapEl.setDisplayed(false);
12270 this.view.on('click', this.onViewClick, this);
12274 this.store.on('beforeload', this.onBeforeLoad, this);
12275 this.store.on('load', this.onLoad, this);
12276 this.store.on('loadexception', this.onLoadException, this);
12279 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12280 "up" : function(e){
12281 this.inKeyMode = true;
12285 "down" : function(e){
12286 this.inKeyMode = true;
12290 "enter" : function(e){
12291 if(this.fireEvent("specialkey", this, e)){
12292 this.onViewClick(false);
12298 "esc" : function(e){
12299 this.onTickableFooterButtonClick(e, false, false);
12302 "tab" : function(e){
12303 this.fireEvent("specialkey", this, e);
12305 this.onTickableFooterButtonClick(e, false, false);
12312 doRelay : function(e, fn, key){
12313 if(this.scope.isExpanded()){
12314 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12323 this.queryDelay = Math.max(this.queryDelay || 10,
12324 this.mode == 'local' ? 10 : 250);
12327 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12329 if(this.typeAhead){
12330 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12333 if(this.editable !== false){
12334 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12339 onDestroy : function(){
12341 this.view.setStore(null);
12342 this.view.el.removeAllListeners();
12343 this.view.el.remove();
12344 this.view.purgeListeners();
12347 this.list.dom.innerHTML = '';
12351 this.store.un('beforeload', this.onBeforeLoad, this);
12352 this.store.un('load', this.onLoad, this);
12353 this.store.un('loadexception', this.onLoadException, this);
12355 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12359 fireKey : function(e){
12360 if(e.isNavKeyPress() && !this.list.isVisible()){
12361 this.fireEvent("specialkey", this, e);
12366 onResize: function(w, h){
12367 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12369 // if(typeof w != 'number'){
12370 // // we do not handle it!?!?
12373 // var tw = this.trigger.getWidth();
12374 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12375 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12377 // this.inputEl().setWidth( this.adjustWidth('input', x));
12379 // //this.trigger.setStyle('left', x+'px');
12381 // if(this.list && this.listWidth === undefined){
12382 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12383 // this.list.setWidth(lw);
12384 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12392 * Allow or prevent the user from directly editing the field text. If false is passed,
12393 * the user will only be able to select from the items defined in the dropdown list. This method
12394 * is the runtime equivalent of setting the 'editable' config option at config time.
12395 * @param {Boolean} value True to allow the user to directly edit the field text
12397 setEditable : function(value){
12398 if(value == this.editable){
12401 this.editable = value;
12403 this.inputEl().dom.setAttribute('readOnly', true);
12404 this.inputEl().on('mousedown', this.onTriggerClick, this);
12405 this.inputEl().addClass('x-combo-noedit');
12407 this.inputEl().dom.setAttribute('readOnly', false);
12408 this.inputEl().un('mousedown', this.onTriggerClick, this);
12409 this.inputEl().removeClass('x-combo-noedit');
12415 onBeforeLoad : function(combo,opts){
12416 if(!this.hasFocus){
12420 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12422 this.restrictHeight();
12423 this.selectedIndex = -1;
12427 onLoad : function(){
12429 this.hasQuery = false;
12431 if(!this.hasFocus){
12435 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12436 this.loading.hide();
12439 if(this.store.getCount() > 0){
12441 this.restrictHeight();
12442 if(this.lastQuery == this.allQuery){
12443 if(this.editable && !this.tickable){
12444 this.inputEl().dom.select();
12448 !this.selectByValue(this.value, true) &&
12451 !this.store.lastOptions ||
12452 typeof(this.store.lastOptions.add) == 'undefined' ||
12453 this.store.lastOptions.add != true
12456 this.select(0, true);
12459 if(this.autoFocus){
12462 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12463 this.taTask.delay(this.typeAheadDelay);
12467 this.onEmptyResults();
12473 onLoadException : function()
12475 this.hasQuery = false;
12477 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12478 this.loading.hide();
12481 if(this.tickable && this.editable){
12486 // only causes errors at present
12487 //Roo.log(this.store.reader.jsonData);
12488 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12490 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12496 onTypeAhead : function(){
12497 if(this.store.getCount() > 0){
12498 var r = this.store.getAt(0);
12499 var newValue = r.data[this.displayField];
12500 var len = newValue.length;
12501 var selStart = this.getRawValue().length;
12503 if(selStart != len){
12504 this.setRawValue(newValue);
12505 this.selectText(selStart, newValue.length);
12511 onSelect : function(record, index){
12513 if(this.fireEvent('beforeselect', this, record, index) !== false){
12515 this.setFromData(index > -1 ? record.data : false);
12518 this.fireEvent('select', this, record, index);
12523 * Returns the currently selected field value or empty string if no value is set.
12524 * @return {String} value The selected value
12526 getValue : function(){
12529 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12532 if(this.valueField){
12533 return typeof this.value != 'undefined' ? this.value : '';
12535 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12540 * Clears any text/value currently set in the field
12542 clearValue : function(){
12543 if(this.hiddenField){
12544 this.hiddenField.dom.value = '';
12547 this.setRawValue('');
12548 this.lastSelectionText = '';
12549 this.lastData = false;
12551 var close = this.closeTriggerEl();
12560 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12561 * will be displayed in the field. If the value does not match the data value of an existing item,
12562 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12563 * Otherwise the field will be blank (although the value will still be set).
12564 * @param {String} value The value to match
12566 setValue : function(v){
12573 if(this.valueField){
12574 var r = this.findRecord(this.valueField, v);
12576 text = r.data[this.displayField];
12577 }else if(this.valueNotFoundText !== undefined){
12578 text = this.valueNotFoundText;
12581 this.lastSelectionText = text;
12582 if(this.hiddenField){
12583 this.hiddenField.dom.value = v;
12585 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12588 var close = this.closeTriggerEl();
12591 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12595 * @property {Object} the last set data for the element
12600 * Sets the value of the field based on a object which is related to the record format for the store.
12601 * @param {Object} value the value to set as. or false on reset?
12603 setFromData : function(o){
12610 var dv = ''; // display value
12611 var vv = ''; // value value..
12613 if (this.displayField) {
12614 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12616 // this is an error condition!!!
12617 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12620 if(this.valueField){
12621 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12624 var close = this.closeTriggerEl();
12627 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12630 if(this.hiddenField){
12631 this.hiddenField.dom.value = vv;
12633 this.lastSelectionText = dv;
12634 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12638 // no hidden field.. - we store the value in 'value', but still display
12639 // display field!!!!
12640 this.lastSelectionText = dv;
12641 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12648 reset : function(){
12649 // overridden so that last data is reset..
12656 this.setValue(this.originalValue);
12657 this.clearInvalid();
12658 this.lastData = false;
12660 this.view.clearSelections();
12664 findRecord : function(prop, value){
12666 if(this.store.getCount() > 0){
12667 this.store.each(function(r){
12668 if(r.data[prop] == value){
12678 getName: function()
12680 // returns hidden if it's set..
12681 if (!this.rendered) {return ''};
12682 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12686 onViewMove : function(e, t){
12687 this.inKeyMode = false;
12691 onViewOver : function(e, t){
12692 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12695 var item = this.view.findItemFromChild(t);
12698 var index = this.view.indexOf(item);
12699 this.select(index, false);
12704 onViewClick : function(view, doFocus, el, e)
12706 var index = this.view.getSelectedIndexes()[0];
12708 var r = this.store.getAt(index);
12712 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12719 Roo.each(this.tickItems, function(v,k){
12721 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12723 _this.tickItems.splice(k, 1);
12725 if(typeof(e) == 'undefined' && view == false){
12726 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12738 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12739 this.tickItems.push(r.data);
12742 if(typeof(e) == 'undefined' && view == false){
12743 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12750 this.onSelect(r, index);
12752 if(doFocus !== false && !this.blockFocus){
12753 this.inputEl().focus();
12758 restrictHeight : function(){
12759 //this.innerList.dom.style.height = '';
12760 //var inner = this.innerList.dom;
12761 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12762 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12763 //this.list.beginUpdate();
12764 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12765 this.list.alignTo(this.inputEl(), this.listAlign);
12766 this.list.alignTo(this.inputEl(), this.listAlign);
12767 //this.list.endUpdate();
12771 onEmptyResults : function(){
12773 if(this.tickable && this.editable){
12774 this.restrictHeight();
12782 * Returns true if the dropdown list is expanded, else false.
12784 isExpanded : function(){
12785 return this.list.isVisible();
12789 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12790 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12791 * @param {String} value The data value of the item to select
12792 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12793 * selected item if it is not currently in view (defaults to true)
12794 * @return {Boolean} True if the value matched an item in the list, else false
12796 selectByValue : function(v, scrollIntoView){
12797 if(v !== undefined && v !== null){
12798 var r = this.findRecord(this.valueField || this.displayField, v);
12800 this.select(this.store.indexOf(r), scrollIntoView);
12808 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12809 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12810 * @param {Number} index The zero-based index of the list item to select
12811 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12812 * selected item if it is not currently in view (defaults to true)
12814 select : function(index, scrollIntoView){
12815 this.selectedIndex = index;
12816 this.view.select(index);
12817 if(scrollIntoView !== false){
12818 var el = this.view.getNode(index);
12820 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12823 this.list.scrollChildIntoView(el, false);
12829 selectNext : function(){
12830 var ct = this.store.getCount();
12832 if(this.selectedIndex == -1){
12834 }else if(this.selectedIndex < ct-1){
12835 this.select(this.selectedIndex+1);
12841 selectPrev : function(){
12842 var ct = this.store.getCount();
12844 if(this.selectedIndex == -1){
12846 }else if(this.selectedIndex != 0){
12847 this.select(this.selectedIndex-1);
12853 onKeyUp : function(e){
12854 if(this.editable !== false && !e.isSpecialKey()){
12855 this.lastKey = e.getKey();
12856 this.dqTask.delay(this.queryDelay);
12861 validateBlur : function(){
12862 return !this.list || !this.list.isVisible();
12866 initQuery : function(){
12868 var v = this.getRawValue();
12870 if(this.tickable && this.editable){
12871 v = this.tickableInputEl().getValue();
12878 doForce : function(){
12879 if(this.inputEl().dom.value.length > 0){
12880 this.inputEl().dom.value =
12881 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12887 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12888 * query allowing the query action to be canceled if needed.
12889 * @param {String} query The SQL query to execute
12890 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12891 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12892 * saved in the current store (defaults to false)
12894 doQuery : function(q, forceAll){
12896 if(q === undefined || q === null){
12901 forceAll: forceAll,
12905 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12910 forceAll = qe.forceAll;
12911 if(forceAll === true || (q.length >= this.minChars)){
12913 this.hasQuery = true;
12915 if(this.lastQuery != q || this.alwaysQuery){
12916 this.lastQuery = q;
12917 if(this.mode == 'local'){
12918 this.selectedIndex = -1;
12920 this.store.clearFilter();
12923 if(this.specialFilter){
12924 this.fireEvent('specialfilter', this);
12929 this.store.filter(this.displayField, q);
12932 this.store.fireEvent("datachanged", this.store);
12939 this.store.baseParams[this.queryParam] = q;
12941 var options = {params : this.getParams(q)};
12944 options.add = true;
12945 options.params.start = this.page * this.pageSize;
12948 this.store.load(options);
12951 * this code will make the page width larger, at the beginning, the list not align correctly,
12952 * we should expand the list on onLoad
12953 * so command out it
12958 this.selectedIndex = -1;
12963 this.loadNext = false;
12967 getParams : function(q){
12969 //p[this.queryParam] = q;
12973 p.limit = this.pageSize;
12979 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12981 collapse : function(){
12982 if(!this.isExpanded()){
12989 this.hasFocus = false;
12991 this.cancelBtn.hide();
12992 this.trigger.show();
12995 this.tickableInputEl().dom.value = '';
12996 this.tickableInputEl().blur();
13001 Roo.get(document).un('mousedown', this.collapseIf, this);
13002 Roo.get(document).un('mousewheel', this.collapseIf, this);
13003 if (!this.editable) {
13004 Roo.get(document).un('keydown', this.listKeyPress, this);
13006 this.fireEvent('collapse', this);
13010 collapseIf : function(e){
13011 var in_combo = e.within(this.el);
13012 var in_list = e.within(this.list);
13013 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13015 if (in_combo || in_list || is_list) {
13016 //e.stopPropagation();
13021 this.onTickableFooterButtonClick(e, false, false);
13029 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13031 expand : function(){
13033 if(this.isExpanded() || !this.hasFocus){
13037 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13038 this.list.setWidth(lw);
13045 this.restrictHeight();
13049 this.tickItems = Roo.apply([], this.item);
13052 this.cancelBtn.show();
13053 this.trigger.hide();
13056 this.tickableInputEl().focus();
13061 Roo.get(document).on('mousedown', this.collapseIf, this);
13062 Roo.get(document).on('mousewheel', this.collapseIf, this);
13063 if (!this.editable) {
13064 Roo.get(document).on('keydown', this.listKeyPress, this);
13067 this.fireEvent('expand', this);
13071 // Implements the default empty TriggerField.onTriggerClick function
13072 onTriggerClick : function(e)
13074 Roo.log('trigger click');
13076 if(this.disabled || !this.triggerList){
13081 this.loadNext = false;
13083 if(this.isExpanded()){
13085 if (!this.blockFocus) {
13086 this.inputEl().focus();
13090 this.hasFocus = true;
13091 if(this.triggerAction == 'all') {
13092 this.doQuery(this.allQuery, true);
13094 this.doQuery(this.getRawValue());
13096 if (!this.blockFocus) {
13097 this.inputEl().focus();
13102 onTickableTriggerClick : function(e)
13109 this.loadNext = false;
13110 this.hasFocus = true;
13112 if(this.triggerAction == 'all') {
13113 this.doQuery(this.allQuery, true);
13115 this.doQuery(this.getRawValue());
13119 onSearchFieldClick : function(e)
13121 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13122 this.onTickableFooterButtonClick(e, false, false);
13126 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13131 this.loadNext = false;
13132 this.hasFocus = true;
13134 if(this.triggerAction == 'all') {
13135 this.doQuery(this.allQuery, true);
13137 this.doQuery(this.getRawValue());
13141 listKeyPress : function(e)
13143 //Roo.log('listkeypress');
13144 // scroll to first matching element based on key pres..
13145 if (e.isSpecialKey()) {
13148 var k = String.fromCharCode(e.getKey()).toUpperCase();
13151 var csel = this.view.getSelectedNodes();
13152 var cselitem = false;
13154 var ix = this.view.indexOf(csel[0]);
13155 cselitem = this.store.getAt(ix);
13156 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13162 this.store.each(function(v) {
13164 // start at existing selection.
13165 if (cselitem.id == v.id) {
13171 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13172 match = this.store.indexOf(v);
13178 if (match === false) {
13179 return true; // no more action?
13182 this.view.select(match);
13183 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13184 sn.scrollIntoView(sn.dom.parentNode, false);
13187 onViewScroll : function(e, t){
13189 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){
13193 this.hasQuery = true;
13195 this.loading = this.list.select('.loading', true).first();
13197 if(this.loading === null){
13198 this.list.createChild({
13200 cls: 'loading select2-more-results select2-active',
13201 html: 'Loading more results...'
13204 this.loading = this.list.select('.loading', true).first();
13206 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13208 this.loading.hide();
13211 this.loading.show();
13216 this.loadNext = true;
13218 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13223 addItem : function(o)
13225 var dv = ''; // display value
13227 if (this.displayField) {
13228 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13230 // this is an error condition!!!
13231 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13238 var choice = this.choices.createChild({
13240 cls: 'select2-search-choice',
13249 cls: 'select2-search-choice-close',
13254 }, this.searchField);
13256 var close = choice.select('a.select2-search-choice-close', true).first();
13258 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13266 this.inputEl().dom.value = '';
13271 onRemoveItem : function(e, _self, o)
13273 e.preventDefault();
13275 this.lastItem = Roo.apply([], this.item);
13277 var index = this.item.indexOf(o.data) * 1;
13280 Roo.log('not this item?!');
13284 this.item.splice(index, 1);
13289 this.fireEvent('remove', this, e);
13295 syncValue : function()
13297 if(!this.item.length){
13304 Roo.each(this.item, function(i){
13305 if(_this.valueField){
13306 value.push(i[_this.valueField]);
13313 this.value = value.join(',');
13315 if(this.hiddenField){
13316 this.hiddenField.dom.value = this.value;
13319 this.store.fireEvent("datachanged", this.store);
13322 clearItem : function()
13324 if(!this.multiple){
13330 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13338 if(this.tickable && !Roo.isTouch){
13339 this.view.refresh();
13343 inputEl: function ()
13345 if(Roo.isTouch && this.mobileTouchView){
13346 return this.el.select('input.form-control',true).first();
13350 return this.searchField;
13353 return this.el.select('input.form-control',true).first();
13357 onTickableFooterButtonClick : function(e, btn, el)
13359 e.preventDefault();
13361 this.lastItem = Roo.apply([], this.item);
13363 if(btn && btn.name == 'cancel'){
13364 this.tickItems = Roo.apply([], this.item);
13373 Roo.each(this.tickItems, function(o){
13381 validate : function()
13383 var v = this.getRawValue();
13386 v = this.getValue();
13389 if(this.disabled || this.allowBlank || v.length){
13394 this.markInvalid();
13398 tickableInputEl : function()
13400 if(!this.tickable || !this.editable){
13401 return this.inputEl();
13404 return this.inputEl().select('.select2-search-field-input', true).first();
13408 getAutoCreateTouchView : function()
13413 cls: 'form-group' //input-group
13419 type : this.inputType,
13420 cls : 'form-control x-combo-noedit',
13421 autocomplete: 'new-password',
13422 placeholder : this.placeholder || '',
13427 input.name = this.name;
13431 input.cls += ' input-' + this.size;
13434 if (this.disabled) {
13435 input.disabled = true;
13446 inputblock.cls += ' input-group';
13448 inputblock.cn.unshift({
13450 cls : 'input-group-addon',
13455 if(this.removable && !this.multiple){
13456 inputblock.cls += ' roo-removable';
13458 inputblock.cn.push({
13461 cls : 'roo-combo-removable-btn close'
13465 if(this.hasFeedback && !this.allowBlank){
13467 inputblock.cls += ' has-feedback';
13469 inputblock.cn.push({
13471 cls: 'glyphicon form-control-feedback'
13478 inputblock.cls += (this.before) ? '' : ' input-group';
13480 inputblock.cn.push({
13482 cls : 'input-group-addon',
13493 cls: 'form-hidden-field'
13507 cls: 'form-hidden-field'
13511 cls: 'select2-choices',
13515 cls: 'select2-search-field',
13528 cls: 'select2-container input-group',
13535 combobox.cls += ' select2-container-multi';
13538 var align = this.labelAlign || this.parentLabelAlign();
13542 if(this.fieldLabel.length){
13544 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13545 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13550 cls : 'control-label ' + lw,
13551 html : this.fieldLabel
13563 var settings = this;
13565 ['xs','sm','md','lg'].map(function(size){
13566 if (settings[size]) {
13567 cfg.cls += ' col-' + size + '-' + settings[size];
13574 initTouchView : function()
13576 this.renderTouchView();
13578 this.touchViewEl.on('scroll', function(){
13579 this.el.dom.scrollTop = 0;
13582 this.inputEl().on("click", this.showTouchView, this);
13584 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13585 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13587 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13589 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13590 this.store.on('load', this.onTouchViewLoad, this);
13591 this.store.on('loadexception', this.onTouchViewLoadException, this);
13593 if(this.hiddenName){
13595 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13597 this.hiddenField.dom.value =
13598 this.hiddenValue !== undefined ? this.hiddenValue :
13599 this.value !== undefined ? this.value : '';
13601 this.el.dom.removeAttribute('name');
13602 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13606 this.choices = this.el.select('ul.select2-choices', true).first();
13607 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13610 if(this.removable && !this.multiple){
13611 var close = this.closeTriggerEl();
13613 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13614 close.on('click', this.removeBtnClick, this, close);
13618 * fix the bug in Safari iOS8
13620 this.inputEl().on("focus", function(e){
13621 document.activeElement.blur();
13629 renderTouchView : function()
13631 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13632 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13634 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13635 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13637 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13638 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639 this.touchViewBodyEl.setStyle('overflow', 'auto');
13641 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13642 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13644 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13645 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13649 showTouchView : function()
13655 this.touchViewHeaderEl.hide();
13657 if(this.fieldLabel.length){
13658 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13659 this.touchViewHeaderEl.show();
13662 this.touchViewEl.show();
13664 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13665 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13667 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13669 if(this.fieldLabel.length){
13670 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13673 this.touchViewBodyEl.setHeight(bodyHeight);
13677 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13679 this.touchViewEl.addClass('in');
13682 this.doTouchViewQuery();
13686 hideTouchView : function()
13688 this.touchViewEl.removeClass('in');
13692 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13694 this.touchViewEl.setStyle('display', 'none');
13699 setTouchViewValue : function()
13706 Roo.each(this.tickItems, function(o){
13711 this.hideTouchView();
13714 doTouchViewQuery : function()
13723 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13727 if(!this.alwaysQuery || this.mode == 'local'){
13728 this.onTouchViewLoad();
13735 onTouchViewBeforeLoad : function(combo,opts)
13741 onTouchViewLoad : function()
13743 if(this.store.getCount() < 1){
13744 this.onTouchViewEmptyResults();
13748 this.clearTouchView();
13750 var rawValue = this.getRawValue();
13752 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13754 this.tickItems = [];
13756 this.store.data.each(function(d, rowIndex){
13757 var row = this.touchViewListGroup.createChild(template);
13759 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13762 html : d.data[this.displayField]
13765 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13766 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13770 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13771 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13774 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13775 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13776 this.tickItems.push(d.data);
13779 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13783 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13785 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13787 if(this.fieldLabel.length){
13788 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13791 var listHeight = this.touchViewListGroup.getHeight();
13795 if(firstChecked && listHeight > bodyHeight){
13796 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13801 onTouchViewLoadException : function()
13803 this.hideTouchView();
13806 onTouchViewEmptyResults : function()
13808 this.clearTouchView();
13810 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13812 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13816 clearTouchView : function()
13818 this.touchViewListGroup.dom.innerHTML = '';
13821 onTouchViewClick : function(e, el, o)
13823 e.preventDefault();
13826 var rowIndex = o.rowIndex;
13828 var r = this.store.getAt(rowIndex);
13830 if(!this.multiple){
13831 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13832 c.dom.removeAttribute('checked');
13835 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13837 this.setFromData(r.data);
13839 var close = this.closeTriggerEl();
13845 this.hideTouchView();
13847 this.fireEvent('select', this, r, rowIndex);
13852 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13853 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13854 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13858 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13859 this.addItem(r.data);
13860 this.tickItems.push(r.data);
13866 * @cfg {Boolean} grow
13870 * @cfg {Number} growMin
13874 * @cfg {Number} growMax
13883 Roo.apply(Roo.bootstrap.ComboBox, {
13887 cls: 'modal-header',
13909 cls: 'list-group-item',
13913 cls: 'roo-combobox-list-group-item-value'
13917 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13931 listItemCheckbox : {
13933 cls: 'list-group-item',
13937 cls: 'roo-combobox-list-group-item-value'
13941 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13957 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13962 cls: 'modal-footer',
13970 cls: 'col-xs-6 text-left',
13973 cls: 'btn btn-danger roo-touch-view-cancel',
13979 cls: 'col-xs-6 text-right',
13982 cls: 'btn btn-success roo-touch-view-ok',
13993 Roo.apply(Roo.bootstrap.ComboBox, {
13995 touchViewTemplate : {
13997 cls: 'modal fade roo-combobox-touch-view',
14001 cls: 'modal-dialog',
14002 style : 'position:fixed', // we have to fix position....
14006 cls: 'modal-content',
14008 Roo.bootstrap.ComboBox.header,
14009 Roo.bootstrap.ComboBox.body,
14010 Roo.bootstrap.ComboBox.footer
14019 * Ext JS Library 1.1.1
14020 * Copyright(c) 2006-2007, Ext JS, LLC.
14022 * Originally Released Under LGPL - original licence link has changed is not relivant.
14025 * <script type="text/javascript">
14030 * @extends Roo.util.Observable
14031 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14032 * This class also supports single and multi selection modes. <br>
14033 * Create a data model bound view:
14035 var store = new Roo.data.Store(...);
14037 var view = new Roo.View({
14039 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14041 singleSelect: true,
14042 selectedClass: "ydataview-selected",
14046 // listen for node click?
14047 view.on("click", function(vw, index, node, e){
14048 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14052 dataModel.load("foobar.xml");
14054 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14056 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14057 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14059 * Note: old style constructor is still suported (container, template, config)
14062 * Create a new View
14063 * @param {Object} config The config object
14066 Roo.View = function(config, depreciated_tpl, depreciated_config){
14068 this.parent = false;
14070 if (typeof(depreciated_tpl) == 'undefined') {
14071 // new way.. - universal constructor.
14072 Roo.apply(this, config);
14073 this.el = Roo.get(this.el);
14076 this.el = Roo.get(config);
14077 this.tpl = depreciated_tpl;
14078 Roo.apply(this, depreciated_config);
14080 this.wrapEl = this.el.wrap().wrap();
14081 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14084 if(typeof(this.tpl) == "string"){
14085 this.tpl = new Roo.Template(this.tpl);
14087 // support xtype ctors..
14088 this.tpl = new Roo.factory(this.tpl, Roo);
14092 this.tpl.compile();
14097 * @event beforeclick
14098 * Fires before a click is processed. Returns false to cancel the default action.
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
14104 "beforeclick" : true,
14107 * Fires when a template node is 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
14116 * Fires when a template node is double 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
14124 * @event contextmenu
14125 * Fires when a template node is right clicked.
14126 * @param {Roo.View} this
14127 * @param {Number} index The index of the target node
14128 * @param {HTMLElement} node The target node
14129 * @param {Roo.EventObject} e The raw event object
14131 "contextmenu" : true,
14133 * @event selectionchange
14134 * Fires when the selected nodes change.
14135 * @param {Roo.View} this
14136 * @param {Array} selections Array of the selected nodes
14138 "selectionchange" : true,
14141 * @event beforeselect
14142 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14143 * @param {Roo.View} this
14144 * @param {HTMLElement} node The node to be selected
14145 * @param {Array} selections Array of currently selected nodes
14147 "beforeselect" : true,
14149 * @event preparedata
14150 * Fires on every row to render, to allow you to change the data.
14151 * @param {Roo.View} this
14152 * @param {Object} data to be rendered (change this)
14154 "preparedata" : true
14162 "click": this.onClick,
14163 "dblclick": this.onDblClick,
14164 "contextmenu": this.onContextMenu,
14168 this.selections = [];
14170 this.cmp = new Roo.CompositeElementLite([]);
14172 this.store = Roo.factory(this.store, Roo.data);
14173 this.setStore(this.store, true);
14176 if ( this.footer && this.footer.xtype) {
14178 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14180 this.footer.dataSource = this.store;
14181 this.footer.container = fctr;
14182 this.footer = Roo.factory(this.footer, Roo);
14183 fctr.insertFirst(this.el);
14185 // this is a bit insane - as the paging toolbar seems to detach the el..
14186 // dom.parentNode.parentNode.parentNode
14187 // they get detached?
14191 Roo.View.superclass.constructor.call(this);
14196 Roo.extend(Roo.View, Roo.util.Observable, {
14199 * @cfg {Roo.data.Store} store Data store to load data from.
14204 * @cfg {String|Roo.Element} el The container element.
14209 * @cfg {String|Roo.Template} tpl The template used by this View
14213 * @cfg {String} dataName the named area of the template to use as the data area
14214 * Works with domtemplates roo-name="name"
14218 * @cfg {String} selectedClass The css class to add to selected nodes
14220 selectedClass : "x-view-selected",
14222 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14227 * @cfg {String} text to display on mask (default Loading)
14231 * @cfg {Boolean} multiSelect Allow multiple selection
14233 multiSelect : false,
14235 * @cfg {Boolean} singleSelect Allow single selection
14237 singleSelect: false,
14240 * @cfg {Boolean} toggleSelect - selecting
14242 toggleSelect : false,
14245 * @cfg {Boolean} tickable - selecting
14250 * Returns the element this view is bound to.
14251 * @return {Roo.Element}
14253 getEl : function(){
14254 return this.wrapEl;
14260 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14262 refresh : function(){
14263 //Roo.log('refresh');
14266 // if we are using something like 'domtemplate', then
14267 // the what gets used is:
14268 // t.applySubtemplate(NAME, data, wrapping data..)
14269 // the outer template then get' applied with
14270 // the store 'extra data'
14271 // and the body get's added to the
14272 // roo-name="data" node?
14273 // <span class='roo-tpl-{name}'></span> ?????
14277 this.clearSelections();
14278 this.el.update("");
14280 var records = this.store.getRange();
14281 if(records.length < 1) {
14283 // is this valid?? = should it render a template??
14285 this.el.update(this.emptyText);
14289 if (this.dataName) {
14290 this.el.update(t.apply(this.store.meta)); //????
14291 el = this.el.child('.roo-tpl-' + this.dataName);
14294 for(var i = 0, len = records.length; i < len; i++){
14295 var data = this.prepareData(records[i].data, i, records[i]);
14296 this.fireEvent("preparedata", this, data, i, records[i]);
14298 var d = Roo.apply({}, data);
14301 Roo.apply(d, {'roo-id' : Roo.id()});
14305 Roo.each(this.parent.item, function(item){
14306 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14309 Roo.apply(d, {'roo-data-checked' : 'checked'});
14313 html[html.length] = Roo.util.Format.trim(
14315 t.applySubtemplate(this.dataName, d, this.store.meta) :
14322 el.update(html.join(""));
14323 this.nodes = el.dom.childNodes;
14324 this.updateIndexes(0);
14329 * Function to override to reformat the data that is sent to
14330 * the template for each node.
14331 * DEPRICATED - use the preparedata event handler.
14332 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14333 * a JSON object for an UpdateManager bound view).
14335 prepareData : function(data, index, record)
14337 this.fireEvent("preparedata", this, data, index, record);
14341 onUpdate : function(ds, record){
14342 // Roo.log('on update');
14343 this.clearSelections();
14344 var index = this.store.indexOf(record);
14345 var n = this.nodes[index];
14346 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14347 n.parentNode.removeChild(n);
14348 this.updateIndexes(index, index);
14354 onAdd : function(ds, records, index)
14356 //Roo.log(['on Add', ds, records, index] );
14357 this.clearSelections();
14358 if(this.nodes.length == 0){
14362 var n = this.nodes[index];
14363 for(var i = 0, len = records.length; i < len; i++){
14364 var d = this.prepareData(records[i].data, i, records[i]);
14366 this.tpl.insertBefore(n, d);
14369 this.tpl.append(this.el, d);
14372 this.updateIndexes(index);
14375 onRemove : function(ds, record, index){
14376 // Roo.log('onRemove');
14377 this.clearSelections();
14378 var el = this.dataName ?
14379 this.el.child('.roo-tpl-' + this.dataName) :
14382 el.dom.removeChild(this.nodes[index]);
14383 this.updateIndexes(index);
14387 * Refresh an individual node.
14388 * @param {Number} index
14390 refreshNode : function(index){
14391 this.onUpdate(this.store, this.store.getAt(index));
14394 updateIndexes : function(startIndex, endIndex){
14395 var ns = this.nodes;
14396 startIndex = startIndex || 0;
14397 endIndex = endIndex || ns.length - 1;
14398 for(var i = startIndex; i <= endIndex; i++){
14399 ns[i].nodeIndex = i;
14404 * Changes the data store this view uses and refresh the view.
14405 * @param {Store} store
14407 setStore : function(store, initial){
14408 if(!initial && this.store){
14409 this.store.un("datachanged", this.refresh);
14410 this.store.un("add", this.onAdd);
14411 this.store.un("remove", this.onRemove);
14412 this.store.un("update", this.onUpdate);
14413 this.store.un("clear", this.refresh);
14414 this.store.un("beforeload", this.onBeforeLoad);
14415 this.store.un("load", this.onLoad);
14416 this.store.un("loadexception", this.onLoad);
14420 store.on("datachanged", this.refresh, this);
14421 store.on("add", this.onAdd, this);
14422 store.on("remove", this.onRemove, this);
14423 store.on("update", this.onUpdate, this);
14424 store.on("clear", this.refresh, this);
14425 store.on("beforeload", this.onBeforeLoad, this);
14426 store.on("load", this.onLoad, this);
14427 store.on("loadexception", this.onLoad, this);
14435 * onbeforeLoad - masks the loading area.
14438 onBeforeLoad : function(store,opts)
14440 //Roo.log('onBeforeLoad');
14442 this.el.update("");
14444 this.el.mask(this.mask ? this.mask : "Loading" );
14446 onLoad : function ()
14453 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14454 * @param {HTMLElement} node
14455 * @return {HTMLElement} The template node
14457 findItemFromChild : function(node){
14458 var el = this.dataName ?
14459 this.el.child('.roo-tpl-' + this.dataName,true) :
14462 if(!node || node.parentNode == el){
14465 var p = node.parentNode;
14466 while(p && p != el){
14467 if(p.parentNode == el){
14476 onClick : function(e){
14477 var item = this.findItemFromChild(e.getTarget());
14479 var index = this.indexOf(item);
14480 if(this.onItemClick(item, index, e) !== false){
14481 this.fireEvent("click", this, index, item, e);
14484 this.clearSelections();
14489 onContextMenu : function(e){
14490 var item = this.findItemFromChild(e.getTarget());
14492 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14497 onDblClick : function(e){
14498 var item = this.findItemFromChild(e.getTarget());
14500 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14504 onItemClick : function(item, index, e)
14506 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14509 if (this.toggleSelect) {
14510 var m = this.isSelected(item) ? 'unselect' : 'select';
14513 _t[m](item, true, false);
14516 if(this.multiSelect || this.singleSelect){
14517 if(this.multiSelect && e.shiftKey && this.lastSelection){
14518 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14520 this.select(item, this.multiSelect && e.ctrlKey);
14521 this.lastSelection = item;
14524 if(!this.tickable){
14525 e.preventDefault();
14533 * Get the number of selected nodes.
14536 getSelectionCount : function(){
14537 return this.selections.length;
14541 * Get the currently selected nodes.
14542 * @return {Array} An array of HTMLElements
14544 getSelectedNodes : function(){
14545 return this.selections;
14549 * Get the indexes of the selected nodes.
14552 getSelectedIndexes : function(){
14553 var indexes = [], s = this.selections;
14554 for(var i = 0, len = s.length; i < len; i++){
14555 indexes.push(s[i].nodeIndex);
14561 * Clear all selections
14562 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14564 clearSelections : function(suppressEvent){
14565 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14566 this.cmp.elements = this.selections;
14567 this.cmp.removeClass(this.selectedClass);
14568 this.selections = [];
14569 if(!suppressEvent){
14570 this.fireEvent("selectionchange", this, this.selections);
14576 * Returns true if the passed node is selected
14577 * @param {HTMLElement/Number} node The node or node index
14578 * @return {Boolean}
14580 isSelected : function(node){
14581 var s = this.selections;
14585 node = this.getNode(node);
14586 return s.indexOf(node) !== -1;
14591 * @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
14592 * @param {Boolean} keepExisting (optional) true to keep existing selections
14593 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14595 select : function(nodeInfo, keepExisting, suppressEvent){
14596 if(nodeInfo instanceof Array){
14598 this.clearSelections(true);
14600 for(var i = 0, len = nodeInfo.length; i < len; i++){
14601 this.select(nodeInfo[i], true, true);
14605 var node = this.getNode(nodeInfo);
14606 if(!node || this.isSelected(node)){
14607 return; // already selected.
14610 this.clearSelections(true);
14613 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14614 Roo.fly(node).addClass(this.selectedClass);
14615 this.selections.push(node);
14616 if(!suppressEvent){
14617 this.fireEvent("selectionchange", this, this.selections);
14625 * @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
14626 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14627 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14629 unselect : function(nodeInfo, keepExisting, suppressEvent)
14631 if(nodeInfo instanceof Array){
14632 Roo.each(this.selections, function(s) {
14633 this.unselect(s, nodeInfo);
14637 var node = this.getNode(nodeInfo);
14638 if(!node || !this.isSelected(node)){
14639 //Roo.log("not selected");
14640 return; // not selected.
14644 Roo.each(this.selections, function(s) {
14646 Roo.fly(node).removeClass(this.selectedClass);
14653 this.selections= ns;
14654 this.fireEvent("selectionchange", this, this.selections);
14658 * Gets a template node.
14659 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14660 * @return {HTMLElement} The node or null if it wasn't found
14662 getNode : function(nodeInfo){
14663 if(typeof nodeInfo == "string"){
14664 return document.getElementById(nodeInfo);
14665 }else if(typeof nodeInfo == "number"){
14666 return this.nodes[nodeInfo];
14672 * Gets a range template nodes.
14673 * @param {Number} startIndex
14674 * @param {Number} endIndex
14675 * @return {Array} An array of nodes
14677 getNodes : function(start, end){
14678 var ns = this.nodes;
14679 start = start || 0;
14680 end = typeof end == "undefined" ? ns.length - 1 : end;
14683 for(var i = start; i <= end; i++){
14687 for(var i = start; i >= end; i--){
14695 * Finds the index of the passed node
14696 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14697 * @return {Number} The index of the node or -1
14699 indexOf : function(node){
14700 node = this.getNode(node);
14701 if(typeof node.nodeIndex == "number"){
14702 return node.nodeIndex;
14704 var ns = this.nodes;
14705 for(var i = 0, len = ns.length; i < len; i++){
14716 * based on jquery fullcalendar
14720 Roo.bootstrap = Roo.bootstrap || {};
14722 * @class Roo.bootstrap.Calendar
14723 * @extends Roo.bootstrap.Component
14724 * Bootstrap Calendar class
14725 * @cfg {Boolean} loadMask (true|false) default false
14726 * @cfg {Object} header generate the user specific header of the calendar, default false
14729 * Create a new Container
14730 * @param {Object} config The config object
14735 Roo.bootstrap.Calendar = function(config){
14736 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14740 * Fires when a date is selected
14741 * @param {DatePicker} this
14742 * @param {Date} date The selected date
14746 * @event monthchange
14747 * Fires when the displayed month changes
14748 * @param {DatePicker} this
14749 * @param {Date} date The selected month
14751 'monthchange': true,
14753 * @event evententer
14754 * Fires when mouse over an event
14755 * @param {Calendar} this
14756 * @param {event} Event
14758 'evententer': true,
14760 * @event eventleave
14761 * Fires when the mouse leaves an
14762 * @param {Calendar} this
14765 'eventleave': true,
14767 * @event eventclick
14768 * Fires when the mouse click an
14769 * @param {Calendar} this
14778 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14781 * @cfg {Number} startDay
14782 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14790 getAutoCreate : function(){
14793 var fc_button = function(name, corner, style, content ) {
14794 return Roo.apply({},{
14796 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14798 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14801 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14812 style : 'width:100%',
14819 cls : 'fc-header-left',
14821 fc_button('prev', 'left', 'arrow', '‹' ),
14822 fc_button('next', 'right', 'arrow', '›' ),
14823 { tag: 'span', cls: 'fc-header-space' },
14824 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14832 cls : 'fc-header-center',
14836 cls: 'fc-header-title',
14839 html : 'month / year'
14847 cls : 'fc-header-right',
14849 /* fc_button('month', 'left', '', 'month' ),
14850 fc_button('week', '', '', 'week' ),
14851 fc_button('day', 'right', '', 'day' )
14863 header = this.header;
14866 var cal_heads = function() {
14868 // fixme - handle this.
14870 for (var i =0; i < Date.dayNames.length; i++) {
14871 var d = Date.dayNames[i];
14874 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14875 html : d.substring(0,3)
14879 ret[0].cls += ' fc-first';
14880 ret[6].cls += ' fc-last';
14883 var cal_cell = function(n) {
14886 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14891 cls: 'fc-day-number',
14895 cls: 'fc-day-content',
14899 style: 'position: relative;' // height: 17px;
14911 var cal_rows = function() {
14914 for (var r = 0; r < 6; r++) {
14921 for (var i =0; i < Date.dayNames.length; i++) {
14922 var d = Date.dayNames[i];
14923 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14926 row.cn[0].cls+=' fc-first';
14927 row.cn[0].cn[0].style = 'min-height:90px';
14928 row.cn[6].cls+=' fc-last';
14932 ret[0].cls += ' fc-first';
14933 ret[4].cls += ' fc-prev-last';
14934 ret[5].cls += ' fc-last';
14941 cls: 'fc-border-separate',
14942 style : 'width:100%',
14950 cls : 'fc-first fc-last',
14968 cls : 'fc-content',
14969 style : "position: relative;",
14972 cls : 'fc-view fc-view-month fc-grid',
14973 style : 'position: relative',
14974 unselectable : 'on',
14977 cls : 'fc-event-container',
14978 style : 'position:absolute;z-index:8;top:0;left:0;'
14996 initEvents : function()
14999 throw "can not find store for calendar";
15005 style: "text-align:center",
15009 style: "background-color:white;width:50%;margin:250 auto",
15013 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15024 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15026 var size = this.el.select('.fc-content', true).first().getSize();
15027 this.maskEl.setSize(size.width, size.height);
15028 this.maskEl.enableDisplayMode("block");
15029 if(!this.loadMask){
15030 this.maskEl.hide();
15033 this.store = Roo.factory(this.store, Roo.data);
15034 this.store.on('load', this.onLoad, this);
15035 this.store.on('beforeload', this.onBeforeLoad, this);
15039 this.cells = this.el.select('.fc-day',true);
15040 //Roo.log(this.cells);
15041 this.textNodes = this.el.query('.fc-day-number');
15042 this.cells.addClassOnOver('fc-state-hover');
15044 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15045 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15046 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15047 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15049 this.on('monthchange', this.onMonthChange, this);
15051 this.update(new Date().clearTime());
15054 resize : function() {
15055 var sz = this.el.getSize();
15057 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15058 this.el.select('.fc-day-content div',true).setHeight(34);
15063 showPrevMonth : function(e){
15064 this.update(this.activeDate.add("mo", -1));
15066 showToday : function(e){
15067 this.update(new Date().clearTime());
15070 showNextMonth : function(e){
15071 this.update(this.activeDate.add("mo", 1));
15075 showPrevYear : function(){
15076 this.update(this.activeDate.add("y", -1));
15080 showNextYear : function(){
15081 this.update(this.activeDate.add("y", 1));
15086 update : function(date)
15088 var vd = this.activeDate;
15089 this.activeDate = date;
15090 // if(vd && this.el){
15091 // var t = date.getTime();
15092 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15093 // Roo.log('using add remove');
15095 // this.fireEvent('monthchange', this, date);
15097 // this.cells.removeClass("fc-state-highlight");
15098 // this.cells.each(function(c){
15099 // if(c.dateValue == t){
15100 // c.addClass("fc-state-highlight");
15101 // setTimeout(function(){
15102 // try{c.dom.firstChild.focus();}catch(e){}
15112 var days = date.getDaysInMonth();
15114 var firstOfMonth = date.getFirstDateOfMonth();
15115 var startingPos = firstOfMonth.getDay()-this.startDay;
15117 if(startingPos < this.startDay){
15121 var pm = date.add(Date.MONTH, -1);
15122 var prevStart = pm.getDaysInMonth()-startingPos;
15124 this.cells = this.el.select('.fc-day',true);
15125 this.textNodes = this.el.query('.fc-day-number');
15126 this.cells.addClassOnOver('fc-state-hover');
15128 var cells = this.cells.elements;
15129 var textEls = this.textNodes;
15131 Roo.each(cells, function(cell){
15132 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15135 days += startingPos;
15137 // convert everything to numbers so it's fast
15138 var day = 86400000;
15139 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15142 //Roo.log(prevStart);
15144 var today = new Date().clearTime().getTime();
15145 var sel = date.clearTime().getTime();
15146 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15147 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15148 var ddMatch = this.disabledDatesRE;
15149 var ddText = this.disabledDatesText;
15150 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15151 var ddaysText = this.disabledDaysText;
15152 var format = this.format;
15154 var setCellClass = function(cal, cell){
15158 //Roo.log('set Cell Class');
15160 var t = d.getTime();
15164 cell.dateValue = t;
15166 cell.className += " fc-today";
15167 cell.className += " fc-state-highlight";
15168 cell.title = cal.todayText;
15171 // disable highlight in other month..
15172 //cell.className += " fc-state-highlight";
15177 cell.className = " fc-state-disabled";
15178 cell.title = cal.minText;
15182 cell.className = " fc-state-disabled";
15183 cell.title = cal.maxText;
15187 if(ddays.indexOf(d.getDay()) != -1){
15188 cell.title = ddaysText;
15189 cell.className = " fc-state-disabled";
15192 if(ddMatch && format){
15193 var fvalue = d.dateFormat(format);
15194 if(ddMatch.test(fvalue)){
15195 cell.title = ddText.replace("%0", fvalue);
15196 cell.className = " fc-state-disabled";
15200 if (!cell.initialClassName) {
15201 cell.initialClassName = cell.dom.className;
15204 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15209 for(; i < startingPos; i++) {
15210 textEls[i].innerHTML = (++prevStart);
15211 d.setDate(d.getDate()+1);
15213 cells[i].className = "fc-past fc-other-month";
15214 setCellClass(this, cells[i]);
15219 for(; i < days; i++){
15220 intDay = i - startingPos + 1;
15221 textEls[i].innerHTML = (intDay);
15222 d.setDate(d.getDate()+1);
15224 cells[i].className = ''; // "x-date-active";
15225 setCellClass(this, cells[i]);
15229 for(; i < 42; i++) {
15230 textEls[i].innerHTML = (++extraDays);
15231 d.setDate(d.getDate()+1);
15233 cells[i].className = "fc-future fc-other-month";
15234 setCellClass(this, cells[i]);
15237 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15239 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15241 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15242 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15244 if(totalRows != 6){
15245 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15246 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15249 this.fireEvent('monthchange', this, date);
15253 if(!this.internalRender){
15254 var main = this.el.dom.firstChild;
15255 var w = main.offsetWidth;
15256 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15257 Roo.fly(main).setWidth(w);
15258 this.internalRender = true;
15259 // opera does not respect the auto grow header center column
15260 // then, after it gets a width opera refuses to recalculate
15261 // without a second pass
15262 if(Roo.isOpera && !this.secondPass){
15263 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15264 this.secondPass = true;
15265 this.update.defer(10, this, [date]);
15272 findCell : function(dt) {
15273 dt = dt.clearTime().getTime();
15275 this.cells.each(function(c){
15276 //Roo.log("check " +c.dateValue + '?=' + dt);
15277 if(c.dateValue == dt){
15287 findCells : function(ev) {
15288 var s = ev.start.clone().clearTime().getTime();
15290 var e= ev.end.clone().clearTime().getTime();
15293 this.cells.each(function(c){
15294 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15296 if(c.dateValue > e){
15299 if(c.dateValue < s){
15308 // findBestRow: function(cells)
15312 // for (var i =0 ; i < cells.length;i++) {
15313 // ret = Math.max(cells[i].rows || 0,ret);
15320 addItem : function(ev)
15322 // look for vertical location slot in
15323 var cells = this.findCells(ev);
15325 // ev.row = this.findBestRow(cells);
15327 // work out the location.
15331 for(var i =0; i < cells.length; i++) {
15333 cells[i].row = cells[0].row;
15336 cells[i].row = cells[i].row + 1;
15346 if (crow.start.getY() == cells[i].getY()) {
15348 crow.end = cells[i];
15365 cells[0].events.push(ev);
15367 this.calevents.push(ev);
15370 clearEvents: function() {
15372 if(!this.calevents){
15376 Roo.each(this.cells.elements, function(c){
15382 Roo.each(this.calevents, function(e) {
15383 Roo.each(e.els, function(el) {
15384 el.un('mouseenter' ,this.onEventEnter, this);
15385 el.un('mouseleave' ,this.onEventLeave, this);
15390 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15396 renderEvents: function()
15400 this.cells.each(function(c) {
15409 if(c.row != c.events.length){
15410 r = 4 - (4 - (c.row - c.events.length));
15413 c.events = ev.slice(0, r);
15414 c.more = ev.slice(r);
15416 if(c.more.length && c.more.length == 1){
15417 c.events.push(c.more.pop());
15420 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15424 this.cells.each(function(c) {
15426 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15429 for (var e = 0; e < c.events.length; e++){
15430 var ev = c.events[e];
15431 var rows = ev.rows;
15433 for(var i = 0; i < rows.length; i++) {
15435 // how many rows should it span..
15438 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15439 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15441 unselectable : "on",
15444 cls: 'fc-event-inner',
15448 // cls: 'fc-event-time',
15449 // html : cells.length > 1 ? '' : ev.time
15453 cls: 'fc-event-title',
15454 html : String.format('{0}', ev.title)
15461 cls: 'ui-resizable-handle ui-resizable-e',
15462 html : '  '
15469 cfg.cls += ' fc-event-start';
15471 if ((i+1) == rows.length) {
15472 cfg.cls += ' fc-event-end';
15475 var ctr = _this.el.select('.fc-event-container',true).first();
15476 var cg = ctr.createChild(cfg);
15478 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15479 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15481 var r = (c.more.length) ? 1 : 0;
15482 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15483 cg.setWidth(ebox.right - sbox.x -2);
15485 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15486 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15487 cg.on('click', _this.onEventClick, _this, ev);
15498 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15499 style : 'position: absolute',
15500 unselectable : "on",
15503 cls: 'fc-event-inner',
15507 cls: 'fc-event-title',
15515 cls: 'ui-resizable-handle ui-resizable-e',
15516 html : '  '
15522 var ctr = _this.el.select('.fc-event-container',true).first();
15523 var cg = ctr.createChild(cfg);
15525 var sbox = c.select('.fc-day-content',true).first().getBox();
15526 var ebox = c.select('.fc-day-content',true).first().getBox();
15528 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15529 cg.setWidth(ebox.right - sbox.x -2);
15531 cg.on('click', _this.onMoreEventClick, _this, c.more);
15541 onEventEnter: function (e, el,event,d) {
15542 this.fireEvent('evententer', this, el, event);
15545 onEventLeave: function (e, el,event,d) {
15546 this.fireEvent('eventleave', this, el, event);
15549 onEventClick: function (e, el,event,d) {
15550 this.fireEvent('eventclick', this, el, event);
15553 onMonthChange: function () {
15557 onMoreEventClick: function(e, el, more)
15561 this.calpopover.placement = 'right';
15562 this.calpopover.setTitle('More');
15564 this.calpopover.setContent('');
15566 var ctr = this.calpopover.el.select('.popover-content', true).first();
15568 Roo.each(more, function(m){
15570 cls : 'fc-event-hori fc-event-draggable',
15573 var cg = ctr.createChild(cfg);
15575 cg.on('click', _this.onEventClick, _this, m);
15578 this.calpopover.show(el);
15583 onLoad: function ()
15585 this.calevents = [];
15588 if(this.store.getCount() > 0){
15589 this.store.data.each(function(d){
15592 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15593 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15594 time : d.data.start_time,
15595 title : d.data.title,
15596 description : d.data.description,
15597 venue : d.data.venue
15602 this.renderEvents();
15604 if(this.calevents.length && this.loadMask){
15605 this.maskEl.hide();
15609 onBeforeLoad: function()
15611 this.clearEvents();
15613 this.maskEl.show();
15627 * @class Roo.bootstrap.Popover
15628 * @extends Roo.bootstrap.Component
15629 * Bootstrap Popover class
15630 * @cfg {String} html contents of the popover (or false to use children..)
15631 * @cfg {String} title of popover (or false to hide)
15632 * @cfg {String} placement how it is placed
15633 * @cfg {String} trigger click || hover (or false to trigger manually)
15634 * @cfg {String} over what (parent or false to trigger manually.)
15635 * @cfg {Number} delay - delay before showing
15638 * Create a new Popover
15639 * @param {Object} config The config object
15642 Roo.bootstrap.Popover = function(config){
15643 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15646 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15648 title: 'Fill in a title',
15651 placement : 'right',
15652 trigger : 'hover', // hover
15658 can_build_overlaid : false,
15660 getChildContainer : function()
15662 return this.el.select('.popover-content',true).first();
15665 getAutoCreate : function(){
15666 Roo.log('make popover?');
15668 cls : 'popover roo-dynamic',
15669 style: 'display:block',
15675 cls : 'popover-inner',
15679 cls: 'popover-title',
15683 cls : 'popover-content',
15694 setTitle: function(str)
15697 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15699 setContent: function(str)
15702 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15704 // as it get's added to the bottom of the page.
15705 onRender : function(ct, position)
15707 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15709 var cfg = Roo.apply({}, this.getAutoCreate());
15713 cfg.cls += ' ' + this.cls;
15716 cfg.style = this.style;
15718 //Roo.log("adding to ");
15719 this.el = Roo.get(document.body).createChild(cfg, position);
15725 initEvents : function()
15727 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15728 this.el.enableDisplayMode('block');
15730 if (this.over === false) {
15733 if (this.triggers === false) {
15736 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15737 var triggers = this.trigger ? this.trigger.split(' ') : [];
15738 Roo.each(triggers, function(trigger) {
15740 if (trigger == 'click') {
15741 on_el.on('click', this.toggle, this);
15742 } else if (trigger != 'manual') {
15743 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15744 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15746 on_el.on(eventIn ,this.enter, this);
15747 on_el.on(eventOut, this.leave, this);
15758 toggle : function () {
15759 this.hoverState == 'in' ? this.leave() : this.enter();
15762 enter : function () {
15765 clearTimeout(this.timeout);
15767 this.hoverState = 'in';
15769 if (!this.delay || !this.delay.show) {
15774 this.timeout = setTimeout(function () {
15775 if (_t.hoverState == 'in') {
15778 }, this.delay.show)
15780 leave : function() {
15781 clearTimeout(this.timeout);
15783 this.hoverState = 'out';
15785 if (!this.delay || !this.delay.hide) {
15790 this.timeout = setTimeout(function () {
15791 if (_t.hoverState == 'out') {
15794 }, this.delay.hide)
15797 show : function (on_el)
15800 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15803 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15804 if (this.html !== false) {
15805 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15807 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15808 if (!this.title.length) {
15809 this.el.select('.popover-title',true).hide();
15812 var placement = typeof this.placement == 'function' ?
15813 this.placement.call(this, this.el, on_el) :
15816 var autoToken = /\s?auto?\s?/i;
15817 var autoPlace = autoToken.test(placement);
15819 placement = placement.replace(autoToken, '') || 'top';
15823 //this.el.setXY([0,0]);
15825 this.el.dom.style.display='block';
15826 this.el.addClass(placement);
15828 //this.el.appendTo(on_el);
15830 var p = this.getPosition();
15831 var box = this.el.getBox();
15836 var align = Roo.bootstrap.Popover.alignment[placement];
15837 this.el.alignTo(on_el, align[0],align[1]);
15838 //var arrow = this.el.select('.arrow',true).first();
15839 //arrow.set(align[2],
15841 this.el.addClass('in');
15844 if (this.el.hasClass('fade')) {
15851 this.el.setXY([0,0]);
15852 this.el.removeClass('in');
15854 this.hoverState = null;
15860 Roo.bootstrap.Popover.alignment = {
15861 'left' : ['r-l', [-10,0], 'right'],
15862 'right' : ['l-r', [10,0], 'left'],
15863 'bottom' : ['t-b', [0,10], 'top'],
15864 'top' : [ 'b-t', [0,-10], 'bottom']
15875 * @class Roo.bootstrap.Progress
15876 * @extends Roo.bootstrap.Component
15877 * Bootstrap Progress class
15878 * @cfg {Boolean} striped striped of the progress bar
15879 * @cfg {Boolean} active animated of the progress bar
15883 * Create a new Progress
15884 * @param {Object} config The config object
15887 Roo.bootstrap.Progress = function(config){
15888 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15891 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15896 getAutoCreate : function(){
15904 cfg.cls += ' progress-striped';
15908 cfg.cls += ' active';
15927 * @class Roo.bootstrap.ProgressBar
15928 * @extends Roo.bootstrap.Component
15929 * Bootstrap ProgressBar class
15930 * @cfg {Number} aria_valuenow aria-value now
15931 * @cfg {Number} aria_valuemin aria-value min
15932 * @cfg {Number} aria_valuemax aria-value max
15933 * @cfg {String} label label for the progress bar
15934 * @cfg {String} panel (success | info | warning | danger )
15935 * @cfg {String} role role of the progress bar
15936 * @cfg {String} sr_only text
15940 * Create a new ProgressBar
15941 * @param {Object} config The config object
15944 Roo.bootstrap.ProgressBar = function(config){
15945 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15948 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15952 aria_valuemax : 100,
15958 getAutoCreate : function()
15963 cls: 'progress-bar',
15964 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15976 cfg.role = this.role;
15979 if(this.aria_valuenow){
15980 cfg['aria-valuenow'] = this.aria_valuenow;
15983 if(this.aria_valuemin){
15984 cfg['aria-valuemin'] = this.aria_valuemin;
15987 if(this.aria_valuemax){
15988 cfg['aria-valuemax'] = this.aria_valuemax;
15991 if(this.label && !this.sr_only){
15992 cfg.html = this.label;
15996 cfg.cls += ' progress-bar-' + this.panel;
16002 update : function(aria_valuenow)
16004 this.aria_valuenow = aria_valuenow;
16006 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16021 * @class Roo.bootstrap.TabGroup
16022 * @extends Roo.bootstrap.Column
16023 * Bootstrap Column class
16024 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16025 * @cfg {Boolean} carousel true to make the group behave like a carousel
16026 * @cfg {Boolean} bullets show bullets for the panels
16027 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16028 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16029 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16032 * Create a new TabGroup
16033 * @param {Object} config The config object
16036 Roo.bootstrap.TabGroup = function(config){
16037 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16039 this.navId = Roo.id();
16042 Roo.bootstrap.TabGroup.register(this);
16046 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16049 transition : false,
16054 slideOnTouch : false,
16056 getAutoCreate : function()
16058 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16060 cfg.cls += ' tab-content';
16062 Roo.log('get auto create...............');
16064 if (this.carousel) {
16065 cfg.cls += ' carousel slide';
16068 cls : 'carousel-inner'
16071 if(this.bullets && !Roo.isTouch){
16074 cls : 'carousel-bullets',
16078 if(this.bullets_cls){
16079 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16082 for (var i = 0; i < this.bullets; i++){
16084 cls : 'bullet bullet-' + i
16092 cfg.cn[0].cn = bullets;
16099 initEvents: function()
16101 Roo.log('-------- init events on tab group ---------');
16107 if(Roo.isTouch && this.slideOnTouch){
16108 this.el.on("touchstart", this.onTouchStart, this);
16111 if(this.autoslide){
16114 this.slideFn = window.setInterval(function() {
16115 _this.showPanelNext();
16121 onTouchStart : function(e, el, o)
16123 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16127 this.showPanelNext();
16130 getChildContainer : function()
16132 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16136 * register a Navigation item
16137 * @param {Roo.bootstrap.NavItem} the navitem to add
16139 register : function(item)
16141 this.tabs.push( item);
16142 item.navId = this.navId; // not really needed..
16147 getActivePanel : function()
16150 Roo.each(this.tabs, function(t) {
16160 getPanelByName : function(n)
16163 Roo.each(this.tabs, function(t) {
16164 if (t.tabId == n) {
16172 indexOfPanel : function(p)
16175 Roo.each(this.tabs, function(t,i) {
16176 if (t.tabId == p.tabId) {
16185 * show a specific panel
16186 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16187 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16189 showPanel : function (pan)
16191 if(this.transition || typeof(pan) == 'undefined'){
16192 Roo.log("waiting for the transitionend");
16196 if (typeof(pan) == 'number') {
16197 pan = this.tabs[pan];
16200 if (typeof(pan) == 'string') {
16201 pan = this.getPanelByName(pan);
16204 var cur = this.getActivePanel();
16207 Roo.log('pan or acitve pan is undefined');
16211 if (pan.tabId == this.getActivePanel().tabId) {
16215 if (false === cur.fireEvent('beforedeactivate')) {
16219 if(this.bullets > 0 && !Roo.isTouch){
16220 this.setActiveBullet(this.indexOfPanel(pan));
16223 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16225 this.transition = true;
16226 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16227 var lr = dir == 'next' ? 'left' : 'right';
16228 pan.el.addClass(dir); // or prev
16229 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16230 cur.el.addClass(lr); // or right
16231 pan.el.addClass(lr);
16234 cur.el.on('transitionend', function() {
16235 Roo.log("trans end?");
16237 pan.el.removeClass([lr,dir]);
16238 pan.setActive(true);
16240 cur.el.removeClass([lr]);
16241 cur.setActive(false);
16243 _this.transition = false;
16245 }, this, { single: true } );
16250 cur.setActive(false);
16251 pan.setActive(true);
16256 showPanelNext : function()
16258 var i = this.indexOfPanel(this.getActivePanel());
16260 if (i >= this.tabs.length - 1 && !this.autoslide) {
16264 if (i >= this.tabs.length - 1 && this.autoslide) {
16268 this.showPanel(this.tabs[i+1]);
16271 showPanelPrev : function()
16273 var i = this.indexOfPanel(this.getActivePanel());
16275 if (i < 1 && !this.autoslide) {
16279 if (i < 1 && this.autoslide) {
16280 i = this.tabs.length;
16283 this.showPanel(this.tabs[i-1]);
16287 addBullet: function()
16289 if(!this.bullets || Roo.isTouch){
16292 var ctr = this.el.select('.carousel-bullets',true).first();
16293 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16294 var bullet = ctr.createChild({
16295 cls : 'bullet bullet-' + i
16296 },ctr.dom.lastChild);
16301 bullet.on('click', (function(e, el, o, ii, t){
16303 e.preventDefault();
16305 this.showPanel(ii);
16307 if(this.autoslide && this.slideFn){
16308 clearInterval(this.slideFn);
16309 this.slideFn = window.setInterval(function() {
16310 _this.showPanelNext();
16314 }).createDelegate(this, [i, bullet], true));
16319 setActiveBullet : function(i)
16325 Roo.each(this.el.select('.bullet', true).elements, function(el){
16326 el.removeClass('selected');
16329 var bullet = this.el.select('.bullet-' + i, true).first();
16335 bullet.addClass('selected');
16346 Roo.apply(Roo.bootstrap.TabGroup, {
16350 * register a Navigation Group
16351 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16353 register : function(navgrp)
16355 this.groups[navgrp.navId] = navgrp;
16359 * fetch a Navigation Group based on the navigation ID
16360 * if one does not exist , it will get created.
16361 * @param {string} the navgroup to add
16362 * @returns {Roo.bootstrap.NavGroup} the navgroup
16364 get: function(navId) {
16365 if (typeof(this.groups[navId]) == 'undefined') {
16366 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16368 return this.groups[navId] ;
16383 * @class Roo.bootstrap.TabPanel
16384 * @extends Roo.bootstrap.Component
16385 * Bootstrap TabPanel class
16386 * @cfg {Boolean} active panel active
16387 * @cfg {String} html panel content
16388 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16389 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16393 * Create a new TabPanel
16394 * @param {Object} config The config object
16397 Roo.bootstrap.TabPanel = function(config){
16398 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16402 * Fires when the active status changes
16403 * @param {Roo.bootstrap.TabPanel} this
16404 * @param {Boolean} state the new state
16409 * @event beforedeactivate
16410 * Fires before a tab is de-activated - can be used to do validation on a form.
16411 * @param {Roo.bootstrap.TabPanel} this
16412 * @return {Boolean} false if there is an error
16415 'beforedeactivate': true
16418 this.tabId = this.tabId || Roo.id();
16422 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16429 getAutoCreate : function(){
16432 // item is needed for carousel - not sure if it has any effect otherwise
16433 cls: 'tab-pane item',
16434 html: this.html || ''
16438 cfg.cls += ' active';
16442 cfg.tabId = this.tabId;
16449 initEvents: function()
16451 Roo.log('-------- init events on tab panel ---------');
16453 var p = this.parent();
16454 this.navId = this.navId || p.navId;
16456 if (typeof(this.navId) != 'undefined') {
16457 // not really needed.. but just in case.. parent should be a NavGroup.
16458 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16459 Roo.log(['register', tg, this]);
16462 var i = tg.tabs.length - 1;
16464 if(this.active && tg.bullets > 0 && i < tg.bullets){
16465 tg.setActiveBullet(i);
16472 onRender : function(ct, position)
16474 // Roo.log("Call onRender: " + this.xtype);
16476 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16484 setActive: function(state)
16486 Roo.log("panel - set active " + this.tabId + "=" + state);
16488 this.active = state;
16490 this.el.removeClass('active');
16492 } else if (!this.el.hasClass('active')) {
16493 this.el.addClass('active');
16496 this.fireEvent('changed', this, state);
16513 * @class Roo.bootstrap.DateField
16514 * @extends Roo.bootstrap.Input
16515 * Bootstrap DateField class
16516 * @cfg {Number} weekStart default 0
16517 * @cfg {String} viewMode default empty, (months|years)
16518 * @cfg {String} minViewMode default empty, (months|years)
16519 * @cfg {Number} startDate default -Infinity
16520 * @cfg {Number} endDate default Infinity
16521 * @cfg {Boolean} todayHighlight default false
16522 * @cfg {Boolean} todayBtn default false
16523 * @cfg {Boolean} calendarWeeks default false
16524 * @cfg {Object} daysOfWeekDisabled default empty
16525 * @cfg {Boolean} singleMode default false (true | false)
16527 * @cfg {Boolean} keyboardNavigation default true
16528 * @cfg {String} language default en
16531 * Create a new DateField
16532 * @param {Object} config The config object
16535 Roo.bootstrap.DateField = function(config){
16536 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16540 * Fires when this field show.
16541 * @param {Roo.bootstrap.DateField} this
16542 * @param {Mixed} date The date value
16547 * Fires when this field hide.
16548 * @param {Roo.bootstrap.DateField} this
16549 * @param {Mixed} date The date value
16554 * Fires when select a date.
16555 * @param {Roo.bootstrap.DateField} this
16556 * @param {Mixed} date The date value
16560 * @event beforeselect
16561 * Fires when before select a date.
16562 * @param {Roo.bootstrap.DateField} this
16563 * @param {Mixed} date The date value
16565 beforeselect : true
16569 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16572 * @cfg {String} format
16573 * The default date format string which can be overriden for localization support. The format must be
16574 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16578 * @cfg {String} altFormats
16579 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16580 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16582 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16590 todayHighlight : false,
16596 keyboardNavigation: true,
16598 calendarWeeks: false,
16600 startDate: -Infinity,
16604 daysOfWeekDisabled: [],
16608 singleMode : false,
16610 UTCDate: function()
16612 return new Date(Date.UTC.apply(Date, arguments));
16615 UTCToday: function()
16617 var today = new Date();
16618 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16621 getDate: function() {
16622 var d = this.getUTCDate();
16623 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16626 getUTCDate: function() {
16630 setDate: function(d) {
16631 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16634 setUTCDate: function(d) {
16636 this.setValue(this.formatDate(this.date));
16639 onRender: function(ct, position)
16642 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16644 this.language = this.language || 'en';
16645 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16646 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16648 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16649 this.format = this.format || 'm/d/y';
16650 this.isInline = false;
16651 this.isInput = true;
16652 this.component = this.el.select('.add-on', true).first() || false;
16653 this.component = (this.component && this.component.length === 0) ? false : this.component;
16654 this.hasInput = this.component && this.inputEL().length;
16656 if (typeof(this.minViewMode === 'string')) {
16657 switch (this.minViewMode) {
16659 this.minViewMode = 1;
16662 this.minViewMode = 2;
16665 this.minViewMode = 0;
16670 if (typeof(this.viewMode === 'string')) {
16671 switch (this.viewMode) {
16684 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16686 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16688 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16690 this.picker().on('mousedown', this.onMousedown, this);
16691 this.picker().on('click', this.onClick, this);
16693 this.picker().addClass('datepicker-dropdown');
16695 this.startViewMode = this.viewMode;
16697 if(this.singleMode){
16698 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16699 v.setVisibilityMode(Roo.Element.DISPLAY);
16703 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16704 v.setStyle('width', '189px');
16708 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16709 if(!this.calendarWeeks){
16714 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16715 v.attr('colspan', function(i, val){
16716 return parseInt(val) + 1;
16721 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16723 this.setStartDate(this.startDate);
16724 this.setEndDate(this.endDate);
16726 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16733 if(this.isInline) {
16738 picker : function()
16740 return this.pickerEl;
16741 // return this.el.select('.datepicker', true).first();
16744 fillDow: function()
16746 var dowCnt = this.weekStart;
16755 if(this.calendarWeeks){
16763 while (dowCnt < this.weekStart + 7) {
16767 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16771 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16774 fillMonths: function()
16777 var months = this.picker().select('>.datepicker-months td', true).first();
16779 months.dom.innerHTML = '';
16785 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16788 months.createChild(month);
16795 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;
16797 if (this.date < this.startDate) {
16798 this.viewDate = new Date(this.startDate);
16799 } else if (this.date > this.endDate) {
16800 this.viewDate = new Date(this.endDate);
16802 this.viewDate = new Date(this.date);
16810 var d = new Date(this.viewDate),
16811 year = d.getUTCFullYear(),
16812 month = d.getUTCMonth(),
16813 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16814 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16815 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16816 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16817 currentDate = this.date && this.date.valueOf(),
16818 today = this.UTCToday();
16820 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16822 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16824 // this.picker.select('>tfoot th.today').
16825 // .text(dates[this.language].today)
16826 // .toggle(this.todayBtn !== false);
16828 this.updateNavArrows();
16831 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16833 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16835 prevMonth.setUTCDate(day);
16837 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16839 var nextMonth = new Date(prevMonth);
16841 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16843 nextMonth = nextMonth.valueOf();
16845 var fillMonths = false;
16847 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16849 while(prevMonth.valueOf() < nextMonth) {
16852 if (prevMonth.getUTCDay() === this.weekStart) {
16854 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16862 if(this.calendarWeeks){
16863 // ISO 8601: First week contains first thursday.
16864 // ISO also states week starts on Monday, but we can be more abstract here.
16866 // Start of current week: based on weekstart/current date
16867 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16868 // Thursday of this week
16869 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16870 // First Thursday of year, year from thursday
16871 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16872 // Calendar week: ms between thursdays, div ms per day, div 7 days
16873 calWeek = (th - yth) / 864e5 / 7 + 1;
16875 fillMonths.cn.push({
16883 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16885 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16888 if (this.todayHighlight &&
16889 prevMonth.getUTCFullYear() == today.getFullYear() &&
16890 prevMonth.getUTCMonth() == today.getMonth() &&
16891 prevMonth.getUTCDate() == today.getDate()) {
16892 clsName += ' today';
16895 if (currentDate && prevMonth.valueOf() === currentDate) {
16896 clsName += ' active';
16899 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16900 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16901 clsName += ' disabled';
16904 fillMonths.cn.push({
16906 cls: 'day ' + clsName,
16907 html: prevMonth.getDate()
16910 prevMonth.setDate(prevMonth.getDate()+1);
16913 var currentYear = this.date && this.date.getUTCFullYear();
16914 var currentMonth = this.date && this.date.getUTCMonth();
16916 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16918 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16919 v.removeClass('active');
16921 if(currentYear === year && k === currentMonth){
16922 v.addClass('active');
16925 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16926 v.addClass('disabled');
16932 year = parseInt(year/10, 10) * 10;
16934 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16936 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16939 for (var i = -1; i < 11; i++) {
16940 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16942 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16950 showMode: function(dir)
16953 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16956 Roo.each(this.picker().select('>div',true).elements, function(v){
16957 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16960 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16965 if(this.isInline) {
16969 this.picker().removeClass(['bottom', 'top']);
16971 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16973 * place to the top of element!
16977 this.picker().addClass('top');
16978 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16983 this.picker().addClass('bottom');
16985 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16988 parseDate : function(value)
16990 if(!value || value instanceof Date){
16993 var v = Date.parseDate(value, this.format);
16994 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16995 v = Date.parseDate(value, 'Y-m-d');
16997 if(!v && this.altFormats){
16998 if(!this.altFormatsArray){
16999 this.altFormatsArray = this.altFormats.split("|");
17001 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17002 v = Date.parseDate(value, this.altFormatsArray[i]);
17008 formatDate : function(date, fmt)
17010 return (!date || !(date instanceof Date)) ?
17011 date : date.dateFormat(fmt || this.format);
17014 onFocus : function()
17016 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17020 onBlur : function()
17022 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17024 var d = this.inputEl().getValue();
17033 this.picker().show();
17037 this.fireEvent('show', this, this.date);
17042 if(this.isInline) {
17045 this.picker().hide();
17046 this.viewMode = this.startViewMode;
17049 this.fireEvent('hide', this, this.date);
17053 onMousedown: function(e)
17055 e.stopPropagation();
17056 e.preventDefault();
17061 Roo.bootstrap.DateField.superclass.keyup.call(this);
17065 setValue: function(v)
17067 if(this.fireEvent('beforeselect', this, v) !== false){
17068 var d = new Date(this.parseDate(v) ).clearTime();
17070 if(isNaN(d.getTime())){
17071 this.date = this.viewDate = '';
17072 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17076 v = this.formatDate(d);
17078 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17080 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17084 this.fireEvent('select', this, this.date);
17088 getValue: function()
17090 return this.formatDate(this.date);
17093 fireKey: function(e)
17095 if (!this.picker().isVisible()){
17096 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17102 var dateChanged = false,
17104 newDate, newViewDate;
17109 e.preventDefault();
17113 if (!this.keyboardNavigation) {
17116 dir = e.keyCode == 37 ? -1 : 1;
17119 newDate = this.moveYear(this.date, dir);
17120 newViewDate = this.moveYear(this.viewDate, dir);
17121 } else if (e.shiftKey){
17122 newDate = this.moveMonth(this.date, dir);
17123 newViewDate = this.moveMonth(this.viewDate, dir);
17125 newDate = new Date(this.date);
17126 newDate.setUTCDate(this.date.getUTCDate() + dir);
17127 newViewDate = new Date(this.viewDate);
17128 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17130 if (this.dateWithinRange(newDate)){
17131 this.date = newDate;
17132 this.viewDate = newViewDate;
17133 this.setValue(this.formatDate(this.date));
17135 e.preventDefault();
17136 dateChanged = true;
17141 if (!this.keyboardNavigation) {
17144 dir = e.keyCode == 38 ? -1 : 1;
17146 newDate = this.moveYear(this.date, dir);
17147 newViewDate = this.moveYear(this.viewDate, dir);
17148 } else if (e.shiftKey){
17149 newDate = this.moveMonth(this.date, dir);
17150 newViewDate = this.moveMonth(this.viewDate, dir);
17152 newDate = new Date(this.date);
17153 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17154 newViewDate = new Date(this.viewDate);
17155 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17157 if (this.dateWithinRange(newDate)){
17158 this.date = newDate;
17159 this.viewDate = newViewDate;
17160 this.setValue(this.formatDate(this.date));
17162 e.preventDefault();
17163 dateChanged = true;
17167 this.setValue(this.formatDate(this.date));
17169 e.preventDefault();
17172 this.setValue(this.formatDate(this.date));
17186 onClick: function(e)
17188 e.stopPropagation();
17189 e.preventDefault();
17191 var target = e.getTarget();
17193 if(target.nodeName.toLowerCase() === 'i'){
17194 target = Roo.get(target).dom.parentNode;
17197 var nodeName = target.nodeName;
17198 var className = target.className;
17199 var html = target.innerHTML;
17200 //Roo.log(nodeName);
17202 switch(nodeName.toLowerCase()) {
17204 switch(className) {
17210 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17211 switch(this.viewMode){
17213 this.viewDate = this.moveMonth(this.viewDate, dir);
17217 this.viewDate = this.moveYear(this.viewDate, dir);
17223 var date = new Date();
17224 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17226 this.setValue(this.formatDate(this.date));
17233 if (className.indexOf('disabled') < 0) {
17234 this.viewDate.setUTCDate(1);
17235 if (className.indexOf('month') > -1) {
17236 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17238 var year = parseInt(html, 10) || 0;
17239 this.viewDate.setUTCFullYear(year);
17243 if(this.singleMode){
17244 this.setValue(this.formatDate(this.viewDate));
17255 //Roo.log(className);
17256 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17257 var day = parseInt(html, 10) || 1;
17258 var year = this.viewDate.getUTCFullYear(),
17259 month = this.viewDate.getUTCMonth();
17261 if (className.indexOf('old') > -1) {
17268 } else if (className.indexOf('new') > -1) {
17276 //Roo.log([year,month,day]);
17277 this.date = this.UTCDate(year, month, day,0,0,0,0);
17278 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17280 //Roo.log(this.formatDate(this.date));
17281 this.setValue(this.formatDate(this.date));
17288 setStartDate: function(startDate)
17290 this.startDate = startDate || -Infinity;
17291 if (this.startDate !== -Infinity) {
17292 this.startDate = this.parseDate(this.startDate);
17295 this.updateNavArrows();
17298 setEndDate: function(endDate)
17300 this.endDate = endDate || Infinity;
17301 if (this.endDate !== Infinity) {
17302 this.endDate = this.parseDate(this.endDate);
17305 this.updateNavArrows();
17308 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17310 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17311 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17312 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17314 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17315 return parseInt(d, 10);
17318 this.updateNavArrows();
17321 updateNavArrows: function()
17323 if(this.singleMode){
17327 var d = new Date(this.viewDate),
17328 year = d.getUTCFullYear(),
17329 month = d.getUTCMonth();
17331 Roo.each(this.picker().select('.prev', true).elements, function(v){
17333 switch (this.viewMode) {
17336 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17342 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17349 Roo.each(this.picker().select('.next', true).elements, function(v){
17351 switch (this.viewMode) {
17354 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17360 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17368 moveMonth: function(date, dir)
17373 var new_date = new Date(date.valueOf()),
17374 day = new_date.getUTCDate(),
17375 month = new_date.getUTCMonth(),
17376 mag = Math.abs(dir),
17378 dir = dir > 0 ? 1 : -1;
17381 // If going back one month, make sure month is not current month
17382 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17384 return new_date.getUTCMonth() == month;
17386 // If going forward one month, make sure month is as expected
17387 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17389 return new_date.getUTCMonth() != new_month;
17391 new_month = month + dir;
17392 new_date.setUTCMonth(new_month);
17393 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17394 if (new_month < 0 || new_month > 11) {
17395 new_month = (new_month + 12) % 12;
17398 // For magnitudes >1, move one month at a time...
17399 for (var i=0; i<mag; i++) {
17400 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17401 new_date = this.moveMonth(new_date, dir);
17403 // ...then reset the day, keeping it in the new month
17404 new_month = new_date.getUTCMonth();
17405 new_date.setUTCDate(day);
17407 return new_month != new_date.getUTCMonth();
17410 // Common date-resetting loop -- if date is beyond end of month, make it
17413 new_date.setUTCDate(--day);
17414 new_date.setUTCMonth(new_month);
17419 moveYear: function(date, dir)
17421 return this.moveMonth(date, dir*12);
17424 dateWithinRange: function(date)
17426 return date >= this.startDate && date <= this.endDate;
17432 this.picker().remove();
17437 Roo.apply(Roo.bootstrap.DateField, {
17448 html: '<i class="fa fa-arrow-left"/>'
17458 html: '<i class="fa fa-arrow-right"/>'
17500 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17501 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17502 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17503 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17504 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17517 navFnc: 'FullYear',
17522 navFnc: 'FullYear',
17527 Roo.apply(Roo.bootstrap.DateField, {
17531 cls: 'datepicker dropdown-menu roo-dynamic',
17535 cls: 'datepicker-days',
17539 cls: 'table-condensed',
17541 Roo.bootstrap.DateField.head,
17545 Roo.bootstrap.DateField.footer
17552 cls: 'datepicker-months',
17556 cls: 'table-condensed',
17558 Roo.bootstrap.DateField.head,
17559 Roo.bootstrap.DateField.content,
17560 Roo.bootstrap.DateField.footer
17567 cls: 'datepicker-years',
17571 cls: 'table-condensed',
17573 Roo.bootstrap.DateField.head,
17574 Roo.bootstrap.DateField.content,
17575 Roo.bootstrap.DateField.footer
17594 * @class Roo.bootstrap.TimeField
17595 * @extends Roo.bootstrap.Input
17596 * Bootstrap DateField class
17600 * Create a new TimeField
17601 * @param {Object} config The config object
17604 Roo.bootstrap.TimeField = function(config){
17605 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17609 * Fires when this field show.
17610 * @param {Roo.bootstrap.DateField} thisthis
17611 * @param {Mixed} date The date value
17616 * Fires when this field hide.
17617 * @param {Roo.bootstrap.DateField} this
17618 * @param {Mixed} date The date value
17623 * Fires when select a date.
17624 * @param {Roo.bootstrap.DateField} this
17625 * @param {Mixed} date The date value
17631 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17634 * @cfg {String} format
17635 * The default time format string which can be overriden for localization support. The format must be
17636 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17640 onRender: function(ct, position)
17643 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17645 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17647 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17649 this.pop = this.picker().select('>.datepicker-time',true).first();
17650 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17652 this.picker().on('mousedown', this.onMousedown, this);
17653 this.picker().on('click', this.onClick, this);
17655 this.picker().addClass('datepicker-dropdown');
17660 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17661 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17662 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17663 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17664 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17665 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17669 fireKey: function(e){
17670 if (!this.picker().isVisible()){
17671 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17677 e.preventDefault();
17685 this.onTogglePeriod();
17688 this.onIncrementMinutes();
17691 this.onDecrementMinutes();
17700 onClick: function(e) {
17701 e.stopPropagation();
17702 e.preventDefault();
17705 picker : function()
17707 return this.el.select('.datepicker', true).first();
17710 fillTime: function()
17712 var time = this.pop.select('tbody', true).first();
17714 time.dom.innerHTML = '';
17729 cls: 'hours-up glyphicon glyphicon-chevron-up'
17749 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17770 cls: 'timepicker-hour',
17785 cls: 'timepicker-minute',
17800 cls: 'btn btn-primary period',
17822 cls: 'hours-down glyphicon glyphicon-chevron-down'
17842 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17860 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17867 var hours = this.time.getHours();
17868 var minutes = this.time.getMinutes();
17881 hours = hours - 12;
17885 hours = '0' + hours;
17889 minutes = '0' + minutes;
17892 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17893 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17894 this.pop.select('button', true).first().dom.innerHTML = period;
17900 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17902 var cls = ['bottom'];
17904 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17911 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17916 this.picker().addClass(cls.join('-'));
17920 Roo.each(cls, function(c){
17922 _this.picker().setTop(_this.inputEl().getHeight());
17926 _this.picker().setTop(0 - _this.picker().getHeight());
17931 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17935 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17942 onFocus : function()
17944 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17948 onBlur : function()
17950 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17956 this.picker().show();
17961 this.fireEvent('show', this, this.date);
17966 this.picker().hide();
17969 this.fireEvent('hide', this, this.date);
17972 setTime : function()
17975 this.setValue(this.time.format(this.format));
17977 this.fireEvent('select', this, this.date);
17982 onMousedown: function(e){
17983 e.stopPropagation();
17984 e.preventDefault();
17987 onIncrementHours: function()
17989 Roo.log('onIncrementHours');
17990 this.time = this.time.add(Date.HOUR, 1);
17995 onDecrementHours: function()
17997 Roo.log('onDecrementHours');
17998 this.time = this.time.add(Date.HOUR, -1);
18002 onIncrementMinutes: function()
18004 Roo.log('onIncrementMinutes');
18005 this.time = this.time.add(Date.MINUTE, 1);
18009 onDecrementMinutes: function()
18011 Roo.log('onDecrementMinutes');
18012 this.time = this.time.add(Date.MINUTE, -1);
18016 onTogglePeriod: function()
18018 Roo.log('onTogglePeriod');
18019 this.time = this.time.add(Date.HOUR, 12);
18026 Roo.apply(Roo.bootstrap.TimeField, {
18056 cls: 'btn btn-info ok',
18068 Roo.apply(Roo.bootstrap.TimeField, {
18072 cls: 'datepicker dropdown-menu',
18076 cls: 'datepicker-time',
18080 cls: 'table-condensed',
18082 Roo.bootstrap.TimeField.content,
18083 Roo.bootstrap.TimeField.footer
18102 * @class Roo.bootstrap.MonthField
18103 * @extends Roo.bootstrap.Input
18104 * Bootstrap MonthField class
18106 * @cfg {String} language default en
18109 * Create a new MonthField
18110 * @param {Object} config The config object
18113 Roo.bootstrap.MonthField = function(config){
18114 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18119 * Fires when this field show.
18120 * @param {Roo.bootstrap.MonthField} this
18121 * @param {Mixed} date The date value
18126 * Fires when this field hide.
18127 * @param {Roo.bootstrap.MonthField} this
18128 * @param {Mixed} date The date value
18133 * Fires when select a date.
18134 * @param {Roo.bootstrap.MonthField} this
18135 * @param {String} oldvalue The old value
18136 * @param {String} newvalue The new value
18142 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18144 onRender: function(ct, position)
18147 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18149 this.language = this.language || 'en';
18150 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18151 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18153 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18154 this.isInline = false;
18155 this.isInput = true;
18156 this.component = this.el.select('.add-on', true).first() || false;
18157 this.component = (this.component && this.component.length === 0) ? false : this.component;
18158 this.hasInput = this.component && this.inputEL().length;
18160 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18162 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18164 this.picker().on('mousedown', this.onMousedown, this);
18165 this.picker().on('click', this.onClick, this);
18167 this.picker().addClass('datepicker-dropdown');
18169 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18170 v.setStyle('width', '189px');
18177 if(this.isInline) {
18183 setValue: function(v, suppressEvent)
18185 var o = this.getValue();
18187 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18191 if(suppressEvent !== true){
18192 this.fireEvent('select', this, o, v);
18197 getValue: function()
18202 onClick: function(e)
18204 e.stopPropagation();
18205 e.preventDefault();
18207 var target = e.getTarget();
18209 if(target.nodeName.toLowerCase() === 'i'){
18210 target = Roo.get(target).dom.parentNode;
18213 var nodeName = target.nodeName;
18214 var className = target.className;
18215 var html = target.innerHTML;
18217 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18221 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18223 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18229 picker : function()
18231 return this.pickerEl;
18234 fillMonths: function()
18237 var months = this.picker().select('>.datepicker-months td', true).first();
18239 months.dom.innerHTML = '';
18245 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18248 months.createChild(month);
18257 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18258 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18261 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18262 e.removeClass('active');
18264 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18265 e.addClass('active');
18272 if(this.isInline) {
18276 this.picker().removeClass(['bottom', 'top']);
18278 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18280 * place to the top of element!
18284 this.picker().addClass('top');
18285 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18290 this.picker().addClass('bottom');
18292 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18295 onFocus : function()
18297 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18301 onBlur : function()
18303 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18305 var d = this.inputEl().getValue();
18314 this.picker().show();
18315 this.picker().select('>.datepicker-months', true).first().show();
18319 this.fireEvent('show', this, this.date);
18324 if(this.isInline) {
18327 this.picker().hide();
18328 this.fireEvent('hide', this, this.date);
18332 onMousedown: function(e)
18334 e.stopPropagation();
18335 e.preventDefault();
18340 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18344 fireKey: function(e)
18346 if (!this.picker().isVisible()){
18347 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18358 e.preventDefault();
18362 dir = e.keyCode == 37 ? -1 : 1;
18364 this.vIndex = this.vIndex + dir;
18366 if(this.vIndex < 0){
18370 if(this.vIndex > 11){
18374 if(isNaN(this.vIndex)){
18378 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18384 dir = e.keyCode == 38 ? -1 : 1;
18386 this.vIndex = this.vIndex + dir * 4;
18388 if(this.vIndex < 0){
18392 if(this.vIndex > 11){
18396 if(isNaN(this.vIndex)){
18400 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18405 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18406 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18410 e.preventDefault();
18413 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18414 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18430 this.picker().remove();
18435 Roo.apply(Roo.bootstrap.MonthField, {
18454 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18455 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18460 Roo.apply(Roo.bootstrap.MonthField, {
18464 cls: 'datepicker dropdown-menu roo-dynamic',
18468 cls: 'datepicker-months',
18472 cls: 'table-condensed',
18474 Roo.bootstrap.DateField.content
18494 * @class Roo.bootstrap.CheckBox
18495 * @extends Roo.bootstrap.Input
18496 * Bootstrap CheckBox class
18498 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18499 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18500 * @cfg {String} boxLabel The text that appears beside the checkbox
18501 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18502 * @cfg {Boolean} checked initnal the element
18503 * @cfg {Boolean} inline inline the element (default false)
18504 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18507 * Create a new CheckBox
18508 * @param {Object} config The config object
18511 Roo.bootstrap.CheckBox = function(config){
18512 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18517 * Fires when the element is checked or unchecked.
18518 * @param {Roo.bootstrap.CheckBox} this This input
18519 * @param {Boolean} checked The new checked value
18526 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18528 inputType: 'checkbox',
18536 getAutoCreate : function()
18538 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18544 cfg.cls = 'form-group ' + this.inputType; //input-group
18547 cfg.cls += ' ' + this.inputType + '-inline';
18553 type : this.inputType,
18554 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18555 cls : 'roo-' + this.inputType, //'form-box',
18556 placeholder : this.placeholder || ''
18560 if (this.weight) { // Validity check?
18561 cfg.cls += " " + this.inputType + "-" + this.weight;
18564 if (this.disabled) {
18565 input.disabled=true;
18569 input.checked = this.checked;
18573 input.name = this.name;
18577 input.cls += ' input-' + this.size;
18582 ['xs','sm','md','lg'].map(function(size){
18583 if (settings[size]) {
18584 cfg.cls += ' col-' + size + '-' + settings[size];
18588 var inputblock = input;
18590 if (this.before || this.after) {
18593 cls : 'input-group',
18598 inputblock.cn.push({
18600 cls : 'input-group-addon',
18605 inputblock.cn.push(input);
18608 inputblock.cn.push({
18610 cls : 'input-group-addon',
18617 if (align ==='left' && this.fieldLabel.length) {
18618 Roo.log("left and has label");
18624 cls : 'control-label col-md-' + this.labelWidth,
18625 html : this.fieldLabel
18629 cls : "col-md-" + (12 - this.labelWidth),
18636 } else if ( this.fieldLabel.length) {
18641 tag: this.boxLabel ? 'span' : 'label',
18643 cls: 'control-label box-input-label',
18644 //cls : 'input-group-addon',
18645 html : this.fieldLabel
18655 Roo.log(" no label && no align");
18656 cfg.cn = [ inputblock ] ;
18661 var boxLabelCfg = {
18663 //'for': id, // box label is handled by onclick - so no for...
18665 html: this.boxLabel
18669 boxLabelCfg.tooltip = this.tooltip;
18672 cfg.cn.push(boxLabelCfg);
18682 * return the real input element.
18684 inputEl: function ()
18686 return this.el.select('input.roo-' + this.inputType,true).first();
18689 labelEl: function()
18691 return this.el.select('label.control-label',true).first();
18693 /* depricated... */
18697 return this.labelEl();
18700 initEvents : function()
18702 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18704 this.inputEl().on('click', this.onClick, this);
18706 if (this.boxLabel) {
18707 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18710 this.startValue = this.getValue();
18713 Roo.bootstrap.CheckBox.register(this);
18717 onClick : function()
18719 this.setChecked(!this.checked);
18722 setChecked : function(state,suppressEvent)
18724 this.startValue = this.getValue();
18726 if(this.inputType == 'radio'){
18728 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18729 e.dom.checked = false;
18732 this.inputEl().dom.checked = true;
18734 this.inputEl().dom.value = this.inputValue;
18736 if(suppressEvent !== true){
18737 this.fireEvent('check', this, true);
18745 this.checked = state;
18747 this.inputEl().dom.checked = state;
18749 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18751 if(suppressEvent !== true){
18752 this.fireEvent('check', this, state);
18758 getValue : function()
18760 if(this.inputType == 'radio'){
18761 return this.getGroupValue();
18764 return this.inputEl().getValue();
18768 getGroupValue : function()
18770 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18774 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18777 setValue : function(v,suppressEvent)
18779 if(this.inputType == 'radio'){
18780 this.setGroupValue(v, suppressEvent);
18784 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18789 setGroupValue : function(v, suppressEvent)
18791 this.startValue = this.getValue();
18793 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18794 e.dom.checked = false;
18796 if(e.dom.value == v){
18797 e.dom.checked = true;
18801 if(suppressEvent !== true){
18802 this.fireEvent('check', this, true);
18810 validate : function()
18814 (this.inputType == 'radio' && this.validateRadio()) ||
18815 (this.inputType == 'checkbox' && this.validateCheckbox())
18821 this.markInvalid();
18825 validateRadio : function()
18829 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18830 if(!e.dom.checked){
18842 validateCheckbox : function()
18845 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18848 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18856 for(var i in group){
18861 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18868 * Mark this field as valid
18870 markValid : function()
18872 if(this.allowBlank){
18878 this.fireEvent('valid', this);
18880 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18883 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18890 if(this.inputType == 'radio'){
18891 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18892 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18893 e.findParent('.form-group', false, true).addClass(_this.validClass);
18900 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18901 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18905 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18911 for(var i in group){
18912 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18913 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18918 * Mark this field as invalid
18919 * @param {String} msg The validation message
18921 markInvalid : function(msg)
18923 if(this.allowBlank){
18929 this.fireEvent('invalid', this, msg);
18931 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18934 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18938 label.markInvalid();
18941 if(this.inputType == 'radio'){
18942 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18943 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18944 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18951 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18952 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18956 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18962 for(var i in group){
18963 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18964 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18971 Roo.apply(Roo.bootstrap.CheckBox, {
18976 * register a CheckBox Group
18977 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18979 register : function(checkbox)
18981 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18982 this.groups[checkbox.groupId] = {};
18985 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18989 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18993 * fetch a CheckBox Group based on the group ID
18994 * @param {string} the group ID
18995 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18997 get: function(groupId) {
18998 if (typeof(this.groups[groupId]) == 'undefined') {
19002 return this.groups[groupId] ;
19014 *<div class="radio">
19016 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19017 Option one is this and that—be sure to include why it's great
19024 *<label class="radio-inline">fieldLabel</label>
19025 *<label class="radio-inline">
19026 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19034 * @class Roo.bootstrap.Radio
19035 * @extends Roo.bootstrap.CheckBox
19036 * Bootstrap Radio class
19039 * Create a new Radio
19040 * @param {Object} config The config object
19043 Roo.bootstrap.Radio = function(config){
19044 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19048 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19050 inputType: 'radio',
19054 getAutoCreate : function()
19056 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19057 align = align || 'left'; // default...
19064 tag : this.inline ? 'span' : 'div',
19069 var inline = this.inline ? ' radio-inline' : '';
19073 // does not need for, as we wrap the input with it..
19075 cls : 'control-label box-label' + inline,
19078 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19082 //cls : 'control-label' + inline,
19083 html : this.fieldLabel,
19084 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19093 type : this.inputType,
19094 //value : (!this.checked) ? this.valueOff : this.inputValue,
19095 value : this.inputValue,
19097 placeholder : this.placeholder || '' // ?? needed????
19100 if (this.weight) { // Validity check?
19101 input.cls += " radio-" + this.weight;
19103 if (this.disabled) {
19104 input.disabled=true;
19108 input.checked = this.checked;
19112 input.name = this.name;
19116 input.cls += ' input-' + this.size;
19119 //?? can span's inline have a width??
19122 ['xs','sm','md','lg'].map(function(size){
19123 if (settings[size]) {
19124 cfg.cls += ' col-' + size + '-' + settings[size];
19128 var inputblock = input;
19130 if (this.before || this.after) {
19133 cls : 'input-group',
19138 inputblock.cn.push({
19140 cls : 'input-group-addon',
19144 inputblock.cn.push(input);
19146 inputblock.cn.push({
19148 cls : 'input-group-addon',
19156 if (this.fieldLabel && this.fieldLabel.length) {
19157 cfg.cn.push(fieldLabel);
19160 // normal bootstrap puts the input inside the label.
19161 // however with our styled version - it has to go after the input.
19163 //lbl.cn.push(inputblock);
19167 cls: 'radio' + inline,
19174 cfg.cn.push( lblwrap);
19179 html: this.boxLabel
19188 initEvents : function()
19190 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19192 this.inputEl().on('click', this.onClick, this);
19193 if (this.boxLabel) {
19194 //Roo.log('find label');
19195 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19200 inputEl: function ()
19202 return this.el.select('input.roo-radio',true).first();
19204 onClick : function()
19207 this.setChecked(true);
19210 setChecked : function(state,suppressEvent)
19213 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19214 v.dom.checked = false;
19217 Roo.log(this.inputEl().dom);
19218 this.checked = state;
19219 this.inputEl().dom.checked = state;
19221 if(suppressEvent !== true){
19222 this.fireEvent('check', this, state);
19225 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19229 getGroupValue : function()
19232 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19233 if(v.dom.checked == true){
19234 value = v.dom.value;
19242 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19243 * @return {Mixed} value The field value
19245 getValue : function(){
19246 return this.getGroupValue();
19252 //<script type="text/javascript">
19255 * Based Ext JS Library 1.1.1
19256 * Copyright(c) 2006-2007, Ext JS, LLC.
19262 * @class Roo.HtmlEditorCore
19263 * @extends Roo.Component
19264 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19266 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19269 Roo.HtmlEditorCore = function(config){
19272 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19277 * @event initialize
19278 * Fires when the editor is fully initialized (including the iframe)
19279 * @param {Roo.HtmlEditorCore} this
19284 * Fires when the editor is first receives the focus. Any insertion must wait
19285 * until after this event.
19286 * @param {Roo.HtmlEditorCore} this
19290 * @event beforesync
19291 * Fires before the textarea is updated with content from the editor iframe. Return false
19292 * to cancel the sync.
19293 * @param {Roo.HtmlEditorCore} this
19294 * @param {String} html
19298 * @event beforepush
19299 * Fires before the iframe editor is updated with content from the textarea. Return false
19300 * to cancel the push.
19301 * @param {Roo.HtmlEditorCore} this
19302 * @param {String} html
19307 * Fires when the textarea is updated with content from the editor iframe.
19308 * @param {Roo.HtmlEditorCore} this
19309 * @param {String} html
19314 * Fires when the iframe editor is updated with content from the textarea.
19315 * @param {Roo.HtmlEditorCore} this
19316 * @param {String} html
19321 * @event editorevent
19322 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19323 * @param {Roo.HtmlEditorCore} this
19329 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19331 // defaults : white / black...
19332 this.applyBlacklists();
19339 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19343 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19349 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19354 * @cfg {Number} height (in pixels)
19358 * @cfg {Number} width (in pixels)
19363 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19366 stylesheets: false,
19371 // private properties
19372 validationEvent : false,
19374 initialized : false,
19376 sourceEditMode : false,
19377 onFocus : Roo.emptyFn,
19379 hideMode:'offsets',
19383 // blacklist + whitelisted elements..
19390 * Protected method that will not generally be called directly. It
19391 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19392 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19394 getDocMarkup : function(){
19398 // inherit styels from page...??
19399 if (this.stylesheets === false) {
19401 Roo.get(document.head).select('style').each(function(node) {
19402 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19405 Roo.get(document.head).select('link').each(function(node) {
19406 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19409 } else if (!this.stylesheets.length) {
19411 st = '<style type="text/css">' +
19412 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19418 st += '<style type="text/css">' +
19419 'IMG { cursor: pointer } ' +
19423 return '<html><head>' + st +
19424 //<style type="text/css">' +
19425 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19427 ' </head><body class="roo-htmleditor-body"></body></html>';
19431 onRender : function(ct, position)
19434 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19435 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19438 this.el.dom.style.border = '0 none';
19439 this.el.dom.setAttribute('tabIndex', -1);
19440 this.el.addClass('x-hidden hide');
19444 if(Roo.isIE){ // fix IE 1px bogus margin
19445 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19449 this.frameId = Roo.id();
19453 var iframe = this.owner.wrap.createChild({
19455 cls: 'form-control', // bootstrap..
19457 name: this.frameId,
19458 frameBorder : 'no',
19459 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19464 this.iframe = iframe.dom;
19466 this.assignDocWin();
19468 this.doc.designMode = 'on';
19471 this.doc.write(this.getDocMarkup());
19475 var task = { // must defer to wait for browser to be ready
19477 //console.log("run task?" + this.doc.readyState);
19478 this.assignDocWin();
19479 if(this.doc.body || this.doc.readyState == 'complete'){
19481 this.doc.designMode="on";
19485 Roo.TaskMgr.stop(task);
19486 this.initEditor.defer(10, this);
19493 Roo.TaskMgr.start(task);
19498 onResize : function(w, h)
19500 Roo.log('resize: ' +w + ',' + h );
19501 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19505 if(typeof w == 'number'){
19507 this.iframe.style.width = w + 'px';
19509 if(typeof h == 'number'){
19511 this.iframe.style.height = h + 'px';
19513 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19520 * Toggles the editor between standard and source edit mode.
19521 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19523 toggleSourceEdit : function(sourceEditMode){
19525 this.sourceEditMode = sourceEditMode === true;
19527 if(this.sourceEditMode){
19529 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19532 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19533 //this.iframe.className = '';
19536 //this.setSize(this.owner.wrap.getSize());
19537 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19544 * Protected method that will not generally be called directly. If you need/want
19545 * custom HTML cleanup, this is the method you should override.
19546 * @param {String} html The HTML to be cleaned
19547 * return {String} The cleaned HTML
19549 cleanHtml : function(html){
19550 html = String(html);
19551 if(html.length > 5){
19552 if(Roo.isSafari){ // strip safari nonsense
19553 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19556 if(html == ' '){
19563 * HTML Editor -> Textarea
19564 * Protected method that will not generally be called directly. Syncs the contents
19565 * of the editor iframe with the textarea.
19567 syncValue : function(){
19568 if(this.initialized){
19569 var bd = (this.doc.body || this.doc.documentElement);
19570 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19571 var html = bd.innerHTML;
19573 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19574 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19576 html = '<div style="'+m[0]+'">' + html + '</div>';
19579 html = this.cleanHtml(html);
19580 // fix up the special chars.. normaly like back quotes in word...
19581 // however we do not want to do this with chinese..
19582 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19583 var cc = b.charCodeAt();
19585 (cc >= 0x4E00 && cc < 0xA000 ) ||
19586 (cc >= 0x3400 && cc < 0x4E00 ) ||
19587 (cc >= 0xf900 && cc < 0xfb00 )
19593 if(this.owner.fireEvent('beforesync', this, html) !== false){
19594 this.el.dom.value = html;
19595 this.owner.fireEvent('sync', this, html);
19601 * Protected method that will not generally be called directly. Pushes the value of the textarea
19602 * into the iframe editor.
19604 pushValue : function(){
19605 if(this.initialized){
19606 var v = this.el.dom.value.trim();
19608 // if(v.length < 1){
19612 if(this.owner.fireEvent('beforepush', this, v) !== false){
19613 var d = (this.doc.body || this.doc.documentElement);
19615 this.cleanUpPaste();
19616 this.el.dom.value = d.innerHTML;
19617 this.owner.fireEvent('push', this, v);
19623 deferFocus : function(){
19624 this.focus.defer(10, this);
19628 focus : function(){
19629 if(this.win && !this.sourceEditMode){
19636 assignDocWin: function()
19638 var iframe = this.iframe;
19641 this.doc = iframe.contentWindow.document;
19642 this.win = iframe.contentWindow;
19644 // if (!Roo.get(this.frameId)) {
19647 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19648 // this.win = Roo.get(this.frameId).dom.contentWindow;
19650 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19654 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19655 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19660 initEditor : function(){
19661 //console.log("INIT EDITOR");
19662 this.assignDocWin();
19666 this.doc.designMode="on";
19668 this.doc.write(this.getDocMarkup());
19671 var dbody = (this.doc.body || this.doc.documentElement);
19672 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19673 // this copies styles from the containing element into thsi one..
19674 // not sure why we need all of this..
19675 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19677 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19678 //ss['background-attachment'] = 'fixed'; // w3c
19679 dbody.bgProperties = 'fixed'; // ie
19680 //Roo.DomHelper.applyStyles(dbody, ss);
19681 Roo.EventManager.on(this.doc, {
19682 //'mousedown': this.onEditorEvent,
19683 'mouseup': this.onEditorEvent,
19684 'dblclick': this.onEditorEvent,
19685 'click': this.onEditorEvent,
19686 'keyup': this.onEditorEvent,
19691 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19693 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19694 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19696 this.initialized = true;
19698 this.owner.fireEvent('initialize', this);
19703 onDestroy : function(){
19709 //for (var i =0; i < this.toolbars.length;i++) {
19710 // // fixme - ask toolbars for heights?
19711 // this.toolbars[i].onDestroy();
19714 //this.wrap.dom.innerHTML = '';
19715 //this.wrap.remove();
19720 onFirstFocus : function(){
19722 this.assignDocWin();
19725 this.activated = true;
19728 if(Roo.isGecko){ // prevent silly gecko errors
19730 var s = this.win.getSelection();
19731 if(!s.focusNode || s.focusNode.nodeType != 3){
19732 var r = s.getRangeAt(0);
19733 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19738 this.execCmd('useCSS', true);
19739 this.execCmd('styleWithCSS', false);
19742 this.owner.fireEvent('activate', this);
19746 adjustFont: function(btn){
19747 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19748 //if(Roo.isSafari){ // safari
19751 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19752 if(Roo.isSafari){ // safari
19753 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19754 v = (v < 10) ? 10 : v;
19755 v = (v > 48) ? 48 : v;
19756 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19761 v = Math.max(1, v+adjust);
19763 this.execCmd('FontSize', v );
19766 onEditorEvent : function(e)
19768 this.owner.fireEvent('editorevent', this, e);
19769 // this.updateToolbar();
19770 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19773 insertTag : function(tg)
19775 // could be a bit smarter... -> wrap the current selected tRoo..
19776 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19778 range = this.createRange(this.getSelection());
19779 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19780 wrappingNode.appendChild(range.extractContents());
19781 range.insertNode(wrappingNode);
19788 this.execCmd("formatblock", tg);
19792 insertText : function(txt)
19796 var range = this.createRange();
19797 range.deleteContents();
19798 //alert(Sender.getAttribute('label'));
19800 range.insertNode(this.doc.createTextNode(txt));
19806 * Executes a Midas editor command on the editor document and performs necessary focus and
19807 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19808 * @param {String} cmd The Midas command
19809 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19811 relayCmd : function(cmd, value){
19813 this.execCmd(cmd, value);
19814 this.owner.fireEvent('editorevent', this);
19815 //this.updateToolbar();
19816 this.owner.deferFocus();
19820 * Executes a Midas editor command directly on the editor document.
19821 * For visual commands, you should use {@link #relayCmd} instead.
19822 * <b>This should only be called after the editor is initialized.</b>
19823 * @param {String} cmd The Midas command
19824 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19826 execCmd : function(cmd, value){
19827 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19834 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19836 * @param {String} text | dom node..
19838 insertAtCursor : function(text)
19843 if(!this.activated){
19849 var r = this.doc.selection.createRange();
19860 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19864 // from jquery ui (MIT licenced)
19866 var win = this.win;
19868 if (win.getSelection && win.getSelection().getRangeAt) {
19869 range = win.getSelection().getRangeAt(0);
19870 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19871 range.insertNode(node);
19872 } else if (win.document.selection && win.document.selection.createRange) {
19873 // no firefox support
19874 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19875 win.document.selection.createRange().pasteHTML(txt);
19877 // no firefox support
19878 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19879 this.execCmd('InsertHTML', txt);
19888 mozKeyPress : function(e){
19890 var c = e.getCharCode(), cmd;
19893 c = String.fromCharCode(c).toLowerCase();
19907 this.cleanUpPaste.defer(100, this);
19915 e.preventDefault();
19923 fixKeys : function(){ // load time branching for fastest keydown performance
19925 return function(e){
19926 var k = e.getKey(), r;
19929 r = this.doc.selection.createRange();
19932 r.pasteHTML('    ');
19939 r = this.doc.selection.createRange();
19941 var target = r.parentElement();
19942 if(!target || target.tagName.toLowerCase() != 'li'){
19944 r.pasteHTML('<br />');
19950 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19951 this.cleanUpPaste.defer(100, this);
19957 }else if(Roo.isOpera){
19958 return function(e){
19959 var k = e.getKey();
19963 this.execCmd('InsertHTML','    ');
19966 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19967 this.cleanUpPaste.defer(100, this);
19972 }else if(Roo.isSafari){
19973 return function(e){
19974 var k = e.getKey();
19978 this.execCmd('InsertText','\t');
19982 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19983 this.cleanUpPaste.defer(100, this);
19991 getAllAncestors: function()
19993 var p = this.getSelectedNode();
19996 a.push(p); // push blank onto stack..
19997 p = this.getParentElement();
20001 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20005 a.push(this.doc.body);
20009 lastSelNode : false,
20012 getSelection : function()
20014 this.assignDocWin();
20015 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20018 getSelectedNode: function()
20020 // this may only work on Gecko!!!
20022 // should we cache this!!!!
20027 var range = this.createRange(this.getSelection()).cloneRange();
20030 var parent = range.parentElement();
20032 var testRange = range.duplicate();
20033 testRange.moveToElementText(parent);
20034 if (testRange.inRange(range)) {
20037 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20040 parent = parent.parentElement;
20045 // is ancestor a text element.
20046 var ac = range.commonAncestorContainer;
20047 if (ac.nodeType == 3) {
20048 ac = ac.parentNode;
20051 var ar = ac.childNodes;
20054 var other_nodes = [];
20055 var has_other_nodes = false;
20056 for (var i=0;i<ar.length;i++) {
20057 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20060 // fullly contained node.
20062 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20067 // probably selected..
20068 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20069 other_nodes.push(ar[i]);
20073 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20078 has_other_nodes = true;
20080 if (!nodes.length && other_nodes.length) {
20081 nodes= other_nodes;
20083 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20089 createRange: function(sel)
20091 // this has strange effects when using with
20092 // top toolbar - not sure if it's a great idea.
20093 //this.editor.contentWindow.focus();
20094 if (typeof sel != "undefined") {
20096 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20098 return this.doc.createRange();
20101 return this.doc.createRange();
20104 getParentElement: function()
20107 this.assignDocWin();
20108 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20110 var range = this.createRange(sel);
20113 var p = range.commonAncestorContainer;
20114 while (p.nodeType == 3) { // text node
20125 * Range intersection.. the hard stuff...
20129 * [ -- selected range --- ]
20133 * if end is before start or hits it. fail.
20134 * if start is after end or hits it fail.
20136 * if either hits (but other is outside. - then it's not
20142 // @see http://www.thismuchiknow.co.uk/?p=64.
20143 rangeIntersectsNode : function(range, node)
20145 var nodeRange = node.ownerDocument.createRange();
20147 nodeRange.selectNode(node);
20149 nodeRange.selectNodeContents(node);
20152 var rangeStartRange = range.cloneRange();
20153 rangeStartRange.collapse(true);
20155 var rangeEndRange = range.cloneRange();
20156 rangeEndRange.collapse(false);
20158 var nodeStartRange = nodeRange.cloneRange();
20159 nodeStartRange.collapse(true);
20161 var nodeEndRange = nodeRange.cloneRange();
20162 nodeEndRange.collapse(false);
20164 return rangeStartRange.compareBoundaryPoints(
20165 Range.START_TO_START, nodeEndRange) == -1 &&
20166 rangeEndRange.compareBoundaryPoints(
20167 Range.START_TO_START, nodeStartRange) == 1;
20171 rangeCompareNode : function(range, node)
20173 var nodeRange = node.ownerDocument.createRange();
20175 nodeRange.selectNode(node);
20177 nodeRange.selectNodeContents(node);
20181 range.collapse(true);
20183 nodeRange.collapse(true);
20185 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20186 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20188 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20190 var nodeIsBefore = ss == 1;
20191 var nodeIsAfter = ee == -1;
20193 if (nodeIsBefore && nodeIsAfter) {
20196 if (!nodeIsBefore && nodeIsAfter) {
20197 return 1; //right trailed.
20200 if (nodeIsBefore && !nodeIsAfter) {
20201 return 2; // left trailed.
20207 // private? - in a new class?
20208 cleanUpPaste : function()
20210 // cleans up the whole document..
20211 Roo.log('cleanuppaste');
20213 this.cleanUpChildren(this.doc.body);
20214 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20215 if (clean != this.doc.body.innerHTML) {
20216 this.doc.body.innerHTML = clean;
20221 cleanWordChars : function(input) {// change the chars to hex code
20222 var he = Roo.HtmlEditorCore;
20224 var output = input;
20225 Roo.each(he.swapCodes, function(sw) {
20226 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20228 output = output.replace(swapper, sw[1]);
20235 cleanUpChildren : function (n)
20237 if (!n.childNodes.length) {
20240 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20241 this.cleanUpChild(n.childNodes[i]);
20248 cleanUpChild : function (node)
20251 //console.log(node);
20252 if (node.nodeName == "#text") {
20253 // clean up silly Windows -- stuff?
20256 if (node.nodeName == "#comment") {
20257 node.parentNode.removeChild(node);
20258 // clean up silly Windows -- stuff?
20261 var lcname = node.tagName.toLowerCase();
20262 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20263 // whitelist of tags..
20265 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20267 node.parentNode.removeChild(node);
20272 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20274 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20275 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20277 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20278 // remove_keep_children = true;
20281 if (remove_keep_children) {
20282 this.cleanUpChildren(node);
20283 // inserts everything just before this node...
20284 while (node.childNodes.length) {
20285 var cn = node.childNodes[0];
20286 node.removeChild(cn);
20287 node.parentNode.insertBefore(cn, node);
20289 node.parentNode.removeChild(node);
20293 if (!node.attributes || !node.attributes.length) {
20294 this.cleanUpChildren(node);
20298 function cleanAttr(n,v)
20301 if (v.match(/^\./) || v.match(/^\//)) {
20304 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20307 if (v.match(/^#/)) {
20310 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20311 node.removeAttribute(n);
20315 var cwhite = this.cwhite;
20316 var cblack = this.cblack;
20318 function cleanStyle(n,v)
20320 if (v.match(/expression/)) { //XSS?? should we even bother..
20321 node.removeAttribute(n);
20325 var parts = v.split(/;/);
20328 Roo.each(parts, function(p) {
20329 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20333 var l = p.split(':').shift().replace(/\s+/g,'');
20334 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20336 if ( cwhite.length && cblack.indexOf(l) > -1) {
20337 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20338 //node.removeAttribute(n);
20342 // only allow 'c whitelisted system attributes'
20343 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20344 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20345 //node.removeAttribute(n);
20355 if (clean.length) {
20356 node.setAttribute(n, clean.join(';'));
20358 node.removeAttribute(n);
20364 for (var i = node.attributes.length-1; i > -1 ; i--) {
20365 var a = node.attributes[i];
20368 if (a.name.toLowerCase().substr(0,2)=='on') {
20369 node.removeAttribute(a.name);
20372 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20373 node.removeAttribute(a.name);
20376 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20377 cleanAttr(a.name,a.value); // fixme..
20380 if (a.name == 'style') {
20381 cleanStyle(a.name,a.value);
20384 /// clean up MS crap..
20385 // tecnically this should be a list of valid class'es..
20388 if (a.name == 'class') {
20389 if (a.value.match(/^Mso/)) {
20390 node.className = '';
20393 if (a.value.match(/body/)) {
20394 node.className = '';
20405 this.cleanUpChildren(node);
20411 * Clean up MS wordisms...
20413 cleanWord : function(node)
20418 this.cleanWord(this.doc.body);
20421 if (node.nodeName == "#text") {
20422 // clean up silly Windows -- stuff?
20425 if (node.nodeName == "#comment") {
20426 node.parentNode.removeChild(node);
20427 // clean up silly Windows -- stuff?
20431 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20432 node.parentNode.removeChild(node);
20436 // remove - but keep children..
20437 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20438 while (node.childNodes.length) {
20439 var cn = node.childNodes[0];
20440 node.removeChild(cn);
20441 node.parentNode.insertBefore(cn, node);
20443 node.parentNode.removeChild(node);
20444 this.iterateChildren(node, this.cleanWord);
20448 if (node.className.length) {
20450 var cn = node.className.split(/\W+/);
20452 Roo.each(cn, function(cls) {
20453 if (cls.match(/Mso[a-zA-Z]+/)) {
20458 node.className = cna.length ? cna.join(' ') : '';
20460 node.removeAttribute("class");
20464 if (node.hasAttribute("lang")) {
20465 node.removeAttribute("lang");
20468 if (node.hasAttribute("style")) {
20470 var styles = node.getAttribute("style").split(";");
20472 Roo.each(styles, function(s) {
20473 if (!s.match(/:/)) {
20476 var kv = s.split(":");
20477 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20480 // what ever is left... we allow.
20483 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20484 if (!nstyle.length) {
20485 node.removeAttribute('style');
20488 this.iterateChildren(node, this.cleanWord);
20494 * iterateChildren of a Node, calling fn each time, using this as the scole..
20495 * @param {DomNode} node node to iterate children of.
20496 * @param {Function} fn method of this class to call on each item.
20498 iterateChildren : function(node, fn)
20500 if (!node.childNodes.length) {
20503 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20504 fn.call(this, node.childNodes[i])
20510 * cleanTableWidths.
20512 * Quite often pasting from word etc.. results in tables with column and widths.
20513 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20516 cleanTableWidths : function(node)
20521 this.cleanTableWidths(this.doc.body);
20526 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20529 Roo.log(node.tagName);
20530 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20531 this.iterateChildren(node, this.cleanTableWidths);
20534 if (node.hasAttribute('width')) {
20535 node.removeAttribute('width');
20539 if (node.hasAttribute("style")) {
20542 var styles = node.getAttribute("style").split(";");
20544 Roo.each(styles, function(s) {
20545 if (!s.match(/:/)) {
20548 var kv = s.split(":");
20549 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20552 // what ever is left... we allow.
20555 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20556 if (!nstyle.length) {
20557 node.removeAttribute('style');
20561 this.iterateChildren(node, this.cleanTableWidths);
20569 domToHTML : function(currentElement, depth, nopadtext) {
20571 depth = depth || 0;
20572 nopadtext = nopadtext || false;
20574 if (!currentElement) {
20575 return this.domToHTML(this.doc.body);
20578 //Roo.log(currentElement);
20580 var allText = false;
20581 var nodeName = currentElement.nodeName;
20582 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20584 if (nodeName == '#text') {
20586 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20591 if (nodeName != 'BODY') {
20594 // Prints the node tagName, such as <A>, <IMG>, etc
20597 for(i = 0; i < currentElement.attributes.length;i++) {
20599 var aname = currentElement.attributes.item(i).name;
20600 if (!currentElement.attributes.item(i).value.length) {
20603 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20606 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20615 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20618 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20623 // Traverse the tree
20625 var currentElementChild = currentElement.childNodes.item(i);
20626 var allText = true;
20627 var innerHTML = '';
20629 while (currentElementChild) {
20630 // Formatting code (indent the tree so it looks nice on the screen)
20631 var nopad = nopadtext;
20632 if (lastnode == 'SPAN') {
20636 if (currentElementChild.nodeName == '#text') {
20637 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20638 toadd = nopadtext ? toadd : toadd.trim();
20639 if (!nopad && toadd.length > 80) {
20640 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20642 innerHTML += toadd;
20645 currentElementChild = currentElement.childNodes.item(i);
20651 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20653 // Recursively traverse the tree structure of the child node
20654 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20655 lastnode = currentElementChild.nodeName;
20657 currentElementChild=currentElement.childNodes.item(i);
20663 // The remaining code is mostly for formatting the tree
20664 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20669 ret+= "</"+tagName+">";
20675 applyBlacklists : function()
20677 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20678 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20682 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20683 if (b.indexOf(tag) > -1) {
20686 this.white.push(tag);
20690 Roo.each(w, function(tag) {
20691 if (b.indexOf(tag) > -1) {
20694 if (this.white.indexOf(tag) > -1) {
20697 this.white.push(tag);
20702 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20703 if (w.indexOf(tag) > -1) {
20706 this.black.push(tag);
20710 Roo.each(b, function(tag) {
20711 if (w.indexOf(tag) > -1) {
20714 if (this.black.indexOf(tag) > -1) {
20717 this.black.push(tag);
20722 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20723 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20727 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20728 if (b.indexOf(tag) > -1) {
20731 this.cwhite.push(tag);
20735 Roo.each(w, function(tag) {
20736 if (b.indexOf(tag) > -1) {
20739 if (this.cwhite.indexOf(tag) > -1) {
20742 this.cwhite.push(tag);
20747 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20748 if (w.indexOf(tag) > -1) {
20751 this.cblack.push(tag);
20755 Roo.each(b, function(tag) {
20756 if (w.indexOf(tag) > -1) {
20759 if (this.cblack.indexOf(tag) > -1) {
20762 this.cblack.push(tag);
20767 setStylesheets : function(stylesheets)
20769 if(typeof(stylesheets) == 'string'){
20770 Roo.get(this.iframe.contentDocument.head).createChild({
20772 rel : 'stylesheet',
20781 Roo.each(stylesheets, function(s) {
20786 Roo.get(_this.iframe.contentDocument.head).createChild({
20788 rel : 'stylesheet',
20797 removeStylesheets : function()
20801 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20806 // hide stuff that is not compatible
20820 * @event specialkey
20824 * @cfg {String} fieldClass @hide
20827 * @cfg {String} focusClass @hide
20830 * @cfg {String} autoCreate @hide
20833 * @cfg {String} inputType @hide
20836 * @cfg {String} invalidClass @hide
20839 * @cfg {String} invalidText @hide
20842 * @cfg {String} msgFx @hide
20845 * @cfg {String} validateOnBlur @hide
20849 Roo.HtmlEditorCore.white = [
20850 'area', 'br', 'img', 'input', 'hr', 'wbr',
20852 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20853 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20854 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20855 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20856 'table', 'ul', 'xmp',
20858 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20861 'dir', 'menu', 'ol', 'ul', 'dl',
20867 Roo.HtmlEditorCore.black = [
20868 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20870 'base', 'basefont', 'bgsound', 'blink', 'body',
20871 'frame', 'frameset', 'head', 'html', 'ilayer',
20872 'iframe', 'layer', 'link', 'meta', 'object',
20873 'script', 'style' ,'title', 'xml' // clean later..
20875 Roo.HtmlEditorCore.clean = [
20876 'script', 'style', 'title', 'xml'
20878 Roo.HtmlEditorCore.remove = [
20883 Roo.HtmlEditorCore.ablack = [
20887 Roo.HtmlEditorCore.aclean = [
20888 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20892 Roo.HtmlEditorCore.pwhite= [
20893 'http', 'https', 'mailto'
20896 // white listed style attributes.
20897 Roo.HtmlEditorCore.cwhite= [
20898 // 'text-align', /// default is to allow most things..
20904 // black listed style attributes.
20905 Roo.HtmlEditorCore.cblack= [
20906 // 'font-size' -- this can be set by the project
20910 Roo.HtmlEditorCore.swapCodes =[
20929 * @class Roo.bootstrap.HtmlEditor
20930 * @extends Roo.bootstrap.TextArea
20931 * Bootstrap HtmlEditor class
20934 * Create a new HtmlEditor
20935 * @param {Object} config The config object
20938 Roo.bootstrap.HtmlEditor = function(config){
20939 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20940 if (!this.toolbars) {
20941 this.toolbars = [];
20943 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20946 * @event initialize
20947 * Fires when the editor is fully initialized (including the iframe)
20948 * @param {HtmlEditor} this
20953 * Fires when the editor is first receives the focus. Any insertion must wait
20954 * until after this event.
20955 * @param {HtmlEditor} this
20959 * @event beforesync
20960 * Fires before the textarea is updated with content from the editor iframe. Return false
20961 * to cancel the sync.
20962 * @param {HtmlEditor} this
20963 * @param {String} html
20967 * @event beforepush
20968 * Fires before the iframe editor is updated with content from the textarea. Return false
20969 * to cancel the push.
20970 * @param {HtmlEditor} this
20971 * @param {String} html
20976 * Fires when the textarea is updated with content from the editor iframe.
20977 * @param {HtmlEditor} this
20978 * @param {String} html
20983 * Fires when the iframe editor is updated with content from the textarea.
20984 * @param {HtmlEditor} this
20985 * @param {String} html
20989 * @event editmodechange
20990 * Fires when the editor switches edit modes
20991 * @param {HtmlEditor} this
20992 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20994 editmodechange: true,
20996 * @event editorevent
20997 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20998 * @param {HtmlEditor} this
21002 * @event firstfocus
21003 * Fires when on first focus - needed by toolbars..
21004 * @param {HtmlEditor} this
21009 * Auto save the htmlEditor value as a file into Events
21010 * @param {HtmlEditor} this
21014 * @event savedpreview
21015 * preview the saved version of htmlEditor
21016 * @param {HtmlEditor} this
21023 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21027 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21032 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21037 * @cfg {Number} height (in pixels)
21041 * @cfg {Number} width (in pixels)
21046 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21049 stylesheets: false,
21054 // private properties
21055 validationEvent : false,
21057 initialized : false,
21060 onFocus : Roo.emptyFn,
21062 hideMode:'offsets',
21065 tbContainer : false,
21067 toolbarContainer :function() {
21068 return this.wrap.select('.x-html-editor-tb',true).first();
21072 * Protected method that will not generally be called directly. It
21073 * is called when the editor creates its toolbar. Override this method if you need to
21074 * add custom toolbar buttons.
21075 * @param {HtmlEditor} editor
21077 createToolbar : function(){
21079 Roo.log("create toolbars");
21081 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21082 this.toolbars[0].render(this.toolbarContainer());
21086 // if (!editor.toolbars || !editor.toolbars.length) {
21087 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21090 // for (var i =0 ; i < editor.toolbars.length;i++) {
21091 // editor.toolbars[i] = Roo.factory(
21092 // typeof(editor.toolbars[i]) == 'string' ?
21093 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21094 // Roo.bootstrap.HtmlEditor);
21095 // editor.toolbars[i].init(editor);
21101 onRender : function(ct, position)
21103 // Roo.log("Call onRender: " + this.xtype);
21105 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21107 this.wrap = this.inputEl().wrap({
21108 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21111 this.editorcore.onRender(ct, position);
21113 if (this.resizable) {
21114 this.resizeEl = new Roo.Resizable(this.wrap, {
21118 minHeight : this.height,
21119 height: this.height,
21120 handles : this.resizable,
21123 resize : function(r, w, h) {
21124 _t.onResize(w,h); // -something
21130 this.createToolbar(this);
21133 if(!this.width && this.resizable){
21134 this.setSize(this.wrap.getSize());
21136 if (this.resizeEl) {
21137 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21138 // should trigger onReize..
21144 onResize : function(w, h)
21146 Roo.log('resize: ' +w + ',' + h );
21147 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21151 if(this.inputEl() ){
21152 if(typeof w == 'number'){
21153 var aw = w - this.wrap.getFrameWidth('lr');
21154 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21157 if(typeof h == 'number'){
21158 var tbh = -11; // fixme it needs to tool bar size!
21159 for (var i =0; i < this.toolbars.length;i++) {
21160 // fixme - ask toolbars for heights?
21161 tbh += this.toolbars[i].el.getHeight();
21162 //if (this.toolbars[i].footer) {
21163 // tbh += this.toolbars[i].footer.el.getHeight();
21171 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21172 ah -= 5; // knock a few pixes off for look..
21173 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21177 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21178 this.editorcore.onResize(ew,eh);
21183 * Toggles the editor between standard and source edit mode.
21184 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21186 toggleSourceEdit : function(sourceEditMode)
21188 this.editorcore.toggleSourceEdit(sourceEditMode);
21190 if(this.editorcore.sourceEditMode){
21191 Roo.log('editor - showing textarea');
21194 // Roo.log(this.syncValue());
21196 this.inputEl().removeClass(['hide', 'x-hidden']);
21197 this.inputEl().dom.removeAttribute('tabIndex');
21198 this.inputEl().focus();
21200 Roo.log('editor - hiding textarea');
21202 // Roo.log(this.pushValue());
21205 this.inputEl().addClass(['hide', 'x-hidden']);
21206 this.inputEl().dom.setAttribute('tabIndex', -1);
21207 //this.deferFocus();
21210 if(this.resizable){
21211 this.setSize(this.wrap.getSize());
21214 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21217 // private (for BoxComponent)
21218 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21220 // private (for BoxComponent)
21221 getResizeEl : function(){
21225 // private (for BoxComponent)
21226 getPositionEl : function(){
21231 initEvents : function(){
21232 this.originalValue = this.getValue();
21236 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21239 // markInvalid : Roo.emptyFn,
21241 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21244 // clearInvalid : Roo.emptyFn,
21246 setValue : function(v){
21247 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21248 this.editorcore.pushValue();
21253 deferFocus : function(){
21254 this.focus.defer(10, this);
21258 focus : function(){
21259 this.editorcore.focus();
21265 onDestroy : function(){
21271 for (var i =0; i < this.toolbars.length;i++) {
21272 // fixme - ask toolbars for heights?
21273 this.toolbars[i].onDestroy();
21276 this.wrap.dom.innerHTML = '';
21277 this.wrap.remove();
21282 onFirstFocus : function(){
21283 //Roo.log("onFirstFocus");
21284 this.editorcore.onFirstFocus();
21285 for (var i =0; i < this.toolbars.length;i++) {
21286 this.toolbars[i].onFirstFocus();
21292 syncValue : function()
21294 this.editorcore.syncValue();
21297 pushValue : function()
21299 this.editorcore.pushValue();
21303 // hide stuff that is not compatible
21317 * @event specialkey
21321 * @cfg {String} fieldClass @hide
21324 * @cfg {String} focusClass @hide
21327 * @cfg {String} autoCreate @hide
21330 * @cfg {String} inputType @hide
21333 * @cfg {String} invalidClass @hide
21336 * @cfg {String} invalidText @hide
21339 * @cfg {String} msgFx @hide
21342 * @cfg {String} validateOnBlur @hide
21351 Roo.namespace('Roo.bootstrap.htmleditor');
21353 * @class Roo.bootstrap.HtmlEditorToolbar1
21358 new Roo.bootstrap.HtmlEditor({
21361 new Roo.bootstrap.HtmlEditorToolbar1({
21362 disable : { fonts: 1 , format: 1, ..., ... , ...],
21368 * @cfg {Object} disable List of elements to disable..
21369 * @cfg {Array} btns List of additional buttons.
21373 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21376 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21379 Roo.apply(this, config);
21381 // default disabled, based on 'good practice'..
21382 this.disable = this.disable || {};
21383 Roo.applyIf(this.disable, {
21386 specialElements : true
21388 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21390 this.editor = config.editor;
21391 this.editorcore = config.editor.editorcore;
21393 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21395 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21396 // dont call parent... till later.
21398 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21403 editorcore : false,
21408 "h1","h2","h3","h4","h5","h6",
21410 "abbr", "acronym", "address", "cite", "samp", "var",
21414 onRender : function(ct, position)
21416 // Roo.log("Call onRender: " + this.xtype);
21418 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21420 this.el.dom.style.marginBottom = '0';
21422 var editorcore = this.editorcore;
21423 var editor= this.editor;
21426 var btn = function(id,cmd , toggle, handler){
21428 var event = toggle ? 'toggle' : 'click';
21433 xns: Roo.bootstrap,
21436 enableToggle:toggle !== false,
21438 pressed : toggle ? false : null,
21441 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21442 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21451 xns: Roo.bootstrap,
21452 glyphicon : 'font',
21456 xns: Roo.bootstrap,
21460 Roo.each(this.formats, function(f) {
21461 style.menu.items.push({
21463 xns: Roo.bootstrap,
21464 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21469 editorcore.insertTag(this.tagname);
21476 children.push(style);
21479 btn('bold',false,true);
21480 btn('italic',false,true);
21481 btn('align-left', 'justifyleft',true);
21482 btn('align-center', 'justifycenter',true);
21483 btn('align-right' , 'justifyright',true);
21484 btn('link', false, false, function(btn) {
21485 //Roo.log("create link?");
21486 var url = prompt(this.createLinkText, this.defaultLinkValue);
21487 if(url && url != 'http:/'+'/'){
21488 this.editorcore.relayCmd('createlink', url);
21491 btn('list','insertunorderedlist',true);
21492 btn('pencil', false,true, function(btn){
21495 this.toggleSourceEdit(btn.pressed);
21501 xns: Roo.bootstrap,
21506 xns: Roo.bootstrap,
21511 cog.menu.items.push({
21513 xns: Roo.bootstrap,
21514 html : Clean styles,
21519 editorcore.insertTag(this.tagname);
21528 this.xtype = 'NavSimplebar';
21530 for(var i=0;i< children.length;i++) {
21532 this.buttons.add(this.addxtypeChild(children[i]));
21536 editor.on('editorevent', this.updateToolbar, this);
21538 onBtnClick : function(id)
21540 this.editorcore.relayCmd(id);
21541 this.editorcore.focus();
21545 * Protected method that will not generally be called directly. It triggers
21546 * a toolbar update by reading the markup state of the current selection in the editor.
21548 updateToolbar: function(){
21550 if(!this.editorcore.activated){
21551 this.editor.onFirstFocus(); // is this neeed?
21555 var btns = this.buttons;
21556 var doc = this.editorcore.doc;
21557 btns.get('bold').setActive(doc.queryCommandState('bold'));
21558 btns.get('italic').setActive(doc.queryCommandState('italic'));
21559 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21561 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21562 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21563 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21565 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21566 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21569 var ans = this.editorcore.getAllAncestors();
21570 if (this.formatCombo) {
21573 var store = this.formatCombo.store;
21574 this.formatCombo.setValue("");
21575 for (var i =0; i < ans.length;i++) {
21576 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21578 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21586 // hides menus... - so this cant be on a menu...
21587 Roo.bootstrap.MenuMgr.hideAll();
21589 Roo.bootstrap.MenuMgr.hideAll();
21590 //this.editorsyncValue();
21592 onFirstFocus: function() {
21593 this.buttons.each(function(item){
21597 toggleSourceEdit : function(sourceEditMode){
21600 if(sourceEditMode){
21601 Roo.log("disabling buttons");
21602 this.buttons.each( function(item){
21603 if(item.cmd != 'pencil'){
21609 Roo.log("enabling buttons");
21610 if(this.editorcore.initialized){
21611 this.buttons.each( function(item){
21617 Roo.log("calling toggole on editor");
21618 // tell the editor that it's been pressed..
21619 this.editor.toggleSourceEdit(sourceEditMode);
21629 * @class Roo.bootstrap.Table.AbstractSelectionModel
21630 * @extends Roo.util.Observable
21631 * Abstract base class for grid SelectionModels. It provides the interface that should be
21632 * implemented by descendant classes. This class should not be directly instantiated.
21635 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21636 this.locked = false;
21637 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21641 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21642 /** @ignore Called by the grid automatically. Do not call directly. */
21643 init : function(grid){
21649 * Locks the selections.
21652 this.locked = true;
21656 * Unlocks the selections.
21658 unlock : function(){
21659 this.locked = false;
21663 * Returns true if the selections are locked.
21664 * @return {Boolean}
21666 isLocked : function(){
21667 return this.locked;
21671 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21672 * @class Roo.bootstrap.Table.RowSelectionModel
21673 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21674 * It supports multiple selections and keyboard selection/navigation.
21676 * @param {Object} config
21679 Roo.bootstrap.Table.RowSelectionModel = function(config){
21680 Roo.apply(this, config);
21681 this.selections = new Roo.util.MixedCollection(false, function(o){
21686 this.lastActive = false;
21690 * @event selectionchange
21691 * Fires when the selection changes
21692 * @param {SelectionModel} this
21694 "selectionchange" : true,
21696 * @event afterselectionchange
21697 * Fires after the selection changes (eg. by key press or clicking)
21698 * @param {SelectionModel} this
21700 "afterselectionchange" : true,
21702 * @event beforerowselect
21703 * Fires when a row is selected being selected, return false to cancel.
21704 * @param {SelectionModel} this
21705 * @param {Number} rowIndex The selected index
21706 * @param {Boolean} keepExisting False if other selections will be cleared
21708 "beforerowselect" : true,
21711 * Fires when a row is selected.
21712 * @param {SelectionModel} this
21713 * @param {Number} rowIndex The selected index
21714 * @param {Roo.data.Record} r The record
21716 "rowselect" : true,
21718 * @event rowdeselect
21719 * Fires when a row is deselected.
21720 * @param {SelectionModel} this
21721 * @param {Number} rowIndex The selected index
21723 "rowdeselect" : true
21725 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21726 this.locked = false;
21729 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21731 * @cfg {Boolean} singleSelect
21732 * True to allow selection of only one row at a time (defaults to false)
21734 singleSelect : false,
21737 initEvents : function(){
21739 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21740 this.grid.on("mousedown", this.handleMouseDown, this);
21741 }else{ // allow click to work like normal
21742 this.grid.on("rowclick", this.handleDragableRowClick, this);
21745 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21746 "up" : function(e){
21748 this.selectPrevious(e.shiftKey);
21749 }else if(this.last !== false && this.lastActive !== false){
21750 var last = this.last;
21751 this.selectRange(this.last, this.lastActive-1);
21752 this.grid.getView().focusRow(this.lastActive);
21753 if(last !== false){
21757 this.selectFirstRow();
21759 this.fireEvent("afterselectionchange", this);
21761 "down" : function(e){
21763 this.selectNext(e.shiftKey);
21764 }else if(this.last !== false && this.lastActive !== false){
21765 var last = this.last;
21766 this.selectRange(this.last, this.lastActive+1);
21767 this.grid.getView().focusRow(this.lastActive);
21768 if(last !== false){
21772 this.selectFirstRow();
21774 this.fireEvent("afterselectionchange", this);
21779 var view = this.grid.view;
21780 view.on("refresh", this.onRefresh, this);
21781 view.on("rowupdated", this.onRowUpdated, this);
21782 view.on("rowremoved", this.onRemove, this);
21786 onRefresh : function(){
21787 var ds = this.grid.dataSource, i, v = this.grid.view;
21788 var s = this.selections;
21789 s.each(function(r){
21790 if((i = ds.indexOfId(r.id)) != -1){
21799 onRemove : function(v, index, r){
21800 this.selections.remove(r);
21804 onRowUpdated : function(v, index, r){
21805 if(this.isSelected(r)){
21806 v.onRowSelect(index);
21812 * @param {Array} records The records to select
21813 * @param {Boolean} keepExisting (optional) True to keep existing selections
21815 selectRecords : function(records, keepExisting){
21817 this.clearSelections();
21819 var ds = this.grid.dataSource;
21820 for(var i = 0, len = records.length; i < len; i++){
21821 this.selectRow(ds.indexOf(records[i]), true);
21826 * Gets the number of selected rows.
21829 getCount : function(){
21830 return this.selections.length;
21834 * Selects the first row in the grid.
21836 selectFirstRow : function(){
21841 * Select the last row.
21842 * @param {Boolean} keepExisting (optional) True to keep existing selections
21844 selectLastRow : function(keepExisting){
21845 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21849 * Selects the row immediately following the last selected row.
21850 * @param {Boolean} keepExisting (optional) True to keep existing selections
21852 selectNext : function(keepExisting){
21853 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21854 this.selectRow(this.last+1, keepExisting);
21855 this.grid.getView().focusRow(this.last);
21860 * Selects the row that precedes the last selected row.
21861 * @param {Boolean} keepExisting (optional) True to keep existing selections
21863 selectPrevious : function(keepExisting){
21865 this.selectRow(this.last-1, keepExisting);
21866 this.grid.getView().focusRow(this.last);
21871 * Returns the selected records
21872 * @return {Array} Array of selected records
21874 getSelections : function(){
21875 return [].concat(this.selections.items);
21879 * Returns the first selected record.
21882 getSelected : function(){
21883 return this.selections.itemAt(0);
21888 * Clears all selections.
21890 clearSelections : function(fast){
21895 var ds = this.grid.dataSource;
21896 var s = this.selections;
21897 s.each(function(r){
21898 this.deselectRow(ds.indexOfId(r.id));
21902 this.selections.clear();
21909 * Selects all rows.
21911 selectAll : function(){
21915 this.selections.clear();
21916 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21917 this.selectRow(i, true);
21922 * Returns True if there is a selection.
21923 * @return {Boolean}
21925 hasSelection : function(){
21926 return this.selections.length > 0;
21930 * Returns True if the specified row is selected.
21931 * @param {Number/Record} record The record or index of the record to check
21932 * @return {Boolean}
21934 isSelected : function(index){
21935 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21936 return (r && this.selections.key(r.id) ? true : false);
21940 * Returns True if the specified record id is selected.
21941 * @param {String} id The id of record to check
21942 * @return {Boolean}
21944 isIdSelected : function(id){
21945 return (this.selections.key(id) ? true : false);
21949 handleMouseDown : function(e, t){
21950 var view = this.grid.getView(), rowIndex;
21951 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21954 if(e.shiftKey && this.last !== false){
21955 var last = this.last;
21956 this.selectRange(last, rowIndex, e.ctrlKey);
21957 this.last = last; // reset the last
21958 view.focusRow(rowIndex);
21960 var isSelected = this.isSelected(rowIndex);
21961 if(e.button !== 0 && isSelected){
21962 view.focusRow(rowIndex);
21963 }else if(e.ctrlKey && isSelected){
21964 this.deselectRow(rowIndex);
21965 }else if(!isSelected){
21966 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21967 view.focusRow(rowIndex);
21970 this.fireEvent("afterselectionchange", this);
21973 handleDragableRowClick : function(grid, rowIndex, e)
21975 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21976 this.selectRow(rowIndex, false);
21977 grid.view.focusRow(rowIndex);
21978 this.fireEvent("afterselectionchange", this);
21983 * Selects multiple rows.
21984 * @param {Array} rows Array of the indexes of the row to select
21985 * @param {Boolean} keepExisting (optional) True to keep existing selections
21987 selectRows : function(rows, keepExisting){
21989 this.clearSelections();
21991 for(var i = 0, len = rows.length; i < len; i++){
21992 this.selectRow(rows[i], true);
21997 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21998 * @param {Number} startRow The index of the first row in the range
21999 * @param {Number} endRow The index of the last row in the range
22000 * @param {Boolean} keepExisting (optional) True to retain existing selections
22002 selectRange : function(startRow, endRow, keepExisting){
22007 this.clearSelections();
22009 if(startRow <= endRow){
22010 for(var i = startRow; i <= endRow; i++){
22011 this.selectRow(i, true);
22014 for(var i = startRow; i >= endRow; i--){
22015 this.selectRow(i, true);
22021 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22022 * @param {Number} startRow The index of the first row in the range
22023 * @param {Number} endRow The index of the last row in the range
22025 deselectRange : function(startRow, endRow, preventViewNotify){
22029 for(var i = startRow; i <= endRow; i++){
22030 this.deselectRow(i, preventViewNotify);
22036 * @param {Number} row The index of the row to select
22037 * @param {Boolean} keepExisting (optional) True to keep existing selections
22039 selectRow : function(index, keepExisting, preventViewNotify){
22040 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22043 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22044 if(!keepExisting || this.singleSelect){
22045 this.clearSelections();
22047 var r = this.grid.dataSource.getAt(index);
22048 this.selections.add(r);
22049 this.last = this.lastActive = index;
22050 if(!preventViewNotify){
22051 this.grid.getView().onRowSelect(index);
22053 this.fireEvent("rowselect", this, index, r);
22054 this.fireEvent("selectionchange", this);
22060 * @param {Number} row The index of the row to deselect
22062 deselectRow : function(index, preventViewNotify){
22066 if(this.last == index){
22069 if(this.lastActive == index){
22070 this.lastActive = false;
22072 var r = this.grid.dataSource.getAt(index);
22073 this.selections.remove(r);
22074 if(!preventViewNotify){
22075 this.grid.getView().onRowDeselect(index);
22077 this.fireEvent("rowdeselect", this, index);
22078 this.fireEvent("selectionchange", this);
22082 restoreLast : function(){
22084 this.last = this._last;
22089 acceptsNav : function(row, col, cm){
22090 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22094 onEditorKey : function(field, e){
22095 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22100 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22102 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22104 }else if(k == e.ENTER && !e.ctrlKey){
22108 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22110 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22112 }else if(k == e.ESC){
22116 g.startEditing(newCell[0], newCell[1]);
22121 * Ext JS Library 1.1.1
22122 * Copyright(c) 2006-2007, Ext JS, LLC.
22124 * Originally Released Under LGPL - original licence link has changed is not relivant.
22127 * <script type="text/javascript">
22131 * @class Roo.bootstrap.PagingToolbar
22132 * @extends Roo.bootstrap.NavSimplebar
22133 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22135 * Create a new PagingToolbar
22136 * @param {Object} config The config object
22137 * @param {Roo.data.Store} store
22139 Roo.bootstrap.PagingToolbar = function(config)
22141 // old args format still supported... - xtype is prefered..
22142 // created from xtype...
22144 this.ds = config.dataSource;
22146 if (config.store && !this.ds) {
22147 this.store= Roo.factory(config.store, Roo.data);
22148 this.ds = this.store;
22149 this.ds.xmodule = this.xmodule || false;
22152 this.toolbarItems = [];
22153 if (config.items) {
22154 this.toolbarItems = config.items;
22157 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22162 this.bind(this.ds);
22165 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22169 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22171 * @cfg {Roo.data.Store} dataSource
22172 * The underlying data store providing the paged data
22175 * @cfg {String/HTMLElement/Element} container
22176 * container The id or element that will contain the toolbar
22179 * @cfg {Boolean} displayInfo
22180 * True to display the displayMsg (defaults to false)
22183 * @cfg {Number} pageSize
22184 * The number of records to display per page (defaults to 20)
22188 * @cfg {String} displayMsg
22189 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22191 displayMsg : 'Displaying {0} - {1} of {2}',
22193 * @cfg {String} emptyMsg
22194 * The message to display when no records are found (defaults to "No data to display")
22196 emptyMsg : 'No data to display',
22198 * Customizable piece of the default paging text (defaults to "Page")
22201 beforePageText : "Page",
22203 * Customizable piece of the default paging text (defaults to "of %0")
22206 afterPageText : "of {0}",
22208 * Customizable piece of the default paging text (defaults to "First Page")
22211 firstText : "First Page",
22213 * Customizable piece of the default paging text (defaults to "Previous Page")
22216 prevText : "Previous Page",
22218 * Customizable piece of the default paging text (defaults to "Next Page")
22221 nextText : "Next Page",
22223 * Customizable piece of the default paging text (defaults to "Last Page")
22226 lastText : "Last Page",
22228 * Customizable piece of the default paging text (defaults to "Refresh")
22231 refreshText : "Refresh",
22235 onRender : function(ct, position)
22237 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22238 this.navgroup.parentId = this.id;
22239 this.navgroup.onRender(this.el, null);
22240 // add the buttons to the navgroup
22242 if(this.displayInfo){
22243 Roo.log(this.el.select('ul.navbar-nav',true).first());
22244 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22245 this.displayEl = this.el.select('.x-paging-info', true).first();
22246 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22247 // this.displayEl = navel.el.select('span',true).first();
22253 Roo.each(_this.buttons, function(e){ // this might need to use render????
22254 Roo.factory(e).onRender(_this.el, null);
22258 Roo.each(_this.toolbarItems, function(e) {
22259 _this.navgroup.addItem(e);
22263 this.first = this.navgroup.addItem({
22264 tooltip: this.firstText,
22266 icon : 'fa fa-backward',
22268 preventDefault: true,
22269 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22272 this.prev = this.navgroup.addItem({
22273 tooltip: this.prevText,
22275 icon : 'fa fa-step-backward',
22277 preventDefault: true,
22278 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22280 //this.addSeparator();
22283 var field = this.navgroup.addItem( {
22285 cls : 'x-paging-position',
22287 html : this.beforePageText +
22288 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22289 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22292 this.field = field.el.select('input', true).first();
22293 this.field.on("keydown", this.onPagingKeydown, this);
22294 this.field.on("focus", function(){this.dom.select();});
22297 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22298 //this.field.setHeight(18);
22299 //this.addSeparator();
22300 this.next = this.navgroup.addItem({
22301 tooltip: this.nextText,
22303 html : ' <i class="fa fa-step-forward">',
22305 preventDefault: true,
22306 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22308 this.last = this.navgroup.addItem({
22309 tooltip: this.lastText,
22310 icon : 'fa fa-forward',
22313 preventDefault: true,
22314 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22316 //this.addSeparator();
22317 this.loading = this.navgroup.addItem({
22318 tooltip: this.refreshText,
22319 icon: 'fa fa-refresh',
22320 preventDefault: true,
22321 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22327 updateInfo : function(){
22328 if(this.displayEl){
22329 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22330 var msg = count == 0 ?
22334 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22336 this.displayEl.update(msg);
22341 onLoad : function(ds, r, o){
22342 this.cursor = o.params ? o.params.start : 0;
22343 var d = this.getPageData(),
22347 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22348 this.field.dom.value = ap;
22349 this.first.setDisabled(ap == 1);
22350 this.prev.setDisabled(ap == 1);
22351 this.next.setDisabled(ap == ps);
22352 this.last.setDisabled(ap == ps);
22353 this.loading.enable();
22358 getPageData : function(){
22359 var total = this.ds.getTotalCount();
22362 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22363 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22368 onLoadError : function(){
22369 this.loading.enable();
22373 onPagingKeydown : function(e){
22374 var k = e.getKey();
22375 var d = this.getPageData();
22377 var v = this.field.dom.value, pageNum;
22378 if(!v || isNaN(pageNum = parseInt(v, 10))){
22379 this.field.dom.value = d.activePage;
22382 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22383 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22386 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))
22388 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22389 this.field.dom.value = pageNum;
22390 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22393 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22395 var v = this.field.dom.value, pageNum;
22396 var increment = (e.shiftKey) ? 10 : 1;
22397 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22400 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22401 this.field.dom.value = d.activePage;
22404 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22406 this.field.dom.value = parseInt(v, 10) + increment;
22407 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22408 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22415 beforeLoad : function(){
22417 this.loading.disable();
22422 onClick : function(which){
22431 ds.load({params:{start: 0, limit: this.pageSize}});
22434 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22437 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22440 var total = ds.getTotalCount();
22441 var extra = total % this.pageSize;
22442 var lastStart = extra ? (total - extra) : total-this.pageSize;
22443 ds.load({params:{start: lastStart, limit: this.pageSize}});
22446 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22452 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22453 * @param {Roo.data.Store} store The data store to unbind
22455 unbind : function(ds){
22456 ds.un("beforeload", this.beforeLoad, this);
22457 ds.un("load", this.onLoad, this);
22458 ds.un("loadexception", this.onLoadError, this);
22459 ds.un("remove", this.updateInfo, this);
22460 ds.un("add", this.updateInfo, this);
22461 this.ds = undefined;
22465 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22466 * @param {Roo.data.Store} store The data store to bind
22468 bind : function(ds){
22469 ds.on("beforeload", this.beforeLoad, this);
22470 ds.on("load", this.onLoad, this);
22471 ds.on("loadexception", this.onLoadError, this);
22472 ds.on("remove", this.updateInfo, this);
22473 ds.on("add", this.updateInfo, this);
22484 * @class Roo.bootstrap.MessageBar
22485 * @extends Roo.bootstrap.Component
22486 * Bootstrap MessageBar class
22487 * @cfg {String} html contents of the MessageBar
22488 * @cfg {String} weight (info | success | warning | danger) default info
22489 * @cfg {String} beforeClass insert the bar before the given class
22490 * @cfg {Boolean} closable (true | false) default false
22491 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22494 * Create a new Element
22495 * @param {Object} config The config object
22498 Roo.bootstrap.MessageBar = function(config){
22499 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22502 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22508 beforeClass: 'bootstrap-sticky-wrap',
22510 getAutoCreate : function(){
22514 cls: 'alert alert-dismissable alert-' + this.weight,
22519 html: this.html || ''
22525 cfg.cls += ' alert-messages-fixed';
22539 onRender : function(ct, position)
22541 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22544 var cfg = Roo.apply({}, this.getAutoCreate());
22548 cfg.cls += ' ' + this.cls;
22551 cfg.style = this.style;
22553 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22555 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22558 this.el.select('>button.close').on('click', this.hide, this);
22564 if (!this.rendered) {
22570 this.fireEvent('show', this);
22576 if (!this.rendered) {
22582 this.fireEvent('hide', this);
22585 update : function()
22587 // var e = this.el.dom.firstChild;
22589 // if(this.closable){
22590 // e = e.nextSibling;
22593 // e.data = this.html || '';
22595 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22611 * @class Roo.bootstrap.Graph
22612 * @extends Roo.bootstrap.Component
22613 * Bootstrap Graph class
22617 @cfg {String} graphtype bar | vbar | pie
22618 @cfg {number} g_x coodinator | centre x (pie)
22619 @cfg {number} g_y coodinator | centre y (pie)
22620 @cfg {number} g_r radius (pie)
22621 @cfg {number} g_height height of the chart (respected by all elements in the set)
22622 @cfg {number} g_width width of the chart (respected by all elements in the set)
22623 @cfg {Object} title The title of the chart
22626 -opts (object) options for the chart
22628 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22629 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22631 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.
22632 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22634 o stretch (boolean)
22636 -opts (object) options for the pie
22639 o startAngle (number)
22640 o endAngle (number)
22644 * Create a new Input
22645 * @param {Object} config The config object
22648 Roo.bootstrap.Graph = function(config){
22649 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22655 * The img click event for the img.
22656 * @param {Roo.EventObject} e
22662 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22673 //g_colors: this.colors,
22680 getAutoCreate : function(){
22691 onRender : function(ct,position){
22692 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22693 this.raphael = Raphael(this.el.dom);
22695 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22696 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22697 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22698 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22700 r.text(160, 10, "Single Series Chart").attr(txtattr);
22701 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22702 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22703 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22705 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22706 r.barchart(330, 10, 300, 220, data1);
22707 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22708 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22711 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22712 // r.barchart(30, 30, 560, 250, xdata, {
22713 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22714 // axis : "0 0 1 1",
22715 // axisxlabels : xdata
22716 // //yvalues : cols,
22719 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22721 // this.load(null,xdata,{
22722 // axis : "0 0 1 1",
22723 // axisxlabels : xdata
22728 load : function(graphtype,xdata,opts){
22729 this.raphael.clear();
22731 graphtype = this.graphtype;
22736 var r = this.raphael,
22737 fin = function () {
22738 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22740 fout = function () {
22741 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22743 pfin = function() {
22744 this.sector.stop();
22745 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22748 this.label[0].stop();
22749 this.label[0].attr({ r: 7.5 });
22750 this.label[1].attr({ "font-weight": 800 });
22753 pfout = function() {
22754 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22757 this.label[0].animate({ r: 5 }, 500, "bounce");
22758 this.label[1].attr({ "font-weight": 400 });
22764 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22767 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22770 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22771 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22773 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22780 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22785 setTitle: function(o)
22790 initEvents: function() {
22793 this.el.on('click', this.onClick, this);
22797 onClick : function(e)
22799 Roo.log('img onclick');
22800 this.fireEvent('click', this, e);
22812 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22815 * @class Roo.bootstrap.dash.NumberBox
22816 * @extends Roo.bootstrap.Component
22817 * Bootstrap NumberBox class
22818 * @cfg {String} headline Box headline
22819 * @cfg {String} content Box content
22820 * @cfg {String} icon Box icon
22821 * @cfg {String} footer Footer text
22822 * @cfg {String} fhref Footer href
22825 * Create a new NumberBox
22826 * @param {Object} config The config object
22830 Roo.bootstrap.dash.NumberBox = function(config){
22831 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22835 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22844 getAutoCreate : function(){
22848 cls : 'small-box ',
22856 cls : 'roo-headline',
22857 html : this.headline
22861 cls : 'roo-content',
22862 html : this.content
22876 cls : 'ion ' + this.icon
22885 cls : 'small-box-footer',
22886 href : this.fhref || '#',
22890 cfg.cn.push(footer);
22897 onRender : function(ct,position){
22898 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22905 setHeadline: function (value)
22907 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22910 setFooter: function (value, href)
22912 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22915 this.el.select('a.small-box-footer',true).first().attr('href', href);
22920 setContent: function (value)
22922 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22925 initEvents: function()
22939 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22942 * @class Roo.bootstrap.dash.TabBox
22943 * @extends Roo.bootstrap.Component
22944 * Bootstrap TabBox class
22945 * @cfg {String} title Title of the TabBox
22946 * @cfg {String} icon Icon of the TabBox
22947 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22948 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22951 * Create a new TabBox
22952 * @param {Object} config The config object
22956 Roo.bootstrap.dash.TabBox = function(config){
22957 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22962 * When a pane is added
22963 * @param {Roo.bootstrap.dash.TabPane} pane
22967 * @event activatepane
22968 * When a pane is activated
22969 * @param {Roo.bootstrap.dash.TabPane} pane
22971 "activatepane" : true
22979 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22984 tabScrollable : false,
22986 getChildContainer : function()
22988 return this.el.select('.tab-content', true).first();
22991 getAutoCreate : function(){
22995 cls: 'pull-left header',
23003 cls: 'fa ' + this.icon
23009 cls: 'nav nav-tabs pull-right',
23015 if(this.tabScrollable){
23022 cls: 'nav nav-tabs pull-right',
23033 cls: 'nav-tabs-custom',
23038 cls: 'tab-content no-padding',
23046 initEvents : function()
23048 //Roo.log('add add pane handler');
23049 this.on('addpane', this.onAddPane, this);
23052 * Updates the box title
23053 * @param {String} html to set the title to.
23055 setTitle : function(value)
23057 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23059 onAddPane : function(pane)
23061 this.panes.push(pane);
23062 //Roo.log('addpane');
23064 // tabs are rendere left to right..
23065 if(!this.showtabs){
23069 var ctr = this.el.select('.nav-tabs', true).first();
23072 var existing = ctr.select('.nav-tab',true);
23073 var qty = existing.getCount();;
23076 var tab = ctr.createChild({
23078 cls : 'nav-tab' + (qty ? '' : ' active'),
23086 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23089 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23091 pane.el.addClass('active');
23096 onTabClick : function(ev,un,ob,pane)
23098 //Roo.log('tab - prev default');
23099 ev.preventDefault();
23102 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23103 pane.tab.addClass('active');
23104 //Roo.log(pane.title);
23105 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23106 // technically we should have a deactivate event.. but maybe add later.
23107 // and it should not de-activate the selected tab...
23108 this.fireEvent('activatepane', pane);
23109 pane.el.addClass('active');
23110 pane.fireEvent('activate');
23115 getActivePane : function()
23118 Roo.each(this.panes, function(p) {
23119 if(p.el.hasClass('active')){
23140 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23142 * @class Roo.bootstrap.TabPane
23143 * @extends Roo.bootstrap.Component
23144 * Bootstrap TabPane class
23145 * @cfg {Boolean} active (false | true) Default false
23146 * @cfg {String} title title of panel
23150 * Create a new TabPane
23151 * @param {Object} config The config object
23154 Roo.bootstrap.dash.TabPane = function(config){
23155 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23161 * When a pane is activated
23162 * @param {Roo.bootstrap.dash.TabPane} pane
23169 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23174 // the tabBox that this is attached to.
23177 getAutoCreate : function()
23185 cfg.cls += ' active';
23190 initEvents : function()
23192 //Roo.log('trigger add pane handler');
23193 this.parent().fireEvent('addpane', this)
23197 * Updates the tab title
23198 * @param {String} html to set the title to.
23200 setTitle: function(str)
23206 this.tab.select('a', true).first().dom.innerHTML = str;
23223 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23226 * @class Roo.bootstrap.menu.Menu
23227 * @extends Roo.bootstrap.Component
23228 * Bootstrap Menu class - container for Menu
23229 * @cfg {String} html Text of the menu
23230 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23231 * @cfg {String} icon Font awesome icon
23232 * @cfg {String} pos Menu align to (top | bottom) default bottom
23236 * Create a new Menu
23237 * @param {Object} config The config object
23241 Roo.bootstrap.menu.Menu = function(config){
23242 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23246 * @event beforeshow
23247 * Fires before this menu is displayed
23248 * @param {Roo.bootstrap.menu.Menu} this
23252 * @event beforehide
23253 * Fires before this menu is hidden
23254 * @param {Roo.bootstrap.menu.Menu} this
23259 * Fires after this menu is displayed
23260 * @param {Roo.bootstrap.menu.Menu} this
23265 * Fires after this menu is hidden
23266 * @param {Roo.bootstrap.menu.Menu} this
23271 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23272 * @param {Roo.bootstrap.menu.Menu} this
23273 * @param {Roo.EventObject} e
23280 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23284 weight : 'default',
23289 getChildContainer : function() {
23290 if(this.isSubMenu){
23294 return this.el.select('ul.dropdown-menu', true).first();
23297 getAutoCreate : function()
23302 cls : 'roo-menu-text',
23310 cls : 'fa ' + this.icon
23321 cls : 'dropdown-button btn btn-' + this.weight,
23326 cls : 'dropdown-toggle btn btn-' + this.weight,
23336 cls : 'dropdown-menu'
23342 if(this.pos == 'top'){
23343 cfg.cls += ' dropup';
23346 if(this.isSubMenu){
23349 cls : 'dropdown-menu'
23356 onRender : function(ct, position)
23358 this.isSubMenu = ct.hasClass('dropdown-submenu');
23360 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23363 initEvents : function()
23365 if(this.isSubMenu){
23369 this.hidden = true;
23371 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23372 this.triggerEl.on('click', this.onTriggerPress, this);
23374 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23375 this.buttonEl.on('click', this.onClick, this);
23381 if(this.isSubMenu){
23385 return this.el.select('ul.dropdown-menu', true).first();
23388 onClick : function(e)
23390 this.fireEvent("click", this, e);
23393 onTriggerPress : function(e)
23395 if (this.isVisible()) {
23402 isVisible : function(){
23403 return !this.hidden;
23408 this.fireEvent("beforeshow", this);
23410 this.hidden = false;
23411 this.el.addClass('open');
23413 Roo.get(document).on("mouseup", this.onMouseUp, this);
23415 this.fireEvent("show", this);
23422 this.fireEvent("beforehide", this);
23424 this.hidden = true;
23425 this.el.removeClass('open');
23427 Roo.get(document).un("mouseup", this.onMouseUp);
23429 this.fireEvent("hide", this);
23432 onMouseUp : function()
23446 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23449 * @class Roo.bootstrap.menu.Item
23450 * @extends Roo.bootstrap.Component
23451 * Bootstrap MenuItem class
23452 * @cfg {Boolean} submenu (true | false) default false
23453 * @cfg {String} html text of the item
23454 * @cfg {String} href the link
23455 * @cfg {Boolean} disable (true | false) default false
23456 * @cfg {Boolean} preventDefault (true | false) default true
23457 * @cfg {String} icon Font awesome icon
23458 * @cfg {String} pos Submenu align to (left | right) default right
23462 * Create a new Item
23463 * @param {Object} config The config object
23467 Roo.bootstrap.menu.Item = function(config){
23468 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23472 * Fires when the mouse is hovering over this menu
23473 * @param {Roo.bootstrap.menu.Item} this
23474 * @param {Roo.EventObject} e
23479 * Fires when the mouse exits this menu
23480 * @param {Roo.bootstrap.menu.Item} this
23481 * @param {Roo.EventObject} e
23487 * The raw click event for the entire grid.
23488 * @param {Roo.EventObject} e
23494 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23499 preventDefault: true,
23504 getAutoCreate : function()
23509 cls : 'roo-menu-item-text',
23517 cls : 'fa ' + this.icon
23526 href : this.href || '#',
23533 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23537 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23539 if(this.pos == 'left'){
23540 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23547 initEvents : function()
23549 this.el.on('mouseover', this.onMouseOver, this);
23550 this.el.on('mouseout', this.onMouseOut, this);
23552 this.el.select('a', true).first().on('click', this.onClick, this);
23556 onClick : function(e)
23558 if(this.preventDefault){
23559 e.preventDefault();
23562 this.fireEvent("click", this, e);
23565 onMouseOver : function(e)
23567 if(this.submenu && this.pos == 'left'){
23568 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23571 this.fireEvent("mouseover", this, e);
23574 onMouseOut : function(e)
23576 this.fireEvent("mouseout", this, e);
23588 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23591 * @class Roo.bootstrap.menu.Separator
23592 * @extends Roo.bootstrap.Component
23593 * Bootstrap Separator class
23596 * Create a new Separator
23597 * @param {Object} config The config object
23601 Roo.bootstrap.menu.Separator = function(config){
23602 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23605 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23607 getAutoCreate : function(){
23628 * @class Roo.bootstrap.Tooltip
23629 * Bootstrap Tooltip class
23630 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23631 * to determine which dom element triggers the tooltip.
23633 * It needs to add support for additional attributes like tooltip-position
23636 * Create a new Toolti
23637 * @param {Object} config The config object
23640 Roo.bootstrap.Tooltip = function(config){
23641 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23644 Roo.apply(Roo.bootstrap.Tooltip, {
23646 * @function init initialize tooltip monitoring.
23650 currentTip : false,
23651 currentRegion : false,
23657 Roo.get(document).on('mouseover', this.enter ,this);
23658 Roo.get(document).on('mouseout', this.leave, this);
23661 this.currentTip = new Roo.bootstrap.Tooltip();
23664 enter : function(ev)
23666 var dom = ev.getTarget();
23668 //Roo.log(['enter',dom]);
23669 var el = Roo.fly(dom);
23670 if (this.currentEl) {
23672 //Roo.log(this.currentEl);
23673 //Roo.log(this.currentEl.contains(dom));
23674 if (this.currentEl == el) {
23677 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23683 if (this.currentTip.el) {
23684 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23689 // you can not look for children, as if el is the body.. then everythign is the child..
23690 if (!el.attr('tooltip')) { //
23691 if (!el.select("[tooltip]").elements.length) {
23694 // is the mouse over this child...?
23695 bindEl = el.select("[tooltip]").first();
23696 var xy = ev.getXY();
23697 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23698 //Roo.log("not in region.");
23701 //Roo.log("child element over..");
23704 this.currentEl = bindEl;
23705 this.currentTip.bind(bindEl);
23706 this.currentRegion = Roo.lib.Region.getRegion(dom);
23707 this.currentTip.enter();
23710 leave : function(ev)
23712 var dom = ev.getTarget();
23713 //Roo.log(['leave',dom]);
23714 if (!this.currentEl) {
23719 if (dom != this.currentEl.dom) {
23722 var xy = ev.getXY();
23723 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23726 // only activate leave if mouse cursor is outside... bounding box..
23731 if (this.currentTip) {
23732 this.currentTip.leave();
23734 //Roo.log('clear currentEl');
23735 this.currentEl = false;
23740 'left' : ['r-l', [-2,0], 'right'],
23741 'right' : ['l-r', [2,0], 'left'],
23742 'bottom' : ['t-b', [0,2], 'top'],
23743 'top' : [ 'b-t', [0,-2], 'bottom']
23749 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23754 delay : null, // can be { show : 300 , hide: 500}
23758 hoverState : null, //???
23760 placement : 'bottom',
23762 getAutoCreate : function(){
23769 cls : 'tooltip-arrow'
23772 cls : 'tooltip-inner'
23779 bind : function(el)
23785 enter : function () {
23787 if (this.timeout != null) {
23788 clearTimeout(this.timeout);
23791 this.hoverState = 'in';
23792 //Roo.log("enter - show");
23793 if (!this.delay || !this.delay.show) {
23798 this.timeout = setTimeout(function () {
23799 if (_t.hoverState == 'in') {
23802 }, this.delay.show);
23806 clearTimeout(this.timeout);
23808 this.hoverState = 'out';
23809 if (!this.delay || !this.delay.hide) {
23815 this.timeout = setTimeout(function () {
23816 //Roo.log("leave - timeout");
23818 if (_t.hoverState == 'out') {
23820 Roo.bootstrap.Tooltip.currentEl = false;
23828 this.render(document.body);
23831 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23833 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23835 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23837 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23839 var placement = typeof this.placement == 'function' ?
23840 this.placement.call(this, this.el, on_el) :
23843 var autoToken = /\s?auto?\s?/i;
23844 var autoPlace = autoToken.test(placement);
23846 placement = placement.replace(autoToken, '') || 'top';
23850 //this.el.setXY([0,0]);
23852 //this.el.dom.style.display='block';
23854 //this.el.appendTo(on_el);
23856 var p = this.getPosition();
23857 var box = this.el.getBox();
23863 var align = Roo.bootstrap.Tooltip.alignment[placement];
23865 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23867 if(placement == 'top' || placement == 'bottom'){
23869 placement = 'right';
23872 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23873 placement = 'left';
23877 align = Roo.bootstrap.Tooltip.alignment[placement];
23879 this.el.alignTo(this.bindEl, align[0],align[1]);
23880 //var arrow = this.el.select('.arrow',true).first();
23881 //arrow.set(align[2],
23883 this.el.addClass(placement);
23885 this.el.addClass('in fade');
23887 this.hoverState = null;
23889 if (this.el.hasClass('fade')) {
23900 //this.el.setXY([0,0]);
23901 this.el.removeClass('in');
23917 * @class Roo.bootstrap.LocationPicker
23918 * @extends Roo.bootstrap.Component
23919 * Bootstrap LocationPicker class
23920 * @cfg {Number} latitude Position when init default 0
23921 * @cfg {Number} longitude Position when init default 0
23922 * @cfg {Number} zoom default 15
23923 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23924 * @cfg {Boolean} mapTypeControl default false
23925 * @cfg {Boolean} disableDoubleClickZoom default false
23926 * @cfg {Boolean} scrollwheel default true
23927 * @cfg {Boolean} streetViewControl default false
23928 * @cfg {Number} radius default 0
23929 * @cfg {String} locationName
23930 * @cfg {Boolean} draggable default true
23931 * @cfg {Boolean} enableAutocomplete default false
23932 * @cfg {Boolean} enableReverseGeocode default true
23933 * @cfg {String} markerTitle
23936 * Create a new LocationPicker
23937 * @param {Object} config The config object
23941 Roo.bootstrap.LocationPicker = function(config){
23943 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23948 * Fires when the picker initialized.
23949 * @param {Roo.bootstrap.LocationPicker} this
23950 * @param {Google Location} location
23954 * @event positionchanged
23955 * Fires when the picker position changed.
23956 * @param {Roo.bootstrap.LocationPicker} this
23957 * @param {Google Location} location
23959 positionchanged : true,
23962 * Fires when the map resize.
23963 * @param {Roo.bootstrap.LocationPicker} this
23968 * Fires when the map show.
23969 * @param {Roo.bootstrap.LocationPicker} this
23974 * Fires when the map hide.
23975 * @param {Roo.bootstrap.LocationPicker} this
23980 * Fires when click the map.
23981 * @param {Roo.bootstrap.LocationPicker} this
23982 * @param {Map event} e
23986 * @event mapRightClick
23987 * Fires when right click the map.
23988 * @param {Roo.bootstrap.LocationPicker} this
23989 * @param {Map event} e
23991 mapRightClick : true,
23993 * @event markerClick
23994 * Fires when click the marker.
23995 * @param {Roo.bootstrap.LocationPicker} this
23996 * @param {Map event} e
23998 markerClick : true,
24000 * @event markerRightClick
24001 * Fires when right click the marker.
24002 * @param {Roo.bootstrap.LocationPicker} this
24003 * @param {Map event} e
24005 markerRightClick : true,
24007 * @event OverlayViewDraw
24008 * Fires when OverlayView Draw
24009 * @param {Roo.bootstrap.LocationPicker} this
24011 OverlayViewDraw : true,
24013 * @event OverlayViewOnAdd
24014 * Fires when OverlayView Draw
24015 * @param {Roo.bootstrap.LocationPicker} this
24017 OverlayViewOnAdd : true,
24019 * @event OverlayViewOnRemove
24020 * Fires when OverlayView Draw
24021 * @param {Roo.bootstrap.LocationPicker} this
24023 OverlayViewOnRemove : true,
24025 * @event OverlayViewShow
24026 * Fires when OverlayView Draw
24027 * @param {Roo.bootstrap.LocationPicker} this
24028 * @param {Pixel} cpx
24030 OverlayViewShow : true,
24032 * @event OverlayViewHide
24033 * Fires when OverlayView Draw
24034 * @param {Roo.bootstrap.LocationPicker} this
24036 OverlayViewHide : true,
24038 * @event loadexception
24039 * Fires when load google lib failed.
24040 * @param {Roo.bootstrap.LocationPicker} this
24042 loadexception : true
24047 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24049 gMapContext: false,
24055 mapTypeControl: false,
24056 disableDoubleClickZoom: false,
24058 streetViewControl: false,
24062 enableAutocomplete: false,
24063 enableReverseGeocode: true,
24066 getAutoCreate: function()
24071 cls: 'roo-location-picker'
24077 initEvents: function(ct, position)
24079 if(!this.el.getWidth() || this.isApplied()){
24083 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24088 initial: function()
24090 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24091 this.fireEvent('loadexception', this);
24095 if(!this.mapTypeId){
24096 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24099 this.gMapContext = this.GMapContext();
24101 this.initOverlayView();
24103 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24107 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24108 _this.setPosition(_this.gMapContext.marker.position);
24111 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24112 _this.fireEvent('mapClick', this, event);
24116 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24117 _this.fireEvent('mapRightClick', this, event);
24121 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24122 _this.fireEvent('markerClick', this, event);
24126 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24127 _this.fireEvent('markerRightClick', this, event);
24131 this.setPosition(this.gMapContext.location);
24133 this.fireEvent('initial', this, this.gMapContext.location);
24136 initOverlayView: function()
24140 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24144 _this.fireEvent('OverlayViewDraw', _this);
24149 _this.fireEvent('OverlayViewOnAdd', _this);
24152 onRemove: function()
24154 _this.fireEvent('OverlayViewOnRemove', _this);
24157 show: function(cpx)
24159 _this.fireEvent('OverlayViewShow', _this, cpx);
24164 _this.fireEvent('OverlayViewHide', _this);
24170 fromLatLngToContainerPixel: function(event)
24172 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24175 isApplied: function()
24177 return this.getGmapContext() == false ? false : true;
24180 getGmapContext: function()
24182 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24185 GMapContext: function()
24187 var position = new google.maps.LatLng(this.latitude, this.longitude);
24189 var _map = new google.maps.Map(this.el.dom, {
24192 mapTypeId: this.mapTypeId,
24193 mapTypeControl: this.mapTypeControl,
24194 disableDoubleClickZoom: this.disableDoubleClickZoom,
24195 scrollwheel: this.scrollwheel,
24196 streetViewControl: this.streetViewControl,
24197 locationName: this.locationName,
24198 draggable: this.draggable,
24199 enableAutocomplete: this.enableAutocomplete,
24200 enableReverseGeocode: this.enableReverseGeocode
24203 var _marker = new google.maps.Marker({
24204 position: position,
24206 title: this.markerTitle,
24207 draggable: this.draggable
24214 location: position,
24215 radius: this.radius,
24216 locationName: this.locationName,
24217 addressComponents: {
24218 formatted_address: null,
24219 addressLine1: null,
24220 addressLine2: null,
24222 streetNumber: null,
24226 stateOrProvince: null
24229 domContainer: this.el.dom,
24230 geodecoder: new google.maps.Geocoder()
24234 drawCircle: function(center, radius, options)
24236 if (this.gMapContext.circle != null) {
24237 this.gMapContext.circle.setMap(null);
24241 options = Roo.apply({}, options, {
24242 strokeColor: "#0000FF",
24243 strokeOpacity: .35,
24245 fillColor: "#0000FF",
24249 options.map = this.gMapContext.map;
24250 options.radius = radius;
24251 options.center = center;
24252 this.gMapContext.circle = new google.maps.Circle(options);
24253 return this.gMapContext.circle;
24259 setPosition: function(location)
24261 this.gMapContext.location = location;
24262 this.gMapContext.marker.setPosition(location);
24263 this.gMapContext.map.panTo(location);
24264 this.drawCircle(location, this.gMapContext.radius, {});
24268 if (this.gMapContext.settings.enableReverseGeocode) {
24269 this.gMapContext.geodecoder.geocode({
24270 latLng: this.gMapContext.location
24271 }, function(results, status) {
24273 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24274 _this.gMapContext.locationName = results[0].formatted_address;
24275 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24277 _this.fireEvent('positionchanged', this, location);
24284 this.fireEvent('positionchanged', this, location);
24289 google.maps.event.trigger(this.gMapContext.map, "resize");
24291 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24293 this.fireEvent('resize', this);
24296 setPositionByLatLng: function(latitude, longitude)
24298 this.setPosition(new google.maps.LatLng(latitude, longitude));
24301 getCurrentPosition: function()
24304 latitude: this.gMapContext.location.lat(),
24305 longitude: this.gMapContext.location.lng()
24309 getAddressName: function()
24311 return this.gMapContext.locationName;
24314 getAddressComponents: function()
24316 return this.gMapContext.addressComponents;
24319 address_component_from_google_geocode: function(address_components)
24323 for (var i = 0; i < address_components.length; i++) {
24324 var component = address_components[i];
24325 if (component.types.indexOf("postal_code") >= 0) {
24326 result.postalCode = component.short_name;
24327 } else if (component.types.indexOf("street_number") >= 0) {
24328 result.streetNumber = component.short_name;
24329 } else if (component.types.indexOf("route") >= 0) {
24330 result.streetName = component.short_name;
24331 } else if (component.types.indexOf("neighborhood") >= 0) {
24332 result.city = component.short_name;
24333 } else if (component.types.indexOf("locality") >= 0) {
24334 result.city = component.short_name;
24335 } else if (component.types.indexOf("sublocality") >= 0) {
24336 result.district = component.short_name;
24337 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24338 result.stateOrProvince = component.short_name;
24339 } else if (component.types.indexOf("country") >= 0) {
24340 result.country = component.short_name;
24344 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24345 result.addressLine2 = "";
24349 setZoomLevel: function(zoom)
24351 this.gMapContext.map.setZoom(zoom);
24364 this.fireEvent('show', this);
24375 this.fireEvent('hide', this);
24380 Roo.apply(Roo.bootstrap.LocationPicker, {
24382 OverlayView : function(map, options)
24384 options = options || {};
24398 * @class Roo.bootstrap.Alert
24399 * @extends Roo.bootstrap.Component
24400 * Bootstrap Alert class
24401 * @cfg {String} title The title of alert
24402 * @cfg {String} html The content of alert
24403 * @cfg {String} weight ( success | info | warning | danger )
24404 * @cfg {String} faicon font-awesomeicon
24407 * Create a new alert
24408 * @param {Object} config The config object
24412 Roo.bootstrap.Alert = function(config){
24413 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24417 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24424 getAutoCreate : function()
24433 cls : 'roo-alert-icon'
24438 cls : 'roo-alert-title',
24443 cls : 'roo-alert-text',
24450 cfg.cn[0].cls += ' fa ' + this.faicon;
24454 cfg.cls += ' alert-' + this.weight;
24460 initEvents: function()
24462 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24465 setTitle : function(str)
24467 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24470 setText : function(str)
24472 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24475 setWeight : function(weight)
24478 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24481 this.weight = weight;
24483 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24486 setIcon : function(icon)
24489 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24492 this.faicon = icon;
24494 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24515 * @class Roo.bootstrap.UploadCropbox
24516 * @extends Roo.bootstrap.Component
24517 * Bootstrap UploadCropbox class
24518 * @cfg {String} emptyText show when image has been loaded
24519 * @cfg {String} rotateNotify show when image too small to rotate
24520 * @cfg {Number} errorTimeout default 3000
24521 * @cfg {Number} minWidth default 300
24522 * @cfg {Number} minHeight default 300
24523 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24524 * @cfg {Boolean} isDocument (true|false) default false
24525 * @cfg {String} url action url
24526 * @cfg {String} paramName default 'imageUpload'
24527 * @cfg {String} method default POST
24528 * @cfg {Boolean} loadMask (true|false) default true
24529 * @cfg {Boolean} loadingText default 'Loading...'
24532 * Create a new UploadCropbox
24533 * @param {Object} config The config object
24536 Roo.bootstrap.UploadCropbox = function(config){
24537 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24541 * @event beforeselectfile
24542 * Fire before select file
24543 * @param {Roo.bootstrap.UploadCropbox} this
24545 "beforeselectfile" : true,
24548 * Fire after initEvent
24549 * @param {Roo.bootstrap.UploadCropbox} this
24554 * Fire after initEvent
24555 * @param {Roo.bootstrap.UploadCropbox} this
24556 * @param {String} data
24561 * Fire when preparing the file data
24562 * @param {Roo.bootstrap.UploadCropbox} this
24563 * @param {Object} file
24568 * Fire when get exception
24569 * @param {Roo.bootstrap.UploadCropbox} this
24570 * @param {XMLHttpRequest} xhr
24572 "exception" : true,
24574 * @event beforeloadcanvas
24575 * Fire before load the canvas
24576 * @param {Roo.bootstrap.UploadCropbox} this
24577 * @param {String} src
24579 "beforeloadcanvas" : true,
24582 * Fire when trash image
24583 * @param {Roo.bootstrap.UploadCropbox} this
24588 * Fire when download the image
24589 * @param {Roo.bootstrap.UploadCropbox} this
24593 * @event footerbuttonclick
24594 * Fire when footerbuttonclick
24595 * @param {Roo.bootstrap.UploadCropbox} this
24596 * @param {String} type
24598 "footerbuttonclick" : true,
24602 * @param {Roo.bootstrap.UploadCropbox} this
24607 * Fire when rotate the image
24608 * @param {Roo.bootstrap.UploadCropbox} this
24609 * @param {String} pos
24614 * Fire when inspect the file
24615 * @param {Roo.bootstrap.UploadCropbox} this
24616 * @param {Object} file
24621 * Fire when xhr upload the file
24622 * @param {Roo.bootstrap.UploadCropbox} this
24623 * @param {Object} data
24628 * Fire when arrange the file data
24629 * @param {Roo.bootstrap.UploadCropbox} this
24630 * @param {Object} formData
24635 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24638 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24640 emptyText : 'Click to upload image',
24641 rotateNotify : 'Image is too small to rotate',
24642 errorTimeout : 3000,
24656 cropType : 'image/jpeg',
24658 canvasLoaded : false,
24659 isDocument : false,
24661 paramName : 'imageUpload',
24663 loadingText : 'Loading...',
24666 getAutoCreate : function()
24670 cls : 'roo-upload-cropbox',
24674 cls : 'roo-upload-cropbox-selector',
24679 cls : 'roo-upload-cropbox-body',
24680 style : 'cursor:pointer',
24684 cls : 'roo-upload-cropbox-preview'
24688 cls : 'roo-upload-cropbox-thumb'
24692 cls : 'roo-upload-cropbox-empty-notify',
24693 html : this.emptyText
24697 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24698 html : this.rotateNotify
24704 cls : 'roo-upload-cropbox-footer',
24707 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24717 onRender : function(ct, position)
24719 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24721 if (this.buttons.length) {
24723 Roo.each(this.buttons, function(bb) {
24725 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24727 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24733 this.maskEl = this.el;
24737 initEvents : function()
24739 this.urlAPI = (window.createObjectURL && window) ||
24740 (window.URL && URL.revokeObjectURL && URL) ||
24741 (window.webkitURL && webkitURL);
24743 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24744 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24746 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24747 this.selectorEl.hide();
24749 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24750 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24752 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24753 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24754 this.thumbEl.hide();
24756 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24757 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24759 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24760 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24761 this.errorEl.hide();
24763 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24764 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24765 this.footerEl.hide();
24767 this.setThumbBoxSize();
24773 this.fireEvent('initial', this);
24780 window.addEventListener("resize", function() { _this.resize(); } );
24782 this.bodyEl.on('click', this.beforeSelectFile, this);
24785 this.bodyEl.on('touchstart', this.onTouchStart, this);
24786 this.bodyEl.on('touchmove', this.onTouchMove, this);
24787 this.bodyEl.on('touchend', this.onTouchEnd, this);
24791 this.bodyEl.on('mousedown', this.onMouseDown, this);
24792 this.bodyEl.on('mousemove', this.onMouseMove, this);
24793 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24794 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24795 Roo.get(document).on('mouseup', this.onMouseUp, this);
24798 this.selectorEl.on('change', this.onFileSelected, this);
24804 this.baseScale = 1;
24806 this.baseRotate = 1;
24807 this.dragable = false;
24808 this.pinching = false;
24811 this.cropData = false;
24812 this.notifyEl.dom.innerHTML = this.emptyText;
24814 this.selectorEl.dom.value = '';
24818 resize : function()
24820 if(this.fireEvent('resize', this) != false){
24821 this.setThumbBoxPosition();
24822 this.setCanvasPosition();
24826 onFooterButtonClick : function(e, el, o, type)
24829 case 'rotate-left' :
24830 this.onRotateLeft(e);
24832 case 'rotate-right' :
24833 this.onRotateRight(e);
24836 this.beforeSelectFile(e);
24851 this.fireEvent('footerbuttonclick', this, type);
24854 beforeSelectFile : function(e)
24856 e.preventDefault();
24858 if(this.fireEvent('beforeselectfile', this) != false){
24859 this.selectorEl.dom.click();
24863 onFileSelected : function(e)
24865 e.preventDefault();
24867 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24871 var file = this.selectorEl.dom.files[0];
24873 if(this.fireEvent('inspect', this, file) != false){
24874 this.prepare(file);
24879 trash : function(e)
24881 this.fireEvent('trash', this);
24884 download : function(e)
24886 this.fireEvent('download', this);
24889 loadCanvas : function(src)
24891 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24895 this.imageEl = document.createElement('img');
24899 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24901 this.imageEl.src = src;
24905 onLoadCanvas : function()
24907 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24908 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24910 this.bodyEl.un('click', this.beforeSelectFile, this);
24912 this.notifyEl.hide();
24913 this.thumbEl.show();
24914 this.footerEl.show();
24916 this.baseRotateLevel();
24918 if(this.isDocument){
24919 this.setThumbBoxSize();
24922 this.setThumbBoxPosition();
24924 this.baseScaleLevel();
24930 this.canvasLoaded = true;
24933 this.maskEl.unmask();
24938 setCanvasPosition : function()
24940 if(!this.canvasEl){
24944 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24945 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24947 this.previewEl.setLeft(pw);
24948 this.previewEl.setTop(ph);
24952 onMouseDown : function(e)
24956 this.dragable = true;
24957 this.pinching = false;
24959 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24960 this.dragable = false;
24964 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24965 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24969 onMouseMove : function(e)
24973 if(!this.canvasLoaded){
24977 if (!this.dragable){
24981 var minX = Math.ceil(this.thumbEl.getLeft(true));
24982 var minY = Math.ceil(this.thumbEl.getTop(true));
24984 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24985 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24987 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24988 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24990 x = x - this.mouseX;
24991 y = y - this.mouseY;
24993 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24994 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24996 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24997 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24999 this.previewEl.setLeft(bgX);
25000 this.previewEl.setTop(bgY);
25002 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25003 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25006 onMouseUp : function(e)
25010 this.dragable = false;
25013 onMouseWheel : function(e)
25017 this.startScale = this.scale;
25019 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25021 if(!this.zoomable()){
25022 this.scale = this.startScale;
25031 zoomable : function()
25033 var minScale = this.thumbEl.getWidth() / this.minWidth;
25035 if(this.minWidth < this.minHeight){
25036 minScale = this.thumbEl.getHeight() / this.minHeight;
25039 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25040 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25044 (this.rotate == 0 || this.rotate == 180) &&
25046 width > this.imageEl.OriginWidth ||
25047 height > this.imageEl.OriginHeight ||
25048 (width < this.minWidth && height < this.minHeight)
25056 (this.rotate == 90 || this.rotate == 270) &&
25058 width > this.imageEl.OriginWidth ||
25059 height > this.imageEl.OriginHeight ||
25060 (width < this.minHeight && height < this.minWidth)
25067 !this.isDocument &&
25068 (this.rotate == 0 || this.rotate == 180) &&
25070 width < this.minWidth ||
25071 width > this.imageEl.OriginWidth ||
25072 height < this.minHeight ||
25073 height > this.imageEl.OriginHeight
25080 !this.isDocument &&
25081 (this.rotate == 90 || this.rotate == 270) &&
25083 width < this.minHeight ||
25084 width > this.imageEl.OriginWidth ||
25085 height < this.minWidth ||
25086 height > this.imageEl.OriginHeight
25096 onRotateLeft : function(e)
25098 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25100 var minScale = this.thumbEl.getWidth() / this.minWidth;
25102 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25103 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25105 this.startScale = this.scale;
25107 while (this.getScaleLevel() < minScale){
25109 this.scale = this.scale + 1;
25111 if(!this.zoomable()){
25116 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25117 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25122 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25129 this.scale = this.startScale;
25131 this.onRotateFail();
25136 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25138 if(this.isDocument){
25139 this.setThumbBoxSize();
25140 this.setThumbBoxPosition();
25141 this.setCanvasPosition();
25146 this.fireEvent('rotate', this, 'left');
25150 onRotateRight : function(e)
25152 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25154 var minScale = this.thumbEl.getWidth() / this.minWidth;
25156 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25157 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25159 this.startScale = this.scale;
25161 while (this.getScaleLevel() < minScale){
25163 this.scale = this.scale + 1;
25165 if(!this.zoomable()){
25170 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25171 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25176 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25183 this.scale = this.startScale;
25185 this.onRotateFail();
25190 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25192 if(this.isDocument){
25193 this.setThumbBoxSize();
25194 this.setThumbBoxPosition();
25195 this.setCanvasPosition();
25200 this.fireEvent('rotate', this, 'right');
25203 onRotateFail : function()
25205 this.errorEl.show(true);
25209 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25214 this.previewEl.dom.innerHTML = '';
25216 var canvasEl = document.createElement("canvas");
25218 var contextEl = canvasEl.getContext("2d");
25220 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25221 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25222 var center = this.imageEl.OriginWidth / 2;
25224 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25225 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25226 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25227 center = this.imageEl.OriginHeight / 2;
25230 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25232 contextEl.translate(center, center);
25233 contextEl.rotate(this.rotate * Math.PI / 180);
25235 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25237 this.canvasEl = document.createElement("canvas");
25239 this.contextEl = this.canvasEl.getContext("2d");
25241 switch (this.rotate) {
25244 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25245 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25247 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25252 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25253 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25255 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
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);
25260 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25265 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25266 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25268 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
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);
25273 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);
25278 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25279 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25281 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25282 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25286 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);
25293 this.previewEl.appendChild(this.canvasEl);
25295 this.setCanvasPosition();
25300 if(!this.canvasLoaded){
25304 var imageCanvas = document.createElement("canvas");
25306 var imageContext = imageCanvas.getContext("2d");
25308 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25309 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25311 var center = imageCanvas.width / 2;
25313 imageContext.translate(center, center);
25315 imageContext.rotate(this.rotate * Math.PI / 180);
25317 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25319 var canvas = document.createElement("canvas");
25321 var context = canvas.getContext("2d");
25323 canvas.width = this.minWidth;
25324 canvas.height = this.minHeight;
25326 switch (this.rotate) {
25329 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25330 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25332 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25333 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25335 var targetWidth = this.minWidth - 2 * x;
25336 var targetHeight = this.minHeight - 2 * y;
25340 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25341 scale = targetWidth / width;
25344 if(x > 0 && y == 0){
25345 scale = targetHeight / height;
25348 if(x > 0 && y > 0){
25349 scale = targetWidth / width;
25351 if(width < height){
25352 scale = targetHeight / height;
25356 context.scale(scale, scale);
25358 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25359 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25361 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25362 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25364 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25369 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25370 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25372 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25373 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25375 var targetWidth = this.minWidth - 2 * x;
25376 var targetHeight = this.minHeight - 2 * y;
25380 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25381 scale = targetWidth / width;
25384 if(x > 0 && y == 0){
25385 scale = targetHeight / height;
25388 if(x > 0 && y > 0){
25389 scale = targetWidth / width;
25391 if(width < height){
25392 scale = targetHeight / height;
25396 context.scale(scale, scale);
25398 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25399 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25401 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25402 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25404 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25406 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25411 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25412 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25414 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25415 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25417 var targetWidth = this.minWidth - 2 * x;
25418 var targetHeight = this.minHeight - 2 * y;
25422 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25423 scale = targetWidth / width;
25426 if(x > 0 && y == 0){
25427 scale = targetHeight / height;
25430 if(x > 0 && y > 0){
25431 scale = targetWidth / width;
25433 if(width < height){
25434 scale = targetHeight / height;
25438 context.scale(scale, scale);
25440 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25441 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25443 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25444 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25446 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25447 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25449 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25454 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25455 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25457 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25458 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25460 var targetWidth = this.minWidth - 2 * x;
25461 var targetHeight = this.minHeight - 2 * y;
25465 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25466 scale = targetWidth / width;
25469 if(x > 0 && y == 0){
25470 scale = targetHeight / height;
25473 if(x > 0 && y > 0){
25474 scale = targetWidth / width;
25476 if(width < height){
25477 scale = targetHeight / height;
25481 context.scale(scale, scale);
25483 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25484 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25486 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25487 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25489 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25491 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25498 this.cropData = canvas.toDataURL(this.cropType);
25500 if(this.fireEvent('crop', this, this.cropData) !== false){
25501 this.process(this.file, this.cropData);
25508 setThumbBoxSize : function()
25512 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25513 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25514 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25516 this.minWidth = width;
25517 this.minHeight = height;
25519 if(this.rotate == 90 || this.rotate == 270){
25520 this.minWidth = height;
25521 this.minHeight = width;
25526 width = Math.ceil(this.minWidth * height / this.minHeight);
25528 if(this.minWidth > this.minHeight){
25530 height = Math.ceil(this.minHeight * width / this.minWidth);
25533 this.thumbEl.setStyle({
25534 width : width + 'px',
25535 height : height + 'px'
25542 setThumbBoxPosition : function()
25544 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25545 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25547 this.thumbEl.setLeft(x);
25548 this.thumbEl.setTop(y);
25552 baseRotateLevel : function()
25554 this.baseRotate = 1;
25557 typeof(this.exif) != 'undefined' &&
25558 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25559 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25561 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25564 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25568 baseScaleLevel : function()
25572 if(this.isDocument){
25574 if(this.baseRotate == 6 || this.baseRotate == 8){
25576 height = this.thumbEl.getHeight();
25577 this.baseScale = height / this.imageEl.OriginWidth;
25579 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25580 width = this.thumbEl.getWidth();
25581 this.baseScale = width / this.imageEl.OriginHeight;
25587 height = this.thumbEl.getHeight();
25588 this.baseScale = height / this.imageEl.OriginHeight;
25590 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25591 width = this.thumbEl.getWidth();
25592 this.baseScale = width / this.imageEl.OriginWidth;
25598 if(this.baseRotate == 6 || this.baseRotate == 8){
25600 width = this.thumbEl.getHeight();
25601 this.baseScale = width / this.imageEl.OriginHeight;
25603 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25604 height = this.thumbEl.getWidth();
25605 this.baseScale = height / this.imageEl.OriginHeight;
25608 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25609 height = this.thumbEl.getWidth();
25610 this.baseScale = height / this.imageEl.OriginHeight;
25612 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25613 width = this.thumbEl.getHeight();
25614 this.baseScale = width / this.imageEl.OriginWidth;
25621 width = this.thumbEl.getWidth();
25622 this.baseScale = width / this.imageEl.OriginWidth;
25624 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25625 height = this.thumbEl.getHeight();
25626 this.baseScale = height / this.imageEl.OriginHeight;
25629 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25631 height = this.thumbEl.getHeight();
25632 this.baseScale = height / this.imageEl.OriginHeight;
25634 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25635 width = this.thumbEl.getWidth();
25636 this.baseScale = width / this.imageEl.OriginWidth;
25644 getScaleLevel : function()
25646 return this.baseScale * Math.pow(1.1, this.scale);
25649 onTouchStart : function(e)
25651 if(!this.canvasLoaded){
25652 this.beforeSelectFile(e);
25656 var touches = e.browserEvent.touches;
25662 if(touches.length == 1){
25663 this.onMouseDown(e);
25667 if(touches.length != 2){
25673 for(var i = 0, finger; finger = touches[i]; i++){
25674 coords.push(finger.pageX, finger.pageY);
25677 var x = Math.pow(coords[0] - coords[2], 2);
25678 var y = Math.pow(coords[1] - coords[3], 2);
25680 this.startDistance = Math.sqrt(x + y);
25682 this.startScale = this.scale;
25684 this.pinching = true;
25685 this.dragable = false;
25689 onTouchMove : function(e)
25691 if(!this.pinching && !this.dragable){
25695 var touches = e.browserEvent.touches;
25702 this.onMouseMove(e);
25708 for(var i = 0, finger; finger = touches[i]; i++){
25709 coords.push(finger.pageX, finger.pageY);
25712 var x = Math.pow(coords[0] - coords[2], 2);
25713 var y = Math.pow(coords[1] - coords[3], 2);
25715 this.endDistance = Math.sqrt(x + y);
25717 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25719 if(!this.zoomable()){
25720 this.scale = this.startScale;
25728 onTouchEnd : function(e)
25730 this.pinching = false;
25731 this.dragable = false;
25735 process : function(file, crop)
25738 this.maskEl.mask(this.loadingText);
25741 this.xhr = new XMLHttpRequest();
25743 file.xhr = this.xhr;
25745 this.xhr.open(this.method, this.url, true);
25748 "Accept": "application/json",
25749 "Cache-Control": "no-cache",
25750 "X-Requested-With": "XMLHttpRequest"
25753 for (var headerName in headers) {
25754 var headerValue = headers[headerName];
25756 this.xhr.setRequestHeader(headerName, headerValue);
25762 this.xhr.onload = function()
25764 _this.xhrOnLoad(_this.xhr);
25767 this.xhr.onerror = function()
25769 _this.xhrOnError(_this.xhr);
25772 var formData = new FormData();
25774 formData.append('returnHTML', 'NO');
25777 formData.append('crop', crop);
25780 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25781 formData.append(this.paramName, file, file.name);
25784 if(typeof(file.filename) != 'undefined'){
25785 formData.append('filename', file.filename);
25788 if(typeof(file.mimetype) != 'undefined'){
25789 formData.append('mimetype', file.mimetype);
25792 if(this.fireEvent('arrange', this, formData) != false){
25793 this.xhr.send(formData);
25797 xhrOnLoad : function(xhr)
25800 this.maskEl.unmask();
25803 if (xhr.readyState !== 4) {
25804 this.fireEvent('exception', this, xhr);
25808 var response = Roo.decode(xhr.responseText);
25810 if(!response.success){
25811 this.fireEvent('exception', this, xhr);
25815 var response = Roo.decode(xhr.responseText);
25817 this.fireEvent('upload', this, response);
25821 xhrOnError : function()
25824 this.maskEl.unmask();
25827 Roo.log('xhr on error');
25829 var response = Roo.decode(xhr.responseText);
25835 prepare : function(file)
25838 this.maskEl.mask(this.loadingText);
25844 if(typeof(file) === 'string'){
25845 this.loadCanvas(file);
25849 if(!file || !this.urlAPI){
25854 this.cropType = file.type;
25858 if(this.fireEvent('prepare', this, this.file) != false){
25860 var reader = new FileReader();
25862 reader.onload = function (e) {
25863 if (e.target.error) {
25864 Roo.log(e.target.error);
25868 var buffer = e.target.result,
25869 dataView = new DataView(buffer),
25871 maxOffset = dataView.byteLength - 4,
25875 if (dataView.getUint16(0) === 0xffd8) {
25876 while (offset < maxOffset) {
25877 markerBytes = dataView.getUint16(offset);
25879 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25880 markerLength = dataView.getUint16(offset + 2) + 2;
25881 if (offset + markerLength > dataView.byteLength) {
25882 Roo.log('Invalid meta data: Invalid segment size.');
25886 if(markerBytes == 0xffe1){
25887 _this.parseExifData(
25894 offset += markerLength;
25904 var url = _this.urlAPI.createObjectURL(_this.file);
25906 _this.loadCanvas(url);
25911 reader.readAsArrayBuffer(this.file);
25917 parseExifData : function(dataView, offset, length)
25919 var tiffOffset = offset + 10,
25923 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25924 // No Exif data, might be XMP data instead
25928 // Check for the ASCII code for "Exif" (0x45786966):
25929 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25930 // No Exif data, might be XMP data instead
25933 if (tiffOffset + 8 > dataView.byteLength) {
25934 Roo.log('Invalid Exif data: Invalid segment size.');
25937 // Check for the two null bytes:
25938 if (dataView.getUint16(offset + 8) !== 0x0000) {
25939 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25942 // Check the byte alignment:
25943 switch (dataView.getUint16(tiffOffset)) {
25945 littleEndian = true;
25948 littleEndian = false;
25951 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25954 // Check for the TIFF tag marker (0x002A):
25955 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25956 Roo.log('Invalid Exif data: Missing TIFF marker.');
25959 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25960 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25962 this.parseExifTags(
25965 tiffOffset + dirOffset,
25970 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25975 if (dirOffset + 6 > dataView.byteLength) {
25976 Roo.log('Invalid Exif data: Invalid directory offset.');
25979 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25980 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25981 if (dirEndOffset + 4 > dataView.byteLength) {
25982 Roo.log('Invalid Exif data: Invalid directory size.');
25985 for (i = 0; i < tagsNumber; i += 1) {
25989 dirOffset + 2 + 12 * i, // tag offset
25993 // Return the offset to the next directory:
25994 return dataView.getUint32(dirEndOffset, littleEndian);
25997 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25999 var tag = dataView.getUint16(offset, littleEndian);
26001 this.exif[tag] = this.getExifValue(
26005 dataView.getUint16(offset + 2, littleEndian), // tag type
26006 dataView.getUint32(offset + 4, littleEndian), // tag length
26011 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26013 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26022 Roo.log('Invalid Exif data: Invalid tag type.');
26026 tagSize = tagType.size * length;
26027 // Determine if the value is contained in the dataOffset bytes,
26028 // or if the value at the dataOffset is a pointer to the actual data:
26029 dataOffset = tagSize > 4 ?
26030 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26031 if (dataOffset + tagSize > dataView.byteLength) {
26032 Roo.log('Invalid Exif data: Invalid data offset.');
26035 if (length === 1) {
26036 return tagType.getValue(dataView, dataOffset, littleEndian);
26039 for (i = 0; i < length; i += 1) {
26040 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26043 if (tagType.ascii) {
26045 // Concatenate the chars:
26046 for (i = 0; i < values.length; i += 1) {
26048 // Ignore the terminating NULL byte(s):
26049 if (c === '\u0000') {
26061 Roo.apply(Roo.bootstrap.UploadCropbox, {
26063 'Orientation': 0x0112
26067 1: 0, //'top-left',
26069 3: 180, //'bottom-right',
26070 // 4: 'bottom-left',
26072 6: 90, //'right-top',
26073 // 7: 'right-bottom',
26074 8: 270 //'left-bottom'
26078 // byte, 8-bit unsigned int:
26080 getValue: function (dataView, dataOffset) {
26081 return dataView.getUint8(dataOffset);
26085 // ascii, 8-bit byte:
26087 getValue: function (dataView, dataOffset) {
26088 return String.fromCharCode(dataView.getUint8(dataOffset));
26093 // short, 16 bit int:
26095 getValue: function (dataView, dataOffset, littleEndian) {
26096 return dataView.getUint16(dataOffset, littleEndian);
26100 // long, 32 bit int:
26102 getValue: function (dataView, dataOffset, littleEndian) {
26103 return dataView.getUint32(dataOffset, littleEndian);
26107 // rational = two long values, first is numerator, second is denominator:
26109 getValue: function (dataView, dataOffset, littleEndian) {
26110 return dataView.getUint32(dataOffset, littleEndian) /
26111 dataView.getUint32(dataOffset + 4, littleEndian);
26115 // slong, 32 bit signed int:
26117 getValue: function (dataView, dataOffset, littleEndian) {
26118 return dataView.getInt32(dataOffset, littleEndian);
26122 // srational, two slongs, first is numerator, second is denominator:
26124 getValue: function (dataView, dataOffset, littleEndian) {
26125 return dataView.getInt32(dataOffset, littleEndian) /
26126 dataView.getInt32(dataOffset + 4, littleEndian);
26136 cls : 'btn-group roo-upload-cropbox-rotate-left',
26137 action : 'rotate-left',
26141 cls : 'btn btn-default',
26142 html : '<i class="fa fa-undo"></i>'
26148 cls : 'btn-group roo-upload-cropbox-picture',
26149 action : 'picture',
26153 cls : 'btn btn-default',
26154 html : '<i class="fa fa-picture-o"></i>'
26160 cls : 'btn-group roo-upload-cropbox-rotate-right',
26161 action : 'rotate-right',
26165 cls : 'btn btn-default',
26166 html : '<i class="fa fa-repeat"></i>'
26174 cls : 'btn-group roo-upload-cropbox-rotate-left',
26175 action : 'rotate-left',
26179 cls : 'btn btn-default',
26180 html : '<i class="fa fa-undo"></i>'
26186 cls : 'btn-group roo-upload-cropbox-download',
26187 action : 'download',
26191 cls : 'btn btn-default',
26192 html : '<i class="fa fa-download"></i>'
26198 cls : 'btn-group roo-upload-cropbox-crop',
26203 cls : 'btn btn-default',
26204 html : '<i class="fa fa-crop"></i>'
26210 cls : 'btn-group roo-upload-cropbox-trash',
26215 cls : 'btn btn-default',
26216 html : '<i class="fa fa-trash"></i>'
26222 cls : 'btn-group roo-upload-cropbox-rotate-right',
26223 action : 'rotate-right',
26227 cls : 'btn btn-default',
26228 html : '<i class="fa fa-repeat"></i>'
26236 cls : 'btn-group roo-upload-cropbox-rotate-left',
26237 action : 'rotate-left',
26241 cls : 'btn btn-default',
26242 html : '<i class="fa fa-undo"></i>'
26248 cls : 'btn-group roo-upload-cropbox-rotate-right',
26249 action : 'rotate-right',
26253 cls : 'btn btn-default',
26254 html : '<i class="fa fa-repeat"></i>'
26267 * @class Roo.bootstrap.DocumentManager
26268 * @extends Roo.bootstrap.Component
26269 * Bootstrap DocumentManager class
26270 * @cfg {String} paramName default 'imageUpload'
26271 * @cfg {String} method default POST
26272 * @cfg {String} url action url
26273 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26274 * @cfg {Boolean} multiple multiple upload default true
26275 * @cfg {Number} thumbSize default 300
26276 * @cfg {String} fieldLabel
26277 * @cfg {Number} labelWidth default 4
26278 * @cfg {String} labelAlign (left|top) default left
26279 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26282 * Create a new DocumentManager
26283 * @param {Object} config The config object
26286 Roo.bootstrap.DocumentManager = function(config){
26287 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26292 * Fire when initial the DocumentManager
26293 * @param {Roo.bootstrap.DocumentManager} this
26298 * inspect selected file
26299 * @param {Roo.bootstrap.DocumentManager} this
26300 * @param {File} file
26305 * Fire when xhr load exception
26306 * @param {Roo.bootstrap.DocumentManager} this
26307 * @param {XMLHttpRequest} xhr
26309 "exception" : true,
26312 * prepare the form data
26313 * @param {Roo.bootstrap.DocumentManager} this
26314 * @param {Object} formData
26319 * Fire when remove the file
26320 * @param {Roo.bootstrap.DocumentManager} this
26321 * @param {Object} file
26326 * Fire after refresh the file
26327 * @param {Roo.bootstrap.DocumentManager} this
26332 * Fire after click the image
26333 * @param {Roo.bootstrap.DocumentManager} this
26334 * @param {Object} file
26339 * Fire when upload a image and editable set to true
26340 * @param {Roo.bootstrap.DocumentManager} this
26341 * @param {Object} file
26345 * @event beforeselectfile
26346 * Fire before select file
26347 * @param {Roo.bootstrap.DocumentManager} this
26349 "beforeselectfile" : true,
26352 * Fire before process file
26353 * @param {Roo.bootstrap.DocumentManager} this
26354 * @param {Object} file
26361 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26370 paramName : 'imageUpload',
26373 labelAlign : 'left',
26380 getAutoCreate : function()
26382 var managerWidget = {
26384 cls : 'roo-document-manager',
26388 cls : 'roo-document-manager-selector',
26393 cls : 'roo-document-manager-uploader',
26397 cls : 'roo-document-manager-upload-btn',
26398 html : '<i class="fa fa-plus"></i>'
26409 cls : 'column col-md-12',
26414 if(this.fieldLabel.length){
26419 cls : 'column col-md-12',
26420 html : this.fieldLabel
26424 cls : 'column col-md-12',
26429 if(this.labelAlign == 'left'){
26433 cls : 'column col-md-' + this.labelWidth,
26434 html : this.fieldLabel
26438 cls : 'column col-md-' + (12 - this.labelWidth),
26448 cls : 'row clearfix',
26456 initEvents : function()
26458 this.managerEl = this.el.select('.roo-document-manager', true).first();
26459 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26461 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26462 this.selectorEl.hide();
26465 this.selectorEl.attr('multiple', 'multiple');
26468 this.selectorEl.on('change', this.onFileSelected, this);
26470 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26471 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26473 this.uploader.on('click', this.onUploaderClick, this);
26475 this.renderProgressDialog();
26479 window.addEventListener("resize", function() { _this.refresh(); } );
26481 this.fireEvent('initial', this);
26484 renderProgressDialog : function()
26488 this.progressDialog = new Roo.bootstrap.Modal({
26489 cls : 'roo-document-manager-progress-dialog',
26490 allow_close : false,
26500 btnclick : function() {
26501 _this.uploadCancel();
26507 this.progressDialog.render(Roo.get(document.body));
26509 this.progress = new Roo.bootstrap.Progress({
26510 cls : 'roo-document-manager-progress',
26515 this.progress.render(this.progressDialog.getChildContainer());
26517 this.progressBar = new Roo.bootstrap.ProgressBar({
26518 cls : 'roo-document-manager-progress-bar',
26521 aria_valuemax : 12,
26525 this.progressBar.render(this.progress.getChildContainer());
26528 onUploaderClick : function(e)
26530 e.preventDefault();
26532 if(this.fireEvent('beforeselectfile', this) != false){
26533 this.selectorEl.dom.click();
26538 onFileSelected : function(e)
26540 e.preventDefault();
26542 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26546 Roo.each(this.selectorEl.dom.files, function(file){
26547 if(this.fireEvent('inspect', this, file) != false){
26548 this.files.push(file);
26558 this.selectorEl.dom.value = '';
26560 if(!this.files.length){
26564 if(this.boxes > 0 && this.files.length > this.boxes){
26565 this.files = this.files.slice(0, this.boxes);
26568 this.uploader.show();
26570 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26571 this.uploader.hide();
26580 Roo.each(this.files, function(file){
26582 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26583 var f = this.renderPreview(file);
26588 if(file.type.indexOf('image') != -1){
26589 this.delegates.push(
26591 _this.process(file);
26592 }).createDelegate(this)
26600 _this.process(file);
26601 }).createDelegate(this)
26606 this.files = files;
26608 this.delegates = this.delegates.concat(docs);
26610 if(!this.delegates.length){
26615 this.progressBar.aria_valuemax = this.delegates.length;
26622 arrange : function()
26624 if(!this.delegates.length){
26625 this.progressDialog.hide();
26630 var delegate = this.delegates.shift();
26632 this.progressDialog.show();
26634 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26636 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26641 refresh : function()
26643 this.uploader.show();
26645 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26646 this.uploader.hide();
26649 Roo.isTouch ? this.closable(false) : this.closable(true);
26651 this.fireEvent('refresh', this);
26654 onRemove : function(e, el, o)
26656 e.preventDefault();
26658 this.fireEvent('remove', this, o);
26662 remove : function(o)
26666 Roo.each(this.files, function(file){
26667 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26676 this.files = files;
26683 Roo.each(this.files, function(file){
26688 file.target.remove();
26697 onClick : function(e, el, o)
26699 e.preventDefault();
26701 this.fireEvent('click', this, o);
26705 closable : function(closable)
26707 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26709 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26721 xhrOnLoad : function(xhr)
26723 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26727 if (xhr.readyState !== 4) {
26729 this.fireEvent('exception', this, xhr);
26733 var response = Roo.decode(xhr.responseText);
26735 if(!response.success){
26737 this.fireEvent('exception', this, xhr);
26741 var file = this.renderPreview(response.data);
26743 this.files.push(file);
26749 xhrOnError : function()
26751 Roo.log('xhr on error');
26753 var response = Roo.decode(xhr.responseText);
26760 process : function(file)
26762 if(this.fireEvent('process', this, file) !== false){
26763 if(this.editable && file.type.indexOf('image') != -1){
26764 this.fireEvent('edit', this, file);
26768 this.uploadStart(file, false);
26775 uploadStart : function(file, crop)
26777 this.xhr = new XMLHttpRequest();
26779 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26784 file.xhr = this.xhr;
26786 this.managerEl.createChild({
26788 cls : 'roo-document-manager-loading',
26792 tooltip : file.name,
26793 cls : 'roo-document-manager-thumb',
26794 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26800 this.xhr.open(this.method, this.url, true);
26803 "Accept": "application/json",
26804 "Cache-Control": "no-cache",
26805 "X-Requested-With": "XMLHttpRequest"
26808 for (var headerName in headers) {
26809 var headerValue = headers[headerName];
26811 this.xhr.setRequestHeader(headerName, headerValue);
26817 this.xhr.onload = function()
26819 _this.xhrOnLoad(_this.xhr);
26822 this.xhr.onerror = function()
26824 _this.xhrOnError(_this.xhr);
26827 var formData = new FormData();
26829 formData.append('returnHTML', 'NO');
26832 formData.append('crop', crop);
26835 formData.append(this.paramName, file, file.name);
26837 if(this.fireEvent('prepare', this, formData) != false){
26838 this.xhr.send(formData);
26842 uploadCancel : function()
26849 this.delegates = [];
26851 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26858 renderPreview : function(file)
26860 if(typeof(file.target) != 'undefined' && file.target){
26864 var previewEl = this.managerEl.createChild({
26866 cls : 'roo-document-manager-preview',
26870 tooltip : file.filename,
26871 cls : 'roo-document-manager-thumb',
26872 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26877 html : '<i class="fa fa-times-circle"></i>'
26882 var close = previewEl.select('button.close', true).first();
26884 close.on('click', this.onRemove, this, file);
26886 file.target = previewEl;
26888 var image = previewEl.select('img', true).first();
26892 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26894 image.on('click', this.onClick, this, file);
26900 onPreviewLoad : function(file, image)
26902 if(typeof(file.target) == 'undefined' || !file.target){
26906 var width = image.dom.naturalWidth || image.dom.width;
26907 var height = image.dom.naturalHeight || image.dom.height;
26909 if(width > height){
26910 file.target.addClass('wide');
26914 file.target.addClass('tall');
26919 uploadFromSource : function(file, crop)
26921 this.xhr = new XMLHttpRequest();
26923 this.managerEl.createChild({
26925 cls : 'roo-document-manager-loading',
26929 tooltip : file.name,
26930 cls : 'roo-document-manager-thumb',
26931 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26937 this.xhr.open(this.method, this.url, true);
26940 "Accept": "application/json",
26941 "Cache-Control": "no-cache",
26942 "X-Requested-With": "XMLHttpRequest"
26945 for (var headerName in headers) {
26946 var headerValue = headers[headerName];
26948 this.xhr.setRequestHeader(headerName, headerValue);
26954 this.xhr.onload = function()
26956 _this.xhrOnLoad(_this.xhr);
26959 this.xhr.onerror = function()
26961 _this.xhrOnError(_this.xhr);
26964 var formData = new FormData();
26966 formData.append('returnHTML', 'NO');
26968 formData.append('crop', crop);
26970 if(typeof(file.filename) != 'undefined'){
26971 formData.append('filename', file.filename);
26974 if(typeof(file.mimetype) != 'undefined'){
26975 formData.append('mimetype', file.mimetype);
26978 if(this.fireEvent('prepare', this, formData) != false){
26979 this.xhr.send(formData);
26989 * @class Roo.bootstrap.DocumentViewer
26990 * @extends Roo.bootstrap.Component
26991 * Bootstrap DocumentViewer class
26994 * Create a new DocumentViewer
26995 * @param {Object} config The config object
26998 Roo.bootstrap.DocumentViewer = function(config){
26999 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27004 * Fire after initEvent
27005 * @param {Roo.bootstrap.DocumentViewer} this
27011 * @param {Roo.bootstrap.DocumentViewer} this
27016 * Fire after trash button
27017 * @param {Roo.bootstrap.DocumentViewer} this
27024 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27026 getAutoCreate : function()
27030 cls : 'roo-document-viewer',
27034 cls : 'roo-document-viewer-body',
27038 cls : 'roo-document-viewer-thumb',
27042 cls : 'roo-document-viewer-image'
27050 cls : 'roo-document-viewer-footer',
27053 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27061 cls : 'btn btn-default roo-document-viewer-trash',
27062 html : '<i class="fa fa-trash"></i>'
27075 initEvents : function()
27078 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27079 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27081 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27082 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27084 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27085 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27087 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27088 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27090 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27091 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27093 this.bodyEl.on('click', this.onClick, this);
27095 this.trashBtn.on('click', this.onTrash, this);
27099 initial : function()
27101 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27104 this.fireEvent('initial', this);
27108 onClick : function(e)
27110 e.preventDefault();
27112 this.fireEvent('click', this);
27115 onTrash : function(e)
27117 e.preventDefault();
27119 this.fireEvent('trash', this);
27131 * @class Roo.bootstrap.NavProgressBar
27132 * @extends Roo.bootstrap.Component
27133 * Bootstrap NavProgressBar class
27136 * Create a new nav progress bar
27137 * @param {Object} config The config object
27140 Roo.bootstrap.NavProgressBar = function(config){
27141 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27143 this.bullets = this.bullets || [];
27145 // Roo.bootstrap.NavProgressBar.register(this);
27149 * Fires when the active item changes
27150 * @param {Roo.bootstrap.NavProgressBar} this
27151 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27152 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27159 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27164 getAutoCreate : function()
27166 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27170 cls : 'roo-navigation-bar-group',
27174 cls : 'roo-navigation-top-bar'
27178 cls : 'roo-navigation-bullets-bar',
27182 cls : 'roo-navigation-bar'
27189 cls : 'roo-navigation-bottom-bar'
27199 initEvents: function()
27204 onRender : function(ct, position)
27206 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27208 if(this.bullets.length){
27209 Roo.each(this.bullets, function(b){
27218 addItem : function(cfg)
27220 var item = new Roo.bootstrap.NavProgressItem(cfg);
27222 item.parentId = this.id;
27223 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27226 var top = new Roo.bootstrap.Element({
27228 cls : 'roo-navigation-bar-text'
27231 var bottom = new Roo.bootstrap.Element({
27233 cls : 'roo-navigation-bar-text'
27236 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27237 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27239 var topText = new Roo.bootstrap.Element({
27241 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27244 var bottomText = new Roo.bootstrap.Element({
27246 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27249 topText.onRender(top.el, null);
27250 bottomText.onRender(bottom.el, null);
27253 item.bottomEl = bottom;
27256 this.barItems.push(item);
27261 getActive : function()
27263 var active = false;
27265 Roo.each(this.barItems, function(v){
27267 if (!v.isActive()) {
27279 setActiveItem : function(item)
27283 Roo.each(this.barItems, function(v){
27284 if (v.rid == item.rid) {
27288 if (v.isActive()) {
27289 v.setActive(false);
27294 item.setActive(true);
27296 this.fireEvent('changed', this, item, prev);
27299 getBarItem: function(rid)
27303 Roo.each(this.barItems, function(e) {
27304 if (e.rid != rid) {
27315 indexOfItem : function(item)
27319 Roo.each(this.barItems, function(v, i){
27321 if (v.rid != item.rid) {
27332 setActiveNext : function()
27334 var i = this.indexOfItem(this.getActive());
27336 if (i > this.barItems.length) {
27340 this.setActiveItem(this.barItems[i+1]);
27343 setActivePrev : function()
27345 var i = this.indexOfItem(this.getActive());
27351 this.setActiveItem(this.barItems[i-1]);
27354 format : function()
27356 if(!this.barItems.length){
27360 var width = 100 / this.barItems.length;
27362 Roo.each(this.barItems, function(i){
27363 i.el.setStyle('width', width + '%');
27364 i.topEl.el.setStyle('width', width + '%');
27365 i.bottomEl.el.setStyle('width', width + '%');
27374 * Nav Progress Item
27379 * @class Roo.bootstrap.NavProgressItem
27380 * @extends Roo.bootstrap.Component
27381 * Bootstrap NavProgressItem class
27382 * @cfg {String} rid the reference id
27383 * @cfg {Boolean} active (true|false) Is item active default false
27384 * @cfg {Boolean} disabled (true|false) Is item active default false
27385 * @cfg {String} html
27386 * @cfg {String} position (top|bottom) text position default bottom
27387 * @cfg {String} icon show icon instead of number
27390 * Create a new NavProgressItem
27391 * @param {Object} config The config object
27393 Roo.bootstrap.NavProgressItem = function(config){
27394 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27399 * The raw click event for the entire grid.
27400 * @param {Roo.bootstrap.NavProgressItem} this
27401 * @param {Roo.EventObject} e
27408 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27414 position : 'bottom',
27417 getAutoCreate : function()
27419 var iconCls = 'roo-navigation-bar-item-icon';
27421 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27425 cls: 'roo-navigation-bar-item',
27435 cfg.cls += ' active';
27438 cfg.cls += ' disabled';
27444 disable : function()
27446 this.setDisabled(true);
27449 enable : function()
27451 this.setDisabled(false);
27454 initEvents: function()
27456 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27458 this.iconEl.on('click', this.onClick, this);
27461 onClick : function(e)
27463 e.preventDefault();
27469 if(this.fireEvent('click', this, e) === false){
27473 this.parent().setActiveItem(this);
27476 isActive: function ()
27478 return this.active;
27481 setActive : function(state)
27483 if(this.active == state){
27487 this.active = state;
27490 this.el.addClass('active');
27494 this.el.removeClass('active');
27499 setDisabled : function(state)
27501 if(this.disabled == state){
27505 this.disabled = state;
27508 this.el.addClass('disabled');
27512 this.el.removeClass('disabled');
27515 tooltipEl : function()
27517 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27530 * @class Roo.bootstrap.FieldLabel
27531 * @extends Roo.bootstrap.Component
27532 * Bootstrap FieldLabel class
27533 * @cfg {String} html contents of the element
27534 * @cfg {String} tag tag of the element default label
27535 * @cfg {String} cls class of the element
27536 * @cfg {String} target label target
27537 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27538 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27539 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27540 * @cfg {String} iconTooltip default "This field is required"
27543 * Create a new FieldLabel
27544 * @param {Object} config The config object
27547 Roo.bootstrap.FieldLabel = function(config){
27548 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27553 * Fires after the field has been marked as invalid.
27554 * @param {Roo.form.FieldLabel} this
27555 * @param {String} msg The validation message
27560 * Fires after the field has been validated with no errors.
27561 * @param {Roo.form.FieldLabel} this
27567 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27574 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27575 validClass : 'text-success fa fa-lg fa-check',
27576 iconTooltip : 'This field is required',
27578 getAutoCreate : function(){
27582 cls : 'roo-bootstrap-field-label ' + this.cls,
27588 tooltip : this.iconTooltip
27600 initEvents: function()
27602 Roo.bootstrap.Element.superclass.initEvents.call(this);
27604 this.iconEl = this.el.select('i', true).first();
27606 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27608 Roo.bootstrap.FieldLabel.register(this);
27612 * Mark this field as valid
27614 markValid : function()
27616 this.iconEl.show();
27618 this.iconEl.removeClass(this.invalidClass);
27620 this.iconEl.addClass(this.validClass);
27622 this.fireEvent('valid', this);
27626 * Mark this field as invalid
27627 * @param {String} msg The validation message
27629 markInvalid : function(msg)
27631 this.iconEl.show();
27633 this.iconEl.removeClass(this.validClass);
27635 this.iconEl.addClass(this.invalidClass);
27637 this.fireEvent('invalid', this, msg);
27643 Roo.apply(Roo.bootstrap.FieldLabel, {
27648 * register a FieldLabel Group
27649 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27651 register : function(label)
27653 if(this.groups.hasOwnProperty(label.target)){
27657 this.groups[label.target] = label;
27661 * fetch a FieldLabel Group based on the target
27662 * @param {string} target
27663 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27665 get: function(target) {
27666 if (typeof(this.groups[target]) == 'undefined') {
27670 return this.groups[target] ;
27679 * page DateSplitField.
27685 * @class Roo.bootstrap.DateSplitField
27686 * @extends Roo.bootstrap.Component
27687 * Bootstrap DateSplitField class
27688 * @cfg {string} fieldLabel - the label associated
27689 * @cfg {Number} labelWidth set the width of label (0-12)
27690 * @cfg {String} labelAlign (top|left)
27691 * @cfg {Boolean} dayAllowBlank (true|false) default false
27692 * @cfg {Boolean} monthAllowBlank (true|false) default false
27693 * @cfg {Boolean} yearAllowBlank (true|false) default false
27694 * @cfg {string} dayPlaceholder
27695 * @cfg {string} monthPlaceholder
27696 * @cfg {string} yearPlaceholder
27697 * @cfg {string} dayFormat default 'd'
27698 * @cfg {string} monthFormat default 'm'
27699 * @cfg {string} yearFormat default 'Y'
27703 * Create a new DateSplitField
27704 * @param {Object} config The config object
27707 Roo.bootstrap.DateSplitField = function(config){
27708 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27714 * getting the data of years
27715 * @param {Roo.bootstrap.DateSplitField} this
27716 * @param {Object} years
27721 * getting the data of days
27722 * @param {Roo.bootstrap.DateSplitField} this
27723 * @param {Object} days
27728 * Fires after the field has been marked as invalid.
27729 * @param {Roo.form.Field} this
27730 * @param {String} msg The validation message
27735 * Fires after the field has been validated with no errors.
27736 * @param {Roo.form.Field} this
27742 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27745 labelAlign : 'top',
27747 dayAllowBlank : false,
27748 monthAllowBlank : false,
27749 yearAllowBlank : false,
27750 dayPlaceholder : '',
27751 monthPlaceholder : '',
27752 yearPlaceholder : '',
27756 isFormField : true,
27758 getAutoCreate : function()
27762 cls : 'row roo-date-split-field-group',
27767 cls : 'form-hidden-field roo-date-split-field-group-value',
27773 if(this.fieldLabel){
27776 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27780 html : this.fieldLabel
27786 Roo.each(['day', 'month', 'year'], function(t){
27789 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27796 inputEl: function ()
27798 return this.el.select('.roo-date-split-field-group-value', true).first();
27801 onRender : function(ct, position)
27805 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27807 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27809 this.dayField = new Roo.bootstrap.ComboBox({
27810 allowBlank : this.dayAllowBlank,
27811 alwaysQuery : true,
27812 displayField : 'value',
27815 forceSelection : true,
27817 placeholder : this.dayPlaceholder,
27818 selectOnFocus : true,
27819 tpl : '<div class="select2-result"><b>{value}</b></div>',
27820 triggerAction : 'all',
27822 valueField : 'value',
27823 store : new Roo.data.SimpleStore({
27824 data : (function() {
27826 _this.fireEvent('days', _this, days);
27829 fields : [ 'value' ]
27832 select : function (_self, record, index)
27834 _this.setValue(_this.getValue());
27839 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27841 this.monthField = new Roo.bootstrap.MonthField({
27842 after : '<i class=\"fa fa-calendar\"></i>',
27843 allowBlank : this.monthAllowBlank,
27844 placeholder : this.monthPlaceholder,
27847 render : function (_self)
27849 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27850 e.preventDefault();
27854 select : function (_self, oldvalue, newvalue)
27856 _this.setValue(_this.getValue());
27861 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27863 this.yearField = new Roo.bootstrap.ComboBox({
27864 allowBlank : this.yearAllowBlank,
27865 alwaysQuery : true,
27866 displayField : 'value',
27869 forceSelection : true,
27871 placeholder : this.yearPlaceholder,
27872 selectOnFocus : true,
27873 tpl : '<div class="select2-result"><b>{value}</b></div>',
27874 triggerAction : 'all',
27876 valueField : 'value',
27877 store : new Roo.data.SimpleStore({
27878 data : (function() {
27880 _this.fireEvent('years', _this, years);
27883 fields : [ 'value' ]
27886 select : function (_self, record, index)
27888 _this.setValue(_this.getValue());
27893 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27896 setValue : function(v, format)
27898 this.inputEl.dom.value = v;
27900 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27902 var d = Date.parseDate(v, f);
27909 this.setDay(d.format(this.dayFormat));
27910 this.setMonth(d.format(this.monthFormat));
27911 this.setYear(d.format(this.yearFormat));
27918 setDay : function(v)
27920 this.dayField.setValue(v);
27921 this.inputEl.dom.value = this.getValue();
27926 setMonth : function(v)
27928 this.monthField.setValue(v, true);
27929 this.inputEl.dom.value = this.getValue();
27934 setYear : function(v)
27936 this.yearField.setValue(v);
27937 this.inputEl.dom.value = this.getValue();
27942 getDay : function()
27944 return this.dayField.getValue();
27947 getMonth : function()
27949 return this.monthField.getValue();
27952 getYear : function()
27954 return this.yearField.getValue();
27957 getValue : function()
27959 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27961 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27971 this.inputEl.dom.value = '';
27976 validate : function()
27978 var d = this.dayField.validate();
27979 var m = this.monthField.validate();
27980 var y = this.yearField.validate();
27985 (!this.dayAllowBlank && !d) ||
27986 (!this.monthAllowBlank && !m) ||
27987 (!this.yearAllowBlank && !y)
27992 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28001 this.markInvalid();
28006 markValid : function()
28009 var label = this.el.select('label', true).first();
28010 var icon = this.el.select('i.fa-star', true).first();
28016 this.fireEvent('valid', this);
28020 * Mark this field as invalid
28021 * @param {String} msg The validation message
28023 markInvalid : function(msg)
28026 var label = this.el.select('label', true).first();
28027 var icon = this.el.select('i.fa-star', true).first();
28029 if(label && !icon){
28030 this.el.select('.roo-date-split-field-label', true).createChild({
28032 cls : 'text-danger fa fa-lg fa-star',
28033 tooltip : 'This field is required',
28034 style : 'margin-right:5px;'
28038 this.fireEvent('invalid', this, msg);
28041 clearInvalid : function()
28043 var label = this.el.select('label', true).first();
28044 var icon = this.el.select('i.fa-star', true).first();
28050 this.fireEvent('valid', this);
28053 getName: function()