4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
994 * @cfg {Boolean} clickable (true|false) default false
998 * Create a new Container
999 * @param {Object} config The config object
1002 Roo.bootstrap.Container = function(config){
1003 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1009 * After the panel has been expand
1011 * @param {Roo.bootstrap.Container} this
1016 * After the panel has been collapsed
1018 * @param {Roo.bootstrap.Container} this
1023 * When a element is chick
1024 * @param {Roo.bootstrap.Container} this
1025 * @param {Roo.EventObject} e
1031 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1049 getChildContainer : function() {
1055 if (this.panel.length) {
1056 return this.el.select('.panel-body',true).first();
1063 getAutoCreate : function(){
1066 tag : this.tag || 'div',
1070 if (this.jumbotron) {
1071 cfg.cls = 'jumbotron';
1076 // - this is applied by the parent..
1078 // cfg.cls = this.cls + '';
1081 if (this.sticky.length) {
1083 var bd = Roo.get(document.body);
1084 if (!bd.hasClass('bootstrap-sticky')) {
1085 bd.addClass('bootstrap-sticky');
1086 Roo.select('html',true).setStyle('height', '100%');
1089 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1093 if (this.well.length) {
1094 switch (this.well) {
1097 cfg.cls +=' well well-' +this.well;
1106 cfg.cls += ' hidden';
1110 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1111 cfg.cls +=' alert alert-' + this.alert;
1116 if (this.panel.length) {
1117 cfg.cls += ' panel panel-' + this.panel;
1119 if (this.header.length) {
1123 if(this.expandable){
1125 cfg.cls = cfg.cls + ' expandable';
1129 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1137 cls : 'panel-title',
1138 html : (this.expandable ? ' ' : '') + this.header
1142 cls: 'panel-header-right',
1148 cls : 'panel-heading',
1149 style : this.expandable ? 'cursor: pointer' : '',
1157 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1162 if (this.footer.length) {
1164 cls : 'panel-footer',
1173 body.html = this.html || cfg.html;
1174 // prefix with the icons..
1176 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1179 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1184 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1185 cfg.cls = 'container';
1191 initEvents: function()
1193 if(this.expandable){
1194 var headerEl = this.headerEl();
1197 headerEl.on('click', this.onToggleClick, this);
1202 this.el.on('click', this.onClick, this);
1207 onToggleClick : function()
1209 var headerEl = this.headerEl();
1225 if(this.fireEvent('expand', this)) {
1227 this.expanded = true;
1229 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1231 this.el.select('.panel-body',true).first().removeClass('hide');
1233 var toggleEl = this.toggleEl();
1239 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1244 collapse : function()
1246 if(this.fireEvent('collapse', this)) {
1248 this.expanded = false;
1250 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1251 this.el.select('.panel-body',true).first().addClass('hide');
1253 var toggleEl = this.toggleEl();
1259 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1263 toggleEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1269 return this.el.select('.panel-heading .fa',true).first();
1272 headerEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-heading',true).first()
1281 titleEl : function()
1283 if(!this.el || !this.panel.length || !this.header.length){
1287 return this.el.select('.panel-title',true).first();
1290 setTitle : function(v)
1292 var titleEl = this.titleEl();
1298 titleEl.dom.innerHTML = v;
1301 getTitle : function()
1304 var titleEl = this.titleEl();
1310 return titleEl.dom.innerHTML;
1313 setRightTitle : function(v)
1315 var t = this.el.select('.panel-header-right',true).first();
1321 t.dom.innerHTML = v;
1324 onClick : function(e)
1328 this.fireEvent('click', this, e);
1342 * @class Roo.bootstrap.Img
1343 * @extends Roo.bootstrap.Component
1344 * Bootstrap Img class
1345 * @cfg {Boolean} imgResponsive false | true
1346 * @cfg {String} border rounded | circle | thumbnail
1347 * @cfg {String} src image source
1348 * @cfg {String} alt image alternative text
1349 * @cfg {String} href a tag href
1350 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1351 * @cfg {String} xsUrl xs image source
1352 * @cfg {String} smUrl sm image source
1353 * @cfg {String} mdUrl md image source
1354 * @cfg {String} lgUrl lg image source
1357 * Create a new Input
1358 * @param {Object} config The config object
1361 Roo.bootstrap.Img = function(config){
1362 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1368 * The img click event for the img.
1369 * @param {Roo.EventObject} e
1375 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1377 imgResponsive: true,
1387 getAutoCreate : function()
1389 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1390 return this.createSingleImg();
1395 cls: 'roo-image-responsive-group',
1400 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1402 if(!_this[size + 'Url']){
1408 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1409 html: _this.html || cfg.html,
1410 src: _this[size + 'Url']
1413 img.cls += ' roo-image-responsive-' + size;
1415 var s = ['xs', 'sm', 'md', 'lg'];
1417 s.splice(s.indexOf(size), 1);
1419 Roo.each(s, function(ss){
1420 img.cls += ' hidden-' + ss;
1423 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1424 cfg.cls += ' img-' + _this.border;
1428 cfg.alt = _this.alt;
1441 a.target = _this.target;
1445 cfg.cn.push((_this.href) ? a : img);
1452 createSingleImg : function()
1456 cls: (this.imgResponsive) ? 'img-responsive' : '',
1458 src : 'about:blank' // just incase src get's set to undefined?!?
1461 cfg.html = this.html || cfg.html;
1463 cfg.src = this.src || cfg.src;
1465 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1466 cfg.cls += ' img-' + this.border;
1483 a.target = this.target;
1488 return (this.href) ? a : cfg;
1491 initEvents: function()
1494 this.el.on('click', this.onClick, this);
1499 onClick : function(e)
1501 Roo.log('img onclick');
1502 this.fireEvent('click', this, e);
1516 * @class Roo.bootstrap.Link
1517 * @extends Roo.bootstrap.Component
1518 * Bootstrap Link Class
1519 * @cfg {String} alt image alternative text
1520 * @cfg {String} href a tag href
1521 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1522 * @cfg {String} html the content of the link.
1523 * @cfg {String} anchor name for the anchor link
1525 * @cfg {Boolean} preventDefault (true | false) default false
1529 * Create a new Input
1530 * @param {Object} config The config object
1533 Roo.bootstrap.Link = function(config){
1534 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1540 * The img click event for the img.
1541 * @param {Roo.EventObject} e
1547 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1551 preventDefault: false,
1555 getAutoCreate : function()
1561 // anchor's do not require html/href...
1562 if (this.anchor === false) {
1563 cfg.html = this.html || '';
1564 cfg.href = this.href || '#';
1566 cfg.name = this.anchor;
1567 if (this.html !== false) {
1568 cfg.html = this.html;
1570 if (this.href !== false) {
1571 cfg.href = this.href;
1575 if(this.alt !== false){
1580 if(this.target !== false) {
1581 cfg.target = this.target;
1587 initEvents: function() {
1589 if(!this.href || this.preventDefault){
1590 this.el.on('click', this.onClick, this);
1594 onClick : function(e)
1596 if(this.preventDefault){
1599 //Roo.log('img onclick');
1600 this.fireEvent('click', this, e);
1613 * @class Roo.bootstrap.Header
1614 * @extends Roo.bootstrap.Component
1615 * Bootstrap Header class
1616 * @cfg {String} html content of header
1617 * @cfg {Number} level (1|2|3|4|5|6) default 1
1620 * Create a new Header
1621 * @param {Object} config The config object
1625 Roo.bootstrap.Header = function(config){
1626 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1629 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1637 getAutoCreate : function(){
1642 tag: 'h' + (1 *this.level),
1643 html: this.html || ''
1655 * Ext JS Library 1.1.1
1656 * Copyright(c) 2006-2007, Ext JS, LLC.
1658 * Originally Released Under LGPL - original licence link has changed is not relivant.
1661 * <script type="text/javascript">
1665 * @class Roo.bootstrap.MenuMgr
1666 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1669 Roo.bootstrap.MenuMgr = function(){
1670 var menus, active, groups = {}, attached = false, lastShow = new Date();
1672 // private - called when first menu is created
1675 active = new Roo.util.MixedCollection();
1676 Roo.get(document).addKeyListener(27, function(){
1677 if(active.length > 0){
1685 if(active && active.length > 0){
1686 var c = active.clone();
1696 if(active.length < 1){
1697 Roo.get(document).un("mouseup", onMouseDown);
1705 var last = active.last();
1706 lastShow = new Date();
1709 Roo.get(document).on("mouseup", onMouseDown);
1714 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1715 m.parentMenu.activeChild = m;
1716 }else if(last && last.isVisible()){
1717 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1722 function onBeforeHide(m){
1724 m.activeChild.hide();
1726 if(m.autoHideTimer){
1727 clearTimeout(m.autoHideTimer);
1728 delete m.autoHideTimer;
1733 function onBeforeShow(m){
1734 var pm = m.parentMenu;
1735 if(!pm && !m.allowOtherMenus){
1737 }else if(pm && pm.activeChild && active != m){
1738 pm.activeChild.hide();
1742 // private this should really trigger on mouseup..
1743 function onMouseDown(e){
1744 Roo.log("on Mouse Up");
1745 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1755 function onBeforeCheck(mi, state){
1757 var g = groups[mi.group];
1758 for(var i = 0, l = g.length; i < l; i++){
1760 g[i].setChecked(false);
1769 * Hides all menus that are currently visible
1771 hideAll : function(){
1776 register : function(menu){
1780 menus[menu.id] = menu;
1781 menu.on("beforehide", onBeforeHide);
1782 menu.on("hide", onHide);
1783 menu.on("beforeshow", onBeforeShow);
1784 menu.on("show", onShow);
1786 if(g && menu.events["checkchange"]){
1790 groups[g].push(menu);
1791 menu.on("checkchange", onCheck);
1796 * Returns a {@link Roo.menu.Menu} object
1797 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1798 * be used to generate and return a new Menu instance.
1800 get : function(menu){
1801 if(typeof menu == "string"){ // menu id
1803 }else if(menu.events){ // menu instance
1806 /*else if(typeof menu.length == 'number'){ // array of menu items?
1807 return new Roo.bootstrap.Menu({items:menu});
1808 }else{ // otherwise, must be a config
1809 return new Roo.bootstrap.Menu(menu);
1816 unregister : function(menu){
1817 delete menus[menu.id];
1818 menu.un("beforehide", onBeforeHide);
1819 menu.un("hide", onHide);
1820 menu.un("beforeshow", onBeforeShow);
1821 menu.un("show", onShow);
1823 if(g && menu.events["checkchange"]){
1824 groups[g].remove(menu);
1825 menu.un("checkchange", onCheck);
1830 registerCheckable : function(menuItem){
1831 var g = menuItem.group;
1836 groups[g].push(menuItem);
1837 menuItem.on("beforecheckchange", onBeforeCheck);
1842 unregisterCheckable : function(menuItem){
1843 var g = menuItem.group;
1845 groups[g].remove(menuItem);
1846 menuItem.un("beforecheckchange", onBeforeCheck);
1858 * @class Roo.bootstrap.Menu
1859 * @extends Roo.bootstrap.Component
1860 * Bootstrap Menu class - container for MenuItems
1861 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1865 * @param {Object} config The config object
1869 Roo.bootstrap.Menu = function(config){
1870 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1871 if (this.registerMenu) {
1872 Roo.bootstrap.MenuMgr.register(this);
1877 * Fires before this menu is displayed
1878 * @param {Roo.menu.Menu} this
1883 * Fires before this menu is hidden
1884 * @param {Roo.menu.Menu} this
1889 * Fires after this menu is displayed
1890 * @param {Roo.menu.Menu} this
1895 * Fires after this menu is hidden
1896 * @param {Roo.menu.Menu} this
1901 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1902 * @param {Roo.menu.Menu} this
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904 * @param {Roo.EventObject} e
1909 * Fires when the mouse is hovering over this menu
1910 * @param {Roo.menu.Menu} this
1911 * @param {Roo.EventObject} e
1912 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1917 * Fires when the mouse exits this menu
1918 * @param {Roo.menu.Menu} this
1919 * @param {Roo.EventObject} e
1920 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1925 * Fires when a menu item contained in this menu is clicked
1926 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1927 * @param {Roo.EventObject} e
1931 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1934 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1938 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1941 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1943 registerMenu : true,
1945 menuItems :false, // stores the menu items..
1951 getChildContainer : function() {
1955 getAutoCreate : function(){
1957 //if (['right'].indexOf(this.align)!==-1) {
1958 // cfg.cn[1].cls += ' pull-right'
1964 cls : 'dropdown-menu' ,
1965 style : 'z-index:1000'
1969 if (this.type === 'submenu') {
1970 cfg.cls = 'submenu active';
1972 if (this.type === 'treeview') {
1973 cfg.cls = 'treeview-menu';
1978 initEvents : function() {
1980 // Roo.log("ADD event");
1981 // Roo.log(this.triggerEl.dom);
1982 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1984 this.triggerEl.addClass('dropdown-toggle');
1987 this.el.on('touchstart' , this.onTouch, this);
1989 this.el.on('click' , this.onClick, this);
1991 this.el.on("mouseover", this.onMouseOver, this);
1992 this.el.on("mouseout", this.onMouseOut, this);
1996 findTargetItem : function(e)
1998 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2002 //Roo.log(t); Roo.log(t.id);
2004 //Roo.log(this.menuitems);
2005 return this.menuitems.get(t.id);
2007 //return this.items.get(t.menuItemId);
2013 onTouch : function(e)
2015 //e.stopEvent(); this make the user popdown broken
2019 onClick : function(e)
2021 Roo.log("menu.onClick");
2022 var t = this.findTargetItem(e);
2023 if(!t || t.isContainer){
2028 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2029 if(t == this.activeItem && t.shouldDeactivate(e)){
2030 this.activeItem.deactivate();
2031 delete this.activeItem;
2035 this.setActiveItem(t, true);
2043 Roo.log('pass click event');
2047 this.fireEvent("click", this, t, e);
2051 onMouseOver : function(e){
2052 var t = this.findTargetItem(e);
2055 // if(t.canActivate && !t.disabled){
2056 // this.setActiveItem(t, true);
2060 this.fireEvent("mouseover", this, e, t);
2062 isVisible : function(){
2063 return !this.hidden;
2065 onMouseOut : function(e){
2066 var t = this.findTargetItem(e);
2069 // if(t == this.activeItem && t.shouldDeactivate(e)){
2070 // this.activeItem.deactivate();
2071 // delete this.activeItem;
2074 this.fireEvent("mouseout", this, e, t);
2079 * Displays this menu relative to another element
2080 * @param {String/HTMLElement/Roo.Element} element The element to align to
2081 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2082 * the element (defaults to this.defaultAlign)
2083 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2085 show : function(el, pos, parentMenu){
2086 this.parentMenu = parentMenu;
2090 this.fireEvent("beforeshow", this);
2091 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2094 * Displays this menu at a specific xy position
2095 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2096 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2098 showAt : function(xy, parentMenu, /* private: */_e){
2099 this.parentMenu = parentMenu;
2104 this.fireEvent("beforeshow", this);
2105 //xy = this.el.adjustForConstraints(xy);
2109 this.hideMenuItems();
2110 this.hidden = false;
2111 this.triggerEl.addClass('open');
2113 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2114 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2119 this.fireEvent("show", this);
2125 this.doFocus.defer(50, this);
2129 doFocus : function(){
2131 this.focusEl.focus();
2136 * Hides this menu and optionally all parent menus
2137 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2139 hide : function(deep){
2141 this.hideMenuItems();
2142 if(this.el && this.isVisible()){
2143 this.fireEvent("beforehide", this);
2144 if(this.activeItem){
2145 this.activeItem.deactivate();
2146 this.activeItem = null;
2148 this.triggerEl.removeClass('open');;
2150 this.fireEvent("hide", this);
2152 if(deep === true && this.parentMenu){
2153 this.parentMenu.hide(true);
2157 onTriggerPress : function(e)
2160 Roo.log('trigger press');
2161 //Roo.log(e.getTarget());
2162 // Roo.log(this.triggerEl.dom);
2163 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2167 if (this.isVisible()) {
2172 this.show(this.triggerEl, false, false);
2181 hideMenuItems : function()
2183 //$(backdrop).remove()
2184 Roo.select('.open',true).each(function(aa) {
2186 aa.removeClass('open');
2187 //var parent = getParent($(this))
2188 //var relatedTarget = { relatedTarget: this }
2190 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2191 //if (e.isDefaultPrevented()) return
2192 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2195 addxtypeChild : function (tree, cntr) {
2196 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2198 this.menuitems.add(comp);
2219 * @class Roo.bootstrap.MenuItem
2220 * @extends Roo.bootstrap.Component
2221 * Bootstrap MenuItem class
2222 * @cfg {String} html the menu label
2223 * @cfg {String} href the link
2224 * @cfg {Boolean} preventDefault (true | false) default true
2225 * @cfg {Boolean} isContainer (true | false) default false
2229 * Create a new MenuItem
2230 * @param {Object} config The config object
2234 Roo.bootstrap.MenuItem = function(config){
2235 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2240 * The raw click event for the entire grid.
2241 * @param {Roo.bootstrap.MenuItem} this
2242 * @param {Roo.EventObject} e
2248 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2252 preventDefault: true,
2253 isContainer : false,
2255 getAutoCreate : function(){
2257 if(this.isContainer){
2260 cls: 'dropdown-menu-item'
2266 cls: 'dropdown-menu-item',
2275 if (this.parent().type == 'treeview') {
2276 cfg.cls = 'treeview-menu';
2279 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2280 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2284 initEvents: function() {
2286 //this.el.select('a').on('click', this.onClick, this);
2289 onClick : function(e)
2291 Roo.log('item on click ');
2292 //if(this.preventDefault){
2293 // e.preventDefault();
2295 //this.parent().hideMenuItems();
2297 this.fireEvent('click', this, e);
2316 * @class Roo.bootstrap.MenuSeparator
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap MenuSeparator class
2321 * Create a new MenuItem
2322 * @param {Object} config The config object
2326 Roo.bootstrap.MenuSeparator = function(config){
2327 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2330 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2332 getAutoCreate : function(){
2351 * @class Roo.bootstrap.Modal
2352 * @extends Roo.bootstrap.Component
2353 * Bootstrap Modal class
2354 * @cfg {String} title Title of dialog
2355 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2356 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2357 * @cfg {Boolean} specificTitle default false
2358 * @cfg {Array} buttons Array of buttons or standard button set..
2359 * @cfg {String} buttonPosition (left|right|center) default right
2360 * @cfg {Boolean} animate default true
2361 * @cfg {Boolean} allow_close default true
2364 * Create a new Modal Dialog
2365 * @param {Object} config The config object
2368 Roo.bootstrap.Modal = function(config){
2369 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2374 * The raw btnclick event for the button
2375 * @param {Roo.EventObject} e
2379 this.buttons = this.buttons || [];
2382 this.tmpl = Roo.factory(this.tmpl);
2387 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2389 title : 'test dialog',
2399 specificTitle: false,
2401 buttonPosition: 'right',
2415 onRender : function(ct, position)
2417 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2420 var cfg = Roo.apply({}, this.getAutoCreate());
2423 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2425 //if (!cfg.name.length) {
2429 cfg.cls += ' ' + this.cls;
2432 cfg.style = this.style;
2434 this.el = Roo.get(document.body).createChild(cfg, position);
2436 //var type = this.el.dom.type;
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
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);
13583 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13584 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13586 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13588 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13589 this.store.on('load', this.onTouchViewLoad, this);
13590 this.store.on('loadexception', this.onTouchViewLoadException, this);
13592 if(this.hiddenName){
13594 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13596 this.hiddenField.dom.value =
13597 this.hiddenValue !== undefined ? this.hiddenValue :
13598 this.value !== undefined ? this.value : '';
13600 this.el.dom.removeAttribute('name');
13601 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13605 this.choices = this.el.select('ul.select2-choices', true).first();
13606 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13609 if(this.removable && !this.multiple){
13610 var close = this.closeTriggerEl();
13612 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13613 close.on('click', this.removeBtnClick, this, close);
13622 renderTouchView : function()
13624 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13625 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13627 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13628 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13630 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13631 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13632 this.touchViewBodyEl.setStyle('overflow', 'auto');
13634 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13635 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13637 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13638 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13642 showTouchView : function()
13644 this.touchViewHeaderEl.hide();
13646 if(this.fieldLabel.length){
13647 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13648 this.touchViewHeaderEl.show();
13651 this.touchViewEl.show();
13653 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13654 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13656 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13658 if(this.fieldLabel.length){
13659 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13662 this.touchViewBodyEl.setHeight(bodyHeight);
13666 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13668 this.touchViewEl.addClass('in');
13671 this.doTouchViewQuery();
13675 hideTouchView : function()
13677 this.touchViewEl.removeClass('in');
13681 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13683 this.touchViewEl.setStyle('display', 'none');
13688 setTouchViewValue : function()
13695 Roo.each(this.tickItems, function(o){
13700 this.hideTouchView();
13703 doTouchViewQuery : function()
13712 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13716 if(!this.alwaysQuery || this.mode == 'local'){
13717 this.onTouchViewLoad();
13724 onTouchViewBeforeLoad : function(combo,opts)
13730 onTouchViewLoad : function()
13732 if(this.store.getCount() < 1){
13733 this.onTouchViewEmptyResults();
13737 this.clearTouchView();
13739 var rawValue = this.getRawValue();
13741 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13743 this.tickItems = [];
13745 this.store.data.each(function(d, rowIndex){
13746 var row = this.touchViewListGroup.createChild(template);
13748 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13751 html : d.data[this.displayField]
13754 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13755 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13759 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13760 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13763 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13764 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13765 this.tickItems.push(d.data);
13768 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13772 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13774 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13776 if(this.fieldLabel.length){
13777 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13780 var listHeight = this.touchViewListGroup.getHeight();
13784 if(firstChecked && listHeight > bodyHeight){
13785 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13790 onTouchViewLoadException : function()
13792 this.hideTouchView();
13795 onTouchViewEmptyResults : function()
13797 this.clearTouchView();
13799 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13801 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13805 clearTouchView : function()
13807 this.touchViewListGroup.dom.innerHTML = '';
13810 onTouchViewClick : function(e, el, o)
13812 e.preventDefault();
13815 var rowIndex = o.rowIndex;
13817 var r = this.store.getAt(rowIndex);
13819 if(!this.multiple){
13820 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13821 c.dom.removeAttribute('checked');
13824 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13826 this.setFromData(r.data);
13828 var close = this.closeTriggerEl();
13834 this.hideTouchView();
13836 this.fireEvent('select', this, r, rowIndex);
13841 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13842 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13843 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13847 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13848 this.addItem(r.data);
13849 this.tickItems.push(r.data);
13855 * @cfg {Boolean} grow
13859 * @cfg {Number} growMin
13863 * @cfg {Number} growMax
13872 Roo.apply(Roo.bootstrap.ComboBox, {
13876 cls: 'modal-header',
13898 cls: 'list-group-item',
13902 cls: 'roo-combobox-list-group-item-value'
13906 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13920 listItemCheckbox : {
13922 cls: 'list-group-item',
13926 cls: 'roo-combobox-list-group-item-value'
13930 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13946 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13951 cls: 'modal-footer',
13959 cls: 'col-xs-6 text-left',
13962 cls: 'btn btn-danger roo-touch-view-cancel',
13968 cls: 'col-xs-6 text-right',
13971 cls: 'btn btn-success roo-touch-view-ok',
13982 Roo.apply(Roo.bootstrap.ComboBox, {
13984 touchViewTemplate : {
13986 cls: 'modal fade roo-combobox-touch-view',
13990 cls: 'modal-dialog',
13991 style : 'position:fixed', // we have to fix position....
13995 cls: 'modal-content',
13997 Roo.bootstrap.ComboBox.header,
13998 Roo.bootstrap.ComboBox.body,
13999 Roo.bootstrap.ComboBox.footer
14008 * Ext JS Library 1.1.1
14009 * Copyright(c) 2006-2007, Ext JS, LLC.
14011 * Originally Released Under LGPL - original licence link has changed is not relivant.
14014 * <script type="text/javascript">
14019 * @extends Roo.util.Observable
14020 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14021 * This class also supports single and multi selection modes. <br>
14022 * Create a data model bound view:
14024 var store = new Roo.data.Store(...);
14026 var view = new Roo.View({
14028 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14030 singleSelect: true,
14031 selectedClass: "ydataview-selected",
14035 // listen for node click?
14036 view.on("click", function(vw, index, node, e){
14037 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14041 dataModel.load("foobar.xml");
14043 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14045 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14046 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14048 * Note: old style constructor is still suported (container, template, config)
14051 * Create a new View
14052 * @param {Object} config The config object
14055 Roo.View = function(config, depreciated_tpl, depreciated_config){
14057 this.parent = false;
14059 if (typeof(depreciated_tpl) == 'undefined') {
14060 // new way.. - universal constructor.
14061 Roo.apply(this, config);
14062 this.el = Roo.get(this.el);
14065 this.el = Roo.get(config);
14066 this.tpl = depreciated_tpl;
14067 Roo.apply(this, depreciated_config);
14069 this.wrapEl = this.el.wrap().wrap();
14070 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14073 if(typeof(this.tpl) == "string"){
14074 this.tpl = new Roo.Template(this.tpl);
14076 // support xtype ctors..
14077 this.tpl = new Roo.factory(this.tpl, Roo);
14081 this.tpl.compile();
14086 * @event beforeclick
14087 * Fires before a click is processed. Returns false to cancel the default action.
14088 * @param {Roo.View} this
14089 * @param {Number} index The index of the target node
14090 * @param {HTMLElement} node The target node
14091 * @param {Roo.EventObject} e The raw event object
14093 "beforeclick" : true,
14096 * Fires when a template node is clicked.
14097 * @param {Roo.View} this
14098 * @param {Number} index The index of the target node
14099 * @param {HTMLElement} node The target node
14100 * @param {Roo.EventObject} e The raw event object
14105 * Fires when a template node is double clicked.
14106 * @param {Roo.View} this
14107 * @param {Number} index The index of the target node
14108 * @param {HTMLElement} node The target node
14109 * @param {Roo.EventObject} e The raw event object
14113 * @event contextmenu
14114 * Fires when a template node is right clicked.
14115 * @param {Roo.View} this
14116 * @param {Number} index The index of the target node
14117 * @param {HTMLElement} node The target node
14118 * @param {Roo.EventObject} e The raw event object
14120 "contextmenu" : true,
14122 * @event selectionchange
14123 * Fires when the selected nodes change.
14124 * @param {Roo.View} this
14125 * @param {Array} selections Array of the selected nodes
14127 "selectionchange" : true,
14130 * @event beforeselect
14131 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14132 * @param {Roo.View} this
14133 * @param {HTMLElement} node The node to be selected
14134 * @param {Array} selections Array of currently selected nodes
14136 "beforeselect" : true,
14138 * @event preparedata
14139 * Fires on every row to render, to allow you to change the data.
14140 * @param {Roo.View} this
14141 * @param {Object} data to be rendered (change this)
14143 "preparedata" : true
14151 "click": this.onClick,
14152 "dblclick": this.onDblClick,
14153 "contextmenu": this.onContextMenu,
14157 this.selections = [];
14159 this.cmp = new Roo.CompositeElementLite([]);
14161 this.store = Roo.factory(this.store, Roo.data);
14162 this.setStore(this.store, true);
14165 if ( this.footer && this.footer.xtype) {
14167 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14169 this.footer.dataSource = this.store;
14170 this.footer.container = fctr;
14171 this.footer = Roo.factory(this.footer, Roo);
14172 fctr.insertFirst(this.el);
14174 // this is a bit insane - as the paging toolbar seems to detach the el..
14175 // dom.parentNode.parentNode.parentNode
14176 // they get detached?
14180 Roo.View.superclass.constructor.call(this);
14185 Roo.extend(Roo.View, Roo.util.Observable, {
14188 * @cfg {Roo.data.Store} store Data store to load data from.
14193 * @cfg {String|Roo.Element} el The container element.
14198 * @cfg {String|Roo.Template} tpl The template used by this View
14202 * @cfg {String} dataName the named area of the template to use as the data area
14203 * Works with domtemplates roo-name="name"
14207 * @cfg {String} selectedClass The css class to add to selected nodes
14209 selectedClass : "x-view-selected",
14211 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14216 * @cfg {String} text to display on mask (default Loading)
14220 * @cfg {Boolean} multiSelect Allow multiple selection
14222 multiSelect : false,
14224 * @cfg {Boolean} singleSelect Allow single selection
14226 singleSelect: false,
14229 * @cfg {Boolean} toggleSelect - selecting
14231 toggleSelect : false,
14234 * @cfg {Boolean} tickable - selecting
14239 * Returns the element this view is bound to.
14240 * @return {Roo.Element}
14242 getEl : function(){
14243 return this.wrapEl;
14249 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14251 refresh : function(){
14252 //Roo.log('refresh');
14255 // if we are using something like 'domtemplate', then
14256 // the what gets used is:
14257 // t.applySubtemplate(NAME, data, wrapping data..)
14258 // the outer template then get' applied with
14259 // the store 'extra data'
14260 // and the body get's added to the
14261 // roo-name="data" node?
14262 // <span class='roo-tpl-{name}'></span> ?????
14266 this.clearSelections();
14267 this.el.update("");
14269 var records = this.store.getRange();
14270 if(records.length < 1) {
14272 // is this valid?? = should it render a template??
14274 this.el.update(this.emptyText);
14278 if (this.dataName) {
14279 this.el.update(t.apply(this.store.meta)); //????
14280 el = this.el.child('.roo-tpl-' + this.dataName);
14283 for(var i = 0, len = records.length; i < len; i++){
14284 var data = this.prepareData(records[i].data, i, records[i]);
14285 this.fireEvent("preparedata", this, data, i, records[i]);
14287 var d = Roo.apply({}, data);
14290 Roo.apply(d, {'roo-id' : Roo.id()});
14294 Roo.each(this.parent.item, function(item){
14295 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14298 Roo.apply(d, {'roo-data-checked' : 'checked'});
14302 html[html.length] = Roo.util.Format.trim(
14304 t.applySubtemplate(this.dataName, d, this.store.meta) :
14311 el.update(html.join(""));
14312 this.nodes = el.dom.childNodes;
14313 this.updateIndexes(0);
14318 * Function to override to reformat the data that is sent to
14319 * the template for each node.
14320 * DEPRICATED - use the preparedata event handler.
14321 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14322 * a JSON object for an UpdateManager bound view).
14324 prepareData : function(data, index, record)
14326 this.fireEvent("preparedata", this, data, index, record);
14330 onUpdate : function(ds, record){
14331 // Roo.log('on update');
14332 this.clearSelections();
14333 var index = this.store.indexOf(record);
14334 var n = this.nodes[index];
14335 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14336 n.parentNode.removeChild(n);
14337 this.updateIndexes(index, index);
14343 onAdd : function(ds, records, index)
14345 //Roo.log(['on Add', ds, records, index] );
14346 this.clearSelections();
14347 if(this.nodes.length == 0){
14351 var n = this.nodes[index];
14352 for(var i = 0, len = records.length; i < len; i++){
14353 var d = this.prepareData(records[i].data, i, records[i]);
14355 this.tpl.insertBefore(n, d);
14358 this.tpl.append(this.el, d);
14361 this.updateIndexes(index);
14364 onRemove : function(ds, record, index){
14365 // Roo.log('onRemove');
14366 this.clearSelections();
14367 var el = this.dataName ?
14368 this.el.child('.roo-tpl-' + this.dataName) :
14371 el.dom.removeChild(this.nodes[index]);
14372 this.updateIndexes(index);
14376 * Refresh an individual node.
14377 * @param {Number} index
14379 refreshNode : function(index){
14380 this.onUpdate(this.store, this.store.getAt(index));
14383 updateIndexes : function(startIndex, endIndex){
14384 var ns = this.nodes;
14385 startIndex = startIndex || 0;
14386 endIndex = endIndex || ns.length - 1;
14387 for(var i = startIndex; i <= endIndex; i++){
14388 ns[i].nodeIndex = i;
14393 * Changes the data store this view uses and refresh the view.
14394 * @param {Store} store
14396 setStore : function(store, initial){
14397 if(!initial && this.store){
14398 this.store.un("datachanged", this.refresh);
14399 this.store.un("add", this.onAdd);
14400 this.store.un("remove", this.onRemove);
14401 this.store.un("update", this.onUpdate);
14402 this.store.un("clear", this.refresh);
14403 this.store.un("beforeload", this.onBeforeLoad);
14404 this.store.un("load", this.onLoad);
14405 this.store.un("loadexception", this.onLoad);
14409 store.on("datachanged", this.refresh, this);
14410 store.on("add", this.onAdd, this);
14411 store.on("remove", this.onRemove, this);
14412 store.on("update", this.onUpdate, this);
14413 store.on("clear", this.refresh, this);
14414 store.on("beforeload", this.onBeforeLoad, this);
14415 store.on("load", this.onLoad, this);
14416 store.on("loadexception", this.onLoad, this);
14424 * onbeforeLoad - masks the loading area.
14427 onBeforeLoad : function(store,opts)
14429 //Roo.log('onBeforeLoad');
14431 this.el.update("");
14433 this.el.mask(this.mask ? this.mask : "Loading" );
14435 onLoad : function ()
14442 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14443 * @param {HTMLElement} node
14444 * @return {HTMLElement} The template node
14446 findItemFromChild : function(node){
14447 var el = this.dataName ?
14448 this.el.child('.roo-tpl-' + this.dataName,true) :
14451 if(!node || node.parentNode == el){
14454 var p = node.parentNode;
14455 while(p && p != el){
14456 if(p.parentNode == el){
14465 onClick : function(e){
14466 var item = this.findItemFromChild(e.getTarget());
14468 var index = this.indexOf(item);
14469 if(this.onItemClick(item, index, e) !== false){
14470 this.fireEvent("click", this, index, item, e);
14473 this.clearSelections();
14478 onContextMenu : function(e){
14479 var item = this.findItemFromChild(e.getTarget());
14481 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14486 onDblClick : function(e){
14487 var item = this.findItemFromChild(e.getTarget());
14489 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14493 onItemClick : function(item, index, e)
14495 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14498 if (this.toggleSelect) {
14499 var m = this.isSelected(item) ? 'unselect' : 'select';
14502 _t[m](item, true, false);
14505 if(this.multiSelect || this.singleSelect){
14506 if(this.multiSelect && e.shiftKey && this.lastSelection){
14507 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14509 this.select(item, this.multiSelect && e.ctrlKey);
14510 this.lastSelection = item;
14513 if(!this.tickable){
14514 e.preventDefault();
14522 * Get the number of selected nodes.
14525 getSelectionCount : function(){
14526 return this.selections.length;
14530 * Get the currently selected nodes.
14531 * @return {Array} An array of HTMLElements
14533 getSelectedNodes : function(){
14534 return this.selections;
14538 * Get the indexes of the selected nodes.
14541 getSelectedIndexes : function(){
14542 var indexes = [], s = this.selections;
14543 for(var i = 0, len = s.length; i < len; i++){
14544 indexes.push(s[i].nodeIndex);
14550 * Clear all selections
14551 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14553 clearSelections : function(suppressEvent){
14554 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14555 this.cmp.elements = this.selections;
14556 this.cmp.removeClass(this.selectedClass);
14557 this.selections = [];
14558 if(!suppressEvent){
14559 this.fireEvent("selectionchange", this, this.selections);
14565 * Returns true if the passed node is selected
14566 * @param {HTMLElement/Number} node The node or node index
14567 * @return {Boolean}
14569 isSelected : function(node){
14570 var s = this.selections;
14574 node = this.getNode(node);
14575 return s.indexOf(node) !== -1;
14580 * @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
14581 * @param {Boolean} keepExisting (optional) true to keep existing selections
14582 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14584 select : function(nodeInfo, keepExisting, suppressEvent){
14585 if(nodeInfo instanceof Array){
14587 this.clearSelections(true);
14589 for(var i = 0, len = nodeInfo.length; i < len; i++){
14590 this.select(nodeInfo[i], true, true);
14594 var node = this.getNode(nodeInfo);
14595 if(!node || this.isSelected(node)){
14596 return; // already selected.
14599 this.clearSelections(true);
14602 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14603 Roo.fly(node).addClass(this.selectedClass);
14604 this.selections.push(node);
14605 if(!suppressEvent){
14606 this.fireEvent("selectionchange", this, this.selections);
14614 * @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
14615 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14616 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14618 unselect : function(nodeInfo, keepExisting, suppressEvent)
14620 if(nodeInfo instanceof Array){
14621 Roo.each(this.selections, function(s) {
14622 this.unselect(s, nodeInfo);
14626 var node = this.getNode(nodeInfo);
14627 if(!node || !this.isSelected(node)){
14628 //Roo.log("not selected");
14629 return; // not selected.
14633 Roo.each(this.selections, function(s) {
14635 Roo.fly(node).removeClass(this.selectedClass);
14642 this.selections= ns;
14643 this.fireEvent("selectionchange", this, this.selections);
14647 * Gets a template node.
14648 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14649 * @return {HTMLElement} The node or null if it wasn't found
14651 getNode : function(nodeInfo){
14652 if(typeof nodeInfo == "string"){
14653 return document.getElementById(nodeInfo);
14654 }else if(typeof nodeInfo == "number"){
14655 return this.nodes[nodeInfo];
14661 * Gets a range template nodes.
14662 * @param {Number} startIndex
14663 * @param {Number} endIndex
14664 * @return {Array} An array of nodes
14666 getNodes : function(start, end){
14667 var ns = this.nodes;
14668 start = start || 0;
14669 end = typeof end == "undefined" ? ns.length - 1 : end;
14672 for(var i = start; i <= end; i++){
14676 for(var i = start; i >= end; i--){
14684 * Finds the index of the passed node
14685 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14686 * @return {Number} The index of the node or -1
14688 indexOf : function(node){
14689 node = this.getNode(node);
14690 if(typeof node.nodeIndex == "number"){
14691 return node.nodeIndex;
14693 var ns = this.nodes;
14694 for(var i = 0, len = ns.length; i < len; i++){
14705 * based on jquery fullcalendar
14709 Roo.bootstrap = Roo.bootstrap || {};
14711 * @class Roo.bootstrap.Calendar
14712 * @extends Roo.bootstrap.Component
14713 * Bootstrap Calendar class
14714 * @cfg {Boolean} loadMask (true|false) default false
14715 * @cfg {Object} header generate the user specific header of the calendar, default false
14718 * Create a new Container
14719 * @param {Object} config The config object
14724 Roo.bootstrap.Calendar = function(config){
14725 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14729 * Fires when a date is selected
14730 * @param {DatePicker} this
14731 * @param {Date} date The selected date
14735 * @event monthchange
14736 * Fires when the displayed month changes
14737 * @param {DatePicker} this
14738 * @param {Date} date The selected month
14740 'monthchange': true,
14742 * @event evententer
14743 * Fires when mouse over an event
14744 * @param {Calendar} this
14745 * @param {event} Event
14747 'evententer': true,
14749 * @event eventleave
14750 * Fires when the mouse leaves an
14751 * @param {Calendar} this
14754 'eventleave': true,
14756 * @event eventclick
14757 * Fires when the mouse click an
14758 * @param {Calendar} this
14767 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14770 * @cfg {Number} startDay
14771 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14779 getAutoCreate : function(){
14782 var fc_button = function(name, corner, style, content ) {
14783 return Roo.apply({},{
14785 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14787 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14790 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14801 style : 'width:100%',
14808 cls : 'fc-header-left',
14810 fc_button('prev', 'left', 'arrow', '‹' ),
14811 fc_button('next', 'right', 'arrow', '›' ),
14812 { tag: 'span', cls: 'fc-header-space' },
14813 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14821 cls : 'fc-header-center',
14825 cls: 'fc-header-title',
14828 html : 'month / year'
14836 cls : 'fc-header-right',
14838 /* fc_button('month', 'left', '', 'month' ),
14839 fc_button('week', '', '', 'week' ),
14840 fc_button('day', 'right', '', 'day' )
14852 header = this.header;
14855 var cal_heads = function() {
14857 // fixme - handle this.
14859 for (var i =0; i < Date.dayNames.length; i++) {
14860 var d = Date.dayNames[i];
14863 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14864 html : d.substring(0,3)
14868 ret[0].cls += ' fc-first';
14869 ret[6].cls += ' fc-last';
14872 var cal_cell = function(n) {
14875 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14880 cls: 'fc-day-number',
14884 cls: 'fc-day-content',
14888 style: 'position: relative;' // height: 17px;
14900 var cal_rows = function() {
14903 for (var r = 0; r < 6; r++) {
14910 for (var i =0; i < Date.dayNames.length; i++) {
14911 var d = Date.dayNames[i];
14912 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14915 row.cn[0].cls+=' fc-first';
14916 row.cn[0].cn[0].style = 'min-height:90px';
14917 row.cn[6].cls+=' fc-last';
14921 ret[0].cls += ' fc-first';
14922 ret[4].cls += ' fc-prev-last';
14923 ret[5].cls += ' fc-last';
14930 cls: 'fc-border-separate',
14931 style : 'width:100%',
14939 cls : 'fc-first fc-last',
14957 cls : 'fc-content',
14958 style : "position: relative;",
14961 cls : 'fc-view fc-view-month fc-grid',
14962 style : 'position: relative',
14963 unselectable : 'on',
14966 cls : 'fc-event-container',
14967 style : 'position:absolute;z-index:8;top:0;left:0;'
14985 initEvents : function()
14988 throw "can not find store for calendar";
14994 style: "text-align:center",
14998 style: "background-color:white;width:50%;margin:250 auto",
15002 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15013 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15015 var size = this.el.select('.fc-content', true).first().getSize();
15016 this.maskEl.setSize(size.width, size.height);
15017 this.maskEl.enableDisplayMode("block");
15018 if(!this.loadMask){
15019 this.maskEl.hide();
15022 this.store = Roo.factory(this.store, Roo.data);
15023 this.store.on('load', this.onLoad, this);
15024 this.store.on('beforeload', this.onBeforeLoad, this);
15028 this.cells = this.el.select('.fc-day',true);
15029 //Roo.log(this.cells);
15030 this.textNodes = this.el.query('.fc-day-number');
15031 this.cells.addClassOnOver('fc-state-hover');
15033 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15034 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15035 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15036 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15038 this.on('monthchange', this.onMonthChange, this);
15040 this.update(new Date().clearTime());
15043 resize : function() {
15044 var sz = this.el.getSize();
15046 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15047 this.el.select('.fc-day-content div',true).setHeight(34);
15052 showPrevMonth : function(e){
15053 this.update(this.activeDate.add("mo", -1));
15055 showToday : function(e){
15056 this.update(new Date().clearTime());
15059 showNextMonth : function(e){
15060 this.update(this.activeDate.add("mo", 1));
15064 showPrevYear : function(){
15065 this.update(this.activeDate.add("y", -1));
15069 showNextYear : function(){
15070 this.update(this.activeDate.add("y", 1));
15075 update : function(date)
15077 var vd = this.activeDate;
15078 this.activeDate = date;
15079 // if(vd && this.el){
15080 // var t = date.getTime();
15081 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15082 // Roo.log('using add remove');
15084 // this.fireEvent('monthchange', this, date);
15086 // this.cells.removeClass("fc-state-highlight");
15087 // this.cells.each(function(c){
15088 // if(c.dateValue == t){
15089 // c.addClass("fc-state-highlight");
15090 // setTimeout(function(){
15091 // try{c.dom.firstChild.focus();}catch(e){}
15101 var days = date.getDaysInMonth();
15103 var firstOfMonth = date.getFirstDateOfMonth();
15104 var startingPos = firstOfMonth.getDay()-this.startDay;
15106 if(startingPos < this.startDay){
15110 var pm = date.add(Date.MONTH, -1);
15111 var prevStart = pm.getDaysInMonth()-startingPos;
15113 this.cells = this.el.select('.fc-day',true);
15114 this.textNodes = this.el.query('.fc-day-number');
15115 this.cells.addClassOnOver('fc-state-hover');
15117 var cells = this.cells.elements;
15118 var textEls = this.textNodes;
15120 Roo.each(cells, function(cell){
15121 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15124 days += startingPos;
15126 // convert everything to numbers so it's fast
15127 var day = 86400000;
15128 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15131 //Roo.log(prevStart);
15133 var today = new Date().clearTime().getTime();
15134 var sel = date.clearTime().getTime();
15135 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15136 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15137 var ddMatch = this.disabledDatesRE;
15138 var ddText = this.disabledDatesText;
15139 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15140 var ddaysText = this.disabledDaysText;
15141 var format = this.format;
15143 var setCellClass = function(cal, cell){
15147 //Roo.log('set Cell Class');
15149 var t = d.getTime();
15153 cell.dateValue = t;
15155 cell.className += " fc-today";
15156 cell.className += " fc-state-highlight";
15157 cell.title = cal.todayText;
15160 // disable highlight in other month..
15161 //cell.className += " fc-state-highlight";
15166 cell.className = " fc-state-disabled";
15167 cell.title = cal.minText;
15171 cell.className = " fc-state-disabled";
15172 cell.title = cal.maxText;
15176 if(ddays.indexOf(d.getDay()) != -1){
15177 cell.title = ddaysText;
15178 cell.className = " fc-state-disabled";
15181 if(ddMatch && format){
15182 var fvalue = d.dateFormat(format);
15183 if(ddMatch.test(fvalue)){
15184 cell.title = ddText.replace("%0", fvalue);
15185 cell.className = " fc-state-disabled";
15189 if (!cell.initialClassName) {
15190 cell.initialClassName = cell.dom.className;
15193 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15198 for(; i < startingPos; i++) {
15199 textEls[i].innerHTML = (++prevStart);
15200 d.setDate(d.getDate()+1);
15202 cells[i].className = "fc-past fc-other-month";
15203 setCellClass(this, cells[i]);
15208 for(; i < days; i++){
15209 intDay = i - startingPos + 1;
15210 textEls[i].innerHTML = (intDay);
15211 d.setDate(d.getDate()+1);
15213 cells[i].className = ''; // "x-date-active";
15214 setCellClass(this, cells[i]);
15218 for(; i < 42; i++) {
15219 textEls[i].innerHTML = (++extraDays);
15220 d.setDate(d.getDate()+1);
15222 cells[i].className = "fc-future fc-other-month";
15223 setCellClass(this, cells[i]);
15226 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15228 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15230 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15231 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15233 if(totalRows != 6){
15234 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15235 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15238 this.fireEvent('monthchange', this, date);
15242 if(!this.internalRender){
15243 var main = this.el.dom.firstChild;
15244 var w = main.offsetWidth;
15245 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15246 Roo.fly(main).setWidth(w);
15247 this.internalRender = true;
15248 // opera does not respect the auto grow header center column
15249 // then, after it gets a width opera refuses to recalculate
15250 // without a second pass
15251 if(Roo.isOpera && !this.secondPass){
15252 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15253 this.secondPass = true;
15254 this.update.defer(10, this, [date]);
15261 findCell : function(dt) {
15262 dt = dt.clearTime().getTime();
15264 this.cells.each(function(c){
15265 //Roo.log("check " +c.dateValue + '?=' + dt);
15266 if(c.dateValue == dt){
15276 findCells : function(ev) {
15277 var s = ev.start.clone().clearTime().getTime();
15279 var e= ev.end.clone().clearTime().getTime();
15282 this.cells.each(function(c){
15283 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15285 if(c.dateValue > e){
15288 if(c.dateValue < s){
15297 // findBestRow: function(cells)
15301 // for (var i =0 ; i < cells.length;i++) {
15302 // ret = Math.max(cells[i].rows || 0,ret);
15309 addItem : function(ev)
15311 // look for vertical location slot in
15312 var cells = this.findCells(ev);
15314 // ev.row = this.findBestRow(cells);
15316 // work out the location.
15320 for(var i =0; i < cells.length; i++) {
15322 cells[i].row = cells[0].row;
15325 cells[i].row = cells[i].row + 1;
15335 if (crow.start.getY() == cells[i].getY()) {
15337 crow.end = cells[i];
15354 cells[0].events.push(ev);
15356 this.calevents.push(ev);
15359 clearEvents: function() {
15361 if(!this.calevents){
15365 Roo.each(this.cells.elements, function(c){
15371 Roo.each(this.calevents, function(e) {
15372 Roo.each(e.els, function(el) {
15373 el.un('mouseenter' ,this.onEventEnter, this);
15374 el.un('mouseleave' ,this.onEventLeave, this);
15379 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15385 renderEvents: function()
15389 this.cells.each(function(c) {
15398 if(c.row != c.events.length){
15399 r = 4 - (4 - (c.row - c.events.length));
15402 c.events = ev.slice(0, r);
15403 c.more = ev.slice(r);
15405 if(c.more.length && c.more.length == 1){
15406 c.events.push(c.more.pop());
15409 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15413 this.cells.each(function(c) {
15415 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15418 for (var e = 0; e < c.events.length; e++){
15419 var ev = c.events[e];
15420 var rows = ev.rows;
15422 for(var i = 0; i < rows.length; i++) {
15424 // how many rows should it span..
15427 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15428 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15430 unselectable : "on",
15433 cls: 'fc-event-inner',
15437 // cls: 'fc-event-time',
15438 // html : cells.length > 1 ? '' : ev.time
15442 cls: 'fc-event-title',
15443 html : String.format('{0}', ev.title)
15450 cls: 'ui-resizable-handle ui-resizable-e',
15451 html : '  '
15458 cfg.cls += ' fc-event-start';
15460 if ((i+1) == rows.length) {
15461 cfg.cls += ' fc-event-end';
15464 var ctr = _this.el.select('.fc-event-container',true).first();
15465 var cg = ctr.createChild(cfg);
15467 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15468 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15470 var r = (c.more.length) ? 1 : 0;
15471 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15472 cg.setWidth(ebox.right - sbox.x -2);
15474 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15475 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15476 cg.on('click', _this.onEventClick, _this, ev);
15487 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15488 style : 'position: absolute',
15489 unselectable : "on",
15492 cls: 'fc-event-inner',
15496 cls: 'fc-event-title',
15504 cls: 'ui-resizable-handle ui-resizable-e',
15505 html : '  '
15511 var ctr = _this.el.select('.fc-event-container',true).first();
15512 var cg = ctr.createChild(cfg);
15514 var sbox = c.select('.fc-day-content',true).first().getBox();
15515 var ebox = c.select('.fc-day-content',true).first().getBox();
15517 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15518 cg.setWidth(ebox.right - sbox.x -2);
15520 cg.on('click', _this.onMoreEventClick, _this, c.more);
15530 onEventEnter: function (e, el,event,d) {
15531 this.fireEvent('evententer', this, el, event);
15534 onEventLeave: function (e, el,event,d) {
15535 this.fireEvent('eventleave', this, el, event);
15538 onEventClick: function (e, el,event,d) {
15539 this.fireEvent('eventclick', this, el, event);
15542 onMonthChange: function () {
15546 onMoreEventClick: function(e, el, more)
15550 this.calpopover.placement = 'right';
15551 this.calpopover.setTitle('More');
15553 this.calpopover.setContent('');
15555 var ctr = this.calpopover.el.select('.popover-content', true).first();
15557 Roo.each(more, function(m){
15559 cls : 'fc-event-hori fc-event-draggable',
15562 var cg = ctr.createChild(cfg);
15564 cg.on('click', _this.onEventClick, _this, m);
15567 this.calpopover.show(el);
15572 onLoad: function ()
15574 this.calevents = [];
15577 if(this.store.getCount() > 0){
15578 this.store.data.each(function(d){
15581 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15582 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15583 time : d.data.start_time,
15584 title : d.data.title,
15585 description : d.data.description,
15586 venue : d.data.venue
15591 this.renderEvents();
15593 if(this.calevents.length && this.loadMask){
15594 this.maskEl.hide();
15598 onBeforeLoad: function()
15600 this.clearEvents();
15602 this.maskEl.show();
15616 * @class Roo.bootstrap.Popover
15617 * @extends Roo.bootstrap.Component
15618 * Bootstrap Popover class
15619 * @cfg {String} html contents of the popover (or false to use children..)
15620 * @cfg {String} title of popover (or false to hide)
15621 * @cfg {String} placement how it is placed
15622 * @cfg {String} trigger click || hover (or false to trigger manually)
15623 * @cfg {String} over what (parent or false to trigger manually.)
15624 * @cfg {Number} delay - delay before showing
15627 * Create a new Popover
15628 * @param {Object} config The config object
15631 Roo.bootstrap.Popover = function(config){
15632 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15635 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15637 title: 'Fill in a title',
15640 placement : 'right',
15641 trigger : 'hover', // hover
15647 can_build_overlaid : false,
15649 getChildContainer : function()
15651 return this.el.select('.popover-content',true).first();
15654 getAutoCreate : function(){
15655 Roo.log('make popover?');
15657 cls : 'popover roo-dynamic',
15658 style: 'display:block',
15664 cls : 'popover-inner',
15668 cls: 'popover-title',
15672 cls : 'popover-content',
15683 setTitle: function(str)
15686 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15688 setContent: function(str)
15691 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15693 // as it get's added to the bottom of the page.
15694 onRender : function(ct, position)
15696 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15698 var cfg = Roo.apply({}, this.getAutoCreate());
15702 cfg.cls += ' ' + this.cls;
15705 cfg.style = this.style;
15707 //Roo.log("adding to ");
15708 this.el = Roo.get(document.body).createChild(cfg, position);
15714 initEvents : function()
15716 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15717 this.el.enableDisplayMode('block');
15719 if (this.over === false) {
15722 if (this.triggers === false) {
15725 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15726 var triggers = this.trigger ? this.trigger.split(' ') : [];
15727 Roo.each(triggers, function(trigger) {
15729 if (trigger == 'click') {
15730 on_el.on('click', this.toggle, this);
15731 } else if (trigger != 'manual') {
15732 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15733 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15735 on_el.on(eventIn ,this.enter, this);
15736 on_el.on(eventOut, this.leave, this);
15747 toggle : function () {
15748 this.hoverState == 'in' ? this.leave() : this.enter();
15751 enter : function () {
15754 clearTimeout(this.timeout);
15756 this.hoverState = 'in';
15758 if (!this.delay || !this.delay.show) {
15763 this.timeout = setTimeout(function () {
15764 if (_t.hoverState == 'in') {
15767 }, this.delay.show)
15769 leave : function() {
15770 clearTimeout(this.timeout);
15772 this.hoverState = 'out';
15774 if (!this.delay || !this.delay.hide) {
15779 this.timeout = setTimeout(function () {
15780 if (_t.hoverState == 'out') {
15783 }, this.delay.hide)
15786 show : function (on_el)
15789 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15792 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15793 if (this.html !== false) {
15794 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15796 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15797 if (!this.title.length) {
15798 this.el.select('.popover-title',true).hide();
15801 var placement = typeof this.placement == 'function' ?
15802 this.placement.call(this, this.el, on_el) :
15805 var autoToken = /\s?auto?\s?/i;
15806 var autoPlace = autoToken.test(placement);
15808 placement = placement.replace(autoToken, '') || 'top';
15812 //this.el.setXY([0,0]);
15814 this.el.dom.style.display='block';
15815 this.el.addClass(placement);
15817 //this.el.appendTo(on_el);
15819 var p = this.getPosition();
15820 var box = this.el.getBox();
15825 var align = Roo.bootstrap.Popover.alignment[placement];
15826 this.el.alignTo(on_el, align[0],align[1]);
15827 //var arrow = this.el.select('.arrow',true).first();
15828 //arrow.set(align[2],
15830 this.el.addClass('in');
15833 if (this.el.hasClass('fade')) {
15840 this.el.setXY([0,0]);
15841 this.el.removeClass('in');
15843 this.hoverState = null;
15849 Roo.bootstrap.Popover.alignment = {
15850 'left' : ['r-l', [-10,0], 'right'],
15851 'right' : ['l-r', [10,0], 'left'],
15852 'bottom' : ['t-b', [0,10], 'top'],
15853 'top' : [ 'b-t', [0,-10], 'bottom']
15864 * @class Roo.bootstrap.Progress
15865 * @extends Roo.bootstrap.Component
15866 * Bootstrap Progress class
15867 * @cfg {Boolean} striped striped of the progress bar
15868 * @cfg {Boolean} active animated of the progress bar
15872 * Create a new Progress
15873 * @param {Object} config The config object
15876 Roo.bootstrap.Progress = function(config){
15877 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15880 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15885 getAutoCreate : function(){
15893 cfg.cls += ' progress-striped';
15897 cfg.cls += ' active';
15916 * @class Roo.bootstrap.ProgressBar
15917 * @extends Roo.bootstrap.Component
15918 * Bootstrap ProgressBar class
15919 * @cfg {Number} aria_valuenow aria-value now
15920 * @cfg {Number} aria_valuemin aria-value min
15921 * @cfg {Number} aria_valuemax aria-value max
15922 * @cfg {String} label label for the progress bar
15923 * @cfg {String} panel (success | info | warning | danger )
15924 * @cfg {String} role role of the progress bar
15925 * @cfg {String} sr_only text
15929 * Create a new ProgressBar
15930 * @param {Object} config The config object
15933 Roo.bootstrap.ProgressBar = function(config){
15934 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15937 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15941 aria_valuemax : 100,
15947 getAutoCreate : function()
15952 cls: 'progress-bar',
15953 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15965 cfg.role = this.role;
15968 if(this.aria_valuenow){
15969 cfg['aria-valuenow'] = this.aria_valuenow;
15972 if(this.aria_valuemin){
15973 cfg['aria-valuemin'] = this.aria_valuemin;
15976 if(this.aria_valuemax){
15977 cfg['aria-valuemax'] = this.aria_valuemax;
15980 if(this.label && !this.sr_only){
15981 cfg.html = this.label;
15985 cfg.cls += ' progress-bar-' + this.panel;
15991 update : function(aria_valuenow)
15993 this.aria_valuenow = aria_valuenow;
15995 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16010 * @class Roo.bootstrap.TabGroup
16011 * @extends Roo.bootstrap.Column
16012 * Bootstrap Column class
16013 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16014 * @cfg {Boolean} carousel true to make the group behave like a carousel
16015 * @cfg {Boolean} bullets show bullets for the panels
16016 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16017 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16018 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16021 * Create a new TabGroup
16022 * @param {Object} config The config object
16025 Roo.bootstrap.TabGroup = function(config){
16026 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16028 this.navId = Roo.id();
16031 Roo.bootstrap.TabGroup.register(this);
16035 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16038 transition : false,
16043 slideOnTouch : false,
16045 getAutoCreate : function()
16047 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16049 cfg.cls += ' tab-content';
16051 Roo.log('get auto create...............');
16053 if (this.carousel) {
16054 cfg.cls += ' carousel slide';
16057 cls : 'carousel-inner'
16060 if(this.bullets && !Roo.isTouch){
16063 cls : 'carousel-bullets',
16067 if(this.bullets_cls){
16068 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16071 for (var i = 0; i < this.bullets; i++){
16073 cls : 'bullet bullet-' + i
16081 cfg.cn[0].cn = bullets;
16088 initEvents: function()
16090 Roo.log('-------- init events on tab group ---------');
16096 if(Roo.isTouch && this.slideOnTouch){
16097 this.el.on("touchstart", this.onTouchStart, this);
16100 if(this.autoslide){
16103 this.slideFn = window.setInterval(function() {
16104 _this.showPanelNext();
16110 onTouchStart : function(e, el, o)
16112 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16116 this.showPanelNext();
16119 getChildContainer : function()
16121 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16125 * register a Navigation item
16126 * @param {Roo.bootstrap.NavItem} the navitem to add
16128 register : function(item)
16130 this.tabs.push( item);
16131 item.navId = this.navId; // not really needed..
16136 getActivePanel : function()
16139 Roo.each(this.tabs, function(t) {
16149 getPanelByName : function(n)
16152 Roo.each(this.tabs, function(t) {
16153 if (t.tabId == n) {
16161 indexOfPanel : function(p)
16164 Roo.each(this.tabs, function(t,i) {
16165 if (t.tabId == p.tabId) {
16174 * show a specific panel
16175 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16176 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16178 showPanel : function (pan)
16180 if(this.transition || typeof(pan) == 'undefined'){
16181 Roo.log("waiting for the transitionend");
16185 if (typeof(pan) == 'number') {
16186 pan = this.tabs[pan];
16189 if (typeof(pan) == 'string') {
16190 pan = this.getPanelByName(pan);
16193 var cur = this.getActivePanel();
16196 Roo.log('pan or acitve pan is undefined');
16200 if (pan.tabId == this.getActivePanel().tabId) {
16204 if (false === cur.fireEvent('beforedeactivate')) {
16208 if(this.bullets > 0 && !Roo.isTouch){
16209 this.setActiveBullet(this.indexOfPanel(pan));
16212 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16214 this.transition = true;
16215 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16216 var lr = dir == 'next' ? 'left' : 'right';
16217 pan.el.addClass(dir); // or prev
16218 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16219 cur.el.addClass(lr); // or right
16220 pan.el.addClass(lr);
16223 cur.el.on('transitionend', function() {
16224 Roo.log("trans end?");
16226 pan.el.removeClass([lr,dir]);
16227 pan.setActive(true);
16229 cur.el.removeClass([lr]);
16230 cur.setActive(false);
16232 _this.transition = false;
16234 }, this, { single: true } );
16239 cur.setActive(false);
16240 pan.setActive(true);
16245 showPanelNext : function()
16247 var i = this.indexOfPanel(this.getActivePanel());
16249 if (i >= this.tabs.length - 1 && !this.autoslide) {
16253 if (i >= this.tabs.length - 1 && this.autoslide) {
16257 this.showPanel(this.tabs[i+1]);
16260 showPanelPrev : function()
16262 var i = this.indexOfPanel(this.getActivePanel());
16264 if (i < 1 && !this.autoslide) {
16268 if (i < 1 && this.autoslide) {
16269 i = this.tabs.length;
16272 this.showPanel(this.tabs[i-1]);
16276 addBullet: function()
16278 if(!this.bullets || Roo.isTouch){
16281 var ctr = this.el.select('.carousel-bullets',true).first();
16282 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16283 var bullet = ctr.createChild({
16284 cls : 'bullet bullet-' + i
16285 },ctr.dom.lastChild);
16290 bullet.on('click', (function(e, el, o, ii, t){
16292 e.preventDefault();
16294 this.showPanel(ii);
16296 if(this.autoslide && this.slideFn){
16297 clearInterval(this.slideFn);
16298 this.slideFn = window.setInterval(function() {
16299 _this.showPanelNext();
16303 }).createDelegate(this, [i, bullet], true));
16308 setActiveBullet : function(i)
16314 Roo.each(this.el.select('.bullet', true).elements, function(el){
16315 el.removeClass('selected');
16318 var bullet = this.el.select('.bullet-' + i, true).first();
16324 bullet.addClass('selected');
16335 Roo.apply(Roo.bootstrap.TabGroup, {
16339 * register a Navigation Group
16340 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16342 register : function(navgrp)
16344 this.groups[navgrp.navId] = navgrp;
16348 * fetch a Navigation Group based on the navigation ID
16349 * if one does not exist , it will get created.
16350 * @param {string} the navgroup to add
16351 * @returns {Roo.bootstrap.NavGroup} the navgroup
16353 get: function(navId) {
16354 if (typeof(this.groups[navId]) == 'undefined') {
16355 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16357 return this.groups[navId] ;
16372 * @class Roo.bootstrap.TabPanel
16373 * @extends Roo.bootstrap.Component
16374 * Bootstrap TabPanel class
16375 * @cfg {Boolean} active panel active
16376 * @cfg {String} html panel content
16377 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16378 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16382 * Create a new TabPanel
16383 * @param {Object} config The config object
16386 Roo.bootstrap.TabPanel = function(config){
16387 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16391 * Fires when the active status changes
16392 * @param {Roo.bootstrap.TabPanel} this
16393 * @param {Boolean} state the new state
16398 * @event beforedeactivate
16399 * Fires before a tab is de-activated - can be used to do validation on a form.
16400 * @param {Roo.bootstrap.TabPanel} this
16401 * @return {Boolean} false if there is an error
16404 'beforedeactivate': true
16407 this.tabId = this.tabId || Roo.id();
16411 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16418 getAutoCreate : function(){
16421 // item is needed for carousel - not sure if it has any effect otherwise
16422 cls: 'tab-pane item',
16423 html: this.html || ''
16427 cfg.cls += ' active';
16431 cfg.tabId = this.tabId;
16438 initEvents: function()
16440 Roo.log('-------- init events on tab panel ---------');
16442 var p = this.parent();
16443 this.navId = this.navId || p.navId;
16445 if (typeof(this.navId) != 'undefined') {
16446 // not really needed.. but just in case.. parent should be a NavGroup.
16447 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16448 Roo.log(['register', tg, this]);
16451 var i = tg.tabs.length - 1;
16453 if(this.active && tg.bullets > 0 && i < tg.bullets){
16454 tg.setActiveBullet(i);
16461 onRender : function(ct, position)
16463 // Roo.log("Call onRender: " + this.xtype);
16465 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16473 setActive: function(state)
16475 Roo.log("panel - set active " + this.tabId + "=" + state);
16477 this.active = state;
16479 this.el.removeClass('active');
16481 } else if (!this.el.hasClass('active')) {
16482 this.el.addClass('active');
16485 this.fireEvent('changed', this, state);
16502 * @class Roo.bootstrap.DateField
16503 * @extends Roo.bootstrap.Input
16504 * Bootstrap DateField class
16505 * @cfg {Number} weekStart default 0
16506 * @cfg {String} viewMode default empty, (months|years)
16507 * @cfg {String} minViewMode default empty, (months|years)
16508 * @cfg {Number} startDate default -Infinity
16509 * @cfg {Number} endDate default Infinity
16510 * @cfg {Boolean} todayHighlight default false
16511 * @cfg {Boolean} todayBtn default false
16512 * @cfg {Boolean} calendarWeeks default false
16513 * @cfg {Object} daysOfWeekDisabled default empty
16514 * @cfg {Boolean} singleMode default false (true | false)
16516 * @cfg {Boolean} keyboardNavigation default true
16517 * @cfg {String} language default en
16520 * Create a new DateField
16521 * @param {Object} config The config object
16524 Roo.bootstrap.DateField = function(config){
16525 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16529 * Fires when this field show.
16530 * @param {Roo.bootstrap.DateField} this
16531 * @param {Mixed} date The date value
16536 * Fires when this field hide.
16537 * @param {Roo.bootstrap.DateField} this
16538 * @param {Mixed} date The date value
16543 * Fires when select a date.
16544 * @param {Roo.bootstrap.DateField} this
16545 * @param {Mixed} date The date value
16549 * @event beforeselect
16550 * Fires when before select a date.
16551 * @param {Roo.bootstrap.DateField} this
16552 * @param {Mixed} date The date value
16554 beforeselect : true
16558 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16561 * @cfg {String} format
16562 * The default date format string which can be overriden for localization support. The format must be
16563 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16567 * @cfg {String} altFormats
16568 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16569 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16571 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16579 todayHighlight : false,
16585 keyboardNavigation: true,
16587 calendarWeeks: false,
16589 startDate: -Infinity,
16593 daysOfWeekDisabled: [],
16597 singleMode : false,
16599 UTCDate: function()
16601 return new Date(Date.UTC.apply(Date, arguments));
16604 UTCToday: function()
16606 var today = new Date();
16607 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16610 getDate: function() {
16611 var d = this.getUTCDate();
16612 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16615 getUTCDate: function() {
16619 setDate: function(d) {
16620 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16623 setUTCDate: function(d) {
16625 this.setValue(this.formatDate(this.date));
16628 onRender: function(ct, position)
16631 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16633 this.language = this.language || 'en';
16634 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16635 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16637 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16638 this.format = this.format || 'm/d/y';
16639 this.isInline = false;
16640 this.isInput = true;
16641 this.component = this.el.select('.add-on', true).first() || false;
16642 this.component = (this.component && this.component.length === 0) ? false : this.component;
16643 this.hasInput = this.component && this.inputEL().length;
16645 if (typeof(this.minViewMode === 'string')) {
16646 switch (this.minViewMode) {
16648 this.minViewMode = 1;
16651 this.minViewMode = 2;
16654 this.minViewMode = 0;
16659 if (typeof(this.viewMode === 'string')) {
16660 switch (this.viewMode) {
16673 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16675 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16677 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16679 this.picker().on('mousedown', this.onMousedown, this);
16680 this.picker().on('click', this.onClick, this);
16682 this.picker().addClass('datepicker-dropdown');
16684 this.startViewMode = this.viewMode;
16686 if(this.singleMode){
16687 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16688 v.setVisibilityMode(Roo.Element.DISPLAY);
16692 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16693 v.setStyle('width', '189px');
16697 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16698 if(!this.calendarWeeks){
16703 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16704 v.attr('colspan', function(i, val){
16705 return parseInt(val) + 1;
16710 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16712 this.setStartDate(this.startDate);
16713 this.setEndDate(this.endDate);
16715 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16722 if(this.isInline) {
16727 picker : function()
16729 return this.pickerEl;
16730 // return this.el.select('.datepicker', true).first();
16733 fillDow: function()
16735 var dowCnt = this.weekStart;
16744 if(this.calendarWeeks){
16752 while (dowCnt < this.weekStart + 7) {
16756 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16760 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16763 fillMonths: function()
16766 var months = this.picker().select('>.datepicker-months td', true).first();
16768 months.dom.innerHTML = '';
16774 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16777 months.createChild(month);
16784 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;
16786 if (this.date < this.startDate) {
16787 this.viewDate = new Date(this.startDate);
16788 } else if (this.date > this.endDate) {
16789 this.viewDate = new Date(this.endDate);
16791 this.viewDate = new Date(this.date);
16799 var d = new Date(this.viewDate),
16800 year = d.getUTCFullYear(),
16801 month = d.getUTCMonth(),
16802 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16803 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16804 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16805 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16806 currentDate = this.date && this.date.valueOf(),
16807 today = this.UTCToday();
16809 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16811 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16813 // this.picker.select('>tfoot th.today').
16814 // .text(dates[this.language].today)
16815 // .toggle(this.todayBtn !== false);
16817 this.updateNavArrows();
16820 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16822 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16824 prevMonth.setUTCDate(day);
16826 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16828 var nextMonth = new Date(prevMonth);
16830 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16832 nextMonth = nextMonth.valueOf();
16834 var fillMonths = false;
16836 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16838 while(prevMonth.valueOf() < nextMonth) {
16841 if (prevMonth.getUTCDay() === this.weekStart) {
16843 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16851 if(this.calendarWeeks){
16852 // ISO 8601: First week contains first thursday.
16853 // ISO also states week starts on Monday, but we can be more abstract here.
16855 // Start of current week: based on weekstart/current date
16856 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16857 // Thursday of this week
16858 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16859 // First Thursday of year, year from thursday
16860 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16861 // Calendar week: ms between thursdays, div ms per day, div 7 days
16862 calWeek = (th - yth) / 864e5 / 7 + 1;
16864 fillMonths.cn.push({
16872 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16874 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16877 if (this.todayHighlight &&
16878 prevMonth.getUTCFullYear() == today.getFullYear() &&
16879 prevMonth.getUTCMonth() == today.getMonth() &&
16880 prevMonth.getUTCDate() == today.getDate()) {
16881 clsName += ' today';
16884 if (currentDate && prevMonth.valueOf() === currentDate) {
16885 clsName += ' active';
16888 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16889 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16890 clsName += ' disabled';
16893 fillMonths.cn.push({
16895 cls: 'day ' + clsName,
16896 html: prevMonth.getDate()
16899 prevMonth.setDate(prevMonth.getDate()+1);
16902 var currentYear = this.date && this.date.getUTCFullYear();
16903 var currentMonth = this.date && this.date.getUTCMonth();
16905 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16907 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16908 v.removeClass('active');
16910 if(currentYear === year && k === currentMonth){
16911 v.addClass('active');
16914 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16915 v.addClass('disabled');
16921 year = parseInt(year/10, 10) * 10;
16923 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16925 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16928 for (var i = -1; i < 11; i++) {
16929 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16931 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16939 showMode: function(dir)
16942 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16945 Roo.each(this.picker().select('>div',true).elements, function(v){
16946 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16949 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16954 if(this.isInline) {
16958 this.picker().removeClass(['bottom', 'top']);
16960 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16962 * place to the top of element!
16966 this.picker().addClass('top');
16967 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16972 this.picker().addClass('bottom');
16974 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16977 parseDate : function(value)
16979 if(!value || value instanceof Date){
16982 var v = Date.parseDate(value, this.format);
16983 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16984 v = Date.parseDate(value, 'Y-m-d');
16986 if(!v && this.altFormats){
16987 if(!this.altFormatsArray){
16988 this.altFormatsArray = this.altFormats.split("|");
16990 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16991 v = Date.parseDate(value, this.altFormatsArray[i]);
16997 formatDate : function(date, fmt)
16999 return (!date || !(date instanceof Date)) ?
17000 date : date.dateFormat(fmt || this.format);
17003 onFocus : function()
17005 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17009 onBlur : function()
17011 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17013 var d = this.inputEl().getValue();
17022 this.picker().show();
17026 this.fireEvent('show', this, this.date);
17031 if(this.isInline) {
17034 this.picker().hide();
17035 this.viewMode = this.startViewMode;
17038 this.fireEvent('hide', this, this.date);
17042 onMousedown: function(e)
17044 e.stopPropagation();
17045 e.preventDefault();
17050 Roo.bootstrap.DateField.superclass.keyup.call(this);
17054 setValue: function(v)
17056 if(this.fireEvent('beforeselect', this, v) !== false){
17057 var d = new Date(this.parseDate(v) ).clearTime();
17059 if(isNaN(d.getTime())){
17060 this.date = this.viewDate = '';
17061 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17065 v = this.formatDate(d);
17067 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17069 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17073 this.fireEvent('select', this, this.date);
17077 getValue: function()
17079 return this.formatDate(this.date);
17082 fireKey: function(e)
17084 if (!this.picker().isVisible()){
17085 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17091 var dateChanged = false,
17093 newDate, newViewDate;
17098 e.preventDefault();
17102 if (!this.keyboardNavigation) {
17105 dir = e.keyCode == 37 ? -1 : 1;
17108 newDate = this.moveYear(this.date, dir);
17109 newViewDate = this.moveYear(this.viewDate, dir);
17110 } else if (e.shiftKey){
17111 newDate = this.moveMonth(this.date, dir);
17112 newViewDate = this.moveMonth(this.viewDate, dir);
17114 newDate = new Date(this.date);
17115 newDate.setUTCDate(this.date.getUTCDate() + dir);
17116 newViewDate = new Date(this.viewDate);
17117 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17119 if (this.dateWithinRange(newDate)){
17120 this.date = newDate;
17121 this.viewDate = newViewDate;
17122 this.setValue(this.formatDate(this.date));
17124 e.preventDefault();
17125 dateChanged = true;
17130 if (!this.keyboardNavigation) {
17133 dir = e.keyCode == 38 ? -1 : 1;
17135 newDate = this.moveYear(this.date, dir);
17136 newViewDate = this.moveYear(this.viewDate, dir);
17137 } else if (e.shiftKey){
17138 newDate = this.moveMonth(this.date, dir);
17139 newViewDate = this.moveMonth(this.viewDate, dir);
17141 newDate = new Date(this.date);
17142 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17143 newViewDate = new Date(this.viewDate);
17144 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17146 if (this.dateWithinRange(newDate)){
17147 this.date = newDate;
17148 this.viewDate = newViewDate;
17149 this.setValue(this.formatDate(this.date));
17151 e.preventDefault();
17152 dateChanged = true;
17156 this.setValue(this.formatDate(this.date));
17158 e.preventDefault();
17161 this.setValue(this.formatDate(this.date));
17175 onClick: function(e)
17177 e.stopPropagation();
17178 e.preventDefault();
17180 var target = e.getTarget();
17182 if(target.nodeName.toLowerCase() === 'i'){
17183 target = Roo.get(target).dom.parentNode;
17186 var nodeName = target.nodeName;
17187 var className = target.className;
17188 var html = target.innerHTML;
17189 //Roo.log(nodeName);
17191 switch(nodeName.toLowerCase()) {
17193 switch(className) {
17199 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17200 switch(this.viewMode){
17202 this.viewDate = this.moveMonth(this.viewDate, dir);
17206 this.viewDate = this.moveYear(this.viewDate, dir);
17212 var date = new Date();
17213 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17215 this.setValue(this.formatDate(this.date));
17222 if (className.indexOf('disabled') < 0) {
17223 this.viewDate.setUTCDate(1);
17224 if (className.indexOf('month') > -1) {
17225 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17227 var year = parseInt(html, 10) || 0;
17228 this.viewDate.setUTCFullYear(year);
17232 if(this.singleMode){
17233 this.setValue(this.formatDate(this.viewDate));
17244 //Roo.log(className);
17245 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17246 var day = parseInt(html, 10) || 1;
17247 var year = this.viewDate.getUTCFullYear(),
17248 month = this.viewDate.getUTCMonth();
17250 if (className.indexOf('old') > -1) {
17257 } else if (className.indexOf('new') > -1) {
17265 //Roo.log([year,month,day]);
17266 this.date = this.UTCDate(year, month, day,0,0,0,0);
17267 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17269 //Roo.log(this.formatDate(this.date));
17270 this.setValue(this.formatDate(this.date));
17277 setStartDate: function(startDate)
17279 this.startDate = startDate || -Infinity;
17280 if (this.startDate !== -Infinity) {
17281 this.startDate = this.parseDate(this.startDate);
17284 this.updateNavArrows();
17287 setEndDate: function(endDate)
17289 this.endDate = endDate || Infinity;
17290 if (this.endDate !== Infinity) {
17291 this.endDate = this.parseDate(this.endDate);
17294 this.updateNavArrows();
17297 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17299 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17300 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17301 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17303 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17304 return parseInt(d, 10);
17307 this.updateNavArrows();
17310 updateNavArrows: function()
17312 if(this.singleMode){
17316 var d = new Date(this.viewDate),
17317 year = d.getUTCFullYear(),
17318 month = d.getUTCMonth();
17320 Roo.each(this.picker().select('.prev', true).elements, function(v){
17322 switch (this.viewMode) {
17325 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17331 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17338 Roo.each(this.picker().select('.next', true).elements, function(v){
17340 switch (this.viewMode) {
17343 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17349 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17357 moveMonth: function(date, dir)
17362 var new_date = new Date(date.valueOf()),
17363 day = new_date.getUTCDate(),
17364 month = new_date.getUTCMonth(),
17365 mag = Math.abs(dir),
17367 dir = dir > 0 ? 1 : -1;
17370 // If going back one month, make sure month is not current month
17371 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17373 return new_date.getUTCMonth() == month;
17375 // If going forward one month, make sure month is as expected
17376 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17378 return new_date.getUTCMonth() != new_month;
17380 new_month = month + dir;
17381 new_date.setUTCMonth(new_month);
17382 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17383 if (new_month < 0 || new_month > 11) {
17384 new_month = (new_month + 12) % 12;
17387 // For magnitudes >1, move one month at a time...
17388 for (var i=0; i<mag; i++) {
17389 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17390 new_date = this.moveMonth(new_date, dir);
17392 // ...then reset the day, keeping it in the new month
17393 new_month = new_date.getUTCMonth();
17394 new_date.setUTCDate(day);
17396 return new_month != new_date.getUTCMonth();
17399 // Common date-resetting loop -- if date is beyond end of month, make it
17402 new_date.setUTCDate(--day);
17403 new_date.setUTCMonth(new_month);
17408 moveYear: function(date, dir)
17410 return this.moveMonth(date, dir*12);
17413 dateWithinRange: function(date)
17415 return date >= this.startDate && date <= this.endDate;
17421 this.picker().remove();
17426 Roo.apply(Roo.bootstrap.DateField, {
17437 html: '<i class="fa fa-arrow-left"/>'
17447 html: '<i class="fa fa-arrow-right"/>'
17489 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17490 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17491 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17492 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17493 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17506 navFnc: 'FullYear',
17511 navFnc: 'FullYear',
17516 Roo.apply(Roo.bootstrap.DateField, {
17520 cls: 'datepicker dropdown-menu roo-dynamic',
17524 cls: 'datepicker-days',
17528 cls: 'table-condensed',
17530 Roo.bootstrap.DateField.head,
17534 Roo.bootstrap.DateField.footer
17541 cls: 'datepicker-months',
17545 cls: 'table-condensed',
17547 Roo.bootstrap.DateField.head,
17548 Roo.bootstrap.DateField.content,
17549 Roo.bootstrap.DateField.footer
17556 cls: 'datepicker-years',
17560 cls: 'table-condensed',
17562 Roo.bootstrap.DateField.head,
17563 Roo.bootstrap.DateField.content,
17564 Roo.bootstrap.DateField.footer
17583 * @class Roo.bootstrap.TimeField
17584 * @extends Roo.bootstrap.Input
17585 * Bootstrap DateField class
17589 * Create a new TimeField
17590 * @param {Object} config The config object
17593 Roo.bootstrap.TimeField = function(config){
17594 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17598 * Fires when this field show.
17599 * @param {Roo.bootstrap.DateField} thisthis
17600 * @param {Mixed} date The date value
17605 * Fires when this field hide.
17606 * @param {Roo.bootstrap.DateField} this
17607 * @param {Mixed} date The date value
17612 * Fires when select a date.
17613 * @param {Roo.bootstrap.DateField} this
17614 * @param {Mixed} date The date value
17620 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17623 * @cfg {String} format
17624 * The default time format string which can be overriden for localization support. The format must be
17625 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17629 onRender: function(ct, position)
17632 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17634 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17636 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17638 this.pop = this.picker().select('>.datepicker-time',true).first();
17639 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17641 this.picker().on('mousedown', this.onMousedown, this);
17642 this.picker().on('click', this.onClick, this);
17644 this.picker().addClass('datepicker-dropdown');
17649 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17650 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17651 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17652 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17653 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17654 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17658 fireKey: function(e){
17659 if (!this.picker().isVisible()){
17660 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17666 e.preventDefault();
17674 this.onTogglePeriod();
17677 this.onIncrementMinutes();
17680 this.onDecrementMinutes();
17689 onClick: function(e) {
17690 e.stopPropagation();
17691 e.preventDefault();
17694 picker : function()
17696 return this.el.select('.datepicker', true).first();
17699 fillTime: function()
17701 var time = this.pop.select('tbody', true).first();
17703 time.dom.innerHTML = '';
17718 cls: 'hours-up glyphicon glyphicon-chevron-up'
17738 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17759 cls: 'timepicker-hour',
17774 cls: 'timepicker-minute',
17789 cls: 'btn btn-primary period',
17811 cls: 'hours-down glyphicon glyphicon-chevron-down'
17831 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17849 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17856 var hours = this.time.getHours();
17857 var minutes = this.time.getMinutes();
17870 hours = hours - 12;
17874 hours = '0' + hours;
17878 minutes = '0' + minutes;
17881 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17882 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17883 this.pop.select('button', true).first().dom.innerHTML = period;
17889 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17891 var cls = ['bottom'];
17893 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17900 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17905 this.picker().addClass(cls.join('-'));
17909 Roo.each(cls, function(c){
17911 _this.picker().setTop(_this.inputEl().getHeight());
17915 _this.picker().setTop(0 - _this.picker().getHeight());
17920 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17924 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17931 onFocus : function()
17933 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17937 onBlur : function()
17939 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17945 this.picker().show();
17950 this.fireEvent('show', this, this.date);
17955 this.picker().hide();
17958 this.fireEvent('hide', this, this.date);
17961 setTime : function()
17964 this.setValue(this.time.format(this.format));
17966 this.fireEvent('select', this, this.date);
17971 onMousedown: function(e){
17972 e.stopPropagation();
17973 e.preventDefault();
17976 onIncrementHours: function()
17978 Roo.log('onIncrementHours');
17979 this.time = this.time.add(Date.HOUR, 1);
17984 onDecrementHours: function()
17986 Roo.log('onDecrementHours');
17987 this.time = this.time.add(Date.HOUR, -1);
17991 onIncrementMinutes: function()
17993 Roo.log('onIncrementMinutes');
17994 this.time = this.time.add(Date.MINUTE, 1);
17998 onDecrementMinutes: function()
18000 Roo.log('onDecrementMinutes');
18001 this.time = this.time.add(Date.MINUTE, -1);
18005 onTogglePeriod: function()
18007 Roo.log('onTogglePeriod');
18008 this.time = this.time.add(Date.HOUR, 12);
18015 Roo.apply(Roo.bootstrap.TimeField, {
18045 cls: 'btn btn-info ok',
18057 Roo.apply(Roo.bootstrap.TimeField, {
18061 cls: 'datepicker dropdown-menu',
18065 cls: 'datepicker-time',
18069 cls: 'table-condensed',
18071 Roo.bootstrap.TimeField.content,
18072 Roo.bootstrap.TimeField.footer
18091 * @class Roo.bootstrap.MonthField
18092 * @extends Roo.bootstrap.Input
18093 * Bootstrap MonthField class
18095 * @cfg {String} language default en
18098 * Create a new MonthField
18099 * @param {Object} config The config object
18102 Roo.bootstrap.MonthField = function(config){
18103 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18108 * Fires when this field show.
18109 * @param {Roo.bootstrap.MonthField} this
18110 * @param {Mixed} date The date value
18115 * Fires when this field hide.
18116 * @param {Roo.bootstrap.MonthField} this
18117 * @param {Mixed} date The date value
18122 * Fires when select a date.
18123 * @param {Roo.bootstrap.MonthField} this
18124 * @param {String} oldvalue The old value
18125 * @param {String} newvalue The new value
18131 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18133 onRender: function(ct, position)
18136 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18138 this.language = this.language || 'en';
18139 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18140 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18142 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18143 this.isInline = false;
18144 this.isInput = true;
18145 this.component = this.el.select('.add-on', true).first() || false;
18146 this.component = (this.component && this.component.length === 0) ? false : this.component;
18147 this.hasInput = this.component && this.inputEL().length;
18149 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18151 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18153 this.picker().on('mousedown', this.onMousedown, this);
18154 this.picker().on('click', this.onClick, this);
18156 this.picker().addClass('datepicker-dropdown');
18158 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18159 v.setStyle('width', '189px');
18166 if(this.isInline) {
18172 setValue: function(v, suppressEvent)
18174 var o = this.getValue();
18176 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18180 if(suppressEvent !== true){
18181 this.fireEvent('select', this, o, v);
18186 getValue: function()
18191 onClick: function(e)
18193 e.stopPropagation();
18194 e.preventDefault();
18196 var target = e.getTarget();
18198 if(target.nodeName.toLowerCase() === 'i'){
18199 target = Roo.get(target).dom.parentNode;
18202 var nodeName = target.nodeName;
18203 var className = target.className;
18204 var html = target.innerHTML;
18206 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18210 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18212 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18218 picker : function()
18220 return this.pickerEl;
18223 fillMonths: function()
18226 var months = this.picker().select('>.datepicker-months td', true).first();
18228 months.dom.innerHTML = '';
18234 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18237 months.createChild(month);
18246 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18247 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18250 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18251 e.removeClass('active');
18253 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18254 e.addClass('active');
18261 if(this.isInline) {
18265 this.picker().removeClass(['bottom', 'top']);
18267 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18269 * place to the top of element!
18273 this.picker().addClass('top');
18274 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18279 this.picker().addClass('bottom');
18281 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18284 onFocus : function()
18286 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18290 onBlur : function()
18292 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18294 var d = this.inputEl().getValue();
18303 this.picker().show();
18304 this.picker().select('>.datepicker-months', true).first().show();
18308 this.fireEvent('show', this, this.date);
18313 if(this.isInline) {
18316 this.picker().hide();
18317 this.fireEvent('hide', this, this.date);
18321 onMousedown: function(e)
18323 e.stopPropagation();
18324 e.preventDefault();
18329 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18333 fireKey: function(e)
18335 if (!this.picker().isVisible()){
18336 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18347 e.preventDefault();
18351 dir = e.keyCode == 37 ? -1 : 1;
18353 this.vIndex = this.vIndex + dir;
18355 if(this.vIndex < 0){
18359 if(this.vIndex > 11){
18363 if(isNaN(this.vIndex)){
18367 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18373 dir = e.keyCode == 38 ? -1 : 1;
18375 this.vIndex = this.vIndex + dir * 4;
18377 if(this.vIndex < 0){
18381 if(this.vIndex > 11){
18385 if(isNaN(this.vIndex)){
18389 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18394 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18395 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18399 e.preventDefault();
18402 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18403 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18419 this.picker().remove();
18424 Roo.apply(Roo.bootstrap.MonthField, {
18443 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18444 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18449 Roo.apply(Roo.bootstrap.MonthField, {
18453 cls: 'datepicker dropdown-menu roo-dynamic',
18457 cls: 'datepicker-months',
18461 cls: 'table-condensed',
18463 Roo.bootstrap.DateField.content
18483 * @class Roo.bootstrap.CheckBox
18484 * @extends Roo.bootstrap.Input
18485 * Bootstrap CheckBox class
18487 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18488 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18489 * @cfg {String} boxLabel The text that appears beside the checkbox
18490 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18491 * @cfg {Boolean} checked initnal the element
18492 * @cfg {Boolean} inline inline the element (default false)
18493 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18496 * Create a new CheckBox
18497 * @param {Object} config The config object
18500 Roo.bootstrap.CheckBox = function(config){
18501 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18506 * Fires when the element is checked or unchecked.
18507 * @param {Roo.bootstrap.CheckBox} this This input
18508 * @param {Boolean} checked The new checked value
18515 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18517 inputType: 'checkbox',
18525 getAutoCreate : function()
18527 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18533 cfg.cls = 'form-group ' + this.inputType; //input-group
18536 cfg.cls += ' ' + this.inputType + '-inline';
18542 type : this.inputType,
18543 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18544 cls : 'roo-' + this.inputType, //'form-box',
18545 placeholder : this.placeholder || ''
18549 if (this.weight) { // Validity check?
18550 cfg.cls += " " + this.inputType + "-" + this.weight;
18553 if (this.disabled) {
18554 input.disabled=true;
18558 input.checked = this.checked;
18562 input.name = this.name;
18566 input.cls += ' input-' + this.size;
18571 ['xs','sm','md','lg'].map(function(size){
18572 if (settings[size]) {
18573 cfg.cls += ' col-' + size + '-' + settings[size];
18577 var inputblock = input;
18579 if (this.before || this.after) {
18582 cls : 'input-group',
18587 inputblock.cn.push({
18589 cls : 'input-group-addon',
18594 inputblock.cn.push(input);
18597 inputblock.cn.push({
18599 cls : 'input-group-addon',
18606 if (align ==='left' && this.fieldLabel.length) {
18607 Roo.log("left and has label");
18613 cls : 'control-label col-md-' + this.labelWidth,
18614 html : this.fieldLabel
18618 cls : "col-md-" + (12 - this.labelWidth),
18625 } else if ( this.fieldLabel.length) {
18630 tag: this.boxLabel ? 'span' : 'label',
18632 cls: 'control-label box-input-label',
18633 //cls : 'input-group-addon',
18634 html : this.fieldLabel
18644 Roo.log(" no label && no align");
18645 cfg.cn = [ inputblock ] ;
18650 var boxLabelCfg = {
18652 //'for': id, // box label is handled by onclick - so no for...
18654 html: this.boxLabel
18658 boxLabelCfg.tooltip = this.tooltip;
18661 cfg.cn.push(boxLabelCfg);
18671 * return the real input element.
18673 inputEl: function ()
18675 return this.el.select('input.roo-' + this.inputType,true).first();
18678 labelEl: function()
18680 return this.el.select('label.control-label',true).first();
18682 /* depricated... */
18686 return this.labelEl();
18689 initEvents : function()
18691 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18693 this.inputEl().on('click', this.onClick, this);
18695 if (this.boxLabel) {
18696 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18699 this.startValue = this.getValue();
18702 Roo.bootstrap.CheckBox.register(this);
18706 onClick : function()
18708 this.setChecked(!this.checked);
18711 setChecked : function(state,suppressEvent)
18713 this.startValue = this.getValue();
18715 if(this.inputType == 'radio'){
18717 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18718 e.dom.checked = false;
18721 this.inputEl().dom.checked = true;
18723 this.inputEl().dom.value = this.inputValue;
18725 if(suppressEvent !== true){
18726 this.fireEvent('check', this, true);
18734 this.checked = state;
18736 this.inputEl().dom.checked = state;
18738 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18740 if(suppressEvent !== true){
18741 this.fireEvent('check', this, state);
18747 getValue : function()
18749 if(this.inputType == 'radio'){
18750 return this.getGroupValue();
18753 return this.inputEl().getValue();
18757 getGroupValue : function()
18759 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18763 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18766 setValue : function(v,suppressEvent)
18768 if(this.inputType == 'radio'){
18769 this.setGroupValue(v, suppressEvent);
18773 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18778 setGroupValue : function(v, suppressEvent)
18780 this.startValue = this.getValue();
18782 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18783 e.dom.checked = false;
18785 if(e.dom.value == v){
18786 e.dom.checked = true;
18790 if(suppressEvent !== true){
18791 this.fireEvent('check', this, true);
18799 validate : function()
18803 (this.inputType == 'radio' && this.validateRadio()) ||
18804 (this.inputType == 'checkbox' && this.validateCheckbox())
18810 this.markInvalid();
18814 validateRadio : function()
18818 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18819 if(!e.dom.checked){
18831 validateCheckbox : function()
18834 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18837 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18845 for(var i in group){
18850 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18857 * Mark this field as valid
18859 markValid : function()
18861 if(this.allowBlank){
18867 this.fireEvent('valid', this);
18869 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18872 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18879 if(this.inputType == 'radio'){
18880 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18881 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18882 e.findParent('.form-group', false, true).addClass(_this.validClass);
18889 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18890 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18894 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18900 for(var i in group){
18901 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18902 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18907 * Mark this field as invalid
18908 * @param {String} msg The validation message
18910 markInvalid : function(msg)
18912 if(this.allowBlank){
18918 this.fireEvent('invalid', this, msg);
18920 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18923 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18927 label.markInvalid();
18930 if(this.inputType == 'radio'){
18931 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18932 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18933 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18940 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18941 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18945 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18951 for(var i in group){
18952 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18953 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18960 Roo.apply(Roo.bootstrap.CheckBox, {
18965 * register a CheckBox Group
18966 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18968 register : function(checkbox)
18970 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18971 this.groups[checkbox.groupId] = {};
18974 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18978 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18982 * fetch a CheckBox Group based on the group ID
18983 * @param {string} the group ID
18984 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18986 get: function(groupId) {
18987 if (typeof(this.groups[groupId]) == 'undefined') {
18991 return this.groups[groupId] ;
19003 *<div class="radio">
19005 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19006 Option one is this and that—be sure to include why it's great
19013 *<label class="radio-inline">fieldLabel</label>
19014 *<label class="radio-inline">
19015 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19023 * @class Roo.bootstrap.Radio
19024 * @extends Roo.bootstrap.CheckBox
19025 * Bootstrap Radio class
19028 * Create a new Radio
19029 * @param {Object} config The config object
19032 Roo.bootstrap.Radio = function(config){
19033 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19037 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19039 inputType: 'radio',
19043 getAutoCreate : function()
19045 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19046 align = align || 'left'; // default...
19053 tag : this.inline ? 'span' : 'div',
19058 var inline = this.inline ? ' radio-inline' : '';
19062 // does not need for, as we wrap the input with it..
19064 cls : 'control-label box-label' + inline,
19067 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19071 //cls : 'control-label' + inline,
19072 html : this.fieldLabel,
19073 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19082 type : this.inputType,
19083 //value : (!this.checked) ? this.valueOff : this.inputValue,
19084 value : this.inputValue,
19086 placeholder : this.placeholder || '' // ?? needed????
19089 if (this.weight) { // Validity check?
19090 input.cls += " radio-" + this.weight;
19092 if (this.disabled) {
19093 input.disabled=true;
19097 input.checked = this.checked;
19101 input.name = this.name;
19105 input.cls += ' input-' + this.size;
19108 //?? can span's inline have a width??
19111 ['xs','sm','md','lg'].map(function(size){
19112 if (settings[size]) {
19113 cfg.cls += ' col-' + size + '-' + settings[size];
19117 var inputblock = input;
19119 if (this.before || this.after) {
19122 cls : 'input-group',
19127 inputblock.cn.push({
19129 cls : 'input-group-addon',
19133 inputblock.cn.push(input);
19135 inputblock.cn.push({
19137 cls : 'input-group-addon',
19145 if (this.fieldLabel && this.fieldLabel.length) {
19146 cfg.cn.push(fieldLabel);
19149 // normal bootstrap puts the input inside the label.
19150 // however with our styled version - it has to go after the input.
19152 //lbl.cn.push(inputblock);
19156 cls: 'radio' + inline,
19163 cfg.cn.push( lblwrap);
19168 html: this.boxLabel
19177 initEvents : function()
19179 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19181 this.inputEl().on('click', this.onClick, this);
19182 if (this.boxLabel) {
19183 //Roo.log('find label');
19184 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19189 inputEl: function ()
19191 return this.el.select('input.roo-radio',true).first();
19193 onClick : function()
19196 this.setChecked(true);
19199 setChecked : function(state,suppressEvent)
19202 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19203 v.dom.checked = false;
19206 Roo.log(this.inputEl().dom);
19207 this.checked = state;
19208 this.inputEl().dom.checked = state;
19210 if(suppressEvent !== true){
19211 this.fireEvent('check', this, state);
19214 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19218 getGroupValue : function()
19221 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19222 if(v.dom.checked == true){
19223 value = v.dom.value;
19231 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19232 * @return {Mixed} value The field value
19234 getValue : function(){
19235 return this.getGroupValue();
19241 //<script type="text/javascript">
19244 * Based Ext JS Library 1.1.1
19245 * Copyright(c) 2006-2007, Ext JS, LLC.
19251 * @class Roo.HtmlEditorCore
19252 * @extends Roo.Component
19253 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19255 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19258 Roo.HtmlEditorCore = function(config){
19261 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19266 * @event initialize
19267 * Fires when the editor is fully initialized (including the iframe)
19268 * @param {Roo.HtmlEditorCore} this
19273 * Fires when the editor is first receives the focus. Any insertion must wait
19274 * until after this event.
19275 * @param {Roo.HtmlEditorCore} this
19279 * @event beforesync
19280 * Fires before the textarea is updated with content from the editor iframe. Return false
19281 * to cancel the sync.
19282 * @param {Roo.HtmlEditorCore} this
19283 * @param {String} html
19287 * @event beforepush
19288 * Fires before the iframe editor is updated with content from the textarea. Return false
19289 * to cancel the push.
19290 * @param {Roo.HtmlEditorCore} this
19291 * @param {String} html
19296 * Fires when the textarea is updated with content from the editor iframe.
19297 * @param {Roo.HtmlEditorCore} this
19298 * @param {String} html
19303 * Fires when the iframe editor is updated with content from the textarea.
19304 * @param {Roo.HtmlEditorCore} this
19305 * @param {String} html
19310 * @event editorevent
19311 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19312 * @param {Roo.HtmlEditorCore} this
19318 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19320 // defaults : white / black...
19321 this.applyBlacklists();
19328 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19332 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19338 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19343 * @cfg {Number} height (in pixels)
19347 * @cfg {Number} width (in pixels)
19352 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19355 stylesheets: false,
19360 // private properties
19361 validationEvent : false,
19363 initialized : false,
19365 sourceEditMode : false,
19366 onFocus : Roo.emptyFn,
19368 hideMode:'offsets',
19372 // blacklist + whitelisted elements..
19379 * Protected method that will not generally be called directly. It
19380 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19381 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19383 getDocMarkup : function(){
19387 // inherit styels from page...??
19388 if (this.stylesheets === false) {
19390 Roo.get(document.head).select('style').each(function(node) {
19391 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19394 Roo.get(document.head).select('link').each(function(node) {
19395 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19398 } else if (!this.stylesheets.length) {
19400 st = '<style type="text/css">' +
19401 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19407 st += '<style type="text/css">' +
19408 'IMG { cursor: pointer } ' +
19412 return '<html><head>' + st +
19413 //<style type="text/css">' +
19414 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19416 ' </head><body class="roo-htmleditor-body"></body></html>';
19420 onRender : function(ct, position)
19423 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19424 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19427 this.el.dom.style.border = '0 none';
19428 this.el.dom.setAttribute('tabIndex', -1);
19429 this.el.addClass('x-hidden hide');
19433 if(Roo.isIE){ // fix IE 1px bogus margin
19434 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19438 this.frameId = Roo.id();
19442 var iframe = this.owner.wrap.createChild({
19444 cls: 'form-control', // bootstrap..
19446 name: this.frameId,
19447 frameBorder : 'no',
19448 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19453 this.iframe = iframe.dom;
19455 this.assignDocWin();
19457 this.doc.designMode = 'on';
19460 this.doc.write(this.getDocMarkup());
19464 var task = { // must defer to wait for browser to be ready
19466 //console.log("run task?" + this.doc.readyState);
19467 this.assignDocWin();
19468 if(this.doc.body || this.doc.readyState == 'complete'){
19470 this.doc.designMode="on";
19474 Roo.TaskMgr.stop(task);
19475 this.initEditor.defer(10, this);
19482 Roo.TaskMgr.start(task);
19487 onResize : function(w, h)
19489 Roo.log('resize: ' +w + ',' + h );
19490 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19494 if(typeof w == 'number'){
19496 this.iframe.style.width = w + 'px';
19498 if(typeof h == 'number'){
19500 this.iframe.style.height = h + 'px';
19502 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19509 * Toggles the editor between standard and source edit mode.
19510 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19512 toggleSourceEdit : function(sourceEditMode){
19514 this.sourceEditMode = sourceEditMode === true;
19516 if(this.sourceEditMode){
19518 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19521 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19522 //this.iframe.className = '';
19525 //this.setSize(this.owner.wrap.getSize());
19526 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19533 * Protected method that will not generally be called directly. If you need/want
19534 * custom HTML cleanup, this is the method you should override.
19535 * @param {String} html The HTML to be cleaned
19536 * return {String} The cleaned HTML
19538 cleanHtml : function(html){
19539 html = String(html);
19540 if(html.length > 5){
19541 if(Roo.isSafari){ // strip safari nonsense
19542 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19545 if(html == ' '){
19552 * HTML Editor -> Textarea
19553 * Protected method that will not generally be called directly. Syncs the contents
19554 * of the editor iframe with the textarea.
19556 syncValue : function(){
19557 if(this.initialized){
19558 var bd = (this.doc.body || this.doc.documentElement);
19559 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19560 var html = bd.innerHTML;
19562 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19563 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19565 html = '<div style="'+m[0]+'">' + html + '</div>';
19568 html = this.cleanHtml(html);
19569 // fix up the special chars.. normaly like back quotes in word...
19570 // however we do not want to do this with chinese..
19571 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19572 var cc = b.charCodeAt();
19574 (cc >= 0x4E00 && cc < 0xA000 ) ||
19575 (cc >= 0x3400 && cc < 0x4E00 ) ||
19576 (cc >= 0xf900 && cc < 0xfb00 )
19582 if(this.owner.fireEvent('beforesync', this, html) !== false){
19583 this.el.dom.value = html;
19584 this.owner.fireEvent('sync', this, html);
19590 * Protected method that will not generally be called directly. Pushes the value of the textarea
19591 * into the iframe editor.
19593 pushValue : function(){
19594 if(this.initialized){
19595 var v = this.el.dom.value.trim();
19597 // if(v.length < 1){
19601 if(this.owner.fireEvent('beforepush', this, v) !== false){
19602 var d = (this.doc.body || this.doc.documentElement);
19604 this.cleanUpPaste();
19605 this.el.dom.value = d.innerHTML;
19606 this.owner.fireEvent('push', this, v);
19612 deferFocus : function(){
19613 this.focus.defer(10, this);
19617 focus : function(){
19618 if(this.win && !this.sourceEditMode){
19625 assignDocWin: function()
19627 var iframe = this.iframe;
19630 this.doc = iframe.contentWindow.document;
19631 this.win = iframe.contentWindow;
19633 // if (!Roo.get(this.frameId)) {
19636 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19637 // this.win = Roo.get(this.frameId).dom.contentWindow;
19639 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19643 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19644 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19649 initEditor : function(){
19650 //console.log("INIT EDITOR");
19651 this.assignDocWin();
19655 this.doc.designMode="on";
19657 this.doc.write(this.getDocMarkup());
19660 var dbody = (this.doc.body || this.doc.documentElement);
19661 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19662 // this copies styles from the containing element into thsi one..
19663 // not sure why we need all of this..
19664 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19666 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19667 //ss['background-attachment'] = 'fixed'; // w3c
19668 dbody.bgProperties = 'fixed'; // ie
19669 //Roo.DomHelper.applyStyles(dbody, ss);
19670 Roo.EventManager.on(this.doc, {
19671 //'mousedown': this.onEditorEvent,
19672 'mouseup': this.onEditorEvent,
19673 'dblclick': this.onEditorEvent,
19674 'click': this.onEditorEvent,
19675 'keyup': this.onEditorEvent,
19680 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19682 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19683 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19685 this.initialized = true;
19687 this.owner.fireEvent('initialize', this);
19692 onDestroy : function(){
19698 //for (var i =0; i < this.toolbars.length;i++) {
19699 // // fixme - ask toolbars for heights?
19700 // this.toolbars[i].onDestroy();
19703 //this.wrap.dom.innerHTML = '';
19704 //this.wrap.remove();
19709 onFirstFocus : function(){
19711 this.assignDocWin();
19714 this.activated = true;
19717 if(Roo.isGecko){ // prevent silly gecko errors
19719 var s = this.win.getSelection();
19720 if(!s.focusNode || s.focusNode.nodeType != 3){
19721 var r = s.getRangeAt(0);
19722 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19727 this.execCmd('useCSS', true);
19728 this.execCmd('styleWithCSS', false);
19731 this.owner.fireEvent('activate', this);
19735 adjustFont: function(btn){
19736 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19737 //if(Roo.isSafari){ // safari
19740 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19741 if(Roo.isSafari){ // safari
19742 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19743 v = (v < 10) ? 10 : v;
19744 v = (v > 48) ? 48 : v;
19745 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19750 v = Math.max(1, v+adjust);
19752 this.execCmd('FontSize', v );
19755 onEditorEvent : function(e)
19757 this.owner.fireEvent('editorevent', this, e);
19758 // this.updateToolbar();
19759 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19762 insertTag : function(tg)
19764 // could be a bit smarter... -> wrap the current selected tRoo..
19765 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19767 range = this.createRange(this.getSelection());
19768 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19769 wrappingNode.appendChild(range.extractContents());
19770 range.insertNode(wrappingNode);
19777 this.execCmd("formatblock", tg);
19781 insertText : function(txt)
19785 var range = this.createRange();
19786 range.deleteContents();
19787 //alert(Sender.getAttribute('label'));
19789 range.insertNode(this.doc.createTextNode(txt));
19795 * Executes a Midas editor command on the editor document and performs necessary focus and
19796 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19797 * @param {String} cmd The Midas command
19798 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19800 relayCmd : function(cmd, value){
19802 this.execCmd(cmd, value);
19803 this.owner.fireEvent('editorevent', this);
19804 //this.updateToolbar();
19805 this.owner.deferFocus();
19809 * Executes a Midas editor command directly on the editor document.
19810 * For visual commands, you should use {@link #relayCmd} instead.
19811 * <b>This should only be called after the editor is initialized.</b>
19812 * @param {String} cmd The Midas command
19813 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19815 execCmd : function(cmd, value){
19816 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19823 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19825 * @param {String} text | dom node..
19827 insertAtCursor : function(text)
19832 if(!this.activated){
19838 var r = this.doc.selection.createRange();
19849 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19853 // from jquery ui (MIT licenced)
19855 var win = this.win;
19857 if (win.getSelection && win.getSelection().getRangeAt) {
19858 range = win.getSelection().getRangeAt(0);
19859 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19860 range.insertNode(node);
19861 } else if (win.document.selection && win.document.selection.createRange) {
19862 // no firefox support
19863 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19864 win.document.selection.createRange().pasteHTML(txt);
19866 // no firefox support
19867 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19868 this.execCmd('InsertHTML', txt);
19877 mozKeyPress : function(e){
19879 var c = e.getCharCode(), cmd;
19882 c = String.fromCharCode(c).toLowerCase();
19896 this.cleanUpPaste.defer(100, this);
19904 e.preventDefault();
19912 fixKeys : function(){ // load time branching for fastest keydown performance
19914 return function(e){
19915 var k = e.getKey(), r;
19918 r = this.doc.selection.createRange();
19921 r.pasteHTML('    ');
19928 r = this.doc.selection.createRange();
19930 var target = r.parentElement();
19931 if(!target || target.tagName.toLowerCase() != 'li'){
19933 r.pasteHTML('<br />');
19939 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19940 this.cleanUpPaste.defer(100, this);
19946 }else if(Roo.isOpera){
19947 return function(e){
19948 var k = e.getKey();
19952 this.execCmd('InsertHTML','    ');
19955 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19956 this.cleanUpPaste.defer(100, this);
19961 }else if(Roo.isSafari){
19962 return function(e){
19963 var k = e.getKey();
19967 this.execCmd('InsertText','\t');
19971 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19972 this.cleanUpPaste.defer(100, this);
19980 getAllAncestors: function()
19982 var p = this.getSelectedNode();
19985 a.push(p); // push blank onto stack..
19986 p = this.getParentElement();
19990 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19994 a.push(this.doc.body);
19998 lastSelNode : false,
20001 getSelection : function()
20003 this.assignDocWin();
20004 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20007 getSelectedNode: function()
20009 // this may only work on Gecko!!!
20011 // should we cache this!!!!
20016 var range = this.createRange(this.getSelection()).cloneRange();
20019 var parent = range.parentElement();
20021 var testRange = range.duplicate();
20022 testRange.moveToElementText(parent);
20023 if (testRange.inRange(range)) {
20026 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20029 parent = parent.parentElement;
20034 // is ancestor a text element.
20035 var ac = range.commonAncestorContainer;
20036 if (ac.nodeType == 3) {
20037 ac = ac.parentNode;
20040 var ar = ac.childNodes;
20043 var other_nodes = [];
20044 var has_other_nodes = false;
20045 for (var i=0;i<ar.length;i++) {
20046 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20049 // fullly contained node.
20051 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20056 // probably selected..
20057 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20058 other_nodes.push(ar[i]);
20062 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20067 has_other_nodes = true;
20069 if (!nodes.length && other_nodes.length) {
20070 nodes= other_nodes;
20072 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20078 createRange: function(sel)
20080 // this has strange effects when using with
20081 // top toolbar - not sure if it's a great idea.
20082 //this.editor.contentWindow.focus();
20083 if (typeof sel != "undefined") {
20085 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20087 return this.doc.createRange();
20090 return this.doc.createRange();
20093 getParentElement: function()
20096 this.assignDocWin();
20097 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20099 var range = this.createRange(sel);
20102 var p = range.commonAncestorContainer;
20103 while (p.nodeType == 3) { // text node
20114 * Range intersection.. the hard stuff...
20118 * [ -- selected range --- ]
20122 * if end is before start or hits it. fail.
20123 * if start is after end or hits it fail.
20125 * if either hits (but other is outside. - then it's not
20131 // @see http://www.thismuchiknow.co.uk/?p=64.
20132 rangeIntersectsNode : function(range, node)
20134 var nodeRange = node.ownerDocument.createRange();
20136 nodeRange.selectNode(node);
20138 nodeRange.selectNodeContents(node);
20141 var rangeStartRange = range.cloneRange();
20142 rangeStartRange.collapse(true);
20144 var rangeEndRange = range.cloneRange();
20145 rangeEndRange.collapse(false);
20147 var nodeStartRange = nodeRange.cloneRange();
20148 nodeStartRange.collapse(true);
20150 var nodeEndRange = nodeRange.cloneRange();
20151 nodeEndRange.collapse(false);
20153 return rangeStartRange.compareBoundaryPoints(
20154 Range.START_TO_START, nodeEndRange) == -1 &&
20155 rangeEndRange.compareBoundaryPoints(
20156 Range.START_TO_START, nodeStartRange) == 1;
20160 rangeCompareNode : function(range, node)
20162 var nodeRange = node.ownerDocument.createRange();
20164 nodeRange.selectNode(node);
20166 nodeRange.selectNodeContents(node);
20170 range.collapse(true);
20172 nodeRange.collapse(true);
20174 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20175 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20177 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20179 var nodeIsBefore = ss == 1;
20180 var nodeIsAfter = ee == -1;
20182 if (nodeIsBefore && nodeIsAfter) {
20185 if (!nodeIsBefore && nodeIsAfter) {
20186 return 1; //right trailed.
20189 if (nodeIsBefore && !nodeIsAfter) {
20190 return 2; // left trailed.
20196 // private? - in a new class?
20197 cleanUpPaste : function()
20199 // cleans up the whole document..
20200 Roo.log('cleanuppaste');
20202 this.cleanUpChildren(this.doc.body);
20203 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20204 if (clean != this.doc.body.innerHTML) {
20205 this.doc.body.innerHTML = clean;
20210 cleanWordChars : function(input) {// change the chars to hex code
20211 var he = Roo.HtmlEditorCore;
20213 var output = input;
20214 Roo.each(he.swapCodes, function(sw) {
20215 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20217 output = output.replace(swapper, sw[1]);
20224 cleanUpChildren : function (n)
20226 if (!n.childNodes.length) {
20229 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20230 this.cleanUpChild(n.childNodes[i]);
20237 cleanUpChild : function (node)
20240 //console.log(node);
20241 if (node.nodeName == "#text") {
20242 // clean up silly Windows -- stuff?
20245 if (node.nodeName == "#comment") {
20246 node.parentNode.removeChild(node);
20247 // clean up silly Windows -- stuff?
20250 var lcname = node.tagName.toLowerCase();
20251 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20252 // whitelist of tags..
20254 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20256 node.parentNode.removeChild(node);
20261 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20263 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20264 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20266 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20267 // remove_keep_children = true;
20270 if (remove_keep_children) {
20271 this.cleanUpChildren(node);
20272 // inserts everything just before this node...
20273 while (node.childNodes.length) {
20274 var cn = node.childNodes[0];
20275 node.removeChild(cn);
20276 node.parentNode.insertBefore(cn, node);
20278 node.parentNode.removeChild(node);
20282 if (!node.attributes || !node.attributes.length) {
20283 this.cleanUpChildren(node);
20287 function cleanAttr(n,v)
20290 if (v.match(/^\./) || v.match(/^\//)) {
20293 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20296 if (v.match(/^#/)) {
20299 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20300 node.removeAttribute(n);
20304 var cwhite = this.cwhite;
20305 var cblack = this.cblack;
20307 function cleanStyle(n,v)
20309 if (v.match(/expression/)) { //XSS?? should we even bother..
20310 node.removeAttribute(n);
20314 var parts = v.split(/;/);
20317 Roo.each(parts, function(p) {
20318 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20322 var l = p.split(':').shift().replace(/\s+/g,'');
20323 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20325 if ( cwhite.length && cblack.indexOf(l) > -1) {
20326 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20327 //node.removeAttribute(n);
20331 // only allow 'c whitelisted system attributes'
20332 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20333 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20334 //node.removeAttribute(n);
20344 if (clean.length) {
20345 node.setAttribute(n, clean.join(';'));
20347 node.removeAttribute(n);
20353 for (var i = node.attributes.length-1; i > -1 ; i--) {
20354 var a = node.attributes[i];
20357 if (a.name.toLowerCase().substr(0,2)=='on') {
20358 node.removeAttribute(a.name);
20361 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20362 node.removeAttribute(a.name);
20365 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20366 cleanAttr(a.name,a.value); // fixme..
20369 if (a.name == 'style') {
20370 cleanStyle(a.name,a.value);
20373 /// clean up MS crap..
20374 // tecnically this should be a list of valid class'es..
20377 if (a.name == 'class') {
20378 if (a.value.match(/^Mso/)) {
20379 node.className = '';
20382 if (a.value.match(/body/)) {
20383 node.className = '';
20394 this.cleanUpChildren(node);
20400 * Clean up MS wordisms...
20402 cleanWord : function(node)
20407 this.cleanWord(this.doc.body);
20410 if (node.nodeName == "#text") {
20411 // clean up silly Windows -- stuff?
20414 if (node.nodeName == "#comment") {
20415 node.parentNode.removeChild(node);
20416 // clean up silly Windows -- stuff?
20420 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20421 node.parentNode.removeChild(node);
20425 // remove - but keep children..
20426 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20427 while (node.childNodes.length) {
20428 var cn = node.childNodes[0];
20429 node.removeChild(cn);
20430 node.parentNode.insertBefore(cn, node);
20432 node.parentNode.removeChild(node);
20433 this.iterateChildren(node, this.cleanWord);
20437 if (node.className.length) {
20439 var cn = node.className.split(/\W+/);
20441 Roo.each(cn, function(cls) {
20442 if (cls.match(/Mso[a-zA-Z]+/)) {
20447 node.className = cna.length ? cna.join(' ') : '';
20449 node.removeAttribute("class");
20453 if (node.hasAttribute("lang")) {
20454 node.removeAttribute("lang");
20457 if (node.hasAttribute("style")) {
20459 var styles = node.getAttribute("style").split(";");
20461 Roo.each(styles, function(s) {
20462 if (!s.match(/:/)) {
20465 var kv = s.split(":");
20466 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20469 // what ever is left... we allow.
20472 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20473 if (!nstyle.length) {
20474 node.removeAttribute('style');
20477 this.iterateChildren(node, this.cleanWord);
20483 * iterateChildren of a Node, calling fn each time, using this as the scole..
20484 * @param {DomNode} node node to iterate children of.
20485 * @param {Function} fn method of this class to call on each item.
20487 iterateChildren : function(node, fn)
20489 if (!node.childNodes.length) {
20492 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20493 fn.call(this, node.childNodes[i])
20499 * cleanTableWidths.
20501 * Quite often pasting from word etc.. results in tables with column and widths.
20502 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20505 cleanTableWidths : function(node)
20510 this.cleanTableWidths(this.doc.body);
20515 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20518 Roo.log(node.tagName);
20519 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20520 this.iterateChildren(node, this.cleanTableWidths);
20523 if (node.hasAttribute('width')) {
20524 node.removeAttribute('width');
20528 if (node.hasAttribute("style")) {
20531 var styles = node.getAttribute("style").split(";");
20533 Roo.each(styles, function(s) {
20534 if (!s.match(/:/)) {
20537 var kv = s.split(":");
20538 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20541 // what ever is left... we allow.
20544 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20545 if (!nstyle.length) {
20546 node.removeAttribute('style');
20550 this.iterateChildren(node, this.cleanTableWidths);
20558 domToHTML : function(currentElement, depth, nopadtext) {
20560 depth = depth || 0;
20561 nopadtext = nopadtext || false;
20563 if (!currentElement) {
20564 return this.domToHTML(this.doc.body);
20567 //Roo.log(currentElement);
20569 var allText = false;
20570 var nodeName = currentElement.nodeName;
20571 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20573 if (nodeName == '#text') {
20575 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20580 if (nodeName != 'BODY') {
20583 // Prints the node tagName, such as <A>, <IMG>, etc
20586 for(i = 0; i < currentElement.attributes.length;i++) {
20588 var aname = currentElement.attributes.item(i).name;
20589 if (!currentElement.attributes.item(i).value.length) {
20592 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20595 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20604 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20607 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20612 // Traverse the tree
20614 var currentElementChild = currentElement.childNodes.item(i);
20615 var allText = true;
20616 var innerHTML = '';
20618 while (currentElementChild) {
20619 // Formatting code (indent the tree so it looks nice on the screen)
20620 var nopad = nopadtext;
20621 if (lastnode == 'SPAN') {
20625 if (currentElementChild.nodeName == '#text') {
20626 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20627 toadd = nopadtext ? toadd : toadd.trim();
20628 if (!nopad && toadd.length > 80) {
20629 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20631 innerHTML += toadd;
20634 currentElementChild = currentElement.childNodes.item(i);
20640 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20642 // Recursively traverse the tree structure of the child node
20643 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20644 lastnode = currentElementChild.nodeName;
20646 currentElementChild=currentElement.childNodes.item(i);
20652 // The remaining code is mostly for formatting the tree
20653 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20658 ret+= "</"+tagName+">";
20664 applyBlacklists : function()
20666 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20667 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20671 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20672 if (b.indexOf(tag) > -1) {
20675 this.white.push(tag);
20679 Roo.each(w, function(tag) {
20680 if (b.indexOf(tag) > -1) {
20683 if (this.white.indexOf(tag) > -1) {
20686 this.white.push(tag);
20691 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20692 if (w.indexOf(tag) > -1) {
20695 this.black.push(tag);
20699 Roo.each(b, function(tag) {
20700 if (w.indexOf(tag) > -1) {
20703 if (this.black.indexOf(tag) > -1) {
20706 this.black.push(tag);
20711 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20712 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20716 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20717 if (b.indexOf(tag) > -1) {
20720 this.cwhite.push(tag);
20724 Roo.each(w, function(tag) {
20725 if (b.indexOf(tag) > -1) {
20728 if (this.cwhite.indexOf(tag) > -1) {
20731 this.cwhite.push(tag);
20736 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20737 if (w.indexOf(tag) > -1) {
20740 this.cblack.push(tag);
20744 Roo.each(b, function(tag) {
20745 if (w.indexOf(tag) > -1) {
20748 if (this.cblack.indexOf(tag) > -1) {
20751 this.cblack.push(tag);
20756 setStylesheets : function(stylesheets)
20758 if(typeof(stylesheets) == 'string'){
20759 Roo.get(this.iframe.contentDocument.head).createChild({
20761 rel : 'stylesheet',
20770 Roo.each(stylesheets, function(s) {
20775 Roo.get(_this.iframe.contentDocument.head).createChild({
20777 rel : 'stylesheet',
20786 removeStylesheets : function()
20790 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20795 // hide stuff that is not compatible
20809 * @event specialkey
20813 * @cfg {String} fieldClass @hide
20816 * @cfg {String} focusClass @hide
20819 * @cfg {String} autoCreate @hide
20822 * @cfg {String} inputType @hide
20825 * @cfg {String} invalidClass @hide
20828 * @cfg {String} invalidText @hide
20831 * @cfg {String} msgFx @hide
20834 * @cfg {String} validateOnBlur @hide
20838 Roo.HtmlEditorCore.white = [
20839 'area', 'br', 'img', 'input', 'hr', 'wbr',
20841 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20842 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20843 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20844 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20845 'table', 'ul', 'xmp',
20847 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20850 'dir', 'menu', 'ol', 'ul', 'dl',
20856 Roo.HtmlEditorCore.black = [
20857 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20859 'base', 'basefont', 'bgsound', 'blink', 'body',
20860 'frame', 'frameset', 'head', 'html', 'ilayer',
20861 'iframe', 'layer', 'link', 'meta', 'object',
20862 'script', 'style' ,'title', 'xml' // clean later..
20864 Roo.HtmlEditorCore.clean = [
20865 'script', 'style', 'title', 'xml'
20867 Roo.HtmlEditorCore.remove = [
20872 Roo.HtmlEditorCore.ablack = [
20876 Roo.HtmlEditorCore.aclean = [
20877 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20881 Roo.HtmlEditorCore.pwhite= [
20882 'http', 'https', 'mailto'
20885 // white listed style attributes.
20886 Roo.HtmlEditorCore.cwhite= [
20887 // 'text-align', /// default is to allow most things..
20893 // black listed style attributes.
20894 Roo.HtmlEditorCore.cblack= [
20895 // 'font-size' -- this can be set by the project
20899 Roo.HtmlEditorCore.swapCodes =[
20918 * @class Roo.bootstrap.HtmlEditor
20919 * @extends Roo.bootstrap.TextArea
20920 * Bootstrap HtmlEditor class
20923 * Create a new HtmlEditor
20924 * @param {Object} config The config object
20927 Roo.bootstrap.HtmlEditor = function(config){
20928 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20929 if (!this.toolbars) {
20930 this.toolbars = [];
20932 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20935 * @event initialize
20936 * Fires when the editor is fully initialized (including the iframe)
20937 * @param {HtmlEditor} this
20942 * Fires when the editor is first receives the focus. Any insertion must wait
20943 * until after this event.
20944 * @param {HtmlEditor} this
20948 * @event beforesync
20949 * Fires before the textarea is updated with content from the editor iframe. Return false
20950 * to cancel the sync.
20951 * @param {HtmlEditor} this
20952 * @param {String} html
20956 * @event beforepush
20957 * Fires before the iframe editor is updated with content from the textarea. Return false
20958 * to cancel the push.
20959 * @param {HtmlEditor} this
20960 * @param {String} html
20965 * Fires when the textarea is updated with content from the editor iframe.
20966 * @param {HtmlEditor} this
20967 * @param {String} html
20972 * Fires when the iframe editor is updated with content from the textarea.
20973 * @param {HtmlEditor} this
20974 * @param {String} html
20978 * @event editmodechange
20979 * Fires when the editor switches edit modes
20980 * @param {HtmlEditor} this
20981 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20983 editmodechange: true,
20985 * @event editorevent
20986 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20987 * @param {HtmlEditor} this
20991 * @event firstfocus
20992 * Fires when on first focus - needed by toolbars..
20993 * @param {HtmlEditor} this
20998 * Auto save the htmlEditor value as a file into Events
20999 * @param {HtmlEditor} this
21003 * @event savedpreview
21004 * preview the saved version of htmlEditor
21005 * @param {HtmlEditor} this
21012 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21016 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21021 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21026 * @cfg {Number} height (in pixels)
21030 * @cfg {Number} width (in pixels)
21035 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21038 stylesheets: false,
21043 // private properties
21044 validationEvent : false,
21046 initialized : false,
21049 onFocus : Roo.emptyFn,
21051 hideMode:'offsets',
21054 tbContainer : false,
21056 toolbarContainer :function() {
21057 return this.wrap.select('.x-html-editor-tb',true).first();
21061 * Protected method that will not generally be called directly. It
21062 * is called when the editor creates its toolbar. Override this method if you need to
21063 * add custom toolbar buttons.
21064 * @param {HtmlEditor} editor
21066 createToolbar : function(){
21068 Roo.log("create toolbars");
21070 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21071 this.toolbars[0].render(this.toolbarContainer());
21075 // if (!editor.toolbars || !editor.toolbars.length) {
21076 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21079 // for (var i =0 ; i < editor.toolbars.length;i++) {
21080 // editor.toolbars[i] = Roo.factory(
21081 // typeof(editor.toolbars[i]) == 'string' ?
21082 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21083 // Roo.bootstrap.HtmlEditor);
21084 // editor.toolbars[i].init(editor);
21090 onRender : function(ct, position)
21092 // Roo.log("Call onRender: " + this.xtype);
21094 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21096 this.wrap = this.inputEl().wrap({
21097 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21100 this.editorcore.onRender(ct, position);
21102 if (this.resizable) {
21103 this.resizeEl = new Roo.Resizable(this.wrap, {
21107 minHeight : this.height,
21108 height: this.height,
21109 handles : this.resizable,
21112 resize : function(r, w, h) {
21113 _t.onResize(w,h); // -something
21119 this.createToolbar(this);
21122 if(!this.width && this.resizable){
21123 this.setSize(this.wrap.getSize());
21125 if (this.resizeEl) {
21126 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21127 // should trigger onReize..
21133 onResize : function(w, h)
21135 Roo.log('resize: ' +w + ',' + h );
21136 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21140 if(this.inputEl() ){
21141 if(typeof w == 'number'){
21142 var aw = w - this.wrap.getFrameWidth('lr');
21143 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21146 if(typeof h == 'number'){
21147 var tbh = -11; // fixme it needs to tool bar size!
21148 for (var i =0; i < this.toolbars.length;i++) {
21149 // fixme - ask toolbars for heights?
21150 tbh += this.toolbars[i].el.getHeight();
21151 //if (this.toolbars[i].footer) {
21152 // tbh += this.toolbars[i].footer.el.getHeight();
21160 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21161 ah -= 5; // knock a few pixes off for look..
21162 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21166 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21167 this.editorcore.onResize(ew,eh);
21172 * Toggles the editor between standard and source edit mode.
21173 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21175 toggleSourceEdit : function(sourceEditMode)
21177 this.editorcore.toggleSourceEdit(sourceEditMode);
21179 if(this.editorcore.sourceEditMode){
21180 Roo.log('editor - showing textarea');
21183 // Roo.log(this.syncValue());
21185 this.inputEl().removeClass(['hide', 'x-hidden']);
21186 this.inputEl().dom.removeAttribute('tabIndex');
21187 this.inputEl().focus();
21189 Roo.log('editor - hiding textarea');
21191 // Roo.log(this.pushValue());
21194 this.inputEl().addClass(['hide', 'x-hidden']);
21195 this.inputEl().dom.setAttribute('tabIndex', -1);
21196 //this.deferFocus();
21199 if(this.resizable){
21200 this.setSize(this.wrap.getSize());
21203 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21206 // private (for BoxComponent)
21207 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21209 // private (for BoxComponent)
21210 getResizeEl : function(){
21214 // private (for BoxComponent)
21215 getPositionEl : function(){
21220 initEvents : function(){
21221 this.originalValue = this.getValue();
21225 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21228 // markInvalid : Roo.emptyFn,
21230 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21233 // clearInvalid : Roo.emptyFn,
21235 setValue : function(v){
21236 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21237 this.editorcore.pushValue();
21242 deferFocus : function(){
21243 this.focus.defer(10, this);
21247 focus : function(){
21248 this.editorcore.focus();
21254 onDestroy : function(){
21260 for (var i =0; i < this.toolbars.length;i++) {
21261 // fixme - ask toolbars for heights?
21262 this.toolbars[i].onDestroy();
21265 this.wrap.dom.innerHTML = '';
21266 this.wrap.remove();
21271 onFirstFocus : function(){
21272 //Roo.log("onFirstFocus");
21273 this.editorcore.onFirstFocus();
21274 for (var i =0; i < this.toolbars.length;i++) {
21275 this.toolbars[i].onFirstFocus();
21281 syncValue : function()
21283 this.editorcore.syncValue();
21286 pushValue : function()
21288 this.editorcore.pushValue();
21292 // hide stuff that is not compatible
21306 * @event specialkey
21310 * @cfg {String} fieldClass @hide
21313 * @cfg {String} focusClass @hide
21316 * @cfg {String} autoCreate @hide
21319 * @cfg {String} inputType @hide
21322 * @cfg {String} invalidClass @hide
21325 * @cfg {String} invalidText @hide
21328 * @cfg {String} msgFx @hide
21331 * @cfg {String} validateOnBlur @hide
21340 Roo.namespace('Roo.bootstrap.htmleditor');
21342 * @class Roo.bootstrap.HtmlEditorToolbar1
21347 new Roo.bootstrap.HtmlEditor({
21350 new Roo.bootstrap.HtmlEditorToolbar1({
21351 disable : { fonts: 1 , format: 1, ..., ... , ...],
21357 * @cfg {Object} disable List of elements to disable..
21358 * @cfg {Array} btns List of additional buttons.
21362 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21365 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21368 Roo.apply(this, config);
21370 // default disabled, based on 'good practice'..
21371 this.disable = this.disable || {};
21372 Roo.applyIf(this.disable, {
21375 specialElements : true
21377 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21379 this.editor = config.editor;
21380 this.editorcore = config.editor.editorcore;
21382 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21384 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21385 // dont call parent... till later.
21387 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21392 editorcore : false,
21397 "h1","h2","h3","h4","h5","h6",
21399 "abbr", "acronym", "address", "cite", "samp", "var",
21403 onRender : function(ct, position)
21405 // Roo.log("Call onRender: " + this.xtype);
21407 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21409 this.el.dom.style.marginBottom = '0';
21411 var editorcore = this.editorcore;
21412 var editor= this.editor;
21415 var btn = function(id,cmd , toggle, handler){
21417 var event = toggle ? 'toggle' : 'click';
21422 xns: Roo.bootstrap,
21425 enableToggle:toggle !== false,
21427 pressed : toggle ? false : null,
21430 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21431 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21440 xns: Roo.bootstrap,
21441 glyphicon : 'font',
21445 xns: Roo.bootstrap,
21449 Roo.each(this.formats, function(f) {
21450 style.menu.items.push({
21452 xns: Roo.bootstrap,
21453 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21458 editorcore.insertTag(this.tagname);
21465 children.push(style);
21468 btn('bold',false,true);
21469 btn('italic',false,true);
21470 btn('align-left', 'justifyleft',true);
21471 btn('align-center', 'justifycenter',true);
21472 btn('align-right' , 'justifyright',true);
21473 btn('link', false, false, function(btn) {
21474 //Roo.log("create link?");
21475 var url = prompt(this.createLinkText, this.defaultLinkValue);
21476 if(url && url != 'http:/'+'/'){
21477 this.editorcore.relayCmd('createlink', url);
21480 btn('list','insertunorderedlist',true);
21481 btn('pencil', false,true, function(btn){
21484 this.toggleSourceEdit(btn.pressed);
21490 xns: Roo.bootstrap,
21495 xns: Roo.bootstrap,
21500 cog.menu.items.push({
21502 xns: Roo.bootstrap,
21503 html : Clean styles,
21508 editorcore.insertTag(this.tagname);
21517 this.xtype = 'NavSimplebar';
21519 for(var i=0;i< children.length;i++) {
21521 this.buttons.add(this.addxtypeChild(children[i]));
21525 editor.on('editorevent', this.updateToolbar, this);
21527 onBtnClick : function(id)
21529 this.editorcore.relayCmd(id);
21530 this.editorcore.focus();
21534 * Protected method that will not generally be called directly. It triggers
21535 * a toolbar update by reading the markup state of the current selection in the editor.
21537 updateToolbar: function(){
21539 if(!this.editorcore.activated){
21540 this.editor.onFirstFocus(); // is this neeed?
21544 var btns = this.buttons;
21545 var doc = this.editorcore.doc;
21546 btns.get('bold').setActive(doc.queryCommandState('bold'));
21547 btns.get('italic').setActive(doc.queryCommandState('italic'));
21548 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21550 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21551 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21552 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21554 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21555 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21558 var ans = this.editorcore.getAllAncestors();
21559 if (this.formatCombo) {
21562 var store = this.formatCombo.store;
21563 this.formatCombo.setValue("");
21564 for (var i =0; i < ans.length;i++) {
21565 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21567 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21575 // hides menus... - so this cant be on a menu...
21576 Roo.bootstrap.MenuMgr.hideAll();
21578 Roo.bootstrap.MenuMgr.hideAll();
21579 //this.editorsyncValue();
21581 onFirstFocus: function() {
21582 this.buttons.each(function(item){
21586 toggleSourceEdit : function(sourceEditMode){
21589 if(sourceEditMode){
21590 Roo.log("disabling buttons");
21591 this.buttons.each( function(item){
21592 if(item.cmd != 'pencil'){
21598 Roo.log("enabling buttons");
21599 if(this.editorcore.initialized){
21600 this.buttons.each( function(item){
21606 Roo.log("calling toggole on editor");
21607 // tell the editor that it's been pressed..
21608 this.editor.toggleSourceEdit(sourceEditMode);
21618 * @class Roo.bootstrap.Table.AbstractSelectionModel
21619 * @extends Roo.util.Observable
21620 * Abstract base class for grid SelectionModels. It provides the interface that should be
21621 * implemented by descendant classes. This class should not be directly instantiated.
21624 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21625 this.locked = false;
21626 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21630 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21631 /** @ignore Called by the grid automatically. Do not call directly. */
21632 init : function(grid){
21638 * Locks the selections.
21641 this.locked = true;
21645 * Unlocks the selections.
21647 unlock : function(){
21648 this.locked = false;
21652 * Returns true if the selections are locked.
21653 * @return {Boolean}
21655 isLocked : function(){
21656 return this.locked;
21660 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21661 * @class Roo.bootstrap.Table.RowSelectionModel
21662 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21663 * It supports multiple selections and keyboard selection/navigation.
21665 * @param {Object} config
21668 Roo.bootstrap.Table.RowSelectionModel = function(config){
21669 Roo.apply(this, config);
21670 this.selections = new Roo.util.MixedCollection(false, function(o){
21675 this.lastActive = false;
21679 * @event selectionchange
21680 * Fires when the selection changes
21681 * @param {SelectionModel} this
21683 "selectionchange" : true,
21685 * @event afterselectionchange
21686 * Fires after the selection changes (eg. by key press or clicking)
21687 * @param {SelectionModel} this
21689 "afterselectionchange" : true,
21691 * @event beforerowselect
21692 * Fires when a row is selected being selected, return false to cancel.
21693 * @param {SelectionModel} this
21694 * @param {Number} rowIndex The selected index
21695 * @param {Boolean} keepExisting False if other selections will be cleared
21697 "beforerowselect" : true,
21700 * Fires when a row is selected.
21701 * @param {SelectionModel} this
21702 * @param {Number} rowIndex The selected index
21703 * @param {Roo.data.Record} r The record
21705 "rowselect" : true,
21707 * @event rowdeselect
21708 * Fires when a row is deselected.
21709 * @param {SelectionModel} this
21710 * @param {Number} rowIndex The selected index
21712 "rowdeselect" : true
21714 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21715 this.locked = false;
21718 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21720 * @cfg {Boolean} singleSelect
21721 * True to allow selection of only one row at a time (defaults to false)
21723 singleSelect : false,
21726 initEvents : function(){
21728 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21729 this.grid.on("mousedown", this.handleMouseDown, this);
21730 }else{ // allow click to work like normal
21731 this.grid.on("rowclick", this.handleDragableRowClick, this);
21734 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21735 "up" : function(e){
21737 this.selectPrevious(e.shiftKey);
21738 }else if(this.last !== false && this.lastActive !== false){
21739 var last = this.last;
21740 this.selectRange(this.last, this.lastActive-1);
21741 this.grid.getView().focusRow(this.lastActive);
21742 if(last !== false){
21746 this.selectFirstRow();
21748 this.fireEvent("afterselectionchange", this);
21750 "down" : function(e){
21752 this.selectNext(e.shiftKey);
21753 }else if(this.last !== false && this.lastActive !== false){
21754 var last = this.last;
21755 this.selectRange(this.last, this.lastActive+1);
21756 this.grid.getView().focusRow(this.lastActive);
21757 if(last !== false){
21761 this.selectFirstRow();
21763 this.fireEvent("afterselectionchange", this);
21768 var view = this.grid.view;
21769 view.on("refresh", this.onRefresh, this);
21770 view.on("rowupdated", this.onRowUpdated, this);
21771 view.on("rowremoved", this.onRemove, this);
21775 onRefresh : function(){
21776 var ds = this.grid.dataSource, i, v = this.grid.view;
21777 var s = this.selections;
21778 s.each(function(r){
21779 if((i = ds.indexOfId(r.id)) != -1){
21788 onRemove : function(v, index, r){
21789 this.selections.remove(r);
21793 onRowUpdated : function(v, index, r){
21794 if(this.isSelected(r)){
21795 v.onRowSelect(index);
21801 * @param {Array} records The records to select
21802 * @param {Boolean} keepExisting (optional) True to keep existing selections
21804 selectRecords : function(records, keepExisting){
21806 this.clearSelections();
21808 var ds = this.grid.dataSource;
21809 for(var i = 0, len = records.length; i < len; i++){
21810 this.selectRow(ds.indexOf(records[i]), true);
21815 * Gets the number of selected rows.
21818 getCount : function(){
21819 return this.selections.length;
21823 * Selects the first row in the grid.
21825 selectFirstRow : function(){
21830 * Select the last row.
21831 * @param {Boolean} keepExisting (optional) True to keep existing selections
21833 selectLastRow : function(keepExisting){
21834 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21838 * Selects the row immediately following the last selected row.
21839 * @param {Boolean} keepExisting (optional) True to keep existing selections
21841 selectNext : function(keepExisting){
21842 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21843 this.selectRow(this.last+1, keepExisting);
21844 this.grid.getView().focusRow(this.last);
21849 * Selects the row that precedes the last selected row.
21850 * @param {Boolean} keepExisting (optional) True to keep existing selections
21852 selectPrevious : function(keepExisting){
21854 this.selectRow(this.last-1, keepExisting);
21855 this.grid.getView().focusRow(this.last);
21860 * Returns the selected records
21861 * @return {Array} Array of selected records
21863 getSelections : function(){
21864 return [].concat(this.selections.items);
21868 * Returns the first selected record.
21871 getSelected : function(){
21872 return this.selections.itemAt(0);
21877 * Clears all selections.
21879 clearSelections : function(fast){
21884 var ds = this.grid.dataSource;
21885 var s = this.selections;
21886 s.each(function(r){
21887 this.deselectRow(ds.indexOfId(r.id));
21891 this.selections.clear();
21898 * Selects all rows.
21900 selectAll : function(){
21904 this.selections.clear();
21905 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21906 this.selectRow(i, true);
21911 * Returns True if there is a selection.
21912 * @return {Boolean}
21914 hasSelection : function(){
21915 return this.selections.length > 0;
21919 * Returns True if the specified row is selected.
21920 * @param {Number/Record} record The record or index of the record to check
21921 * @return {Boolean}
21923 isSelected : function(index){
21924 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21925 return (r && this.selections.key(r.id) ? true : false);
21929 * Returns True if the specified record id is selected.
21930 * @param {String} id The id of record to check
21931 * @return {Boolean}
21933 isIdSelected : function(id){
21934 return (this.selections.key(id) ? true : false);
21938 handleMouseDown : function(e, t){
21939 var view = this.grid.getView(), rowIndex;
21940 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21943 if(e.shiftKey && this.last !== false){
21944 var last = this.last;
21945 this.selectRange(last, rowIndex, e.ctrlKey);
21946 this.last = last; // reset the last
21947 view.focusRow(rowIndex);
21949 var isSelected = this.isSelected(rowIndex);
21950 if(e.button !== 0 && isSelected){
21951 view.focusRow(rowIndex);
21952 }else if(e.ctrlKey && isSelected){
21953 this.deselectRow(rowIndex);
21954 }else if(!isSelected){
21955 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21956 view.focusRow(rowIndex);
21959 this.fireEvent("afterselectionchange", this);
21962 handleDragableRowClick : function(grid, rowIndex, e)
21964 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21965 this.selectRow(rowIndex, false);
21966 grid.view.focusRow(rowIndex);
21967 this.fireEvent("afterselectionchange", this);
21972 * Selects multiple rows.
21973 * @param {Array} rows Array of the indexes of the row to select
21974 * @param {Boolean} keepExisting (optional) True to keep existing selections
21976 selectRows : function(rows, keepExisting){
21978 this.clearSelections();
21980 for(var i = 0, len = rows.length; i < len; i++){
21981 this.selectRow(rows[i], true);
21986 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21987 * @param {Number} startRow The index of the first row in the range
21988 * @param {Number} endRow The index of the last row in the range
21989 * @param {Boolean} keepExisting (optional) True to retain existing selections
21991 selectRange : function(startRow, endRow, keepExisting){
21996 this.clearSelections();
21998 if(startRow <= endRow){
21999 for(var i = startRow; i <= endRow; i++){
22000 this.selectRow(i, true);
22003 for(var i = startRow; i >= endRow; i--){
22004 this.selectRow(i, true);
22010 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22011 * @param {Number} startRow The index of the first row in the range
22012 * @param {Number} endRow The index of the last row in the range
22014 deselectRange : function(startRow, endRow, preventViewNotify){
22018 for(var i = startRow; i <= endRow; i++){
22019 this.deselectRow(i, preventViewNotify);
22025 * @param {Number} row The index of the row to select
22026 * @param {Boolean} keepExisting (optional) True to keep existing selections
22028 selectRow : function(index, keepExisting, preventViewNotify){
22029 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22032 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22033 if(!keepExisting || this.singleSelect){
22034 this.clearSelections();
22036 var r = this.grid.dataSource.getAt(index);
22037 this.selections.add(r);
22038 this.last = this.lastActive = index;
22039 if(!preventViewNotify){
22040 this.grid.getView().onRowSelect(index);
22042 this.fireEvent("rowselect", this, index, r);
22043 this.fireEvent("selectionchange", this);
22049 * @param {Number} row The index of the row to deselect
22051 deselectRow : function(index, preventViewNotify){
22055 if(this.last == index){
22058 if(this.lastActive == index){
22059 this.lastActive = false;
22061 var r = this.grid.dataSource.getAt(index);
22062 this.selections.remove(r);
22063 if(!preventViewNotify){
22064 this.grid.getView().onRowDeselect(index);
22066 this.fireEvent("rowdeselect", this, index);
22067 this.fireEvent("selectionchange", this);
22071 restoreLast : function(){
22073 this.last = this._last;
22078 acceptsNav : function(row, col, cm){
22079 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22083 onEditorKey : function(field, e){
22084 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22089 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22091 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22093 }else if(k == e.ENTER && !e.ctrlKey){
22097 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22099 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22101 }else if(k == e.ESC){
22105 g.startEditing(newCell[0], newCell[1]);
22110 * Ext JS Library 1.1.1
22111 * Copyright(c) 2006-2007, Ext JS, LLC.
22113 * Originally Released Under LGPL - original licence link has changed is not relivant.
22116 * <script type="text/javascript">
22120 * @class Roo.bootstrap.PagingToolbar
22121 * @extends Roo.bootstrap.NavSimplebar
22122 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22124 * Create a new PagingToolbar
22125 * @param {Object} config The config object
22126 * @param {Roo.data.Store} store
22128 Roo.bootstrap.PagingToolbar = function(config)
22130 // old args format still supported... - xtype is prefered..
22131 // created from xtype...
22133 this.ds = config.dataSource;
22135 if (config.store && !this.ds) {
22136 this.store= Roo.factory(config.store, Roo.data);
22137 this.ds = this.store;
22138 this.ds.xmodule = this.xmodule || false;
22141 this.toolbarItems = [];
22142 if (config.items) {
22143 this.toolbarItems = config.items;
22146 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22151 this.bind(this.ds);
22154 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22158 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22160 * @cfg {Roo.data.Store} dataSource
22161 * The underlying data store providing the paged data
22164 * @cfg {String/HTMLElement/Element} container
22165 * container The id or element that will contain the toolbar
22168 * @cfg {Boolean} displayInfo
22169 * True to display the displayMsg (defaults to false)
22172 * @cfg {Number} pageSize
22173 * The number of records to display per page (defaults to 20)
22177 * @cfg {String} displayMsg
22178 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22180 displayMsg : 'Displaying {0} - {1} of {2}',
22182 * @cfg {String} emptyMsg
22183 * The message to display when no records are found (defaults to "No data to display")
22185 emptyMsg : 'No data to display',
22187 * Customizable piece of the default paging text (defaults to "Page")
22190 beforePageText : "Page",
22192 * Customizable piece of the default paging text (defaults to "of %0")
22195 afterPageText : "of {0}",
22197 * Customizable piece of the default paging text (defaults to "First Page")
22200 firstText : "First Page",
22202 * Customizable piece of the default paging text (defaults to "Previous Page")
22205 prevText : "Previous Page",
22207 * Customizable piece of the default paging text (defaults to "Next Page")
22210 nextText : "Next Page",
22212 * Customizable piece of the default paging text (defaults to "Last Page")
22215 lastText : "Last Page",
22217 * Customizable piece of the default paging text (defaults to "Refresh")
22220 refreshText : "Refresh",
22224 onRender : function(ct, position)
22226 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22227 this.navgroup.parentId = this.id;
22228 this.navgroup.onRender(this.el, null);
22229 // add the buttons to the navgroup
22231 if(this.displayInfo){
22232 Roo.log(this.el.select('ul.navbar-nav',true).first());
22233 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22234 this.displayEl = this.el.select('.x-paging-info', true).first();
22235 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22236 // this.displayEl = navel.el.select('span',true).first();
22242 Roo.each(_this.buttons, function(e){ // this might need to use render????
22243 Roo.factory(e).onRender(_this.el, null);
22247 Roo.each(_this.toolbarItems, function(e) {
22248 _this.navgroup.addItem(e);
22252 this.first = this.navgroup.addItem({
22253 tooltip: this.firstText,
22255 icon : 'fa fa-backward',
22257 preventDefault: true,
22258 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22261 this.prev = this.navgroup.addItem({
22262 tooltip: this.prevText,
22264 icon : 'fa fa-step-backward',
22266 preventDefault: true,
22267 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22269 //this.addSeparator();
22272 var field = this.navgroup.addItem( {
22274 cls : 'x-paging-position',
22276 html : this.beforePageText +
22277 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22278 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22281 this.field = field.el.select('input', true).first();
22282 this.field.on("keydown", this.onPagingKeydown, this);
22283 this.field.on("focus", function(){this.dom.select();});
22286 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22287 //this.field.setHeight(18);
22288 //this.addSeparator();
22289 this.next = this.navgroup.addItem({
22290 tooltip: this.nextText,
22292 html : ' <i class="fa fa-step-forward">',
22294 preventDefault: true,
22295 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22297 this.last = this.navgroup.addItem({
22298 tooltip: this.lastText,
22299 icon : 'fa fa-forward',
22302 preventDefault: true,
22303 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22305 //this.addSeparator();
22306 this.loading = this.navgroup.addItem({
22307 tooltip: this.refreshText,
22308 icon: 'fa fa-refresh',
22309 preventDefault: true,
22310 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22316 updateInfo : function(){
22317 if(this.displayEl){
22318 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22319 var msg = count == 0 ?
22323 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22325 this.displayEl.update(msg);
22330 onLoad : function(ds, r, o){
22331 this.cursor = o.params ? o.params.start : 0;
22332 var d = this.getPageData(),
22336 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22337 this.field.dom.value = ap;
22338 this.first.setDisabled(ap == 1);
22339 this.prev.setDisabled(ap == 1);
22340 this.next.setDisabled(ap == ps);
22341 this.last.setDisabled(ap == ps);
22342 this.loading.enable();
22347 getPageData : function(){
22348 var total = this.ds.getTotalCount();
22351 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22352 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22357 onLoadError : function(){
22358 this.loading.enable();
22362 onPagingKeydown : function(e){
22363 var k = e.getKey();
22364 var d = this.getPageData();
22366 var v = this.field.dom.value, pageNum;
22367 if(!v || isNaN(pageNum = parseInt(v, 10))){
22368 this.field.dom.value = d.activePage;
22371 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22372 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22375 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))
22377 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22378 this.field.dom.value = pageNum;
22379 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22382 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22384 var v = this.field.dom.value, pageNum;
22385 var increment = (e.shiftKey) ? 10 : 1;
22386 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22389 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22390 this.field.dom.value = d.activePage;
22393 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22395 this.field.dom.value = parseInt(v, 10) + increment;
22396 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22397 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22404 beforeLoad : function(){
22406 this.loading.disable();
22411 onClick : function(which){
22420 ds.load({params:{start: 0, limit: this.pageSize}});
22423 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22426 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22429 var total = ds.getTotalCount();
22430 var extra = total % this.pageSize;
22431 var lastStart = extra ? (total - extra) : total-this.pageSize;
22432 ds.load({params:{start: lastStart, limit: this.pageSize}});
22435 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22441 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22442 * @param {Roo.data.Store} store The data store to unbind
22444 unbind : function(ds){
22445 ds.un("beforeload", this.beforeLoad, this);
22446 ds.un("load", this.onLoad, this);
22447 ds.un("loadexception", this.onLoadError, this);
22448 ds.un("remove", this.updateInfo, this);
22449 ds.un("add", this.updateInfo, this);
22450 this.ds = undefined;
22454 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22455 * @param {Roo.data.Store} store The data store to bind
22457 bind : function(ds){
22458 ds.on("beforeload", this.beforeLoad, this);
22459 ds.on("load", this.onLoad, this);
22460 ds.on("loadexception", this.onLoadError, this);
22461 ds.on("remove", this.updateInfo, this);
22462 ds.on("add", this.updateInfo, this);
22473 * @class Roo.bootstrap.MessageBar
22474 * @extends Roo.bootstrap.Component
22475 * Bootstrap MessageBar class
22476 * @cfg {String} html contents of the MessageBar
22477 * @cfg {String} weight (info | success | warning | danger) default info
22478 * @cfg {String} beforeClass insert the bar before the given class
22479 * @cfg {Boolean} closable (true | false) default false
22480 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22483 * Create a new Element
22484 * @param {Object} config The config object
22487 Roo.bootstrap.MessageBar = function(config){
22488 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22491 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22497 beforeClass: 'bootstrap-sticky-wrap',
22499 getAutoCreate : function(){
22503 cls: 'alert alert-dismissable alert-' + this.weight,
22508 html: this.html || ''
22514 cfg.cls += ' alert-messages-fixed';
22528 onRender : function(ct, position)
22530 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22533 var cfg = Roo.apply({}, this.getAutoCreate());
22537 cfg.cls += ' ' + this.cls;
22540 cfg.style = this.style;
22542 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22544 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22547 this.el.select('>button.close').on('click', this.hide, this);
22553 if (!this.rendered) {
22559 this.fireEvent('show', this);
22565 if (!this.rendered) {
22571 this.fireEvent('hide', this);
22574 update : function()
22576 // var e = this.el.dom.firstChild;
22578 // if(this.closable){
22579 // e = e.nextSibling;
22582 // e.data = this.html || '';
22584 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22600 * @class Roo.bootstrap.Graph
22601 * @extends Roo.bootstrap.Component
22602 * Bootstrap Graph class
22606 @cfg {String} graphtype bar | vbar | pie
22607 @cfg {number} g_x coodinator | centre x (pie)
22608 @cfg {number} g_y coodinator | centre y (pie)
22609 @cfg {number} g_r radius (pie)
22610 @cfg {number} g_height height of the chart (respected by all elements in the set)
22611 @cfg {number} g_width width of the chart (respected by all elements in the set)
22612 @cfg {Object} title The title of the chart
22615 -opts (object) options for the chart
22617 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22618 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22620 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.
22621 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22623 o stretch (boolean)
22625 -opts (object) options for the pie
22628 o startAngle (number)
22629 o endAngle (number)
22633 * Create a new Input
22634 * @param {Object} config The config object
22637 Roo.bootstrap.Graph = function(config){
22638 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22644 * The img click event for the img.
22645 * @param {Roo.EventObject} e
22651 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22662 //g_colors: this.colors,
22669 getAutoCreate : function(){
22680 onRender : function(ct,position){
22681 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22682 this.raphael = Raphael(this.el.dom);
22684 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22685 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22686 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22687 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22689 r.text(160, 10, "Single Series Chart").attr(txtattr);
22690 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22691 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22692 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22694 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22695 r.barchart(330, 10, 300, 220, data1);
22696 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22697 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22700 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22701 // r.barchart(30, 30, 560, 250, xdata, {
22702 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22703 // axis : "0 0 1 1",
22704 // axisxlabels : xdata
22705 // //yvalues : cols,
22708 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22710 // this.load(null,xdata,{
22711 // axis : "0 0 1 1",
22712 // axisxlabels : xdata
22717 load : function(graphtype,xdata,opts){
22718 this.raphael.clear();
22720 graphtype = this.graphtype;
22725 var r = this.raphael,
22726 fin = function () {
22727 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22729 fout = function () {
22730 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22732 pfin = function() {
22733 this.sector.stop();
22734 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22737 this.label[0].stop();
22738 this.label[0].attr({ r: 7.5 });
22739 this.label[1].attr({ "font-weight": 800 });
22742 pfout = function() {
22743 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22746 this.label[0].animate({ r: 5 }, 500, "bounce");
22747 this.label[1].attr({ "font-weight": 400 });
22753 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22756 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22759 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22760 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22762 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22769 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22774 setTitle: function(o)
22779 initEvents: function() {
22782 this.el.on('click', this.onClick, this);
22786 onClick : function(e)
22788 Roo.log('img onclick');
22789 this.fireEvent('click', this, e);
22801 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22804 * @class Roo.bootstrap.dash.NumberBox
22805 * @extends Roo.bootstrap.Component
22806 * Bootstrap NumberBox class
22807 * @cfg {String} headline Box headline
22808 * @cfg {String} content Box content
22809 * @cfg {String} icon Box icon
22810 * @cfg {String} footer Footer text
22811 * @cfg {String} fhref Footer href
22814 * Create a new NumberBox
22815 * @param {Object} config The config object
22819 Roo.bootstrap.dash.NumberBox = function(config){
22820 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22824 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22833 getAutoCreate : function(){
22837 cls : 'small-box ',
22845 cls : 'roo-headline',
22846 html : this.headline
22850 cls : 'roo-content',
22851 html : this.content
22865 cls : 'ion ' + this.icon
22874 cls : 'small-box-footer',
22875 href : this.fhref || '#',
22879 cfg.cn.push(footer);
22886 onRender : function(ct,position){
22887 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22894 setHeadline: function (value)
22896 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22899 setFooter: function (value, href)
22901 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22904 this.el.select('a.small-box-footer',true).first().attr('href', href);
22909 setContent: function (value)
22911 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22914 initEvents: function()
22928 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22931 * @class Roo.bootstrap.dash.TabBox
22932 * @extends Roo.bootstrap.Component
22933 * Bootstrap TabBox class
22934 * @cfg {String} title Title of the TabBox
22935 * @cfg {String} icon Icon of the TabBox
22936 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22937 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22940 * Create a new TabBox
22941 * @param {Object} config The config object
22945 Roo.bootstrap.dash.TabBox = function(config){
22946 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22951 * When a pane is added
22952 * @param {Roo.bootstrap.dash.TabPane} pane
22956 * @event activatepane
22957 * When a pane is activated
22958 * @param {Roo.bootstrap.dash.TabPane} pane
22960 "activatepane" : true
22968 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22973 tabScrollable : false,
22975 getChildContainer : function()
22977 return this.el.select('.tab-content', true).first();
22980 getAutoCreate : function(){
22984 cls: 'pull-left header',
22992 cls: 'fa ' + this.icon
22998 cls: 'nav nav-tabs pull-right',
23004 if(this.tabScrollable){
23011 cls: 'nav nav-tabs pull-right',
23022 cls: 'nav-tabs-custom',
23027 cls: 'tab-content no-padding',
23035 initEvents : function()
23037 //Roo.log('add add pane handler');
23038 this.on('addpane', this.onAddPane, this);
23041 * Updates the box title
23042 * @param {String} html to set the title to.
23044 setTitle : function(value)
23046 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23048 onAddPane : function(pane)
23050 this.panes.push(pane);
23051 //Roo.log('addpane');
23053 // tabs are rendere left to right..
23054 if(!this.showtabs){
23058 var ctr = this.el.select('.nav-tabs', true).first();
23061 var existing = ctr.select('.nav-tab',true);
23062 var qty = existing.getCount();;
23065 var tab = ctr.createChild({
23067 cls : 'nav-tab' + (qty ? '' : ' active'),
23075 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23078 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23080 pane.el.addClass('active');
23085 onTabClick : function(ev,un,ob,pane)
23087 //Roo.log('tab - prev default');
23088 ev.preventDefault();
23091 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23092 pane.tab.addClass('active');
23093 //Roo.log(pane.title);
23094 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23095 // technically we should have a deactivate event.. but maybe add later.
23096 // and it should not de-activate the selected tab...
23097 this.fireEvent('activatepane', pane);
23098 pane.el.addClass('active');
23099 pane.fireEvent('activate');
23104 getActivePane : function()
23107 Roo.each(this.panes, function(p) {
23108 if(p.el.hasClass('active')){
23129 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23131 * @class Roo.bootstrap.TabPane
23132 * @extends Roo.bootstrap.Component
23133 * Bootstrap TabPane class
23134 * @cfg {Boolean} active (false | true) Default false
23135 * @cfg {String} title title of panel
23139 * Create a new TabPane
23140 * @param {Object} config The config object
23143 Roo.bootstrap.dash.TabPane = function(config){
23144 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23150 * When a pane is activated
23151 * @param {Roo.bootstrap.dash.TabPane} pane
23158 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23163 // the tabBox that this is attached to.
23166 getAutoCreate : function()
23174 cfg.cls += ' active';
23179 initEvents : function()
23181 //Roo.log('trigger add pane handler');
23182 this.parent().fireEvent('addpane', this)
23186 * Updates the tab title
23187 * @param {String} html to set the title to.
23189 setTitle: function(str)
23195 this.tab.select('a', true).first().dom.innerHTML = str;
23212 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23215 * @class Roo.bootstrap.menu.Menu
23216 * @extends Roo.bootstrap.Component
23217 * Bootstrap Menu class - container for Menu
23218 * @cfg {String} html Text of the menu
23219 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23220 * @cfg {String} icon Font awesome icon
23221 * @cfg {String} pos Menu align to (top | bottom) default bottom
23225 * Create a new Menu
23226 * @param {Object} config The config object
23230 Roo.bootstrap.menu.Menu = function(config){
23231 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23235 * @event beforeshow
23236 * Fires before this menu is displayed
23237 * @param {Roo.bootstrap.menu.Menu} this
23241 * @event beforehide
23242 * Fires before this menu is hidden
23243 * @param {Roo.bootstrap.menu.Menu} this
23248 * Fires after this menu is displayed
23249 * @param {Roo.bootstrap.menu.Menu} this
23254 * Fires after this menu is hidden
23255 * @param {Roo.bootstrap.menu.Menu} this
23260 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23261 * @param {Roo.bootstrap.menu.Menu} this
23262 * @param {Roo.EventObject} e
23269 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23273 weight : 'default',
23278 getChildContainer : function() {
23279 if(this.isSubMenu){
23283 return this.el.select('ul.dropdown-menu', true).first();
23286 getAutoCreate : function()
23291 cls : 'roo-menu-text',
23299 cls : 'fa ' + this.icon
23310 cls : 'dropdown-button btn btn-' + this.weight,
23315 cls : 'dropdown-toggle btn btn-' + this.weight,
23325 cls : 'dropdown-menu'
23331 if(this.pos == 'top'){
23332 cfg.cls += ' dropup';
23335 if(this.isSubMenu){
23338 cls : 'dropdown-menu'
23345 onRender : function(ct, position)
23347 this.isSubMenu = ct.hasClass('dropdown-submenu');
23349 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23352 initEvents : function()
23354 if(this.isSubMenu){
23358 this.hidden = true;
23360 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23361 this.triggerEl.on('click', this.onTriggerPress, this);
23363 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23364 this.buttonEl.on('click', this.onClick, this);
23370 if(this.isSubMenu){
23374 return this.el.select('ul.dropdown-menu', true).first();
23377 onClick : function(e)
23379 this.fireEvent("click", this, e);
23382 onTriggerPress : function(e)
23384 if (this.isVisible()) {
23391 isVisible : function(){
23392 return !this.hidden;
23397 this.fireEvent("beforeshow", this);
23399 this.hidden = false;
23400 this.el.addClass('open');
23402 Roo.get(document).on("mouseup", this.onMouseUp, this);
23404 this.fireEvent("show", this);
23411 this.fireEvent("beforehide", this);
23413 this.hidden = true;
23414 this.el.removeClass('open');
23416 Roo.get(document).un("mouseup", this.onMouseUp);
23418 this.fireEvent("hide", this);
23421 onMouseUp : function()
23435 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23438 * @class Roo.bootstrap.menu.Item
23439 * @extends Roo.bootstrap.Component
23440 * Bootstrap MenuItem class
23441 * @cfg {Boolean} submenu (true | false) default false
23442 * @cfg {String} html text of the item
23443 * @cfg {String} href the link
23444 * @cfg {Boolean} disable (true | false) default false
23445 * @cfg {Boolean} preventDefault (true | false) default true
23446 * @cfg {String} icon Font awesome icon
23447 * @cfg {String} pos Submenu align to (left | right) default right
23451 * Create a new Item
23452 * @param {Object} config The config object
23456 Roo.bootstrap.menu.Item = function(config){
23457 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23461 * Fires when the mouse is hovering over this menu
23462 * @param {Roo.bootstrap.menu.Item} this
23463 * @param {Roo.EventObject} e
23468 * Fires when the mouse exits this menu
23469 * @param {Roo.bootstrap.menu.Item} this
23470 * @param {Roo.EventObject} e
23476 * The raw click event for the entire grid.
23477 * @param {Roo.EventObject} e
23483 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23488 preventDefault: true,
23493 getAutoCreate : function()
23498 cls : 'roo-menu-item-text',
23506 cls : 'fa ' + this.icon
23515 href : this.href || '#',
23522 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23526 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23528 if(this.pos == 'left'){
23529 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23536 initEvents : function()
23538 this.el.on('mouseover', this.onMouseOver, this);
23539 this.el.on('mouseout', this.onMouseOut, this);
23541 this.el.select('a', true).first().on('click', this.onClick, this);
23545 onClick : function(e)
23547 if(this.preventDefault){
23548 e.preventDefault();
23551 this.fireEvent("click", this, e);
23554 onMouseOver : function(e)
23556 if(this.submenu && this.pos == 'left'){
23557 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23560 this.fireEvent("mouseover", this, e);
23563 onMouseOut : function(e)
23565 this.fireEvent("mouseout", this, e);
23577 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23580 * @class Roo.bootstrap.menu.Separator
23581 * @extends Roo.bootstrap.Component
23582 * Bootstrap Separator class
23585 * Create a new Separator
23586 * @param {Object} config The config object
23590 Roo.bootstrap.menu.Separator = function(config){
23591 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23594 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23596 getAutoCreate : function(){
23617 * @class Roo.bootstrap.Tooltip
23618 * Bootstrap Tooltip class
23619 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23620 * to determine which dom element triggers the tooltip.
23622 * It needs to add support for additional attributes like tooltip-position
23625 * Create a new Toolti
23626 * @param {Object} config The config object
23629 Roo.bootstrap.Tooltip = function(config){
23630 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23633 Roo.apply(Roo.bootstrap.Tooltip, {
23635 * @function init initialize tooltip monitoring.
23639 currentTip : false,
23640 currentRegion : false,
23646 Roo.get(document).on('mouseover', this.enter ,this);
23647 Roo.get(document).on('mouseout', this.leave, this);
23650 this.currentTip = new Roo.bootstrap.Tooltip();
23653 enter : function(ev)
23655 var dom = ev.getTarget();
23657 //Roo.log(['enter',dom]);
23658 var el = Roo.fly(dom);
23659 if (this.currentEl) {
23661 //Roo.log(this.currentEl);
23662 //Roo.log(this.currentEl.contains(dom));
23663 if (this.currentEl == el) {
23666 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23672 if (this.currentTip.el) {
23673 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23678 // you can not look for children, as if el is the body.. then everythign is the child..
23679 if (!el.attr('tooltip')) { //
23680 if (!el.select("[tooltip]").elements.length) {
23683 // is the mouse over this child...?
23684 bindEl = el.select("[tooltip]").first();
23685 var xy = ev.getXY();
23686 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23687 //Roo.log("not in region.");
23690 //Roo.log("child element over..");
23693 this.currentEl = bindEl;
23694 this.currentTip.bind(bindEl);
23695 this.currentRegion = Roo.lib.Region.getRegion(dom);
23696 this.currentTip.enter();
23699 leave : function(ev)
23701 var dom = ev.getTarget();
23702 //Roo.log(['leave',dom]);
23703 if (!this.currentEl) {
23708 if (dom != this.currentEl.dom) {
23711 var xy = ev.getXY();
23712 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23715 // only activate leave if mouse cursor is outside... bounding box..
23720 if (this.currentTip) {
23721 this.currentTip.leave();
23723 //Roo.log('clear currentEl');
23724 this.currentEl = false;
23729 'left' : ['r-l', [-2,0], 'right'],
23730 'right' : ['l-r', [2,0], 'left'],
23731 'bottom' : ['t-b', [0,2], 'top'],
23732 'top' : [ 'b-t', [0,-2], 'bottom']
23738 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23743 delay : null, // can be { show : 300 , hide: 500}
23747 hoverState : null, //???
23749 placement : 'bottom',
23751 getAutoCreate : function(){
23758 cls : 'tooltip-arrow'
23761 cls : 'tooltip-inner'
23768 bind : function(el)
23774 enter : function () {
23776 if (this.timeout != null) {
23777 clearTimeout(this.timeout);
23780 this.hoverState = 'in';
23781 //Roo.log("enter - show");
23782 if (!this.delay || !this.delay.show) {
23787 this.timeout = setTimeout(function () {
23788 if (_t.hoverState == 'in') {
23791 }, this.delay.show);
23795 clearTimeout(this.timeout);
23797 this.hoverState = 'out';
23798 if (!this.delay || !this.delay.hide) {
23804 this.timeout = setTimeout(function () {
23805 //Roo.log("leave - timeout");
23807 if (_t.hoverState == 'out') {
23809 Roo.bootstrap.Tooltip.currentEl = false;
23817 this.render(document.body);
23820 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23822 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23824 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23826 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23828 var placement = typeof this.placement == 'function' ?
23829 this.placement.call(this, this.el, on_el) :
23832 var autoToken = /\s?auto?\s?/i;
23833 var autoPlace = autoToken.test(placement);
23835 placement = placement.replace(autoToken, '') || 'top';
23839 //this.el.setXY([0,0]);
23841 //this.el.dom.style.display='block';
23843 //this.el.appendTo(on_el);
23845 var p = this.getPosition();
23846 var box = this.el.getBox();
23852 var align = Roo.bootstrap.Tooltip.alignment[placement];
23854 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23856 if(placement == 'top' || placement == 'bottom'){
23858 placement = 'right';
23861 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23862 placement = 'left';
23866 align = Roo.bootstrap.Tooltip.alignment[placement];
23868 this.el.alignTo(this.bindEl, align[0],align[1]);
23869 //var arrow = this.el.select('.arrow',true).first();
23870 //arrow.set(align[2],
23872 this.el.addClass(placement);
23874 this.el.addClass('in fade');
23876 this.hoverState = null;
23878 if (this.el.hasClass('fade')) {
23889 //this.el.setXY([0,0]);
23890 this.el.removeClass('in');
23906 * @class Roo.bootstrap.LocationPicker
23907 * @extends Roo.bootstrap.Component
23908 * Bootstrap LocationPicker class
23909 * @cfg {Number} latitude Position when init default 0
23910 * @cfg {Number} longitude Position when init default 0
23911 * @cfg {Number} zoom default 15
23912 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23913 * @cfg {Boolean} mapTypeControl default false
23914 * @cfg {Boolean} disableDoubleClickZoom default false
23915 * @cfg {Boolean} scrollwheel default true
23916 * @cfg {Boolean} streetViewControl default false
23917 * @cfg {Number} radius default 0
23918 * @cfg {String} locationName
23919 * @cfg {Boolean} draggable default true
23920 * @cfg {Boolean} enableAutocomplete default false
23921 * @cfg {Boolean} enableReverseGeocode default true
23922 * @cfg {String} markerTitle
23925 * Create a new LocationPicker
23926 * @param {Object} config The config object
23930 Roo.bootstrap.LocationPicker = function(config){
23932 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23937 * Fires when the picker initialized.
23938 * @param {Roo.bootstrap.LocationPicker} this
23939 * @param {Google Location} location
23943 * @event positionchanged
23944 * Fires when the picker position changed.
23945 * @param {Roo.bootstrap.LocationPicker} this
23946 * @param {Google Location} location
23948 positionchanged : true,
23951 * Fires when the map resize.
23952 * @param {Roo.bootstrap.LocationPicker} this
23957 * Fires when the map show.
23958 * @param {Roo.bootstrap.LocationPicker} this
23963 * Fires when the map hide.
23964 * @param {Roo.bootstrap.LocationPicker} this
23969 * Fires when click the map.
23970 * @param {Roo.bootstrap.LocationPicker} this
23971 * @param {Map event} e
23975 * @event mapRightClick
23976 * Fires when right click the map.
23977 * @param {Roo.bootstrap.LocationPicker} this
23978 * @param {Map event} e
23980 mapRightClick : true,
23982 * @event markerClick
23983 * Fires when click the marker.
23984 * @param {Roo.bootstrap.LocationPicker} this
23985 * @param {Map event} e
23987 markerClick : true,
23989 * @event markerRightClick
23990 * Fires when right click the marker.
23991 * @param {Roo.bootstrap.LocationPicker} this
23992 * @param {Map event} e
23994 markerRightClick : true,
23996 * @event OverlayViewDraw
23997 * Fires when OverlayView Draw
23998 * @param {Roo.bootstrap.LocationPicker} this
24000 OverlayViewDraw : true,
24002 * @event OverlayViewOnAdd
24003 * Fires when OverlayView Draw
24004 * @param {Roo.bootstrap.LocationPicker} this
24006 OverlayViewOnAdd : true,
24008 * @event OverlayViewOnRemove
24009 * Fires when OverlayView Draw
24010 * @param {Roo.bootstrap.LocationPicker} this
24012 OverlayViewOnRemove : true,
24014 * @event OverlayViewShow
24015 * Fires when OverlayView Draw
24016 * @param {Roo.bootstrap.LocationPicker} this
24017 * @param {Pixel} cpx
24019 OverlayViewShow : true,
24021 * @event OverlayViewHide
24022 * Fires when OverlayView Draw
24023 * @param {Roo.bootstrap.LocationPicker} this
24025 OverlayViewHide : true,
24027 * @event loadexception
24028 * Fires when load google lib failed.
24029 * @param {Roo.bootstrap.LocationPicker} this
24031 loadexception : true
24036 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24038 gMapContext: false,
24044 mapTypeControl: false,
24045 disableDoubleClickZoom: false,
24047 streetViewControl: false,
24051 enableAutocomplete: false,
24052 enableReverseGeocode: true,
24055 getAutoCreate: function()
24060 cls: 'roo-location-picker'
24066 initEvents: function(ct, position)
24068 if(!this.el.getWidth() || this.isApplied()){
24072 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24077 initial: function()
24079 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24080 this.fireEvent('loadexception', this);
24084 if(!this.mapTypeId){
24085 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24088 this.gMapContext = this.GMapContext();
24090 this.initOverlayView();
24092 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24096 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24097 _this.setPosition(_this.gMapContext.marker.position);
24100 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24101 _this.fireEvent('mapClick', this, event);
24105 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24106 _this.fireEvent('mapRightClick', this, event);
24110 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24111 _this.fireEvent('markerClick', this, event);
24115 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24116 _this.fireEvent('markerRightClick', this, event);
24120 this.setPosition(this.gMapContext.location);
24122 this.fireEvent('initial', this, this.gMapContext.location);
24125 initOverlayView: function()
24129 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24133 _this.fireEvent('OverlayViewDraw', _this);
24138 _this.fireEvent('OverlayViewOnAdd', _this);
24141 onRemove: function()
24143 _this.fireEvent('OverlayViewOnRemove', _this);
24146 show: function(cpx)
24148 _this.fireEvent('OverlayViewShow', _this, cpx);
24153 _this.fireEvent('OverlayViewHide', _this);
24159 fromLatLngToContainerPixel: function(event)
24161 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24164 isApplied: function()
24166 return this.getGmapContext() == false ? false : true;
24169 getGmapContext: function()
24171 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24174 GMapContext: function()
24176 var position = new google.maps.LatLng(this.latitude, this.longitude);
24178 var _map = new google.maps.Map(this.el.dom, {
24181 mapTypeId: this.mapTypeId,
24182 mapTypeControl: this.mapTypeControl,
24183 disableDoubleClickZoom: this.disableDoubleClickZoom,
24184 scrollwheel: this.scrollwheel,
24185 streetViewControl: this.streetViewControl,
24186 locationName: this.locationName,
24187 draggable: this.draggable,
24188 enableAutocomplete: this.enableAutocomplete,
24189 enableReverseGeocode: this.enableReverseGeocode
24192 var _marker = new google.maps.Marker({
24193 position: position,
24195 title: this.markerTitle,
24196 draggable: this.draggable
24203 location: position,
24204 radius: this.radius,
24205 locationName: this.locationName,
24206 addressComponents: {
24207 formatted_address: null,
24208 addressLine1: null,
24209 addressLine2: null,
24211 streetNumber: null,
24215 stateOrProvince: null
24218 domContainer: this.el.dom,
24219 geodecoder: new google.maps.Geocoder()
24223 drawCircle: function(center, radius, options)
24225 if (this.gMapContext.circle != null) {
24226 this.gMapContext.circle.setMap(null);
24230 options = Roo.apply({}, options, {
24231 strokeColor: "#0000FF",
24232 strokeOpacity: .35,
24234 fillColor: "#0000FF",
24238 options.map = this.gMapContext.map;
24239 options.radius = radius;
24240 options.center = center;
24241 this.gMapContext.circle = new google.maps.Circle(options);
24242 return this.gMapContext.circle;
24248 setPosition: function(location)
24250 this.gMapContext.location = location;
24251 this.gMapContext.marker.setPosition(location);
24252 this.gMapContext.map.panTo(location);
24253 this.drawCircle(location, this.gMapContext.radius, {});
24257 if (this.gMapContext.settings.enableReverseGeocode) {
24258 this.gMapContext.geodecoder.geocode({
24259 latLng: this.gMapContext.location
24260 }, function(results, status) {
24262 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24263 _this.gMapContext.locationName = results[0].formatted_address;
24264 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24266 _this.fireEvent('positionchanged', this, location);
24273 this.fireEvent('positionchanged', this, location);
24278 google.maps.event.trigger(this.gMapContext.map, "resize");
24280 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24282 this.fireEvent('resize', this);
24285 setPositionByLatLng: function(latitude, longitude)
24287 this.setPosition(new google.maps.LatLng(latitude, longitude));
24290 getCurrentPosition: function()
24293 latitude: this.gMapContext.location.lat(),
24294 longitude: this.gMapContext.location.lng()
24298 getAddressName: function()
24300 return this.gMapContext.locationName;
24303 getAddressComponents: function()
24305 return this.gMapContext.addressComponents;
24308 address_component_from_google_geocode: function(address_components)
24312 for (var i = 0; i < address_components.length; i++) {
24313 var component = address_components[i];
24314 if (component.types.indexOf("postal_code") >= 0) {
24315 result.postalCode = component.short_name;
24316 } else if (component.types.indexOf("street_number") >= 0) {
24317 result.streetNumber = component.short_name;
24318 } else if (component.types.indexOf("route") >= 0) {
24319 result.streetName = component.short_name;
24320 } else if (component.types.indexOf("neighborhood") >= 0) {
24321 result.city = component.short_name;
24322 } else if (component.types.indexOf("locality") >= 0) {
24323 result.city = component.short_name;
24324 } else if (component.types.indexOf("sublocality") >= 0) {
24325 result.district = component.short_name;
24326 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24327 result.stateOrProvince = component.short_name;
24328 } else if (component.types.indexOf("country") >= 0) {
24329 result.country = component.short_name;
24333 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24334 result.addressLine2 = "";
24338 setZoomLevel: function(zoom)
24340 this.gMapContext.map.setZoom(zoom);
24353 this.fireEvent('show', this);
24364 this.fireEvent('hide', this);
24369 Roo.apply(Roo.bootstrap.LocationPicker, {
24371 OverlayView : function(map, options)
24373 options = options || {};
24387 * @class Roo.bootstrap.Alert
24388 * @extends Roo.bootstrap.Component
24389 * Bootstrap Alert class
24390 * @cfg {String} title The title of alert
24391 * @cfg {String} html The content of alert
24392 * @cfg {String} weight ( success | info | warning | danger )
24393 * @cfg {String} faicon font-awesomeicon
24396 * Create a new alert
24397 * @param {Object} config The config object
24401 Roo.bootstrap.Alert = function(config){
24402 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24406 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24413 getAutoCreate : function()
24422 cls : 'roo-alert-icon'
24427 cls : 'roo-alert-title',
24432 cls : 'roo-alert-text',
24439 cfg.cn[0].cls += ' fa ' + this.faicon;
24443 cfg.cls += ' alert-' + this.weight;
24449 initEvents: function()
24451 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24454 setTitle : function(str)
24456 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24459 setText : function(str)
24461 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24464 setWeight : function(weight)
24467 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24470 this.weight = weight;
24472 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24475 setIcon : function(icon)
24478 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24481 this.faicon = icon;
24483 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24504 * @class Roo.bootstrap.UploadCropbox
24505 * @extends Roo.bootstrap.Component
24506 * Bootstrap UploadCropbox class
24507 * @cfg {String} emptyText show when image has been loaded
24508 * @cfg {String} rotateNotify show when image too small to rotate
24509 * @cfg {Number} errorTimeout default 3000
24510 * @cfg {Number} minWidth default 300
24511 * @cfg {Number} minHeight default 300
24512 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24513 * @cfg {Boolean} isDocument (true|false) default false
24514 * @cfg {String} url action url
24515 * @cfg {String} paramName default 'imageUpload'
24516 * @cfg {String} method default POST
24517 * @cfg {Boolean} loadMask (true|false) default true
24518 * @cfg {Boolean} loadingText default 'Loading...'
24521 * Create a new UploadCropbox
24522 * @param {Object} config The config object
24525 Roo.bootstrap.UploadCropbox = function(config){
24526 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24530 * @event beforeselectfile
24531 * Fire before select file
24532 * @param {Roo.bootstrap.UploadCropbox} this
24534 "beforeselectfile" : true,
24537 * Fire after initEvent
24538 * @param {Roo.bootstrap.UploadCropbox} this
24543 * Fire after initEvent
24544 * @param {Roo.bootstrap.UploadCropbox} this
24545 * @param {String} data
24550 * Fire when preparing the file data
24551 * @param {Roo.bootstrap.UploadCropbox} this
24552 * @param {Object} file
24557 * Fire when get exception
24558 * @param {Roo.bootstrap.UploadCropbox} this
24559 * @param {XMLHttpRequest} xhr
24561 "exception" : true,
24563 * @event beforeloadcanvas
24564 * Fire before load the canvas
24565 * @param {Roo.bootstrap.UploadCropbox} this
24566 * @param {String} src
24568 "beforeloadcanvas" : true,
24571 * Fire when trash image
24572 * @param {Roo.bootstrap.UploadCropbox} this
24577 * Fire when download the image
24578 * @param {Roo.bootstrap.UploadCropbox} this
24582 * @event footerbuttonclick
24583 * Fire when footerbuttonclick
24584 * @param {Roo.bootstrap.UploadCropbox} this
24585 * @param {String} type
24587 "footerbuttonclick" : true,
24591 * @param {Roo.bootstrap.UploadCropbox} this
24596 * Fire when rotate the image
24597 * @param {Roo.bootstrap.UploadCropbox} this
24598 * @param {String} pos
24603 * Fire when inspect the file
24604 * @param {Roo.bootstrap.UploadCropbox} this
24605 * @param {Object} file
24610 * Fire when xhr upload the file
24611 * @param {Roo.bootstrap.UploadCropbox} this
24612 * @param {Object} data
24617 * Fire when arrange the file data
24618 * @param {Roo.bootstrap.UploadCropbox} this
24619 * @param {Object} formData
24624 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24627 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24629 emptyText : 'Click to upload image',
24630 rotateNotify : 'Image is too small to rotate',
24631 errorTimeout : 3000,
24645 cropType : 'image/jpeg',
24647 canvasLoaded : false,
24648 isDocument : false,
24650 paramName : 'imageUpload',
24652 loadingText : 'Loading...',
24655 getAutoCreate : function()
24659 cls : 'roo-upload-cropbox',
24663 cls : 'roo-upload-cropbox-selector',
24668 cls : 'roo-upload-cropbox-body',
24669 style : 'cursor:pointer',
24673 cls : 'roo-upload-cropbox-preview'
24677 cls : 'roo-upload-cropbox-thumb'
24681 cls : 'roo-upload-cropbox-empty-notify',
24682 html : this.emptyText
24686 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24687 html : this.rotateNotify
24693 cls : 'roo-upload-cropbox-footer',
24696 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24706 onRender : function(ct, position)
24708 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24710 if (this.buttons.length) {
24712 Roo.each(this.buttons, function(bb) {
24714 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24716 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24722 this.maskEl = this.el;
24726 initEvents : function()
24728 this.urlAPI = (window.createObjectURL && window) ||
24729 (window.URL && URL.revokeObjectURL && URL) ||
24730 (window.webkitURL && webkitURL);
24732 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24733 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24735 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24736 this.selectorEl.hide();
24738 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24739 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24741 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24742 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24743 this.thumbEl.hide();
24745 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24746 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24748 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24749 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750 this.errorEl.hide();
24752 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24753 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24754 this.footerEl.hide();
24756 this.setThumbBoxSize();
24762 this.fireEvent('initial', this);
24769 window.addEventListener("resize", function() { _this.resize(); } );
24771 this.bodyEl.on('click', this.beforeSelectFile, this);
24774 this.bodyEl.on('touchstart', this.onTouchStart, this);
24775 this.bodyEl.on('touchmove', this.onTouchMove, this);
24776 this.bodyEl.on('touchend', this.onTouchEnd, this);
24780 this.bodyEl.on('mousedown', this.onMouseDown, this);
24781 this.bodyEl.on('mousemove', this.onMouseMove, this);
24782 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24783 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24784 Roo.get(document).on('mouseup', this.onMouseUp, this);
24787 this.selectorEl.on('change', this.onFileSelected, this);
24793 this.baseScale = 1;
24795 this.baseRotate = 1;
24796 this.dragable = false;
24797 this.pinching = false;
24800 this.cropData = false;
24801 this.notifyEl.dom.innerHTML = this.emptyText;
24803 this.selectorEl.dom.value = '';
24807 resize : function()
24809 if(this.fireEvent('resize', this) != false){
24810 this.setThumbBoxPosition();
24811 this.setCanvasPosition();
24815 onFooterButtonClick : function(e, el, o, type)
24818 case 'rotate-left' :
24819 this.onRotateLeft(e);
24821 case 'rotate-right' :
24822 this.onRotateRight(e);
24825 this.beforeSelectFile(e);
24840 this.fireEvent('footerbuttonclick', this, type);
24843 beforeSelectFile : function(e)
24845 e.preventDefault();
24847 if(this.fireEvent('beforeselectfile', this) != false){
24848 this.selectorEl.dom.click();
24852 onFileSelected : function(e)
24854 e.preventDefault();
24856 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24860 var file = this.selectorEl.dom.files[0];
24862 if(this.fireEvent('inspect', this, file) != false){
24863 this.prepare(file);
24868 trash : function(e)
24870 this.fireEvent('trash', this);
24873 download : function(e)
24875 this.fireEvent('download', this);
24878 loadCanvas : function(src)
24880 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24884 this.imageEl = document.createElement('img');
24888 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24890 this.imageEl.src = src;
24894 onLoadCanvas : function()
24896 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24897 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24899 this.bodyEl.un('click', this.beforeSelectFile, this);
24901 this.notifyEl.hide();
24902 this.thumbEl.show();
24903 this.footerEl.show();
24905 this.baseRotateLevel();
24907 if(this.isDocument){
24908 this.setThumbBoxSize();
24911 this.setThumbBoxPosition();
24913 this.baseScaleLevel();
24919 this.canvasLoaded = true;
24922 this.maskEl.unmask();
24927 setCanvasPosition : function()
24929 if(!this.canvasEl){
24933 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24934 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24936 this.previewEl.setLeft(pw);
24937 this.previewEl.setTop(ph);
24941 onMouseDown : function(e)
24945 this.dragable = true;
24946 this.pinching = false;
24948 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24949 this.dragable = false;
24953 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24954 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24958 onMouseMove : function(e)
24962 if(!this.canvasLoaded){
24966 if (!this.dragable){
24970 var minX = Math.ceil(this.thumbEl.getLeft(true));
24971 var minY = Math.ceil(this.thumbEl.getTop(true));
24973 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24974 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24976 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24977 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24979 x = x - this.mouseX;
24980 y = y - this.mouseY;
24982 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24983 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24985 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24986 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24988 this.previewEl.setLeft(bgX);
24989 this.previewEl.setTop(bgY);
24991 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24992 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24995 onMouseUp : function(e)
24999 this.dragable = false;
25002 onMouseWheel : function(e)
25006 this.startScale = this.scale;
25008 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25010 if(!this.zoomable()){
25011 this.scale = this.startScale;
25020 zoomable : function()
25022 var minScale = this.thumbEl.getWidth() / this.minWidth;
25024 if(this.minWidth < this.minHeight){
25025 minScale = this.thumbEl.getHeight() / this.minHeight;
25028 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25029 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25033 (this.rotate == 0 || this.rotate == 180) &&
25035 width > this.imageEl.OriginWidth ||
25036 height > this.imageEl.OriginHeight ||
25037 (width < this.minWidth && height < this.minHeight)
25045 (this.rotate == 90 || this.rotate == 270) &&
25047 width > this.imageEl.OriginWidth ||
25048 height > this.imageEl.OriginHeight ||
25049 (width < this.minHeight && height < this.minWidth)
25056 !this.isDocument &&
25057 (this.rotate == 0 || this.rotate == 180) &&
25059 width < this.minWidth ||
25060 width > this.imageEl.OriginWidth ||
25061 height < this.minHeight ||
25062 height > this.imageEl.OriginHeight
25069 !this.isDocument &&
25070 (this.rotate == 90 || this.rotate == 270) &&
25072 width < this.minHeight ||
25073 width > this.imageEl.OriginWidth ||
25074 height < this.minWidth ||
25075 height > this.imageEl.OriginHeight
25085 onRotateLeft : function(e)
25087 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25089 var minScale = this.thumbEl.getWidth() / this.minWidth;
25091 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25092 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25094 this.startScale = this.scale;
25096 while (this.getScaleLevel() < minScale){
25098 this.scale = this.scale + 1;
25100 if(!this.zoomable()){
25105 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25106 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25111 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25118 this.scale = this.startScale;
25120 this.onRotateFail();
25125 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25127 if(this.isDocument){
25128 this.setThumbBoxSize();
25129 this.setThumbBoxPosition();
25130 this.setCanvasPosition();
25135 this.fireEvent('rotate', this, 'left');
25139 onRotateRight : function(e)
25141 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25143 var minScale = this.thumbEl.getWidth() / this.minWidth;
25145 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25146 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25148 this.startScale = this.scale;
25150 while (this.getScaleLevel() < minScale){
25152 this.scale = this.scale + 1;
25154 if(!this.zoomable()){
25159 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25160 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25165 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25172 this.scale = this.startScale;
25174 this.onRotateFail();
25179 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25181 if(this.isDocument){
25182 this.setThumbBoxSize();
25183 this.setThumbBoxPosition();
25184 this.setCanvasPosition();
25189 this.fireEvent('rotate', this, 'right');
25192 onRotateFail : function()
25194 this.errorEl.show(true);
25198 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25203 this.previewEl.dom.innerHTML = '';
25205 var canvasEl = document.createElement("canvas");
25207 var contextEl = canvasEl.getContext("2d");
25209 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25210 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25211 var center = this.imageEl.OriginWidth / 2;
25213 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25214 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25215 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25216 center = this.imageEl.OriginHeight / 2;
25219 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25221 contextEl.translate(center, center);
25222 contextEl.rotate(this.rotate * Math.PI / 180);
25224 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25226 this.canvasEl = document.createElement("canvas");
25228 this.contextEl = this.canvasEl.getContext("2d");
25230 switch (this.rotate) {
25233 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25234 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25236 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25241 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25242 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25244 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25245 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);
25249 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25254 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25255 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25257 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25258 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);
25262 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);
25267 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25268 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25270 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25271 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25275 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);
25282 this.previewEl.appendChild(this.canvasEl);
25284 this.setCanvasPosition();
25289 if(!this.canvasLoaded){
25293 var imageCanvas = document.createElement("canvas");
25295 var imageContext = imageCanvas.getContext("2d");
25297 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25298 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25300 var center = imageCanvas.width / 2;
25302 imageContext.translate(center, center);
25304 imageContext.rotate(this.rotate * Math.PI / 180);
25306 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25308 var canvas = document.createElement("canvas");
25310 var context = canvas.getContext("2d");
25312 canvas.width = this.minWidth;
25313 canvas.height = this.minHeight;
25315 switch (this.rotate) {
25318 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25319 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25321 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25322 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25324 var targetWidth = this.minWidth - 2 * x;
25325 var targetHeight = this.minHeight - 2 * y;
25329 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25330 scale = targetWidth / width;
25333 if(x > 0 && y == 0){
25334 scale = targetHeight / height;
25337 if(x > 0 && y > 0){
25338 scale = targetWidth / width;
25340 if(width < height){
25341 scale = targetHeight / height;
25345 context.scale(scale, scale);
25347 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25348 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25350 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25351 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25353 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25358 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25359 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25361 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25362 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25364 var targetWidth = this.minWidth - 2 * x;
25365 var targetHeight = this.minHeight - 2 * y;
25369 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25370 scale = targetWidth / width;
25373 if(x > 0 && y == 0){
25374 scale = targetHeight / height;
25377 if(x > 0 && y > 0){
25378 scale = targetWidth / width;
25380 if(width < height){
25381 scale = targetHeight / height;
25385 context.scale(scale, scale);
25387 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25388 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25390 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25391 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25393 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25395 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25400 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25401 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25403 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25404 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25406 var targetWidth = this.minWidth - 2 * x;
25407 var targetHeight = this.minHeight - 2 * y;
25411 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25412 scale = targetWidth / width;
25415 if(x > 0 && y == 0){
25416 scale = targetHeight / height;
25419 if(x > 0 && y > 0){
25420 scale = targetWidth / width;
25422 if(width < height){
25423 scale = targetHeight / height;
25427 context.scale(scale, scale);
25429 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25430 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25432 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25433 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25435 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25436 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25438 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25443 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25444 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25446 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25447 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25449 var targetWidth = this.minWidth - 2 * x;
25450 var targetHeight = this.minHeight - 2 * y;
25454 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25455 scale = targetWidth / width;
25458 if(x > 0 && y == 0){
25459 scale = targetHeight / height;
25462 if(x > 0 && y > 0){
25463 scale = targetWidth / width;
25465 if(width < height){
25466 scale = targetHeight / height;
25470 context.scale(scale, scale);
25472 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25473 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25475 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25476 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25478 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25480 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25487 this.cropData = canvas.toDataURL(this.cropType);
25489 if(this.fireEvent('crop', this, this.cropData) !== false){
25490 this.process(this.file, this.cropData);
25497 setThumbBoxSize : function()
25501 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25502 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25503 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25505 this.minWidth = width;
25506 this.minHeight = height;
25508 if(this.rotate == 90 || this.rotate == 270){
25509 this.minWidth = height;
25510 this.minHeight = width;
25515 width = Math.ceil(this.minWidth * height / this.minHeight);
25517 if(this.minWidth > this.minHeight){
25519 height = Math.ceil(this.minHeight * width / this.minWidth);
25522 this.thumbEl.setStyle({
25523 width : width + 'px',
25524 height : height + 'px'
25531 setThumbBoxPosition : function()
25533 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25534 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25536 this.thumbEl.setLeft(x);
25537 this.thumbEl.setTop(y);
25541 baseRotateLevel : function()
25543 this.baseRotate = 1;
25546 typeof(this.exif) != 'undefined' &&
25547 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25548 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25550 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25553 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25557 baseScaleLevel : function()
25561 if(this.isDocument){
25563 if(this.baseRotate == 6 || this.baseRotate == 8){
25565 height = this.thumbEl.getHeight();
25566 this.baseScale = height / this.imageEl.OriginWidth;
25568 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25569 width = this.thumbEl.getWidth();
25570 this.baseScale = width / this.imageEl.OriginHeight;
25576 height = this.thumbEl.getHeight();
25577 this.baseScale = height / this.imageEl.OriginHeight;
25579 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25580 width = this.thumbEl.getWidth();
25581 this.baseScale = width / this.imageEl.OriginWidth;
25587 if(this.baseRotate == 6 || this.baseRotate == 8){
25589 width = this.thumbEl.getHeight();
25590 this.baseScale = width / this.imageEl.OriginHeight;
25592 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25593 height = this.thumbEl.getWidth();
25594 this.baseScale = height / this.imageEl.OriginHeight;
25597 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25598 height = this.thumbEl.getWidth();
25599 this.baseScale = height / this.imageEl.OriginHeight;
25601 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25602 width = this.thumbEl.getHeight();
25603 this.baseScale = width / this.imageEl.OriginWidth;
25610 width = this.thumbEl.getWidth();
25611 this.baseScale = width / this.imageEl.OriginWidth;
25613 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25614 height = this.thumbEl.getHeight();
25615 this.baseScale = height / this.imageEl.OriginHeight;
25618 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25620 height = this.thumbEl.getHeight();
25621 this.baseScale = height / this.imageEl.OriginHeight;
25623 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25624 width = this.thumbEl.getWidth();
25625 this.baseScale = width / this.imageEl.OriginWidth;
25633 getScaleLevel : function()
25635 return this.baseScale * Math.pow(1.1, this.scale);
25638 onTouchStart : function(e)
25640 if(!this.canvasLoaded){
25641 this.beforeSelectFile(e);
25645 var touches = e.browserEvent.touches;
25651 if(touches.length == 1){
25652 this.onMouseDown(e);
25656 if(touches.length != 2){
25662 for(var i = 0, finger; finger = touches[i]; i++){
25663 coords.push(finger.pageX, finger.pageY);
25666 var x = Math.pow(coords[0] - coords[2], 2);
25667 var y = Math.pow(coords[1] - coords[3], 2);
25669 this.startDistance = Math.sqrt(x + y);
25671 this.startScale = this.scale;
25673 this.pinching = true;
25674 this.dragable = false;
25678 onTouchMove : function(e)
25680 if(!this.pinching && !this.dragable){
25684 var touches = e.browserEvent.touches;
25691 this.onMouseMove(e);
25697 for(var i = 0, finger; finger = touches[i]; i++){
25698 coords.push(finger.pageX, finger.pageY);
25701 var x = Math.pow(coords[0] - coords[2], 2);
25702 var y = Math.pow(coords[1] - coords[3], 2);
25704 this.endDistance = Math.sqrt(x + y);
25706 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25708 if(!this.zoomable()){
25709 this.scale = this.startScale;
25717 onTouchEnd : function(e)
25719 this.pinching = false;
25720 this.dragable = false;
25724 process : function(file, crop)
25727 this.maskEl.mask(this.loadingText);
25730 this.xhr = new XMLHttpRequest();
25732 file.xhr = this.xhr;
25734 this.xhr.open(this.method, this.url, true);
25737 "Accept": "application/json",
25738 "Cache-Control": "no-cache",
25739 "X-Requested-With": "XMLHttpRequest"
25742 for (var headerName in headers) {
25743 var headerValue = headers[headerName];
25745 this.xhr.setRequestHeader(headerName, headerValue);
25751 this.xhr.onload = function()
25753 _this.xhrOnLoad(_this.xhr);
25756 this.xhr.onerror = function()
25758 _this.xhrOnError(_this.xhr);
25761 var formData = new FormData();
25763 formData.append('returnHTML', 'NO');
25766 formData.append('crop', crop);
25769 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25770 formData.append(this.paramName, file, file.name);
25773 if(typeof(file.filename) != 'undefined'){
25774 formData.append('filename', file.filename);
25777 if(typeof(file.mimetype) != 'undefined'){
25778 formData.append('mimetype', file.mimetype);
25781 if(this.fireEvent('arrange', this, formData) != false){
25782 this.xhr.send(formData);
25786 xhrOnLoad : function(xhr)
25789 this.maskEl.unmask();
25792 if (xhr.readyState !== 4) {
25793 this.fireEvent('exception', this, xhr);
25797 var response = Roo.decode(xhr.responseText);
25799 if(!response.success){
25800 this.fireEvent('exception', this, xhr);
25804 var response = Roo.decode(xhr.responseText);
25806 this.fireEvent('upload', this, response);
25810 xhrOnError : function()
25813 this.maskEl.unmask();
25816 Roo.log('xhr on error');
25818 var response = Roo.decode(xhr.responseText);
25824 prepare : function(file)
25827 this.maskEl.mask(this.loadingText);
25833 if(typeof(file) === 'string'){
25834 this.loadCanvas(file);
25838 if(!file || !this.urlAPI){
25843 this.cropType = file.type;
25847 if(this.fireEvent('prepare', this, this.file) != false){
25849 var reader = new FileReader();
25851 reader.onload = function (e) {
25852 if (e.target.error) {
25853 Roo.log(e.target.error);
25857 var buffer = e.target.result,
25858 dataView = new DataView(buffer),
25860 maxOffset = dataView.byteLength - 4,
25864 if (dataView.getUint16(0) === 0xffd8) {
25865 while (offset < maxOffset) {
25866 markerBytes = dataView.getUint16(offset);
25868 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25869 markerLength = dataView.getUint16(offset + 2) + 2;
25870 if (offset + markerLength > dataView.byteLength) {
25871 Roo.log('Invalid meta data: Invalid segment size.');
25875 if(markerBytes == 0xffe1){
25876 _this.parseExifData(
25883 offset += markerLength;
25893 var url = _this.urlAPI.createObjectURL(_this.file);
25895 _this.loadCanvas(url);
25900 reader.readAsArrayBuffer(this.file);
25906 parseExifData : function(dataView, offset, length)
25908 var tiffOffset = offset + 10,
25912 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25913 // No Exif data, might be XMP data instead
25917 // Check for the ASCII code for "Exif" (0x45786966):
25918 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25919 // No Exif data, might be XMP data instead
25922 if (tiffOffset + 8 > dataView.byteLength) {
25923 Roo.log('Invalid Exif data: Invalid segment size.');
25926 // Check for the two null bytes:
25927 if (dataView.getUint16(offset + 8) !== 0x0000) {
25928 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25931 // Check the byte alignment:
25932 switch (dataView.getUint16(tiffOffset)) {
25934 littleEndian = true;
25937 littleEndian = false;
25940 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25943 // Check for the TIFF tag marker (0x002A):
25944 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25945 Roo.log('Invalid Exif data: Missing TIFF marker.');
25948 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25949 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25951 this.parseExifTags(
25954 tiffOffset + dirOffset,
25959 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25964 if (dirOffset + 6 > dataView.byteLength) {
25965 Roo.log('Invalid Exif data: Invalid directory offset.');
25968 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25969 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25970 if (dirEndOffset + 4 > dataView.byteLength) {
25971 Roo.log('Invalid Exif data: Invalid directory size.');
25974 for (i = 0; i < tagsNumber; i += 1) {
25978 dirOffset + 2 + 12 * i, // tag offset
25982 // Return the offset to the next directory:
25983 return dataView.getUint32(dirEndOffset, littleEndian);
25986 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25988 var tag = dataView.getUint16(offset, littleEndian);
25990 this.exif[tag] = this.getExifValue(
25994 dataView.getUint16(offset + 2, littleEndian), // tag type
25995 dataView.getUint32(offset + 4, littleEndian), // tag length
26000 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26002 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26011 Roo.log('Invalid Exif data: Invalid tag type.');
26015 tagSize = tagType.size * length;
26016 // Determine if the value is contained in the dataOffset bytes,
26017 // or if the value at the dataOffset is a pointer to the actual data:
26018 dataOffset = tagSize > 4 ?
26019 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26020 if (dataOffset + tagSize > dataView.byteLength) {
26021 Roo.log('Invalid Exif data: Invalid data offset.');
26024 if (length === 1) {
26025 return tagType.getValue(dataView, dataOffset, littleEndian);
26028 for (i = 0; i < length; i += 1) {
26029 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26032 if (tagType.ascii) {
26034 // Concatenate the chars:
26035 for (i = 0; i < values.length; i += 1) {
26037 // Ignore the terminating NULL byte(s):
26038 if (c === '\u0000') {
26050 Roo.apply(Roo.bootstrap.UploadCropbox, {
26052 'Orientation': 0x0112
26056 1: 0, //'top-left',
26058 3: 180, //'bottom-right',
26059 // 4: 'bottom-left',
26061 6: 90, //'right-top',
26062 // 7: 'right-bottom',
26063 8: 270 //'left-bottom'
26067 // byte, 8-bit unsigned int:
26069 getValue: function (dataView, dataOffset) {
26070 return dataView.getUint8(dataOffset);
26074 // ascii, 8-bit byte:
26076 getValue: function (dataView, dataOffset) {
26077 return String.fromCharCode(dataView.getUint8(dataOffset));
26082 // short, 16 bit int:
26084 getValue: function (dataView, dataOffset, littleEndian) {
26085 return dataView.getUint16(dataOffset, littleEndian);
26089 // long, 32 bit int:
26091 getValue: function (dataView, dataOffset, littleEndian) {
26092 return dataView.getUint32(dataOffset, littleEndian);
26096 // rational = two long values, first is numerator, second is denominator:
26098 getValue: function (dataView, dataOffset, littleEndian) {
26099 return dataView.getUint32(dataOffset, littleEndian) /
26100 dataView.getUint32(dataOffset + 4, littleEndian);
26104 // slong, 32 bit signed int:
26106 getValue: function (dataView, dataOffset, littleEndian) {
26107 return dataView.getInt32(dataOffset, littleEndian);
26111 // srational, two slongs, first is numerator, second is denominator:
26113 getValue: function (dataView, dataOffset, littleEndian) {
26114 return dataView.getInt32(dataOffset, littleEndian) /
26115 dataView.getInt32(dataOffset + 4, littleEndian);
26125 cls : 'btn-group roo-upload-cropbox-rotate-left',
26126 action : 'rotate-left',
26130 cls : 'btn btn-default',
26131 html : '<i class="fa fa-undo"></i>'
26137 cls : 'btn-group roo-upload-cropbox-picture',
26138 action : 'picture',
26142 cls : 'btn btn-default',
26143 html : '<i class="fa fa-picture-o"></i>'
26149 cls : 'btn-group roo-upload-cropbox-rotate-right',
26150 action : 'rotate-right',
26154 cls : 'btn btn-default',
26155 html : '<i class="fa fa-repeat"></i>'
26163 cls : 'btn-group roo-upload-cropbox-rotate-left',
26164 action : 'rotate-left',
26168 cls : 'btn btn-default',
26169 html : '<i class="fa fa-undo"></i>'
26175 cls : 'btn-group roo-upload-cropbox-download',
26176 action : 'download',
26180 cls : 'btn btn-default',
26181 html : '<i class="fa fa-download"></i>'
26187 cls : 'btn-group roo-upload-cropbox-crop',
26192 cls : 'btn btn-default',
26193 html : '<i class="fa fa-crop"></i>'
26199 cls : 'btn-group roo-upload-cropbox-trash',
26204 cls : 'btn btn-default',
26205 html : '<i class="fa fa-trash"></i>'
26211 cls : 'btn-group roo-upload-cropbox-rotate-right',
26212 action : 'rotate-right',
26216 cls : 'btn btn-default',
26217 html : '<i class="fa fa-repeat"></i>'
26225 cls : 'btn-group roo-upload-cropbox-rotate-left',
26226 action : 'rotate-left',
26230 cls : 'btn btn-default',
26231 html : '<i class="fa fa-undo"></i>'
26237 cls : 'btn-group roo-upload-cropbox-rotate-right',
26238 action : 'rotate-right',
26242 cls : 'btn btn-default',
26243 html : '<i class="fa fa-repeat"></i>'
26256 * @class Roo.bootstrap.DocumentManager
26257 * @extends Roo.bootstrap.Component
26258 * Bootstrap DocumentManager class
26259 * @cfg {String} paramName default 'imageUpload'
26260 * @cfg {String} method default POST
26261 * @cfg {String} url action url
26262 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26263 * @cfg {Boolean} multiple multiple upload default true
26264 * @cfg {Number} thumbSize default 300
26265 * @cfg {String} fieldLabel
26266 * @cfg {Number} labelWidth default 4
26267 * @cfg {String} labelAlign (left|top) default left
26268 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26271 * Create a new DocumentManager
26272 * @param {Object} config The config object
26275 Roo.bootstrap.DocumentManager = function(config){
26276 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26281 * Fire when initial the DocumentManager
26282 * @param {Roo.bootstrap.DocumentManager} this
26287 * inspect selected file
26288 * @param {Roo.bootstrap.DocumentManager} this
26289 * @param {File} file
26294 * Fire when xhr load exception
26295 * @param {Roo.bootstrap.DocumentManager} this
26296 * @param {XMLHttpRequest} xhr
26298 "exception" : true,
26301 * prepare the form data
26302 * @param {Roo.bootstrap.DocumentManager} this
26303 * @param {Object} formData
26308 * Fire when remove the file
26309 * @param {Roo.bootstrap.DocumentManager} this
26310 * @param {Object} file
26315 * Fire after refresh the file
26316 * @param {Roo.bootstrap.DocumentManager} this
26321 * Fire after click the image
26322 * @param {Roo.bootstrap.DocumentManager} this
26323 * @param {Object} file
26328 * Fire when upload a image and editable set to true
26329 * @param {Roo.bootstrap.DocumentManager} this
26330 * @param {Object} file
26334 * @event beforeselectfile
26335 * Fire before select file
26336 * @param {Roo.bootstrap.DocumentManager} this
26338 "beforeselectfile" : true,
26341 * Fire before process file
26342 * @param {Roo.bootstrap.DocumentManager} this
26343 * @param {Object} file
26350 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26359 paramName : 'imageUpload',
26362 labelAlign : 'left',
26369 getAutoCreate : function()
26371 var managerWidget = {
26373 cls : 'roo-document-manager',
26377 cls : 'roo-document-manager-selector',
26382 cls : 'roo-document-manager-uploader',
26386 cls : 'roo-document-manager-upload-btn',
26387 html : '<i class="fa fa-plus"></i>'
26398 cls : 'column col-md-12',
26403 if(this.fieldLabel.length){
26408 cls : 'column col-md-12',
26409 html : this.fieldLabel
26413 cls : 'column col-md-12',
26418 if(this.labelAlign == 'left'){
26422 cls : 'column col-md-' + this.labelWidth,
26423 html : this.fieldLabel
26427 cls : 'column col-md-' + (12 - this.labelWidth),
26437 cls : 'row clearfix',
26445 initEvents : function()
26447 this.managerEl = this.el.select('.roo-document-manager', true).first();
26448 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26450 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26451 this.selectorEl.hide();
26454 this.selectorEl.attr('multiple', 'multiple');
26457 this.selectorEl.on('change', this.onFileSelected, this);
26459 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26460 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26462 this.uploader.on('click', this.onUploaderClick, this);
26464 this.renderProgressDialog();
26468 window.addEventListener("resize", function() { _this.refresh(); } );
26470 this.fireEvent('initial', this);
26473 renderProgressDialog : function()
26477 this.progressDialog = new Roo.bootstrap.Modal({
26478 cls : 'roo-document-manager-progress-dialog',
26479 allow_close : false,
26489 btnclick : function() {
26490 _this.uploadCancel();
26496 this.progressDialog.render(Roo.get(document.body));
26498 this.progress = new Roo.bootstrap.Progress({
26499 cls : 'roo-document-manager-progress',
26504 this.progress.render(this.progressDialog.getChildContainer());
26506 this.progressBar = new Roo.bootstrap.ProgressBar({
26507 cls : 'roo-document-manager-progress-bar',
26510 aria_valuemax : 12,
26514 this.progressBar.render(this.progress.getChildContainer());
26517 onUploaderClick : function(e)
26519 e.preventDefault();
26521 if(this.fireEvent('beforeselectfile', this) != false){
26522 this.selectorEl.dom.click();
26527 onFileSelected : function(e)
26529 e.preventDefault();
26531 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26535 Roo.each(this.selectorEl.dom.files, function(file){
26536 if(this.fireEvent('inspect', this, file) != false){
26537 this.files.push(file);
26547 this.selectorEl.dom.value = '';
26549 if(!this.files.length){
26553 if(this.boxes > 0 && this.files.length > this.boxes){
26554 this.files = this.files.slice(0, this.boxes);
26557 this.uploader.show();
26559 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26560 this.uploader.hide();
26569 Roo.each(this.files, function(file){
26571 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26572 var f = this.renderPreview(file);
26577 if(file.type.indexOf('image') != -1){
26578 this.delegates.push(
26580 _this.process(file);
26581 }).createDelegate(this)
26589 _this.process(file);
26590 }).createDelegate(this)
26595 this.files = files;
26597 this.delegates = this.delegates.concat(docs);
26599 if(!this.delegates.length){
26604 this.progressBar.aria_valuemax = this.delegates.length;
26611 arrange : function()
26613 if(!this.delegates.length){
26614 this.progressDialog.hide();
26619 var delegate = this.delegates.shift();
26621 this.progressDialog.show();
26623 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26625 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26630 refresh : function()
26632 this.uploader.show();
26634 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26635 this.uploader.hide();
26638 Roo.isTouch ? this.closable(false) : this.closable(true);
26640 this.fireEvent('refresh', this);
26643 onRemove : function(e, el, o)
26645 e.preventDefault();
26647 this.fireEvent('remove', this, o);
26651 remove : function(o)
26655 Roo.each(this.files, function(file){
26656 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26665 this.files = files;
26672 Roo.each(this.files, function(file){
26677 file.target.remove();
26686 onClick : function(e, el, o)
26688 e.preventDefault();
26690 this.fireEvent('click', this, o);
26694 closable : function(closable)
26696 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26698 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26710 xhrOnLoad : function(xhr)
26712 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26716 if (xhr.readyState !== 4) {
26718 this.fireEvent('exception', this, xhr);
26722 var response = Roo.decode(xhr.responseText);
26724 if(!response.success){
26726 this.fireEvent('exception', this, xhr);
26730 var file = this.renderPreview(response.data);
26732 this.files.push(file);
26738 xhrOnError : function()
26740 Roo.log('xhr on error');
26742 var response = Roo.decode(xhr.responseText);
26749 process : function(file)
26751 if(this.fireEvent('process', this, file) !== false){
26752 if(this.editable && file.type.indexOf('image') != -1){
26753 this.fireEvent('edit', this, file);
26757 this.uploadStart(file, false);
26764 uploadStart : function(file, crop)
26766 this.xhr = new XMLHttpRequest();
26768 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26773 file.xhr = this.xhr;
26775 this.managerEl.createChild({
26777 cls : 'roo-document-manager-loading',
26781 tooltip : file.name,
26782 cls : 'roo-document-manager-thumb',
26783 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26789 this.xhr.open(this.method, this.url, true);
26792 "Accept": "application/json",
26793 "Cache-Control": "no-cache",
26794 "X-Requested-With": "XMLHttpRequest"
26797 for (var headerName in headers) {
26798 var headerValue = headers[headerName];
26800 this.xhr.setRequestHeader(headerName, headerValue);
26806 this.xhr.onload = function()
26808 _this.xhrOnLoad(_this.xhr);
26811 this.xhr.onerror = function()
26813 _this.xhrOnError(_this.xhr);
26816 var formData = new FormData();
26818 formData.append('returnHTML', 'NO');
26821 formData.append('crop', crop);
26824 formData.append(this.paramName, file, file.name);
26826 if(this.fireEvent('prepare', this, formData) != false){
26827 this.xhr.send(formData);
26831 uploadCancel : function()
26838 this.delegates = [];
26840 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26847 renderPreview : function(file)
26849 if(typeof(file.target) != 'undefined' && file.target){
26853 var previewEl = this.managerEl.createChild({
26855 cls : 'roo-document-manager-preview',
26859 tooltip : file.filename,
26860 cls : 'roo-document-manager-thumb',
26861 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26866 html : '<i class="fa fa-times-circle"></i>'
26871 var close = previewEl.select('button.close', true).first();
26873 close.on('click', this.onRemove, this, file);
26875 file.target = previewEl;
26877 var image = previewEl.select('img', true).first();
26881 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26883 image.on('click', this.onClick, this, file);
26889 onPreviewLoad : function(file, image)
26891 if(typeof(file.target) == 'undefined' || !file.target){
26895 var width = image.dom.naturalWidth || image.dom.width;
26896 var height = image.dom.naturalHeight || image.dom.height;
26898 if(width > height){
26899 file.target.addClass('wide');
26903 file.target.addClass('tall');
26908 uploadFromSource : function(file, crop)
26910 this.xhr = new XMLHttpRequest();
26912 this.managerEl.createChild({
26914 cls : 'roo-document-manager-loading',
26918 tooltip : file.name,
26919 cls : 'roo-document-manager-thumb',
26920 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26926 this.xhr.open(this.method, this.url, true);
26929 "Accept": "application/json",
26930 "Cache-Control": "no-cache",
26931 "X-Requested-With": "XMLHttpRequest"
26934 for (var headerName in headers) {
26935 var headerValue = headers[headerName];
26937 this.xhr.setRequestHeader(headerName, headerValue);
26943 this.xhr.onload = function()
26945 _this.xhrOnLoad(_this.xhr);
26948 this.xhr.onerror = function()
26950 _this.xhrOnError(_this.xhr);
26953 var formData = new FormData();
26955 formData.append('returnHTML', 'NO');
26957 formData.append('crop', crop);
26959 if(typeof(file.filename) != 'undefined'){
26960 formData.append('filename', file.filename);
26963 if(typeof(file.mimetype) != 'undefined'){
26964 formData.append('mimetype', file.mimetype);
26967 if(this.fireEvent('prepare', this, formData) != false){
26968 this.xhr.send(formData);
26978 * @class Roo.bootstrap.DocumentViewer
26979 * @extends Roo.bootstrap.Component
26980 * Bootstrap DocumentViewer class
26983 * Create a new DocumentViewer
26984 * @param {Object} config The config object
26987 Roo.bootstrap.DocumentViewer = function(config){
26988 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26993 * Fire after initEvent
26994 * @param {Roo.bootstrap.DocumentViewer} this
27000 * @param {Roo.bootstrap.DocumentViewer} this
27005 * Fire after trash button
27006 * @param {Roo.bootstrap.DocumentViewer} this
27013 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27015 getAutoCreate : function()
27019 cls : 'roo-document-viewer',
27023 cls : 'roo-document-viewer-body',
27027 cls : 'roo-document-viewer-thumb',
27031 cls : 'roo-document-viewer-image'
27039 cls : 'roo-document-viewer-footer',
27042 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27050 cls : 'btn btn-default roo-document-viewer-trash',
27051 html : '<i class="fa fa-trash"></i>'
27064 initEvents : function()
27067 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27068 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27071 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27074 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27077 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27080 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27082 this.bodyEl.on('click', this.onClick, this);
27084 this.trashBtn.on('click', this.onTrash, this);
27088 initial : function()
27090 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27093 this.fireEvent('initial', this);
27097 onClick : function(e)
27099 e.preventDefault();
27101 this.fireEvent('click', this);
27104 onTrash : function(e)
27106 e.preventDefault();
27108 this.fireEvent('trash', this);
27120 * @class Roo.bootstrap.NavProgressBar
27121 * @extends Roo.bootstrap.Component
27122 * Bootstrap NavProgressBar class
27125 * Create a new nav progress bar
27126 * @param {Object} config The config object
27129 Roo.bootstrap.NavProgressBar = function(config){
27130 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27132 this.bullets = this.bullets || [];
27134 // Roo.bootstrap.NavProgressBar.register(this);
27138 * Fires when the active item changes
27139 * @param {Roo.bootstrap.NavProgressBar} this
27140 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27141 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27148 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27153 getAutoCreate : function()
27155 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27159 cls : 'roo-navigation-bar-group',
27163 cls : 'roo-navigation-top-bar'
27167 cls : 'roo-navigation-bullets-bar',
27171 cls : 'roo-navigation-bar'
27178 cls : 'roo-navigation-bottom-bar'
27188 initEvents: function()
27193 onRender : function(ct, position)
27195 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27197 if(this.bullets.length){
27198 Roo.each(this.bullets, function(b){
27207 addItem : function(cfg)
27209 var item = new Roo.bootstrap.NavProgressItem(cfg);
27211 item.parentId = this.id;
27212 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27215 var top = new Roo.bootstrap.Element({
27217 cls : 'roo-navigation-bar-text'
27220 var bottom = new Roo.bootstrap.Element({
27222 cls : 'roo-navigation-bar-text'
27225 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27226 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27228 var topText = new Roo.bootstrap.Element({
27230 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27233 var bottomText = new Roo.bootstrap.Element({
27235 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27238 topText.onRender(top.el, null);
27239 bottomText.onRender(bottom.el, null);
27242 item.bottomEl = bottom;
27245 this.barItems.push(item);
27250 getActive : function()
27252 var active = false;
27254 Roo.each(this.barItems, function(v){
27256 if (!v.isActive()) {
27268 setActiveItem : function(item)
27272 Roo.each(this.barItems, function(v){
27273 if (v.rid == item.rid) {
27277 if (v.isActive()) {
27278 v.setActive(false);
27283 item.setActive(true);
27285 this.fireEvent('changed', this, item, prev);
27288 getBarItem: function(rid)
27292 Roo.each(this.barItems, function(e) {
27293 if (e.rid != rid) {
27304 indexOfItem : function(item)
27308 Roo.each(this.barItems, function(v, i){
27310 if (v.rid != item.rid) {
27321 setActiveNext : function()
27323 var i = this.indexOfItem(this.getActive());
27325 if (i > this.barItems.length) {
27329 this.setActiveItem(this.barItems[i+1]);
27332 setActivePrev : function()
27334 var i = this.indexOfItem(this.getActive());
27340 this.setActiveItem(this.barItems[i-1]);
27343 format : function()
27345 if(!this.barItems.length){
27349 var width = 100 / this.barItems.length;
27351 Roo.each(this.barItems, function(i){
27352 i.el.setStyle('width', width + '%');
27353 i.topEl.el.setStyle('width', width + '%');
27354 i.bottomEl.el.setStyle('width', width + '%');
27363 * Nav Progress Item
27368 * @class Roo.bootstrap.NavProgressItem
27369 * @extends Roo.bootstrap.Component
27370 * Bootstrap NavProgressItem class
27371 * @cfg {String} rid the reference id
27372 * @cfg {Boolean} active (true|false) Is item active default false
27373 * @cfg {Boolean} disabled (true|false) Is item active default false
27374 * @cfg {String} html
27375 * @cfg {String} position (top|bottom) text position default bottom
27376 * @cfg {String} icon show icon instead of number
27379 * Create a new NavProgressItem
27380 * @param {Object} config The config object
27382 Roo.bootstrap.NavProgressItem = function(config){
27383 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27388 * The raw click event for the entire grid.
27389 * @param {Roo.bootstrap.NavProgressItem} this
27390 * @param {Roo.EventObject} e
27397 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27403 position : 'bottom',
27406 getAutoCreate : function()
27408 var iconCls = 'roo-navigation-bar-item-icon';
27410 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27414 cls: 'roo-navigation-bar-item',
27424 cfg.cls += ' active';
27427 cfg.cls += ' disabled';
27433 disable : function()
27435 this.setDisabled(true);
27438 enable : function()
27440 this.setDisabled(false);
27443 initEvents: function()
27445 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27447 this.iconEl.on('click', this.onClick, this);
27450 onClick : function(e)
27452 e.preventDefault();
27458 if(this.fireEvent('click', this, e) === false){
27462 this.parent().setActiveItem(this);
27465 isActive: function ()
27467 return this.active;
27470 setActive : function(state)
27472 if(this.active == state){
27476 this.active = state;
27479 this.el.addClass('active');
27483 this.el.removeClass('active');
27488 setDisabled : function(state)
27490 if(this.disabled == state){
27494 this.disabled = state;
27497 this.el.addClass('disabled');
27501 this.el.removeClass('disabled');
27504 tooltipEl : function()
27506 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27519 * @class Roo.bootstrap.FieldLabel
27520 * @extends Roo.bootstrap.Component
27521 * Bootstrap FieldLabel class
27522 * @cfg {String} html contents of the element
27523 * @cfg {String} tag tag of the element default label
27524 * @cfg {String} cls class of the element
27525 * @cfg {String} target label target
27526 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27527 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27528 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27529 * @cfg {String} iconTooltip default "This field is required"
27532 * Create a new FieldLabel
27533 * @param {Object} config The config object
27536 Roo.bootstrap.FieldLabel = function(config){
27537 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27542 * Fires after the field has been marked as invalid.
27543 * @param {Roo.form.FieldLabel} this
27544 * @param {String} msg The validation message
27549 * Fires after the field has been validated with no errors.
27550 * @param {Roo.form.FieldLabel} this
27556 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27563 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27564 validClass : 'text-success fa fa-lg fa-check',
27565 iconTooltip : 'This field is required',
27567 getAutoCreate : function(){
27571 cls : 'roo-bootstrap-field-label ' + this.cls,
27577 tooltip : this.iconTooltip
27589 initEvents: function()
27591 Roo.bootstrap.Element.superclass.initEvents.call(this);
27593 this.iconEl = this.el.select('i', true).first();
27595 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27597 Roo.bootstrap.FieldLabel.register(this);
27601 * Mark this field as valid
27603 markValid : function()
27605 this.iconEl.show();
27607 this.iconEl.removeClass(this.invalidClass);
27609 this.iconEl.addClass(this.validClass);
27611 this.fireEvent('valid', this);
27615 * Mark this field as invalid
27616 * @param {String} msg The validation message
27618 markInvalid : function(msg)
27620 this.iconEl.show();
27622 this.iconEl.removeClass(this.validClass);
27624 this.iconEl.addClass(this.invalidClass);
27626 this.fireEvent('invalid', this, msg);
27632 Roo.apply(Roo.bootstrap.FieldLabel, {
27637 * register a FieldLabel Group
27638 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27640 register : function(label)
27642 if(this.groups.hasOwnProperty(label.target)){
27646 this.groups[label.target] = label;
27650 * fetch a FieldLabel Group based on the target
27651 * @param {string} target
27652 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27654 get: function(target) {
27655 if (typeof(this.groups[target]) == 'undefined') {
27659 return this.groups[target] ;
27668 * page DateSplitField.
27674 * @class Roo.bootstrap.DateSplitField
27675 * @extends Roo.bootstrap.Component
27676 * Bootstrap DateSplitField class
27677 * @cfg {string} fieldLabel - the label associated
27678 * @cfg {Number} labelWidth set the width of label (0-12)
27679 * @cfg {String} labelAlign (top|left)
27680 * @cfg {Boolean} dayAllowBlank (true|false) default false
27681 * @cfg {Boolean} monthAllowBlank (true|false) default false
27682 * @cfg {Boolean} yearAllowBlank (true|false) default false
27683 * @cfg {string} dayPlaceholder
27684 * @cfg {string} monthPlaceholder
27685 * @cfg {string} yearPlaceholder
27686 * @cfg {string} dayFormat default 'd'
27687 * @cfg {string} monthFormat default 'm'
27688 * @cfg {string} yearFormat default 'Y'
27692 * Create a new DateSplitField
27693 * @param {Object} config The config object
27696 Roo.bootstrap.DateSplitField = function(config){
27697 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27703 * getting the data of years
27704 * @param {Roo.bootstrap.DateSplitField} this
27705 * @param {Object} years
27710 * getting the data of days
27711 * @param {Roo.bootstrap.DateSplitField} this
27712 * @param {Object} days
27717 * Fires after the field has been marked as invalid.
27718 * @param {Roo.form.Field} this
27719 * @param {String} msg The validation message
27724 * Fires after the field has been validated with no errors.
27725 * @param {Roo.form.Field} this
27731 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27734 labelAlign : 'top',
27736 dayAllowBlank : false,
27737 monthAllowBlank : false,
27738 yearAllowBlank : false,
27739 dayPlaceholder : '',
27740 monthPlaceholder : '',
27741 yearPlaceholder : '',
27745 isFormField : true,
27747 getAutoCreate : function()
27751 cls : 'row roo-date-split-field-group',
27756 cls : 'form-hidden-field roo-date-split-field-group-value',
27762 if(this.fieldLabel){
27765 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27769 html : this.fieldLabel
27775 Roo.each(['day', 'month', 'year'], function(t){
27778 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27785 inputEl: function ()
27787 return this.el.select('.roo-date-split-field-group-value', true).first();
27790 onRender : function(ct, position)
27794 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27796 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27798 this.dayField = new Roo.bootstrap.ComboBox({
27799 allowBlank : this.dayAllowBlank,
27800 alwaysQuery : true,
27801 displayField : 'value',
27804 forceSelection : true,
27806 placeholder : this.dayPlaceholder,
27807 selectOnFocus : true,
27808 tpl : '<div class="select2-result"><b>{value}</b></div>',
27809 triggerAction : 'all',
27811 valueField : 'value',
27812 store : new Roo.data.SimpleStore({
27813 data : (function() {
27815 _this.fireEvent('days', _this, days);
27818 fields : [ 'value' ]
27821 select : function (_self, record, index)
27823 _this.setValue(_this.getValue());
27828 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27830 this.monthField = new Roo.bootstrap.MonthField({
27831 after : '<i class=\"fa fa-calendar\"></i>',
27832 allowBlank : this.monthAllowBlank,
27833 placeholder : this.monthPlaceholder,
27836 render : function (_self)
27838 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27839 e.preventDefault();
27843 select : function (_self, oldvalue, newvalue)
27845 _this.setValue(_this.getValue());
27850 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27852 this.yearField = new Roo.bootstrap.ComboBox({
27853 allowBlank : this.yearAllowBlank,
27854 alwaysQuery : true,
27855 displayField : 'value',
27858 forceSelection : true,
27860 placeholder : this.yearPlaceholder,
27861 selectOnFocus : true,
27862 tpl : '<div class="select2-result"><b>{value}</b></div>',
27863 triggerAction : 'all',
27865 valueField : 'value',
27866 store : new Roo.data.SimpleStore({
27867 data : (function() {
27869 _this.fireEvent('years', _this, years);
27872 fields : [ 'value' ]
27875 select : function (_self, record, index)
27877 _this.setValue(_this.getValue());
27882 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27885 setValue : function(v, format)
27887 this.inputEl.dom.value = v;
27889 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27891 var d = Date.parseDate(v, f);
27898 this.setDay(d.format(this.dayFormat));
27899 this.setMonth(d.format(this.monthFormat));
27900 this.setYear(d.format(this.yearFormat));
27907 setDay : function(v)
27909 this.dayField.setValue(v);
27910 this.inputEl.dom.value = this.getValue();
27915 setMonth : function(v)
27917 this.monthField.setValue(v, true);
27918 this.inputEl.dom.value = this.getValue();
27923 setYear : function(v)
27925 this.yearField.setValue(v);
27926 this.inputEl.dom.value = this.getValue();
27931 getDay : function()
27933 return this.dayField.getValue();
27936 getMonth : function()
27938 return this.monthField.getValue();
27941 getYear : function()
27943 return this.yearField.getValue();
27946 getValue : function()
27948 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27950 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27960 this.inputEl.dom.value = '';
27965 validate : function()
27967 var d = this.dayField.validate();
27968 var m = this.monthField.validate();
27969 var y = this.yearField.validate();
27974 (!this.dayAllowBlank && !d) ||
27975 (!this.monthAllowBlank && !m) ||
27976 (!this.yearAllowBlank && !y)
27981 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27990 this.markInvalid();
27995 markValid : function()
27998 var label = this.el.select('label', true).first();
27999 var icon = this.el.select('i.fa-star', true).first();
28005 this.fireEvent('valid', this);
28009 * Mark this field as invalid
28010 * @param {String} msg The validation message
28012 markInvalid : function(msg)
28015 var label = this.el.select('label', true).first();
28016 var icon = this.el.select('i.fa-star', true).first();
28018 if(label && !icon){
28019 this.el.select('.roo-date-split-field-label', true).createChild({
28021 cls : 'text-danger fa fa-lg fa-star',
28022 tooltip : 'This field is required',
28023 style : 'margin-right:5px;'
28027 this.fireEvent('invalid', this, msg);
28030 clearInvalid : function()
28032 var label = this.el.select('label', true).first();
28033 var icon = this.el.select('i.fa-star', true).first();
28039 this.fireEvent('valid', this);
28042 getName: function()